Mirror of https://github.com/roostorg/coop github.com/roostorg/coop
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 118 lines 4.0 kB view raw
1import { type Dependencies } from '../../iocContainer/index.js'; 2import { inject } from '../../iocContainer/utils.js'; 3import { 4 type ItemSubmission, 5 type NormalizedItemData, 6 type RawItemData, 7} from '../../services/itemProcessingService/index.js'; 8import { fromCorrelationId } from '../../utils/correlationIds.js'; 9import { jsonStringifyUnstable } from '../../utils/encoding.js'; 10import { getUtcDateOnlyString } from '../../utils/time.js'; 11import { type RuleExecutionCorrelationId } from './ruleExecutionLoggingUtils.js'; 12 13// NB: when an incoming POST /content api request fails, the content submission 14// logged to the data warehouse might not be in a valid, processable shape (in fact, it 15// may be that the content api request failed _because_ the content submission 16// was invalid). 17export type ContentApiRequestLogEntry<HasFailure extends boolean> = { 18 requestId: RuleExecutionCorrelationId; 19 orgId: string; 20 itemSubmission: Pick< 21 ItemSubmission, 22 'submissionId' | 'creator' | 'itemId' | 'itemType' | 'submissionTime' 23 > & 24 (HasFailure extends false 25 ? { data: NormalizedItemData } 26 : { data: NormalizedItemData | RawItemData }); 27 failureReason: HasFailure extends true 28 ? string 29 : HasFailure extends false 30 ? undefined 31 : string | undefined; 32}; 33 34export type ContentDetailsApiRequestLogEntry = { 35 orgId: string; 36 contentId: string; 37 failureReason?: string; 38}; 39 40class ContentApiLogger { 41 constructor( 42 private readonly analytics: Dependencies['DataWarehouseAnalytics'], 43 private readonly dataWarehouse: Dependencies['DataWarehouse'], 44 private readonly tracer: Dependencies['Tracer'], 45 ) {} 46 47 async logContentApiRequest<HasFailure extends boolean>( 48 data: ContentApiRequestLogEntry<HasFailure>, 49 skipBatch: boolean, 50 ) { 51 const { failureReason, itemSubmission } = data; 52 const { itemType } = itemSubmission; 53 const now = new Date(); 54 await this.analytics.bulkWrite( 55 'CONTENT_API_REQUESTS' as any, 56 [ 57 { 58 ds: getUtcDateOnlyString(now), 59 ts: now.valueOf(), 60 item_id: itemSubmission.itemId, 61 item_data: jsonStringifyUnstable(itemSubmission.data), 62 ...(itemSubmission.creator !== undefined 63 ? { 64 item_creator_id: itemSubmission.creator.id, 65 item_creator_type_id: itemSubmission.creator.typeId, 66 } 67 : {}), 68 item_type_kind: itemType.kind, 69 item_type_name: itemType.name, 70 item_type_version: itemType.version, 71 item_type_schema_variant: itemType.schemaVariant, 72 item_type_id: itemType.id, 73 item_type_schema: jsonStringifyUnstable(itemType.schema), 74 item_type_schema_field_roles: itemType.schemaFieldRoles, 75 org_id: data.orgId, 76 request_id: fromCorrelationId(data.requestId), 77 submission_id: itemSubmission.submissionId, 78 79 ...(failureReason != null 80 ? { 81 event: 'REQUEST_FAILED' as const, 82 failure_reason: failureReason, 83 } 84 : { event: 'REQUEST_SUCCEEDED' as const }), 85 }, 86 ], 87 { batchTimeout: skipBatch ? 0 : undefined }, 88 ); 89 } 90 91 async logContentDetailsApiRequest(data: ContentDetailsApiRequestLogEntry) { 92 const { failureReason } = data; 93 const now = new Date(); 94 await this.dataWarehouse.query( 95 `INSERT INTO CONTENT_DETAILS_API_REQUESTS 96 (ds, ts, content_id, org_id, event${ 97 failureReason ? ', failure_reason' : '' 98 }) 99 VALUES (?, ?, ?, ?, ?${failureReason ? `, ?` : ''});`, 100 this.tracer, 101 [ 102 getUtcDateOnlyString(now), 103 now.valueOf(), 104 data.contentId, 105 data.orgId, 106 ...(failureReason != null 107 ? ['REQUEST_FAILED', failureReason] 108 : ['REQUEST_SUCCEEDED']), 109 ], 110 ); 111 } 112} 113 114export default inject( 115 ['DataWarehouseAnalytics', 'DataWarehouse', 'Tracer'], 116 ContentApiLogger, 117); 118export { type ContentApiLogger };