Dwayne Harris 5 years ago
parent
commit
fc7ba2e3cd
  1. 84
      src/plugins/api/posts.ts
  2. 18
      src/schemas.ts
  3. 5
      src/types/collections.ts

84
src/plugins/api/posts.ts

@ -37,7 +37,6 @@ import {
GroupItemType, GroupItemType,
Installation, Installation,
App, App,
Group,
} from '../../types/collections' } from '../../types/collections'
import { PluginOptions } from '../../types' import { PluginOptions } from '../../types'
@ -45,22 +44,25 @@ import { PluginOptions } from '../../types'
interface PostBody { interface PostBody {
text?: string text?: string
cover?: string cover?: string
installation: string
visible: boolean visible: boolean
status?: Status status?: Status
attachments: PostAttachment[] attachments: PostAttachment[]
data: object
parent: string parent: string
} }
const postBodySchema = { const postBodySchema = {
type: 'object', type: 'object',
required: ['visible'],
required: ['installation', 'visible'],
properties: { properties: {
installation: { type: 'string' },
visible: { type: 'boolean' },
text: { type: 'string' }, text: { type: 'string' },
cover: { cover: {
type: 'string', type: 'string',
maxLength: SHORT_TEXT_LENGTH, maxLength: SHORT_TEXT_LENGTH,
}, },
visible: { type: 'boolean' },
status: { status: {
type: 'object', type: 'object',
required: ['date'], required: ['date'],
@ -77,10 +79,10 @@ const postBodySchema = {
type: 'array', type: 'array',
items: { items: {
type: 'object', type: 'object',
required: ['imageUrl'],
required: ['url'],
properties: { properties: {
imageUrl: { type: 'string' },
caption: {
url: { type: 'string' },
text: {
type: 'string', type: 'string',
maxLength: SHORT_TEXT_LENGTH, maxLength: SHORT_TEXT_LENGTH,
}, },
@ -91,11 +93,14 @@ const postBodySchema = {
}, },
}, },
}, },
data: {
type: 'object',
},
parent: { type: 'string' }, parent: { type: 'string' },
}, },
} }
async function createPost(client: CosmosClient, userId: string, body: PostBody, reply: FastifyReply<ServerResponse>, logger: Logger) {
async function createPost(client: CosmosClient, userId: string, appId: string, body: PostBody, reply: FastifyReply<ServerResponse>, logger: Logger) {
let newPostRelationship: PostRelationship | undefined let newPostRelationship: PostRelationship | undefined
const postContainer = containerFor(client, 'Posts') const postContainer = containerFor(client, 'Posts')
@ -111,7 +116,8 @@ async function createPost(client: CosmosClient, userId: string, body: PostBody,
const postId = createPostId() const postId = createPostId()
if (body.parent) { if (body.parent) {
const parent = await getItem<Post>({ container: postContainer, id: body.parent })
const parentItem = postContainer.item(body.parent, body.parent)
const { resource: parent } = await parentItem.read<Post>()
if (!parent) return badRequestFormError(reply, 'parent', 'Invalid parent') if (!parent) return badRequestFormError(reply, 'parent', 'Invalid parent')
const parentRelationship = await getItem<PostRelationship>({ const parentRelationship = await getItem<PostRelationship>({
@ -130,6 +136,13 @@ async function createPost(client: CosmosClient, userId: string, body: PostBody,
parent.id, parent.id,
] ]
} }
if (body.visible) {
await parentItem.replace<Post>({
...parent,
replies: parent.replies + 1,
})
}
} }
const post: Post = { const post: Post = {
@ -137,13 +150,16 @@ async function createPost(client: CosmosClient, userId: string, body: PostBody,
pk: postId, pk: postId,
t: PostItemType.Post, t: PostItemType.Post,
userId, userId,
appId,
root: newPostRelationship ? newPostRelationship.pk : postId, root: newPostRelationship ? newPostRelationship.pk : postId,
parents: newPostRelationship ? newPostRelationship.parents : [], parents: newPostRelationship ? newPostRelationship.parents : [],
text: trimContent(body.text, 1000), text: trimContent(body.text, 1000),
cover: trimContent(body.cover), cover: trimContent(body.cover),
visible: body.visible, visible: body.visible,
attachments: [],
attachments: body.attachments,
data: body.data,
awards: 0, awards: 0,
replies: 0,
latestAwards: [], latestAwards: [],
created: Date.now(), created: Date.now(),
} }
@ -169,8 +185,6 @@ async function createPost(client: CosmosClient, userId: string, body: PostBody,
logger, logger,
}) })
logger.debug('subscribers', subscribers)
for (const uid of [userId, ...subscribers.map(s => s.userId)]) { for (const uid of [userId, ...subscribers.map(s => s.userId)]) {
await userContainer.items.create<UserTimelinePost>({ await userContainer.items.create<UserTimelinePost>({
id: postId, id: postId,
@ -205,13 +219,32 @@ function createPostRoute(server: FastifyInstance<Server, IncomingMessage, Server
if (!server.database) return serverError(reply) if (!server.database) return serverError(reply)
if (!request.viewer) return unauthorizedError(reply) if (!request.viewer) return unauthorizedError(reply)
return await createPost(server.database.client, request.viewer.id, request.body, reply, request.log)
const installationId = request.body.installation
const container = containerFor(server.database.client, 'Apps')
const installation = await getItem<Installation>({
container,
id: installationId,
partitionKey: INSTALLATION_PARTITION_KEY,
})
if (!installation) return badRequestError(reply, 'Installation not found')
if (installation.userId !== request.viewer.id) return badRequestError(reply)
const app = await getItem<App>({
container,
id: installation.appId,
partitionKey: APP_PARTITION_KEY,
})
if (!app) return serverError(reply)
return await createPost(server.database.client, request.viewer.id, app.id, request.body, reply, request.log)
}) })
} }
function createAppPostRoute(server: FastifyInstance<Server, IncomingMessage, ServerResponse>) { function createAppPostRoute(server: FastifyInstance<Server, IncomingMessage, ServerResponse>) {
interface Headers { interface Headers {
installation: string
timestamp: string timestamp: string
signature: string signature: string
} }
@ -221,7 +254,6 @@ function createAppPostRoute(server: FastifyInstance<Server, IncomingMessage, Ser
headers: { headers: {
type: 'object', type: 'object',
properties: { properties: {
installation: { type: 'string' },
timestamp: { type: 'string' }, timestamp: { type: 'string' },
signature: { type: 'string' }, signature: { type: 'string' },
}, },
@ -242,12 +274,13 @@ function createAppPostRoute(server: FastifyInstance<Server, IncomingMessage, Ser
server.post<DefaultQuery, DefaultParams, Headers, PostBody>('/api/app/post', options, async (request, reply) => { server.post<DefaultQuery, DefaultParams, Headers, PostBody>('/api/app/post', options, async (request, reply) => {
if (!server.database) return serverError(reply) if (!server.database) return serverError(reply)
const { installation: installationId, timestamp, signature } = request.headers
const { timestamp, signature } = request.headers
if (!installationId) return badRequestError(reply, '"installation" header required')
if (!timestamp) return badRequestError(reply, '"timestamp" header required') if (!timestamp) return badRequestError(reply, '"timestamp" header required')
if (!signature) return badRequestError(reply, '"signature" header required') if (!signature) return badRequestError(reply, '"signature" header required')
const installationId = request.body.installation
const container = containerFor(server.database.client, 'Apps') const container = containerFor(server.database.client, 'Apps')
const installation = await getItem<Installation>({ const installation = await getItem<Installation>({
container, container,
@ -269,7 +302,7 @@ function createAppPostRoute(server: FastifyInstance<Server, IncomingMessage, Ser
return badRequestError(reply, 'Invalid signature') return badRequestError(reply, 'Invalid signature')
} }
return await createPost(server.database.client, installation.userId, request.body, reply, request.log)
return await createPost(server.database.client, installation.userId, app.id, request.body, reply, request.log)
}) })
} }
@ -389,6 +422,7 @@ function timelineRoute(server: FastifyInstance<Server, IncomingMessage, ServerRe
type: 'array', type: 'array',
items: postSchema, items: postSchema,
}, },
continuation: { type: 'string' },
}, },
}, },
400: errorSchema, 400: errorSchema,
@ -455,11 +489,11 @@ function postRoute(server: FastifyInstance<Server, IncomingMessage, ServerRespon
type: 'object', type: 'object',
properties: { properties: {
post: postSchema, post: postSchema,
descendants: {
parents: {
type: 'array', type: 'array',
items: postSchema, items: postSchema,
}, },
ancestors: {
children: {
type: 'array', type: 'array',
items: postSchema, items: postSchema,
}, },
@ -527,7 +561,7 @@ function postRoute(server: FastifyInstance<Server, IncomingMessage, ServerRespon
SELECT g.userId FROM Groups g WHERE SELECT g.userId FROM Groups g WHERE
g.pk = @viewerGroup AND g.pk = @viewerGroup AND
g.t = @type AND g.t = @type AND
(g.blockedId = @viewer OR g.blockedId = @viewerGroup)
(g.blockedId = @viewer OR g.blockedId = @viewerGroup) AND
ARRAY_CONTAINS(@ids, g.userId) ARRAY_CONTAINS(@ids, g.userId)
`, { `, {
viewer: viewer.id, viewer: viewer.id,
@ -539,7 +573,7 @@ function postRoute(server: FastifyInstance<Server, IncomingMessage, ServerRespon
const blocks = await queryItems<GroupBlock>({ const blocks = await queryItems<GroupBlock>({
container: containerFor(server.database.client, 'Groups'), container: containerFor(server.database.client, 'Groups'),
query: blockQuery, query: blockQuery,
logger: request.log
logger: request.log,
}) })
const blockedUserIds = blocks.map(b => b.userId) const blockedUserIds = blocks.map(b => b.userId)
@ -547,16 +581,16 @@ function postRoute(server: FastifyInstance<Server, IncomingMessage, ServerRespon
return { return {
post, post,
descendants: descendants.filter(p => !blockedUserIds.includes(p.userId)),
ancestors: ancestors.filter(p => !blockedUserIds.includes(p.userId)),
children: descendants.filter(p => !blockedUserIds.includes(p.userId)),
parents: ancestors.filter(p => !blockedUserIds.includes(p.userId)),
users: users.filter(u => !blockedUserIds.includes(u.id)), users: users.filter(u => !blockedUserIds.includes(u.id)),
} }
} }
return { return {
post, post,
descendants,
ancestors,
children: descendants,
parents: ancestors,
users, users,
} }
}) })

18
src/schemas.ts

@ -78,10 +78,28 @@ export const postSchema: JSONSchema = {
type: 'object', type: 'object',
properties: { properties: {
id: { type: 'string' }, id: { type: 'string' },
userId: { type: 'string' },
appId: { type: 'string' },
user: userSchema, user: userSchema,
text: { type: 'string' }, text: { type: 'string' },
cover: { type: 'string' }, cover: { type: 'string' },
attachments: {
type: 'array',
items: {
type: 'object',
properties: {
url: { type: 'string' },
text: { type: 'string' },
cover: { type: 'string' },
},
},
},
data: {
type: 'object',
additionalProperties: true,
},
visible: { type: 'boolean' }, visible: { type: 'boolean' },
replies: { type: 'number' },
created: { type: 'number' }, created: { type: 'number' },
}, },
} }

5
src/types/collections.ts

@ -260,8 +260,8 @@ export interface UserTimelinePost {
} }
export interface PostAttachment { export interface PostAttachment {
imageUrl: string
caption?: string
url: string
text?: string
cover?: string cover?: string
} }
@ -288,6 +288,7 @@ export interface Post {
status?: Status status?: Status
data?: PostData data?: PostData
visible: boolean visible: boolean
replies: number
awards: number awards: number
latestAwards: PostAwardPartial[] latestAwards: PostAwardPartial[]
created: number created: number

Loading…
Cancel
Save