diff --git a/src/packages/external-event/external-event.ts b/src/packages/external-event/external-event.ts index 6c12980..fef7378 100644 --- a/src/packages/external-event/external-event.ts +++ b/src/packages/external-event/external-event.ts @@ -33,7 +33,9 @@ async function uploadExternalEventType(req: Request, res: Response) { // Validate schema is valid JSON Schema const schemaIsValid: boolean = ajv.validateSchema(attribute_schema); if (!schemaIsValid) { - logger.error(`POST /uploadExternalEventType: Schema validation failed for External Event Type ${external_event_type_name}`); + logger.error( + `POST /uploadExternalEventType: Schema validation failed for External Event Type ${external_event_type_name}`, + ); ajv.errors?.forEach(ajvError => logger.error(ajvError)); res.status(500).send({ message: ajv.errors }); return; @@ -41,11 +43,13 @@ async function uploadExternalEventType(req: Request, res: Response) { // Make sure name in schema (title) and provided name match try { - if (attribute_schema["title"] === undefined || attribute_schema.title !== external_event_type_name) { - throw new Error("Schema title does not match provided external event type name.") + if (attribute_schema['title'] === undefined || attribute_schema.title !== external_event_type_name) { + throw new Error('Schema title does not match provided external event type name.'); } } catch (error) { - logger.error(`POST /uploadExternalEventType: Error occurred during External Event Type ${external_event_type_name} upload`); + logger.error( + `POST /uploadExternalEventType: Error occurred during External Event Type ${external_event_type_name} upload`, + ); logger.error((error as Error).message); res.status(500).send({ message: (error as Error).message }); return; @@ -57,7 +61,7 @@ async function uploadExternalEventType(req: Request, res: Response) { const externalEventTypeInsertInput: ExternalEventTypeInsertInput = { attribute_schema: attribute_schema, name: external_event_type_name, - } + }; const response = await fetch(GQL_API_URL, { body: JSON.stringify({ @@ -68,7 +72,9 @@ async function uploadExternalEventType(req: Request, res: Response) { method: 'POST', }); - type CreateExternalEventTypeResponse = { data: { createExternalEventType: { attribute_schema: object, name: string } | null } }; + type CreateExternalEventTypeResponse = { + data: { createExternalEventType: { attribute_schema: object; name: string } | null }; + }; const jsonResponse = await response.json(); const createExternalEventTypeResponse = jsonResponse as CreateExternalEventTypeResponse | HasuraError; diff --git a/src/packages/external-event/gql.ts b/src/packages/external-event/gql.ts index 60f7421..8b9dae3 100644 --- a/src/packages/external-event/gql.ts +++ b/src/packages/external-event/gql.ts @@ -7,5 +7,5 @@ export default { name } } - ` -} + `, +}; diff --git a/src/packages/external-source/external-source.ts b/src/packages/external-source/external-source.ts index 99e59b8..8338052 100644 --- a/src/packages/external-source/external-source.ts +++ b/src/packages/external-source/external-source.ts @@ -1,5 +1,9 @@ import type { Express, Request, Response } from 'express'; -import type { DerivationGroupInsertInput, ExternalSourceInsertInput, ExternalSourceTypeInsertInput } from '../../types/external-source.js'; +import type { + DerivationGroupInsertInput, + ExternalSourceInsertInput, + ExternalSourceTypeInsertInput, +} from '../../types/external-source.js'; import type { ExternalEventInsertInput } from '../../types/external-event.js'; import Ajv from 'ajv'; import { getEnv } from '../../env.js'; @@ -9,9 +13,15 @@ import { externalSourceSchema } from '../schemas/external-event-validation-schem import { HasuraError } from '../../types/hasura.js'; type CreateExternalSourceResponse = { data: { createExternalSource: { name: string } | null } }; -type CreateExternalSourceTypeResponse = { data: { createExternalSourceType: { attribute_schema: object, name: string } | null } }; -type GetExternalSourceTypeAttributeSchemaResponse = { data: { external_source_type_by_pk: { attribute_schema: object } | null } }; -type GetExternalEventTypeAttributeSchemaResponse = { data: { external_event_type_by_pk: { attribute_schema: object } | null } }; +type CreateExternalSourceTypeResponse = { + data: { createExternalSourceType: { attribute_schema: object; name: string } | null }; +}; +type GetExternalSourceTypeAttributeSchemaResponse = { + data: { external_source_type_by_pk: { attribute_schema: object } | null }; +}; +type GetExternalEventTypeAttributeSchemaResponse = { + data: { external_event_type_by_pk: { attribute_schema: object } | null }; +}; const logger = getLogger('packages/external-source/external-source'); const { HASURA_API_URL } = getEnv(); @@ -30,7 +40,6 @@ async function uploadExternalSourceType(req: Request, res: Response) { const { external_source_type_name, attribute_schema } = body; logger.info(`POST /uploadExternalSourceType: Uploading External Source Type: ${external_source_type_name}`); - const headers: HeadersInit = { Authorization: authorizationHeader ?? '', 'Content-Type': 'application/json', @@ -44,7 +53,9 @@ async function uploadExternalSourceType(req: Request, res: Response) { // and only really get punished for it when validating a source. const schemaIsValid: boolean = ajv.validateSchema(attribute_schema); if (!schemaIsValid) { - logger.error(`POST /uploadExternalSourceType: Schema validation failed for External Source Type ${external_source_type_name}`); + logger.error( + `POST /uploadExternalSourceType: Schema validation failed for External Source Type ${external_source_type_name}`, + ); ajv.errors?.forEach(ajvError => logger.error(ajvError)); res.status(500).send({ message: ajv.errors }); return; @@ -54,8 +65,10 @@ async function uploadExternalSourceType(req: Request, res: Response) { // Make sure name in schema (title) and provided name match try { - if (attribute_schema["title"] === undefined || attribute_schema.title !== external_source_type_name) { - throw new Error(`${external_source_type_name} attribute schema title does not match provided external source type name.`) + if (attribute_schema['title'] === undefined || attribute_schema.title !== external_source_type_name) { + throw new Error( + `${external_source_type_name} attribute schema title does not match provided external source type name.`, + ); } } catch (error) { res.status(500).send({ message: (error as Error).message }); @@ -66,7 +79,7 @@ async function uploadExternalSourceType(req: Request, res: Response) { const externalSourceTypeInput: ExternalSourceTypeInsertInput = { attribute_schema: attribute_schema, name: external_source_type_name, - } + }; const response = await fetch(GQL_API_URL, { body: JSON.stringify({ @@ -89,22 +102,9 @@ async function uploadExternalSource(req: Request, res: Response) { headers: { 'x-hasura-role': roleHeader, 'x-hasura-user-id': userHeader }, } = req; const { body } = req; - const { - external_events, - source - } = body; - const { - attributes, - derivation_group_name, - key, - source_type_name, - period, - valid_at - } = source; - const { - end_time, - start_time - } = period; + const { external_events, source } = body; + const { attributes, derivation_group_name, key, source_type_name, period, valid_at } = source; + const { end_time, start_time } = period; const headers: HeadersInit = { Authorization: authorizationHeader ?? '', 'Content-Type': 'application/json', @@ -112,7 +112,7 @@ async function uploadExternalSource(req: Request, res: Response) { 'x-hasura-user-id': userHeader ? `${userHeader}` : '', }; - logger.info(`POST /uploadExternalSource: Uploading External Source: ${key}`) + logger.info(`POST /uploadExternalSource: Uploading External Source: ${key}`); // Verify that this is a valid external source! let sourceIsValid: boolean = false; @@ -130,28 +130,34 @@ async function uploadExternalSource(req: Request, res: Response) { body: JSON.stringify({ query: gql.GET_EXTERNAL_SOURCE_TYPE_ATTRIBUTE_SCHEMA, variables: { - name: source_type_name - } + name: source_type_name, + }, }), headers, - method: 'POST' + method: 'POST', }); // Validate the attributes on the External Source let sourceAttributesAreValid: boolean = false; let sourceSchema: Ajv.ValidateFunction | undefined = undefined; - const sourceTypeResponseJSON = await sourceAttributeSchema.json(); - const getExternalSourceTypeAttributeSchemaResponse = sourceTypeResponseJSON as GetExternalSourceTypeAttributeSchemaResponse | HasuraError; - if ((getExternalSourceTypeAttributeSchemaResponse as GetExternalSourceTypeAttributeSchemaResponse).data?.external_source_type_by_pk?.attribute_schema !== null) { - const { data: { external_source_type_by_pk: sourceAttributeSchema } } = getExternalSourceTypeAttributeSchemaResponse as GetExternalSourceTypeAttributeSchemaResponse; + const sourceTypeResponseJSON = await sourceAttributeSchema.json(); + const getExternalSourceTypeAttributeSchemaResponse = sourceTypeResponseJSON as + | GetExternalSourceTypeAttributeSchemaResponse + | HasuraError; + if ( + (getExternalSourceTypeAttributeSchemaResponse as GetExternalSourceTypeAttributeSchemaResponse).data + ?.external_source_type_by_pk?.attribute_schema !== null + ) { + const { + data: { external_source_type_by_pk: sourceAttributeSchema }, + } = getExternalSourceTypeAttributeSchemaResponse as GetExternalSourceTypeAttributeSchemaResponse; if (sourceAttributeSchema !== undefined && sourceAttributeSchema !== null) { sourceSchema = ajv.compile(sourceAttributeSchema.attribute_schema); sourceAttributesAreValid = await sourceSchema(attributes); - } - else { + } else { // source type does not exist! logger.error(`POST /uploadExternalSource: External Source Type ${source_type_name} does not exist!`); - res.status(500).send({ message: `External Source Type ${source_type_name} does not exist!`}); + res.status(500).send({ message: `External Source Type ${source_type_name} does not exist!` }); return; } } @@ -171,11 +177,12 @@ async function uploadExternalSource(req: Request, res: Response) { // Get the attribute schema(s) for all external event types used by the source's events // get list of all used event types - const usedExternalEventTypes = external_events.map((externalEvent: ExternalEventInsertInput) => externalEvent.event_type_name).reduce( - (acc: string[], externalEventType: string) => { + const usedExternalEventTypes = external_events + .map((externalEvent: ExternalEventInsertInput) => externalEvent.event_type_name) + .reduce((acc: string[], externalEventType: string) => { if (!acc.includes(externalEventType)) { - acc.push(externalEventType) - }; + acc.push(externalEventType); + } return acc; }, []); @@ -185,17 +192,24 @@ async function uploadExternalSource(req: Request, res: Response) { body: JSON.stringify({ query: gql.GET_EXTERNAL_EVENT_TYPE_ATTRIBUTE_SCHEMA, variables: { - name: eventType - } + name: eventType, + }, }), headers, - method: 'POST' + method: 'POST', }); - const eventTypeJSONResponse = await eventAttributeSchema.json(); - const getExternalEventTypeAttributeSchemaResponse = eventTypeJSONResponse as GetExternalEventTypeAttributeSchemaResponse | HasuraError; + const eventTypeJSONResponse = await eventAttributeSchema.json(); + const getExternalEventTypeAttributeSchemaResponse = eventTypeJSONResponse as + | GetExternalEventTypeAttributeSchemaResponse + | HasuraError; - if ((getExternalEventTypeAttributeSchemaResponse as GetExternalEventTypeAttributeSchemaResponse).data?.external_event_type_by_pk?.attribute_schema !== null) { - const { data: { external_event_type_by_pk: eventAttributeSchema } } = getExternalEventTypeAttributeSchemaResponse as GetExternalEventTypeAttributeSchemaResponse; + if ( + (getExternalEventTypeAttributeSchemaResponse as GetExternalEventTypeAttributeSchemaResponse).data + ?.external_event_type_by_pk?.attribute_schema !== null + ) { + const { + data: { external_event_type_by_pk: eventAttributeSchema }, + } = getExternalEventTypeAttributeSchemaResponse as GetExternalEventTypeAttributeSchemaResponse; if (eventAttributeSchema !== undefined && eventAttributeSchema !== null) { usedExternalEventTypesAttributesSchemas[eventType] = ajv.compile(eventAttributeSchema.attribute_schema); } @@ -205,10 +219,16 @@ async function uploadExternalSource(req: Request, res: Response) { for (const externalEvent of external_events) { try { const currentEventType = externalEvent.event_type_name; - const currentEventSchema: Ajv.ValidateFunction = usedExternalEventTypesAttributesSchemas[currentEventType]; + const currentEventSchema: Ajv.ValidateFunction = usedExternalEventTypesAttributesSchemas[currentEventType]; const eventAttributesAreValid = await currentEventSchema(externalEvent.attributes); if (!eventAttributesAreValid) { - throw new Error(`External Event '${externalEvent.key}' does not have a valid set of attributes, per it's type's schema:\n${JSON.stringify(currentEventSchema.errors)}`); + throw new Error( + `External Event '${ + externalEvent.key + }' does not have a valid set of attributes, per it's type's schema:\n${JSON.stringify( + currentEventSchema.errors, + )}`, + ); } } catch (error) { res.status(500).send({ message: (error as Error).message }); @@ -219,21 +239,21 @@ async function uploadExternalSource(req: Request, res: Response) { // Run the Hasura migration for creating an external source const derivationGroupInsert: DerivationGroupInsertInput = { name: derivation_group_name, - source_type_name: source_type_name - } + source_type_name: source_type_name, + }; const externalSourceInsert: ExternalSourceInsertInput = { attributes: attributes, derivation_group_name: derivation_group_name, end_time: end_time, external_events: { - data: external_events + data: external_events, }, key: key, source_type_name: source_type_name, start_time: start_time, - valid_at: valid_at - } + valid_at: valid_at, + }; const response = await fetch(GQL_API_URL, { body: JSON.stringify({ diff --git a/src/packages/external-source/gql.ts b/src/packages/external-source/gql.ts index eb6c435..78fae46 100644 --- a/src/packages/external-source/gql.ts +++ b/src/packages/external-source/gql.ts @@ -1,4 +1,5 @@ -export default { // TODO: discuss upset for derivation group +export default { + // TODO: discuss upset for derivation group CREATE_EXTERNAL_SOURCE: `#graphql mutation CreateExternalSource( $derivation_group: derivation_group_insert_input!, @@ -54,5 +55,5 @@ export default { // TODO: discuss upset for derivation group attribute_schema } } - ` -} + `, +}; diff --git a/src/packages/schemas/external-event-validation-schemata.ts b/src/packages/schemas/external-event-validation-schemata.ts index 5fcabd8..7d870a7 100644 --- a/src/packages/schemas/external-event-validation-schemata.ts +++ b/src/packages/schemas/external-event-validation-schemata.ts @@ -2,7 +2,6 @@ // Currently, we do the latter but the former doesn't seem like a bad idea! // The main argument against the former is what we have works and introducing new schemas could be a rabbit hole. - // export const externalEventTypeSchema = { // additionalProperties: false, // properties: { @@ -77,64 +76,67 @@ // }; export const externalSourceSchema = { - additionalProperties: false, - properties: { - external_events: { - items: { - additionalProperties: false, - properties: { - attributes: { - additionalProperties: true, - properties: {}, - required: [], - type: 'object' - }, - duration: { type: 'string' }, - event_type_name: { type: 'string' }, - key: { type: 'string' }, - start_time: { type: 'string' }, - }, - required: ['duration', 'event_type_name', 'key', 'attributes', 'start_time'], - type: 'object' - }, - type: 'array' + additionalProperties: false, + properties: { + external_events: { + items: { + additionalProperties: false, + properties: { + attributes: { + additionalProperties: true, + properties: {}, + required: [], + type: 'object', + }, + duration: { type: 'string' }, + event_type_name: { type: 'string' }, + key: { type: 'string' }, + start_time: { type: 'string' }, + }, + required: ['duration', 'event_type_name', 'key', 'attributes', 'start_time'], + type: 'object', + }, + type: 'array', + }, + source: { + additionalProperties: false, + properties: { + attributes: { + additionalProperties: true, + properties: {}, // constrained by type, checked by DB trigger on upload. TODO: CHECK LOCALLY? + required: [], + type: 'object', }, - source: { - additionalProperties: false, - properties: { - attributes: { - additionalProperties: true, - properties: {}, // constrained by type, checked by DB trigger on upload. TODO: CHECK LOCALLY? - required: [], - type: 'object' - }, - derivation_group_name: { type: 'string' }, - key: { type: 'string' }, - period: { - additionalProperties: false, - properties: { - end_time: { - pattern: '^(\\d){4}-([0-3][0-9])-([0-9][0-9])T([0-1][0-9]):([0-5][0-9]):([0-5][0-9])(\\+|-)([0-1][0-9]):([0-5][0-9])$', - type: 'string' - }, - start_time: { - pattern: '^(\\d){4}-([0-3][0-9])-([0-9][0-9])T([0-1][0-9]):([0-5][0-9]):([0-5][0-9])(\\+|-)([0-1][0-9]):([0-5][0-9])$', - type: 'string' - } - }, - required: ['start_time', 'end_time'], - type: 'object' - }, - source_type_name: { type: "string" }, - valid_at: { - pattern: '^(\\d){4}-([0-3][0-9])-([0-9][0-9])T([0-1][0-9]):([0-5][0-9]):([0-5][0-9])(\\+|-)([0-1][0-9]):([0-5][0-9])$', - type: "string" - } + derivation_group_name: { type: 'string' }, + key: { type: 'string' }, + period: { + additionalProperties: false, + properties: { + end_time: { + pattern: + '^(\\d){4}-([0-3][0-9])-([0-9][0-9])T([0-1][0-9]):([0-5][0-9]):([0-5][0-9])(\\+|-)([0-1][0-9]):([0-5][0-9])$', + type: 'string', }, - required: ["key", "source_type_name", "valid_at", "period", "attributes"], - type: 'object' - } + start_time: { + pattern: + '^(\\d){4}-([0-3][0-9])-([0-9][0-9])T([0-1][0-9]):([0-5][0-9]):([0-5][0-9])(\\+|-)([0-1][0-9]):([0-5][0-9])$', + type: 'string', + }, + }, + required: ['start_time', 'end_time'], + type: 'object', + }, + source_type_name: { type: 'string' }, + valid_at: { + pattern: + '^(\\d){4}-([0-3][0-9])-([0-9][0-9])T([0-1][0-9]):([0-5][0-9]):([0-5][0-9])(\\+|-)([0-1][0-9]):([0-5][0-9])$', + type: 'string', + }, + }, + required: ['key', 'source_type_name', 'valid_at', 'period', 'attributes'], + type: 'object', }, - required: ['source', 'external_events'], - type: 'object' -} \ No newline at end of file + }, + required: ['source', 'external_events'], + type: 'object', +}; diff --git a/src/types/external-event.ts b/src/types/external-event.ts index 224d5ce..809e022 100644 --- a/src/types/external-event.ts +++ b/src/types/external-event.ts @@ -4,12 +4,12 @@ export type ExternalEventInsertInput = { duration: string; event_type_name: string; key: string; -} +}; export type ExternalEventTypeInsertInput = { name: string; attribute_schema: object; -} +}; export type ExternalEvent = { key: string; @@ -17,4 +17,4 @@ export type ExternalEvent = { start_time: string; duration: string; attributes: object; -} +}; diff --git a/src/types/external-source.ts b/src/types/external-source.ts index 34e9e5c..72746fd 100644 --- a/src/types/external-source.ts +++ b/src/types/external-source.ts @@ -1,12 +1,12 @@ export type DerivationGroupInsertInput = { name: string; source_type_name: string; -} +}; export type ExternalSourceTypeInsertInput = { name: string; attribute_schema: object; -} +}; export type ExternalSourceInsertInput = { attributes: object; @@ -19,9 +19,9 @@ export type ExternalSourceInsertInput = { event_type_name: string; key: string; }[]; - } + }; key: string; source_type_name: string; start_time: string; valid_at: string; -} +}; diff --git a/test/external_source.validation.test.ts b/test/external_source.validation.test.ts index 26b592a..6c35876 100644 --- a/test/external_source.validation.test.ts +++ b/test/external_source.validation.test.ts @@ -6,56 +6,57 @@ const ajv = Ajv(); // type schemas const correctExternalEventTypeSchema = { - $schema: "http://json-schema.org/draft-07/schema", + $schema: 'http://json-schema.org/draft-07/schema', additionalProperties: false, - description: "Schema for the attributes of the TestEventType Type.", + description: 'Schema for the attributes of the TestEventType Type.', properties: { - code: { type: "string" }, - projectUser: { type: "string" } + code: { type: 'string' }, + projectUser: { type: 'string' }, }, - required: ["projectUser", "code"], - title: "TestEventType", - type: "object", -} + required: ['projectUser', 'code'], + title: 'TestEventType', + type: 'object', +}; const incorrectPassingExternalEventTypeSchema = { - $schema: "http://json-schema.org/draft-07/schema", + $schema: 'http://json-schema.org/draft-07/schema', additionalProperties: false, - descriptionFake: "Schema for the attributes of the TestEventType Type.", + descriptionFake: 'Schema for the attributes of the TestEventType Type.', doesntEvenExist: true, - propertgibberish: { // if you have something like this, it just registers as no properties existing, and fails any inserted events with attributes. - code: { type: "string" }, - projectUser: { type: "string" } + propertgibberish: { + // if you have something like this, it just registers as no properties existing, and fails any inserted events with attributes. + code: { type: 'string' }, + projectUser: { type: 'string' }, }, - requiredgibberish: ["projectUser", "code"], - title: "TestEventType", - type: "object", -} + requiredgibberish: ['projectUser', 'code'], + title: 'TestEventType', + type: 'object', +}; const incorrectFailingExternalEventTypeSchema = { - $schema: "http://json-schema.org/draft-07/schema", + $schema: 'http://json-schema.org/draft-07/schema', additionalProperties: false, - description: "Schema for the attributes of the TestEventType Type.", + description: 'Schema for the attributes of the TestEventType Type.', properties: { - code: { type: "string" }, - projectUser: { type: "string" } + code: { type: 'string' }, + projectUser: { type: 'string' }, }, required: 123, // this fails to validate at all since "required" IS well-defined as a field but expects an array - title: "TestEventType", - type: "object", -} + title: 'TestEventType', + type: 'object', +}; const externalSourceTypeSchema = { - $schema: "http://json-schema.org/draft-07/schema", + $schema: 'http://json-schema.org/draft-07/schema', additionalProperties: false, - description: "Schema for the attributes of the TestSourceType Type.", + description: 'Schema for the attributes of the TestSourceType Type.', properties: { - operator: { type: "string" }, - version: { type: "number" } + operator: { type: 'string' }, + version: { type: 'number' }, }, - required: ["version", "operator"], - title: "TestSourceType", - type: "object" + required: ['version', 'operator'], + title: 'TestSourceType', + type: 'object', }; // compiled schemas @@ -68,54 +69,52 @@ const externalSource = { external_events: [ { attributes: { - "code": "A", - "projectUser": "UserA" + code: 'A', + projectUser: 'UserA', }, duration: '01:10:00', event_type_name: 'TestExternalEventType', key: 'Event01', - start_time: '2024-023T00:23:00Z' + start_time: '2024-023T00:23:00Z', }, { attributes: { - "code": "B", - "projectUser": "UserB" + code: 'B', + projectUser: 'UserB', }, duration: '03:40:00', event_type_name: 'DSNContact', key: 'Event02', - start_time: '2024-021T00:21:00Z' - } + start_time: '2024-021T00:21:00Z', + }, ], source: { - attributes: { + attributes: { operator: 'alpha', - version: 1 + version: 1, }, derivation_group_name: 'TestDerivationGroup', key: 'TestExternalSourceKey', period: { end_time: '2024-01-28T00:00:00+00:00', - start_time: '2024-01-21T00:00:00+00:00' + start_time: '2024-01-21T00:00:00+00:00', }, source_type_name: 'TestExternalSourceType', - valid_at: '2024-01-19T00:00:00+00:00' - } -}; + valid_at: '2024-01-19T00:00:00+00:00', + }, +}; // invalid attributes const invalidSourceAttributes = { operator: 1, - version: 1 -} + version: 1, +}; const invalidEventAttributes = { code: 1, - projectUser: "UserB" -} - + projectUser: 'UserB', +}; describe('validation tests', () => { - // test validating type schema validation (demonstrate you can feed it bogus and its fine, but if an existing field gets a wrong type then its a problem) describe('attribute schema validation', () => { test('validating correct external event type schema', () => { @@ -133,7 +132,7 @@ describe('validation tests', () => { expect(schemaIsValid).toBe(false); const errors = ajv.errors; expect(errors?.length).toBe(1); - expect(errors?.at(0)?.message).toContain('should be array') + expect(errors?.at(0)?.message).toContain('should be array'); }); }); @@ -169,7 +168,8 @@ describe('validation tests', () => { test('correct external event type attribute validation', async () => { let eventAttributesAreValid: boolean = true; for (const external_event of externalSource.external_events) { - eventAttributesAreValid = eventAttributesAreValid && await compiledExternalEventTypeSchema(external_event.attributes); + eventAttributesAreValid = + eventAttributesAreValid && (await compiledExternalEventTypeSchema(external_event.attributes)); } expect(eventAttributesAreValid).toBe(true); });