testing add topic

This commit is contained in:
Roland Osborne 2022-02-17 23:56:30 -08:00
parent 9f09191c43
commit 898ad9f4ac
8 changed files with 223 additions and 23 deletions

View File

@ -2434,7 +2434,7 @@ paths:
tags:
- content
description: Get list of assets assigned to an channel. The original assets will only be available to the account holder to provent the accidental sharing of content metadata. Access is granted to the app token of the account holder and the contact token of accounts the channel has been shared with.
operationId: get-channel-assets
operationId: get-channel-topic-assets
security:
- bearerAuth: []
parameters:
@ -2469,7 +2469,7 @@ paths:
tags:
- content
description: Add an an asset to the to an channel. The original posted asset is referenced in the asset list with a null transform. The transformed assets are referenced accordingly. Transforming the asset strips it of metadata and transcodes it into a specified format. Access is granted to the app token of the account holder.
operationId: add-channel-asset
operationId: add-channel-topic-asset
security:
- bearerAuth: []
parameters:
@ -2525,7 +2525,7 @@ paths:
tags:
- content
description: Get asset assigned to an channel. The endpoint supports byte-range requests and responds with the content-type set appropriatly. Access granted to the app tokens of the account holder and in the case of non-original assets, the contact token for accounts with which the channel is shared.
operationId: get-channel-asset
operationId: get-channel-topic-asset
security:
- bearerAuth: []
parameters:
@ -2567,7 +2567,7 @@ paths:
tags:
- content
description: Remove an asset from an channel. Access granted to app tokens of the account holder.
operationId: remove-channel-asset
operationId: remove-channel-topic-asset
security:
- bearerAuth: []
parameters:
@ -2606,7 +2606,7 @@ paths:
tags:
- content
description: Set confirmed state of the channel. Until the confirmed state has been set to true, the channel will not be visible to contacts with which the channel is shared. Access granted to the app tokens of the acocunt holder.
operationId: set-channel-confirmed
operationId: set-channel-topic-confirmed
security:
- bearerAuth: []
parameters:
@ -3155,7 +3155,8 @@ components:
id:
type: string
revision:
type: int64
type: integer
format: int64
data:
$ref: '#/components/schemas/ChannelData'
@ -3235,7 +3236,7 @@ components:
topicDetail:
$ref: '#/components/schemas/TopicDetail'
topicTags::
$ref: '#/components/schemas/TopicSize'
$ref: '#/components/schemas/TopicTags'
TopicDetail:
type: object
@ -3244,7 +3245,7 @@ components:
- dataType
- data
- created
- modified
- updated
- status
properties:
guid:
@ -3256,14 +3257,14 @@ components:
created:
type: integer
format: int64
modified:
updated:
type: integer
format: int64
status:
type: string
enum: [ unconfirmed, confirmed, complete, error ]
TopicSize:
TopicTags:
type: object
required:
- tagCount

View File

@ -0,0 +1,136 @@
package databag
import (
"errors"
"net/http"
"gorm.io/gorm"
"github.com/gorilla/mux"
"github.com/google/uuid"
"databag/internal/store"
)
func AddChannelTopic(w http.ResponseWriter, r *http.Request) {
// scan parameters
params := mux.Vars(r)
channelId := params["channelId"]
var subject Subject
if err := ParseRequest(r, w, &subject); err != nil {
ErrResponse(w, http.StatusBadRequest, err)
return
}
var guid string
var act *store.Account
tokenType := r.Header.Get("TokenType")
if tokenType == APP_TOKENAPP {
account, code, err := BearerAppToken(r, false);
if err != nil {
ErrResponse(w, code, err)
return
}
act = account
guid = account.Guid
} else if tokenType == APP_TOKENCONTACT {
card, code, err := BearerContactToken(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 channelSlot store.ChannelSlot
if err := store.DB.Preload("Channel.Cards").Preload("Channel.Groups.Cards").Where("account_id = ? AND channel_slot_id = ?", act.ID, channelId).First(&channelSlot).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
ErrResponse(w, http.StatusNotFound, err)
} else {
ErrResponse(w, http.StatusInternalServerError, err)
}
return
}
if channelSlot.Channel == nil {
ErrResponse(w, http.StatusNotFound, errors.New("referenced empty channel"))
return
}
// check if a member
if tokenType == APP_TOKENCONTACT {
if !isMember(guid, channelSlot.Channel.Cards) {
ErrResponse(w, http.StatusUnauthorized, errors.New("not a member of channel"))
return
}
}
topicSlot := &store.TopicSlot{}
err := store.DB.Transaction(func(tx *gorm.DB) error {
// add new record
topic := &store.Topic{}
topic.Data = subject.Data
topic.DataType = subject.DataType
topic.Channel = channelSlot.Channel
topic.TagCount = 0
topic.Guid = guid
topic.DetailRevision = act.ChannelRevision + 1
topic.TagRevision = act.ChannelRevision + 1
topic.Status = APP_TOPICUNCONFIRMED
if res := tx.Save(topic).Error; res != nil {
return res
}
topicSlot.TopicSlotId = uuid.New().String()
topicSlot.AccountID = act.ID
topicSlot.TopicID = topic.ID
topicSlot.Revision = act.ChannelRevision + 1
topicSlot.Topic = topic
if res := tx.Save(topicSlot).Error; res != nil {
return res
}
// update parent revision
if res := tx.Model(&channelSlot).Update("revision", act.ChannelRevision + 1).Error; res != nil {
return res
}
if res := tx.Model(&act).Update("channel_revision", act.ChannelRevision + 1).Error; res != nil {
return res
}
return nil
})
if err != nil {
ErrResponse(w, http.StatusInternalServerError, err)
return
}
// determine affected contact list
cards := make(map[string]store.Card)
for _, card := range channelSlot.Channel.Cards {
cards[card.Guid] = card
}
for _, group := range channelSlot.Channel.Groups {
for _, card := range group.Cards {
cards[card.Guid] = card
}
}
SetStatus(act)
for _, card := range cards {
SetContactChannelNotification(act, &card)
}
WriteResponse(w, getTopicModel(topicSlot, true, true))
}
func isMember(guid string, cards []store.Card) bool {
for _, card := range cards {
if guid == card.Guid {
return true
}
}
return false
}

View File

@ -18,11 +18,6 @@ func AddChannelAsset(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
}
func AddChannelTopic(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.WriteHeader(http.StatusOK)
}
func AddChannelTopicTag(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.WriteHeader(http.StatusOK)

View File

View File

@ -215,3 +215,55 @@ func getChannelModel(slot *store.ChannelSlot, showData bool, showList bool) *Cha
func getTopicRevisionModel(slot *store.TopicSlot, showData bool) *Topic {
if !showData || slot.Topic == nil {
return &Topic{
Id: slot.TopicSlotId,
Revision: slot.Revision,
}
}
return &Topic{
Id: slot.TopicSlotId,
Revision: slot.Revision,
Data: &TopicData {
DetailRevision: slot.Topic.DetailRevision,
TagRevision: slot.Topic.TagRevision,
},
}
}
func getTopicModel(slot *store.TopicSlot, showData bool, showList bool) *Topic {
if !showData || slot.Topic == nil {
return &Topic{
Id: slot.TopicSlotId,
Revision: slot.Revision,
}
}
return &Topic{
Id: slot.TopicSlotId,
Revision: slot.Revision,
Data: &TopicData {
DetailRevision: slot.Topic.DetailRevision,
TopicDetail: &TopicDetail{
Guid: slot.Topic.Guid,
DataType: slot.Topic.DataType,
Data: slot.Topic.Data,
Created: slot.Topic.Created,
Updated: slot.Topic.Updated,
Status: slot.Topic.Status,
},
TagRevision: slot.Topic.TagRevision,
TopicTags: &TopicTags{
TagCount: slot.Topic.TagCount,
TagUpdated: slot.Topic.TagUpdated,
},
},
}
}

View File

@ -363,7 +363,7 @@ type Tag struct {
Id string `json:"id"`
Revision string `json:"revision"`
Revision int64 `json:"revision"`
Data *TagData `json:"data"`
}
@ -385,7 +385,7 @@ type Topic struct {
Id string `json:"id"`
Revision string `json:"revision"`
Revision int64 `json:"revision"`
Data *TopicData `json:"data"`
}
@ -398,7 +398,7 @@ type TopicData struct {
TopicDetail *TopicDetail `json:"topicDetail,omitempty"`
TopicTags *TopicSize `json:"topicTags:,omitempty"`
TopicTags *TopicTags `json:"topicTags:,omitempty"`
}
type TopicDetail struct {
@ -411,12 +411,12 @@ type TopicDetail struct {
Created int64 `json:"created"`
Modified int64 `json:"modified"`
Updated int64 `json:"updated"`
Status string `json:"status"`
}
type TopicSize struct {
type TopicTags struct {
TagCount int32 `json:"tagCount"`

View File

@ -224,9 +224,9 @@ type Topic struct {
Status string `gorm:"not null;index"`
Created int64 `gorm:"autoCreateTime"`
Updated int64 `gorm:"autoUpdateTime"`
SizeRevision int64 `gorm:"not null"`
TagUpdated int64 `gorm:"not null"`
TagRevision uint64 `gorm:"not null"`
TagCount int32 `gorm:"not null"`
TagUpdated int64 `gorm:"autoUpdateTime"`
TagRevision int64 `gorm:"not null"`
Channel *Channel
Assets []Asset
Tags []Tag

View File

@ -8,6 +8,7 @@ import (
)
func TestTopicShare(t *testing.T) {
var topic *Topic
var channel *Channel
var subject *Subject
params := make(map[string]string)
@ -68,7 +69,22 @@ func TestTopicShare(t *testing.T) {
assert.Zero(t, bytes.Compare(img, data))
// add a topc
topic = &Topic{}
subject = &Subject{ DataType: "topicdatatype", Data: "subjectfromA" }
assert.NoError(t, ApiTestMsg(AddChannelTopic, "POST", "/content/channels/{channelId}/topics",
&params, subject, APP_TOKENAPP, set.A.Token, topic, nil))
topic = &Topic{}
subject = &Subject{ DataType: "topicdatatype", Data: "subjectfromB" }
assert.NoError(t, ApiTestMsg(AddChannelTopic, "POST", "/content/channels/{channelId}/topics",
&params, subject, APP_TOKENCONTACT, set.B.A.Token, topic, nil))
topic = &Topic{}
subject = &Subject{ DataType: "topicdatatype", Data: "subjectfromC" }
assert.NoError(t, ApiTestMsg(AddChannelTopic, "POST", "/content/channels/{channelId}/topics",
&params, subject, APP_TOKENCONTACT, set.C.A.Token, topic, nil))
PrintMsg(topic)
}