mirror of
https://github.com/balzack/databag.git
synced 2025-02-12 03:29:16 +00:00
adding channel summary method
This commit is contained in:
parent
0416389be0
commit
8e14f51d58
39
doc/api.oa3
39
doc/api.oa3
@ -2144,6 +2144,37 @@ paths:
|
|||||||
'500':
|
'500':
|
||||||
description: internal server error
|
description: internal server error
|
||||||
|
|
||||||
|
/content/channels/{channelId}/summary:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- content
|
||||||
|
description: Get summary of channel.
|
||||||
|
operationId: get-channel-summary
|
||||||
|
security:
|
||||||
|
- bearerAuth: []
|
||||||
|
parameters:
|
||||||
|
- name: channelId
|
||||||
|
in: path
|
||||||
|
description: specified channel id
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: success
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ChannelSummary'
|
||||||
|
'401':
|
||||||
|
description: invalid password
|
||||||
|
'404':
|
||||||
|
description: channel not found
|
||||||
|
'410':
|
||||||
|
description: account disabled
|
||||||
|
'500':
|
||||||
|
description: internal server error
|
||||||
|
|
||||||
/content/channels/{channelId}:
|
/content/channels/{channelId}:
|
||||||
delete:
|
delete:
|
||||||
tags:
|
tags:
|
||||||
@ -3439,6 +3470,8 @@ components:
|
|||||||
topicRevision:
|
topicRevision:
|
||||||
type: integer
|
type: integer
|
||||||
format: int64
|
format: int64
|
||||||
|
channelSummary:
|
||||||
|
$ref: '#/components/schemas/ChannelSummary'
|
||||||
channelDetail:
|
channelDetail:
|
||||||
$ref: '#/components/schemas/ChannelDetail'
|
$ref: '#/components/schemas/ChannelDetail'
|
||||||
|
|
||||||
@ -3466,6 +3499,12 @@ components:
|
|||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
type: string
|
type: string
|
||||||
|
|
||||||
|
ChannelSummary:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
lastTopic:
|
||||||
|
$ref: '#/components/schemas/TopicDetail'
|
||||||
|
|
||||||
ChannelContacts:
|
ChannelContacts:
|
||||||
type: object
|
type: object
|
||||||
|
63
net/server/internal/api_getChannelDetail.go
Normal file
63
net/server/internal/api_getChannelDetail.go
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
package databag
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"databag/internal/store"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetChannelDetail(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
// scan parameters
|
||||||
|
params := mux.Vars(r)
|
||||||
|
channelId := params["channelId"]
|
||||||
|
|
||||||
|
var guid string
|
||||||
|
var act *store.Account
|
||||||
|
tokenType := ParamTokenType(r)
|
||||||
|
if tokenType == APP_TOKENAGENT {
|
||||||
|
account, code, err := ParamAgentToken(r, false);
|
||||||
|
if err != nil {
|
||||||
|
ErrResponse(w, code, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
act = account
|
||||||
|
} else if tokenType == APP_TOKENCONTACT {
|
||||||
|
card, code, err := ParamContactToken(r, true)
|
||||||
|
if err != nil {
|
||||||
|
ErrResponse(w, code, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
act = &card.Account
|
||||||
|
guid = card.Guid
|
||||||
|
} else {
|
||||||
|
ErrResponse(w, http.StatusBadRequest, errors.New("unknown token type"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// load channel
|
||||||
|
var slot store.ChannelSlot
|
||||||
|
if err := store.DB.Preload("Channel.Cards.CardSlot").Preload("Channel.Groups.Cards").Preload("Channel.Groups.GroupSlot").Where("account_id = ? AND channel_slot_id = ?", act.ID, channelId).First(&slot).Error; err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
ErrResponse(w, http.StatusNotFound, err)
|
||||||
|
} else {
|
||||||
|
ErrResponse(w, http.StatusInternalServerError, err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// return model data
|
||||||
|
if guid != "" {
|
||||||
|
if isChannelShared(guid, slot.Channel) {
|
||||||
|
WriteResponse(w, getChannelDetailModel(&slot, false))
|
||||||
|
} else {
|
||||||
|
ErrResponse(w, http.StatusNotFound, errors.New("channel not shared with requestor"));
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
WriteResponse(w, getChannelDetailModel(&slot, true))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
65
net/server/internal/api_getChannelSummary.go
Normal file
65
net/server/internal/api_getChannelSummary.go
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
package databag
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"databag/internal/store"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetChannelSummary(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
// scan parameters
|
||||||
|
params := mux.Vars(r)
|
||||||
|
channelId := params["channelId"]
|
||||||
|
|
||||||
|
var guid string
|
||||||
|
var act *store.Account
|
||||||
|
tokenType := ParamTokenType(r)
|
||||||
|
if tokenType == APP_TOKENAGENT {
|
||||||
|
account, code, err := ParamAgentToken(r, false);
|
||||||
|
if err != nil {
|
||||||
|
ErrResponse(w, code, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
act = account
|
||||||
|
} else if tokenType == APP_TOKENCONTACT {
|
||||||
|
card, code, err := ParamContactToken(r, true)
|
||||||
|
if err != nil {
|
||||||
|
ErrResponse(w, code, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
act = &card.Account
|
||||||
|
guid = card.Guid
|
||||||
|
} else {
|
||||||
|
ErrResponse(w, http.StatusBadRequest, errors.New("unknown token type"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// load channel
|
||||||
|
var slot store.ChannelSlot
|
||||||
|
if err := store.DB.Preload("Channel.Topics", func(db *gorm.DB) *gorm.DB {
|
||||||
|
return store.DB.Order("topics.id DESC").Limit(1)
|
||||||
|
}).Preload("Channel.Cards.CardSlot").Preload("Channel.Groups.Cards").Preload("Channel.Groups.GroupSlot").Where("account_id = ? AND channel_slot_id = ?", act.ID, channelId).First(&slot).Error; err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
ErrResponse(w, http.StatusNotFound, err)
|
||||||
|
} else {
|
||||||
|
ErrResponse(w, http.StatusInternalServerError, err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// return model data
|
||||||
|
if guid != "" {
|
||||||
|
if isChannelShared(guid, slot.Channel) {
|
||||||
|
WriteResponse(w, getChannelSummaryModel(&slot))
|
||||||
|
} else {
|
||||||
|
ErrResponse(w, http.StatusNotFound, errors.New("channel not shared with requestor"));
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
WriteResponse(w, getChannelSummaryModel(&slot))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -66,7 +66,9 @@ func GetChannels(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if err := store.DB.Preload("Channel.Cards.CardSlot").Preload("Channel.Groups.GroupSlot").Where("account_id = ? AND channel_id != 0", account.ID).Find(&slots).Error; err != nil {
|
if err := store.DB.Preload("Channel.Topics", func(db *gorm.DB) *gorm.DB {
|
||||||
|
return store.DB.Order("topics.id DESC").Limit(1)
|
||||||
|
}).Preload("Channel.Cards.CardSlot").Preload("Channel.Groups.GroupSlot").Where("account_id = ? AND channel_id != 0", account.ID).Find(&slots).Error; err != nil {
|
||||||
ErrResponse(w, http.StatusInternalServerError, err)
|
ErrResponse(w, http.StatusInternalServerError, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -102,14 +104,14 @@ func GetChannels(w http.ResponseWriter, r *http.Request) {
|
|||||||
account := &card.Account
|
account := &card.Account
|
||||||
var slots []store.ChannelSlot
|
var slots []store.ChannelSlot
|
||||||
if channelRevisionSet {
|
if channelRevisionSet {
|
||||||
if err := store.DB.Preload("Channel.Topics", func(db *gorm.DB) *gorm.DB {
|
if err := store.DB.Preload("Channel.Cards").Preload("Channel.Groups.Cards").Where("account_id = ? AND revision > ?", account.ID, channelRevision).Find(&slots).Error; err != nil {
|
||||||
return store.DB.Order("topics.id DESC").Limit(1)
|
|
||||||
}).Preload("Channel.Cards").Preload("Channel.Groups.Cards").Where("account_id = ? AND revision > ?", account.ID, channelRevision).Find(&slots).Error; err != nil {
|
|
||||||
ErrResponse(w, http.StatusInternalServerError, err)
|
ErrResponse(w, http.StatusInternalServerError, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if err := store.DB.Preload("Channel.Cards").Preload("Channel.Groups.Cards").Where("account_id = ? AND channel_id != 0", account.ID).Find(&slots).Error; err != nil {
|
if err := store.DB.Preload("Channel.Topics", func(db *gorm.DB) *gorm.DB {
|
||||||
|
return store.DB.Order("topics.id DESC").Limit(1)
|
||||||
|
}).Preload("Channel.Cards").Preload("Channel.Groups.Cards").Where("account_id = ? AND channel_id != 0", account.ID).Find(&slots).Error; err != nil {
|
||||||
ErrResponse(w, http.StatusInternalServerError, err)
|
ErrResponse(w, http.StatusInternalServerError, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -199,6 +199,27 @@ func getChannelDetailModel(slot *store.ChannelSlot, showList bool) *ChannelDetai
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getChannelSummaryModel(slot *store.ChannelSlot) *ChannelSummary {
|
||||||
|
|
||||||
|
if slot.Channel == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
topicDetail := TopicDetail{};
|
||||||
|
if len(slot.Channel.Topics) > 0 {
|
||||||
|
topicDetail.Guid = slot.Channel.Topics[0].Guid;
|
||||||
|
topicDetail.DataType = slot.Channel.Topics[0].DataType;
|
||||||
|
topicDetail.Data = slot.Channel.Topics[0].Data;
|
||||||
|
topicDetail.Created = slot.Channel.Topics[0].Created;
|
||||||
|
topicDetail.Updated = slot.Channel.Topics[0].Updated;
|
||||||
|
topicDetail.Status = slot.Channel.Topics[0].Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ChannelSummary{
|
||||||
|
LastTopic: &topicDetail,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func getChannelModel(slot *store.ChannelSlot, showData bool, showList bool) *Channel {
|
func getChannelModel(slot *store.ChannelSlot, showData bool, showList bool) *Channel {
|
||||||
|
|
||||||
if !showData || slot.Channel == nil {
|
if !showData || slot.Channel == nil {
|
||||||
@ -215,6 +236,7 @@ func getChannelModel(slot *store.ChannelSlot, showData bool, showList bool) *Cha
|
|||||||
DetailRevision: slot.Channel.DetailRevision,
|
DetailRevision: slot.Channel.DetailRevision,
|
||||||
TopicRevision: slot.Channel.TopicRevision,
|
TopicRevision: slot.Channel.TopicRevision,
|
||||||
ChannelDetail: getChannelDetailModel(slot, showList),
|
ChannelDetail: getChannelDetailModel(slot, showList),
|
||||||
|
ChannelSummary: getChannelSummaryModel(slot),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -175,6 +175,8 @@ type ChannelData struct {
|
|||||||
|
|
||||||
TopicRevision int64 `json:"topicRevision"`
|
TopicRevision int64 `json:"topicRevision"`
|
||||||
|
|
||||||
|
ChannelSummary *ChannelSummary `json:"channelSummary,omitempty"`
|
||||||
|
|
||||||
ChannelDetail *ChannelDetail `json:"channelDetail,omitempty"`
|
ChannelDetail *ChannelDetail `json:"channelDetail,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,6 +195,11 @@ type ChannelDetail struct {
|
|||||||
Members []string `json:"members"`
|
Members []string `json:"members"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ChannelSummary struct {
|
||||||
|
|
||||||
|
LastTopic *TopicDetail `json:"lastTopic,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type ChannelParams struct {
|
type ChannelParams struct {
|
||||||
|
|
||||||
DataType string `json:"dataType"`
|
DataType string `json:"dataType"`
|
||||||
@ -443,7 +450,7 @@ type TopicDetail struct {
|
|||||||
|
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
|
|
||||||
Transform string `json:"transform"`
|
Transform string `json:"transform,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type TopicTags struct {
|
type TopicTags struct {
|
||||||
|
@ -545,6 +545,13 @@ var routes = Routes{
|
|||||||
GetChannelDetail,
|
GetChannelDetail,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
Route{
|
||||||
|
"GetChannelSummary",
|
||||||
|
strings.ToUpper("Get"),
|
||||||
|
"/content/channels/{channelId}/summary",
|
||||||
|
GetChannelSummary,
|
||||||
|
},
|
||||||
|
|
||||||
Route{
|
Route{
|
||||||
"GetChannelSubjectField",
|
"GetChannelSubjectField",
|
||||||
strings.ToUpper("Get"),
|
strings.ToUpper("Get"),
|
||||||
|
8
net/web/src/api/getChannelDetail.js
Normal file
8
net/web/src/api/getChannelDetail.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { checkResponse, fetchWithTimeout } from './fetchUtil';
|
||||||
|
|
||||||
|
export async function getChannelDetail(token, channelId) {
|
||||||
|
let detail = await fetchWithTimeout(`/content/channels/${channelId}/detail?agent=${token}`, { method: 'GET' });
|
||||||
|
checkResponse(detail)
|
||||||
|
return await detail.json()
|
||||||
|
}
|
||||||
|
|
8
net/web/src/api/getChannelSummary.js
Normal file
8
net/web/src/api/getChannelSummary.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { checkResponse, fetchWithTimeout } from './fetchUtil';
|
||||||
|
|
||||||
|
export async function getChannelSummary(token, channelId) {
|
||||||
|
let summary = await fetchWithTimeout(`/content/channels/${channelId}/summary?agent=${token}`, { method: 'GET' });
|
||||||
|
checkResponse(summary)
|
||||||
|
return await summary.json()
|
||||||
|
}
|
||||||
|
|
8
net/web/src/api/getContactChannelDetail.js
Normal file
8
net/web/src/api/getContactChannelDetail.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { checkResponse, fetchWithTimeout } from './fetchUtil';
|
||||||
|
|
||||||
|
export async function getContactChannelDetail(token, channelId) {
|
||||||
|
let detail = await fetchWithTimeout(`/content/channels/${channelId}/detail?contact=${token}`, { method: 'GET' });
|
||||||
|
checkResponse(detail)
|
||||||
|
return await detail.json()
|
||||||
|
}
|
||||||
|
|
8
net/web/src/api/getContactChannelSummary.js
Normal file
8
net/web/src/api/getContactChannelSummary.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { checkResponse, fetchWithTimeout } from './fetchUtil';
|
||||||
|
|
||||||
|
export async function getContactChannelSummary(token, channelId) {
|
||||||
|
let summary = await fetchWithTimeout(`/content/channels/${channelId}/summary?contact=${token}`, { method: 'GET' });
|
||||||
|
checkResponse(summary)
|
||||||
|
return await summary.json()
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
|||||||
import { useEffect, useState, useRef } from 'react';
|
import { useEffect, useState, useRef } from 'react';
|
||||||
import { getContactChannels } from 'api/getContactChannels';
|
import { getContactChannels } from 'api/getContactChannels';
|
||||||
import { getContactChannelDetail } from 'api/getContactChannelDetail';
|
import { getContactChannelDetail } from 'api/getContactChannelDetail';
|
||||||
|
import { getContactChannelSummary } from 'api/getContactChannelSummary';
|
||||||
import { getContactProfile } from 'api/getContactProfile';
|
import { getContactProfile } from 'api/getContactProfile';
|
||||||
import { setCardProfile } from 'api/setCardProfile';
|
import { setCardProfile } from 'api/setCardProfile';
|
||||||
import { getCards } from 'api/getCards';
|
import { getCards } from 'api/getCards';
|
||||||
@ -116,15 +117,23 @@ export function useCardContext() {
|
|||||||
if (cur.data.detailRevision != channel.data.detailRevision) {
|
if (cur.data.detailRevision != channel.data.detailRevision) {
|
||||||
if (channel.data.channelDetail != null) {
|
if (channel.data.channelDetail != null) {
|
||||||
cur.data.channelDetail = channel.data.channelDetail;
|
cur.data.channelDetail = channel.data.channelDetail;
|
||||||
cur.data.detailRevision = channel.data.detailRevision;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
let detail = await getContactChannelDetail(guid + "." + token, channel.id);
|
let detail = await getContactChannelDetail(guid + "." + token, channel.id);
|
||||||
cur.data.channelDetail = detail;
|
cur.data.channelDetail = detail;
|
||||||
cur.data.detailRevision = channel.data.detailRevision;
|
|
||||||
}
|
}
|
||||||
|
cur.data.detailRevision = channel.data.detailRevision;
|
||||||
|
}
|
||||||
|
if (cur.data.detailRevision != channel.data.detailRevision) {
|
||||||
|
if (channel.data.channelSummary != null) {
|
||||||
|
cur.data.channelSummary = channel.data.channelSummary;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let summary = await getContactChannelSummary(guid + "." + token, channel.id);
|
||||||
|
cur.data.channelSummary = summary;
|
||||||
|
}
|
||||||
|
cur.data.topicRevision = channel.data.topicRevision;
|
||||||
}
|
}
|
||||||
cur.data.topicRevision = channel.data.topicRevision;
|
|
||||||
cur.revision = channel.revision;
|
cur.revision = channel.revision;
|
||||||
channelMap.set(channel.id, cur);
|
channelMap.set(channel.id, cur);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { useEffect, useState, useRef } from 'react';
|
import { useEffect, useState, useRef } from 'react';
|
||||||
import { getChannels } from 'api/getChannels';
|
import { getChannels } from 'api/getChannels';
|
||||||
import { getChannelDetail } from 'api/getChannelDetail';
|
import { getChannelDetail } from 'api/getChannelDetail';
|
||||||
|
import { getChannelSummary } from 'api/getChannelSummary';
|
||||||
import { addChannel } from 'api/addChannel';
|
import { addChannel } from 'api/addChannel';
|
||||||
import { addChannelTopic } from 'api/addChannelTopic';
|
import { addChannelTopic } from 'api/addChannelTopic';
|
||||||
import { getChannelTopics } from 'api/getChannelTopics';
|
import { getChannelTopics } from 'api/getChannelTopics';
|
||||||
@ -32,15 +33,23 @@ export function useChannelContext() {
|
|||||||
if (cur.data.detailRevision != channel.data.detailRevision) {
|
if (cur.data.detailRevision != channel.data.detailRevision) {
|
||||||
if (channel.data.channelDetail != null) {
|
if (channel.data.channelDetail != null) {
|
||||||
cur.data.channelDetail = channel.data.channelDetail;
|
cur.data.channelDetail = channel.data.channelDetail;
|
||||||
cur.data.detailRevision = channel.data.detailRevision;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
let detail = await getChannelDetail(access.current, channel.id);
|
let detail = await getChannelDetail(access.current, channel.id);
|
||||||
cur.data.channelDetail = detail;
|
cur.data.channelDetail = detail;
|
||||||
cur.data.detailRevision = channel.data.detailRevision;
|
|
||||||
}
|
}
|
||||||
|
cur.data.detailRevision = channel.data.detailRevision;
|
||||||
|
}
|
||||||
|
if (cur.data.topicRevision != channel.data.topicRevision) {
|
||||||
|
if (channel.data.channelDetail != null) {
|
||||||
|
cur.data.channelDetail = channel.data.channelDetail;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let summary = await getChannelSummary(access.current, channel.id);
|
||||||
|
cur.data.channelSummary = summary;
|
||||||
|
}
|
||||||
|
cur.data.topicRevision = channel.data.topicRevision;
|
||||||
}
|
}
|
||||||
cur.data.topicRevision = channel.data.topicRevision;
|
|
||||||
cur.revision = channel.revision;
|
cur.revision = channel.revision;
|
||||||
channels.current.set(channel.id, cur);
|
channels.current.set(channel.id, cur);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user