@sprucelabs/spruce-cli
Version:
Command line interface for building Spruce skills.
280 lines (241 loc) • 9.6 kB
text/typescript
import { EventContract } from '@sprucelabs/mercury-types'
import { Schema, SchemaError, SchemaTemplateItem } from '@sprucelabs/schema'
import {
eventContractUtil,
NamedEventSignature,
} from '@sprucelabs/spruce-event-utils'
import {
MERCURY_API_NAMESPACE,
namesUtil,
} from '@sprucelabs/spruce-skill-utils'
import {
EventContractTemplateItem,
EventSignatureTemplateItem,
} from '@sprucelabs/spruce-templates'
import schemaDiskUtil from '../features/schema/utilities/schemaDisk.utility'
import SchemaTemplateItemBuilder from './SchemaTemplateItemBuilder'
export default class EventTemplateItemBuilder {
public buildTemplateItems(options: {
contracts: EventContract[]
localNamespace?: string
eventBuilderFile?: string
}): {
eventContractTemplateItems: EventContractTemplateItem[]
schemaTemplateItems: SchemaTemplateItem[]
} {
const { contracts, localNamespace, eventBuilderFile } = options
const eventContractTemplateItems: EventContractTemplateItem[] = []
const schemaTemplateItems: SchemaTemplateItem[] = []
for (const contract of contracts) {
const {
schemaTemplateItems: schemaItems,
eventContractTemplateItems: contractItems,
} = this.buildTemplateItemsForContract(
contract,
localNamespace,
eventBuilderFile
)
eventContractTemplateItems.push(...contractItems)
schemaTemplateItems.push(...schemaItems)
}
eventContractTemplateItems.sort((a, b) => {
if (a.nameCamel > b.nameCamel) {
return 1
} else if (a.nameCamel < b.nameCamel) {
return -1
}
return 0
})
return { eventContractTemplateItems, schemaTemplateItems }
}
public buildEventTemplateItemForName(
contracts: EventContract[],
fullyQualifiedEventName: string
): {
responsePayloadSchemaTemplateItem: SchemaTemplateItem | undefined
emitPayloadSchemaTemplateItem: SchemaTemplateItem | undefined
} {
for (const contract of contracts) {
const namedSignatures =
eventContractUtil.getNamedEventSignatures(contract)
for (const namedSig of namedSignatures) {
if (
namedSig.fullyQualifiedEventName === fullyQualifiedEventName
) {
const schemaTemplateItems: SchemaTemplateItem[] =
this.mapEventSigsToSchemaTemplateItems(namedSignatures)
const signatureTemplateItem: EventSignatureTemplateItem =
this.buildEventSigTemplateItem(
namedSig,
schemaTemplateItems
)
return {
emitPayloadSchemaTemplateItem:
signatureTemplateItem.emitPayloadSchema,
responsePayloadSchemaTemplateItem:
signatureTemplateItem.responsePayloadSchema,
}
}
}
}
throw new SchemaError({
code: 'INVALID_PARAMETERS',
parameters: ['fullyQualifiedEventName'],
friendlyMessages: [
`I could not find any events that match ${fullyQualifiedEventName}.`,
],
})
}
private buildTemplateItemsForContract(
contract: EventContract,
localNamespace?: string,
eventBuilderFile?: string
): {
eventContractTemplateItems: EventContractTemplateItem[]
schemaTemplateItems: SchemaTemplateItem[]
} {
const namedSignatures =
eventContractUtil.getNamedEventSignatures(contract)
const schemaTemplateItems: SchemaTemplateItem[] =
this.mapEventSigsToSchemaTemplateItems(namedSignatures)
const eventContractTemplateItems: EventContractTemplateItem[] = []
for (const namedSig of namedSignatures) {
const item: EventContractTemplateItem =
this.buildTemplateItemForEventSignature(
namedSig,
schemaTemplateItems,
namedSig.eventNamespace === localNamespace,
eventBuilderFile
)
eventContractTemplateItems.push(item)
}
return {
eventContractTemplateItems,
schemaTemplateItems,
}
}
private buildTemplateItemForEventSignature(
namedSig: NamedEventSignature,
schemaTemplateItems: SchemaTemplateItem[],
isLocal: boolean,
eventBuilderFile?: string
) {
const namespacePascal = this.sigToNamespacePascal(namedSig)
const signatureTemplateItem: EventSignatureTemplateItem =
this.buildEventSigTemplateItem(namedSig, schemaTemplateItems)
const item: EventContractTemplateItem = {
nameCamel: namesUtil.toCamel(namedSig.eventName),
namePascal: namesUtil.toPascal(namedSig.eventName),
version: namedSig.version ?? '***MISSING***',
isLocal,
namespace: namesUtil.toKebab(
namedSig.eventNamespace ?? MERCURY_API_NAMESPACE
),
namespaceCamel: namesUtil.toCamel(
namedSig.eventNamespace ?? MERCURY_API_NAMESPACE
),
imports: [
signatureTemplateItem.emitPayloadSchema as SchemaTemplateItem,
signatureTemplateItem.responsePayloadSchema as SchemaTemplateItem,
]
.filter((i) => !!i)
.map((item: SchemaTemplateItem) => ({
package: schemaDiskUtil.resolvePath({
destination: '#spruce/schemas',
schema: item.schema,
shouldIncludeFileExtension: false,
}),
importAs: `${item.nameCamel}Schema`,
})),
namespacePascal,
eventSignatures: {
[namedSig.fullyQualifiedEventName]: signatureTemplateItem,
},
}
item.imports.push({
importAs: '{ buildEventContract }',
package: eventBuilderFile ?? '@sprucelabs/mercury-types',
})
if (
namedSig.signature.listenPermissionContract ||
namedSig.signature.emitPermissionContract
) {
item.imports.push({
importAs: '{ buildPermissionContract }',
package: eventBuilderFile ?? '@sprucelabs/mercury-types',
})
}
return item
}
private buildEventSigTemplateItem(
namedSig: NamedEventSignature,
schemaItems: SchemaTemplateItem[]
) {
const { emitPayloadSchema, responsePayloadSchema, ...signature } =
namedSig.signature
const signatureTemplateItem: EventSignatureTemplateItem = {
...signature,
}
if (emitPayloadSchema) {
signatureTemplateItem.emitPayloadSchema = schemaItems.find(
(i) =>
i.id === emitPayloadSchema.id &&
(!emitPayloadSchema.namespace ||
i.namespace === emitPayloadSchema.namespace)
)
}
if (responsePayloadSchema) {
signatureTemplateItem.responsePayloadSchema = schemaItems.find(
(i) =>
i.id === responsePayloadSchema.id &&
(!responsePayloadSchema.namespace ||
i.namespace === responsePayloadSchema.namespace)
)
}
return signatureTemplateItem
}
private mapEventSigsToSchemaTemplateItems(
namedSignatures: NamedEventSignature[]
) {
const schemasByNamespace: Record<string, Schema[]> =
this.mapEventSigsToSchemasByNamepace(namedSignatures)
const schemaTemplateItemBuilder = new SchemaTemplateItemBuilder('Cli')
const schemaItems: SchemaTemplateItem[] =
schemaTemplateItemBuilder.buildTemplateItems(
schemasByNamespace,
'#spruce/events'
)
return schemaItems
}
private mapEventSigsToSchemasByNamepace(
namedSignatures: NamedEventSignature[]
) {
const schemasByNamespace: Record<string, Schema[]> = {}
for (const namedSig of namedSignatures) {
const namespacePascal = this.sigToNamespacePascal(namedSig)
const { emitPayloadSchema, responsePayloadSchema } =
namedSig.signature
if (!schemasByNamespace[namespacePascal]) {
schemasByNamespace[namespacePascal] = []
}
if (emitPayloadSchema) {
schemasByNamespace[namespacePascal].push({
version: namedSig.version,
...emitPayloadSchema,
})
}
if (responsePayloadSchema) {
schemasByNamespace[namespacePascal].push({
version: namedSig.version,
...responsePayloadSchema,
})
}
}
return schemasByNamespace
}
private sigToNamespacePascal(namedSig: NamedEventSignature) {
return namesUtil.toPascal(
namedSig.eventNamespace ?? MERCURY_API_NAMESPACE
)
}
}