Update groups API

This commit is contained in:
Ian Philips 2022-09-06 09:24:26 -06:00
parent 2ee067c072
commit 5af92a7d81
5 changed files with 95 additions and 20 deletions

View File

@ -54,6 +54,10 @@ Returns the authenticated user.
Gets all groups, in no particular order. Gets all groups, in no particular order.
Parameters:
- `availableToUserId`: Optional. if specified, only groups that the user can
join and groups they've already joined will be returned.
Requires no authorization. Requires no authorization.
### `GET /v0/groups/[slug]` ### `GET /v0/groups/[slug]`
@ -62,12 +66,18 @@ Gets a group by its slug.
Requires no authorization. Requires no authorization.
### `GET /v0/groups/by-id/[id]` ### `GET /v0/group/by-id/[id]`
Gets a group by its unique ID. Gets a group by its unique ID.
Requires no authorization. Requires no authorization.
### `GET /v0/group/by-id/[id]/markets`
Gets a group's markets by its unique ID.
Requires no authorization.
### `GET /v0/markets` ### `GET /v0/markets`
Lists all markets, ordered by creation date descending. Lists all markets, ordered by creation date descending.

View File

@ -11,7 +11,7 @@ import {
updateDoc, updateDoc,
where, where,
} from 'firebase/firestore' } from 'firebase/firestore'
import { uniq } from 'lodash' import { uniq, uniqBy } from 'lodash'
import { Group, GROUP_CHAT_SLUG, GroupLink } from 'common/group' import { Group, GROUP_CHAT_SLUG, GroupLink } from 'common/group'
import { import {
coll, coll,
@ -21,7 +21,7 @@ import {
listenForValues, listenForValues,
} from './utils' } from './utils'
import { Contract } from 'common/contract' import { Contract } from 'common/contract'
import { updateContract } from 'web/lib/firebase/contracts' import { getContractFromId, updateContract } from 'web/lib/firebase/contracts'
import { db } from 'web/lib/firebase/init' import { db } from 'web/lib/firebase/init'
import { filterDefined } from 'common/util/array' import { filterDefined } from 'common/util/array'
import { getUser } from 'web/lib/firebase/users' import { getUser } from 'web/lib/firebase/users'
@ -31,6 +31,9 @@ export const groupMembers = (groupId: string) =>
collection(groups, groupId, 'groupMembers') collection(groups, groupId, 'groupMembers')
export const groupContracts = (groupId: string) => export const groupContracts = (groupId: string) =>
collection(groups, groupId, 'groupContracts') collection(groups, groupId, 'groupContracts')
const openGroupsQuery = query(groups, where('anyoneCanJoin', '==', true))
const memberGroupsQuery = (userId: string) =>
query(collectionGroup(db, 'groupMembers'), where('userId', '==', userId))
export function groupPath( export function groupPath(
groupSlug: string, groupSlug: string,
@ -78,23 +81,24 @@ export function listenForGroupContractDocs(
return listenForValues(groupContracts(groupId), setContractDocs) return listenForValues(groupContracts(groupId), setContractDocs)
} }
export function listenForOpenGroups(setGroups: (groups: Group[]) => void) { export async function listGroupContracts(groupId: string) {
return listenForValues( const contractDocs = await getValues<{
query(groups, where('anyoneCanJoin', '==', true)), contractId: string
setGroups createdTime: number
}>(groupContracts(groupId))
return Promise.all(
contractDocs.map((doc) => getContractFromId(doc.contractId))
) )
} }
export function listenForOpenGroups(setGroups: (groups: Group[]) => void) {
return listenForValues(openGroupsQuery, setGroups)
}
export function getGroup(groupId: string) { export function getGroup(groupId: string) {
return getValue<Group>(doc(groups, groupId)) return getValue<Group>(doc(groups, groupId))
} }
export function getGroupContracts(groupId: string) {
return getValues<{ contractId: string; createdTime: number }>(
groupContracts(groupId)
)
}
export async function getGroupBySlug(slug: string) { export async function getGroupBySlug(slug: string) {
const q = query(groups, where('slug', '==', slug)) const q = query(groups, where('slug', '==', slug))
const docs = (await getDocs(q)).docs const docs = (await getDocs(q)).docs
@ -112,10 +116,7 @@ export function listenForMemberGroupIds(
userId: string, userId: string,
setGroupIds: (groupIds: string[]) => void setGroupIds: (groupIds: string[]) => void
) { ) {
const q = query( const q = memberGroupsQuery(userId)
collectionGroup(db, 'groupMembers'),
where('userId', '==', userId)
)
return onSnapshot(q, { includeMetadataChanges: true }, (snapshot) => { return onSnapshot(q, { includeMetadataChanges: true }, (snapshot) => {
if (snapshot.metadata.fromCache) return if (snapshot.metadata.fromCache) return
@ -136,6 +137,24 @@ export function listenForMemberGroups(
}) })
} }
export async function listAvailableGroups(userId: string) {
const [openGroups, memberGroupSnapshot] = await Promise.all([
getValues<Group>(openGroupsQuery),
getDocs(memberGroupsQuery(userId)),
])
const memberGroups = filterDefined(
await Promise.all(
memberGroupSnapshot.docs.map((doc) => {
return doc.ref.parent.parent?.id
? getGroup(doc.ref.parent.parent?.id)
: null
})
)
)
return uniqBy([...openGroups, ...memberGroups], (g) => g.id)
}
export async function addUserToGroupViaId(groupId: string, userId: string) { export async function addUserToGroupViaId(groupId: string, userId: string) {
// get group to get the member ids // get group to get the member ids
const group = await getGroup(groupId) const group = await getGroup(groupId)

View File

@ -0,0 +1,18 @@
import { NextApiRequest, NextApiResponse } from 'next'
import { applyCorsHeaders, CORS_UNRESTRICTED } from 'web/lib/api/cors'
import { listGroupContracts } from 'web/lib/firebase/groups'
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
await applyCorsHeaders(req, res, CORS_UNRESTRICTED)
const { id } = req.query
const contracts = await listGroupContracts(id as string)
if (!contracts) {
res.status(404).json({ error: 'Group not found' })
return
}
res.setHeader('Cache-Control', 'no-cache')
return res.status(200).json(contracts)
}

View File

@ -1,14 +1,42 @@
import type { NextApiRequest, NextApiResponse } from 'next' import type { NextApiRequest, NextApiResponse } from 'next'
import { listAllGroups } from 'web/lib/firebase/groups' import { listAllGroups, listAvailableGroups } from 'web/lib/firebase/groups'
import { applyCorsHeaders, CORS_UNRESTRICTED } from 'web/lib/api/cors' import { applyCorsHeaders, CORS_UNRESTRICTED } from 'web/lib/api/cors'
import { z } from 'zod'
import { validate } from 'web/pages/api/v0/_validate'
import { ValidationError } from 'web/pages/api/v0/_types'
type Data = any[] const queryParams = z
.object({
availableToUserId: z.string().optional(),
})
.strict()
export default async function handler( export default async function handler(
req: NextApiRequest, req: NextApiRequest,
res: NextApiResponse<Data> res: NextApiResponse
) { ) {
await applyCorsHeaders(req, res, CORS_UNRESTRICTED) await applyCorsHeaders(req, res, CORS_UNRESTRICTED)
let params: z.infer<typeof queryParams>
try {
params = validate(queryParams, req.query)
} catch (e) {
if (e instanceof ValidationError) {
return res.status(400).json(e)
}
console.error(`Unknown error during validation: ${e}`)
return res.status(500).json({ error: 'Unknown error during validation' })
}
const { availableToUserId } = params
// TODO: should we check if the user is a real user?
if (availableToUserId) {
const groups = await listAvailableGroups(availableToUserId)
res.setHeader('Cache-Control', 'max-age=0')
res.status(200).json(groups)
return
}
const groups = await listAllGroups() const groups = await listAllGroups()
res.setHeader('Cache-Control', 'max-age=0') res.setHeader('Cache-Control', 'max-age=0')
res.status(200).json(groups) res.status(200).json(groups)