Skip to content

Commit

Permalink
Add more typing for history objects (#277)
Browse files Browse the repository at this point in the history
  • Loading branch information
zichongkao committed Apr 29, 2023
1 parent cf68302 commit 75d7bf5
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 26 deletions.
16 changes: 12 additions & 4 deletions src/db/ChangeLogType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ import { ClimbEditOperationType, ClimbType } from './ClimbTypes.js'
import { OperationType as OrganizationOpType, OrganizationType } from './OrganizationTypes.js'

export type DBOperation = 'insert' | 'update' | 'delete'
export enum DocumentKind {
areas = 'areas',
climbs = 'climbs',
organizations = 'organizations'
}

export interface ChangeLogType<T = SupportedCollectionTypes> {
_id: mongose.Types.ObjectId
Expand All @@ -14,7 +19,7 @@ export interface ChangeLogType<T = SupportedCollectionTypes> {
changes: Array<BaseChangeRecordType<T>>
}

// DIY since ResumeToke is defined as unknown in mongo TS
// DIY since ResumeToken is defined as unknown in mongo TS
export interface ResumeToken {
_data: string
}
Expand All @@ -29,7 +34,7 @@ export interface BaseChangeRecordType<FullDocumentType = SupportedCollectionType
dbOp: DBOperation
fullDocument: FullDocumentType
updateDescription: UpdateDescription
kind: string
kind: DocumentKind
}

export type OpType = AreaOpType | ClimbEditOperationType | OrganizationOpType
Expand All @@ -47,7 +52,7 @@ export interface ChangeRecordMetadataType {
}

export interface WithDiscriminator {
kind: string
kind: DocumentKind
}

export type AreaChangeLogType = ChangeLogType<AreaType>
Expand All @@ -56,7 +61,10 @@ export type AreaChangeRecordType = BaseChangeRecordType<AreaType>
export type ClimbChangeLogType = ChangeLogType<ClimbType>
export type OrganizationChangeLogType = ChangeLogType<OrganizationType>

export type SupportedCollectionTypes = AreaType & WithDiscriminator | ClimbType & WithDiscriminator
export type SupportedCollectionTypes =
| AreaType & WithDiscriminator
| ClimbType & WithDiscriminator
| OrganizationType & WithDiscriminator

export interface GetHistoryInputFilterType {
uuidList: string[]
Expand Down
22 changes: 12 additions & 10 deletions src/db/edit/streamListener.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import dot from 'dot-object'

import { changelogDataSource } from '../../model/ChangeLogDataSource.js'
import { logger } from '../../logger.js'
import { BaseChangeRecordType, ResumeToken, UpdateDescription, DBOperation, SupportedCollectionTypes } from '../ChangeLogType.js'
import { BaseChangeRecordType, ResumeToken, UpdateDescription, DBOperation, SupportedCollectionTypes, DocumentKind } from '../ChangeLogType.js'
import { checkVar } from '../index.js'
import { updateAreaIndex } from '../export/Typesense/Client.js'
import { AreaType } from '../AreaTypes.js'
import { exhaustiveCheck } from '../../utils/helpers.js'

/**
* Start a new stream listener to track changes
Expand Down Expand Up @@ -49,7 +50,7 @@ const onChange = (change: ChangeStreamDocument): void => {
case 'replace':
case 'update': {
let dbOp: DBOperation = 'update'
const source = change.ns.coll
const source = DocumentKind[change.ns.coll]
const { fullDocument, _id, updateDescription } = change as ChangeStreamUpdateDocument
if (fullDocument?._deleting != null) {
dbOp = 'delete'
Expand All @@ -60,7 +61,7 @@ const onChange = (change: ChangeStreamDocument): void => {
}
case 'insert': {
const dbOp = 'insert'
const source = change.ns.coll
const source = DocumentKind[change.ns.coll]
const { fullDocument, _id } = change
void recordChange({ _id: _id as ResumeToken, source, fullDocument: fullDocument as SupportedCollectionTypes, dbOp })
break
Expand All @@ -70,7 +71,7 @@ const onChange = (change: ChangeStreamDocument): void => {

interface ChangeRecordType {
_id: ResumeToken
source: string
source: DocumentKind
fullDocument: SupportedCollectionTypes
updateDescription?: any
dbOp: DBOperation
Expand All @@ -79,41 +80,42 @@ interface ChangeRecordType {
const recordChange = async ({ source, dbOp, fullDocument, updateDescription, _id }: ChangeRecordType): Promise<void> => {
fullDocument.kind = source
switch (source) {
case 'climbs': {
case DocumentKind.climbs: {
const newDocument: BaseChangeRecordType = {
_id,
dbOp,
fullDocument,
updateDescription: dotifyUpdateDescription(updateDescription),
kind: 'climbs'
kind: DocumentKind.climbs
}
void changelogDataSource.record(newDocument)
break
}
case 'areas': {
case DocumentKind.areas: {
const newDocument: BaseChangeRecordType = {
_id,
dbOp,
fullDocument,
updateDescription: dotifyUpdateDescription(updateDescription),
kind: 'areas'
kind: DocumentKind.areas
}
void changelogDataSource.record(newDocument)
void updateAreaIndex(fullDocument as AreaType, dbOp)
break
}
case 'organizations': {
case DocumentKind.organizations: {
const newDocument: BaseChangeRecordType = {
_id,
dbOp,
fullDocument,
updateDescription: dotifyUpdateDescription(updateDescription),
kind: 'organizations'
kind: DocumentKind.organizations
}
void changelogDataSource.record(newDocument)
break
}
default:
exhaustiveCheck(source)
}
}

Expand Down
21 changes: 11 additions & 10 deletions src/graphql/history/HistoryFieldResolvers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ChangeLogType, BaseChangeRecordType, SupportedCollectionTypes } from '../../db/ChangeLogType.js'
import { ChangeLogType, BaseChangeRecordType, SupportedCollectionTypes, DocumentKind } from '../../db/ChangeLogType.js'
import { exhaustiveCheck } from '../../utils/helpers.js'

/**
* Customize to resolve individual fields
Expand All @@ -24,16 +25,16 @@ const resolvers = {

Document: {
__resolveType (node: SupportedCollectionTypes) {
if (node.kind === 'areas') {
return 'Area'
switch (node.kind) {
case DocumentKind.areas:
return 'Area'
case DocumentKind.climbs:
return 'Climb'
case DocumentKind.organizations:
return 'Organization'
default:
return exhaustiveCheck(node.kind)
}
if (node.kind === 'climbs') {
return 'Climb'
}
if (node.kind === 'organizations') {
return 'Organization'
}
return null
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion src/graphql/history/HistoryQueries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ const HistoryQueries = {
getChangeHistory: async (_, { filter }, { dataSources }): Promise<any> => {
const { history }: DataSourcesType = dataSources
const { uuidList }: GetHistoryInputFilterType = filter ?? {}
// Note: userUuid, fromDate, toDate filters don't currently work.
// Note: though we pull uuidList, we don't use it either.

// convert array of uuid in string to UUID[]
// Convert array of uuid in string to UUID[]
const muidList = uuidList?.map(entry => muid.from(entry)) ?? []
return await history.getChangeSets(muidList)
},
Expand Down
2 changes: 1 addition & 1 deletion src/model/ChangeLogDataSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export default class ChangeLogDataSource extends MongoDataSource<ChangeLogType>
* @param uuidList optional filter
* @returns change sets
*/
async getChangeSets (uuidList: MUUID[]): Promise<Array<AreaChangeLogType | ClimbChangeLogType>> {
async getChangeSets (uuidList: MUUID[]): Promise<Array<AreaChangeLogType | ClimbChangeLogType | OrganizationChangeLogType>> {
const rs = await this.changeLogModel.aggregate([
{
$sort: {
Expand Down
16 changes: 16 additions & 0 deletions src/utils/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,19 @@ export const isMuuidHexStr = (s: string): boolean => {
const regex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/
return regex.test(s)
}

/**
* Ensures that type-checking errors out if enums are not
* handlded exhaustively in switch statements.
* Eg.
* switch(val) {
* case enumOne:
* ...
* default:
* exhaustiveCheck(val)
* }
* @param _value
*/
export function exhaustiveCheck (_value: never): never {
throw new Error(`ERROR! Enum not handled for ${JSON.stringify(_value)}`)
}

0 comments on commit 75d7bf5

Please sign in to comment.