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

18
src/schemas.ts

@ -78,10 +78,28 @@ export const postSchema: JSONSchema = {
type: 'object',
properties: {
id: { type: 'string' },
userId: { type: 'string' },
appId: { type: 'string' },
user: userSchema,
text: { 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' },
replies: { type: 'number' },
created: { type: 'number' },
},
}

5
src/types/collections.ts

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

Loading…
Cancel
Save