import chalk from 'chalk'; import { RootConfigRead } from './root-config'; import { JsonObject } from './json-types'; export interface CLICodeFile { fileName: string; fileContent: string; } export interface CLIAlterationHook { key: string; name: string; schema: Record[]; } export interface CLIProductModuleDefinition { key: string; rootConfig: RootConfigRead; readmeMarkdown: string; documents: CLIDocumentsWithSupplementaryTerms; workflows: CLIWorkflows; apiDocsSchema: JsonObject; codeFiles: Array; sourceCodeFiles?: Array; unitTestCodeFiles: Array; productModuleName: string; } export interface CLIProductModuleDefinitionOutput extends CLIProductModuleDefinition { version: string; productModuleName: string; organizationId: string; productModuleDefinitionId: string; } export interface SupplementaryTermsFile { type: string; pdfBase64: string; } export enum CLIDocumentKey { TermsPdfBase64 = 'termsPdfBase64', PolicyScheduleHtml = 'policyScheduleHtml', WelcomeLetterHtml = 'welcomeLetterHtml', AnniversaryLetterHtml = 'anniversaryLetterHtml', QuoteSummaryHtml = 'quoteSummaryHtml', MemberCertificateHtml = 'memberCertificateHtml', CertificateHtml = 'certificateHtml', InvoiceTemplateHtml = 'invoiceTemplateHtml', } export const isCLIDocumentKey = (key: string): key is CLIDocumentKey => { return Object.values(CLIDocumentKey).includes(key as CLIDocumentKey); }; /** The keys of this type are the values of the CLIDocumentKey enum. All properties except 'termsPdfBase64' are optional. */ export type CLIDocuments = { [K in CLIDocumentKey]: K extends CLIDocumentKey.TermsPdfBase64 ? string : string | undefined; }; export interface CLIDocumentsWithSupplementaryTerms extends CLIDocuments { supplementaryTerms: SupplementaryTermsFile[]; } export interface CLIRequiredWorkflows { quoteSchema: Record[]; applicationSchema: Record[]; claimsBlocks: Record[]; } export interface CLIWorkflows extends CLIRequiredWorkflows { alterationHooks: CLIAlterationHook[]; applicationAlterationHooks: CLIAlterationHook[]; memberAlterationHooks: CLIAlterationHook[]; productModuleEmbedConfig?: Record; } export interface NetworkProductModuleDefinitionRequest { root_config: RootConfigRead; readme_markdown: string; documents: NetworkCLIDocuments; workflows: NetworkCLIWorkflows; api_docs_schema: JsonObject; code_files: Array; source_code_files?: Array; unit_test_code_files: Array; } export interface NetworkProductModuleDefinitionResponse extends NetworkProductModuleDefinitionRequest { product_module_key: string; product_module_name: string; organization_id: string; product_module_definition_id: string; version: string; } export interface NetworkCLICodeFile { file_name: string; file_content: string; } export interface NetworkCLIDocuments { terms_pdf_base_64: string; policy_schedule_html?: string; welcome_letter_html?: string; anniversary_letter_html?: string; quote_summary_html?: string; member_certificate_html?: string; certificate_html?: string; invoice_template_html?: string; supplementary_terms: { type: string; pdf_base_64: string; }[]; } export interface NetworkCLIWorkflows { quote_schema: Record[]; application_schema: Record[]; claims_blocks: Record[]; alteration_hooks: CLIAlterationHook[]; application_alteration_hooks?: CLIAlterationHook[]; member_alteration_hooks?: CLIAlterationHook[]; product_module_embed_config?: Record; } export const isValidApiDocsSchema = (schema: unknown): schema is JsonObject => schema != null && typeof schema === 'object' && !Array.isArray(schema); /** * Coerce an api_docs_schema value to a JsonObject, warning loudly if the value * is in an unexpected shape (e.g. the legacy array format). Falling back to * `{}` silently on push would overwrite valid remote docs. */ const coerceApiDocsSchema = (schema: unknown, source: 'remote' | 'local'): JsonObject => { if (isValidApiDocsSchema(schema)) { return schema; } console.log( chalk.yellow( `Warning: ${source} api_docs_schema is not a valid JSON object (legacy array format?). Falling back to empty object.`, ), ); return {}; }; export const productModuleDefinitionFromNetwork = ( networkDefinition: NetworkProductModuleDefinitionResponse, ): CLIProductModuleDefinitionOutput => { return { key: networkDefinition.product_module_key, productModuleName: networkDefinition.product_module_name, organizationId: networkDefinition.organization_id, readmeMarkdown: networkDefinition.readme_markdown, rootConfig: networkDefinition.root_config, productModuleDefinitionId: networkDefinition.product_module_definition_id, documents: { policyScheduleHtml: networkDefinition.documents.policy_schedule_html, welcomeLetterHtml: networkDefinition.documents.welcome_letter_html, anniversaryLetterHtml: networkDefinition.documents.anniversary_letter_html, quoteSummaryHtml: networkDefinition.documents.quote_summary_html, termsPdfBase64: networkDefinition.documents.terms_pdf_base_64, memberCertificateHtml: networkDefinition.documents.member_certificate_html, certificateHtml: networkDefinition.documents.certificate_html, invoiceTemplateHtml: networkDefinition.documents.invoice_template_html, supplementaryTerms: networkDefinition.documents.supplementary_terms?.map((file) => ({ type: file.type, pdfBase64: file.pdf_base_64, })), }, workflows: { quoteSchema: networkDefinition.workflows.quote_schema, applicationSchema: networkDefinition.workflows.application_schema, claimsBlocks: networkDefinition.workflows.claims_blocks, alterationHooks: networkDefinition.workflows.alteration_hooks, applicationAlterationHooks: networkDefinition.workflows.application_alteration_hooks || [], memberAlterationHooks: networkDefinition.workflows.member_alteration_hooks || [], productModuleEmbedConfig: networkDefinition.workflows.product_module_embed_config, }, apiDocsSchema: coerceApiDocsSchema(networkDefinition.api_docs_schema, 'remote'), codeFiles: networkDefinition.code_files.map((code_file) => ({ fileName: code_file.file_name, fileContent: code_file.file_content, })), sourceCodeFiles: networkDefinition.source_code_files?.map((source_code_file) => ({ fileName: source_code_file.file_name, fileContent: source_code_file.file_content, })), unitTestCodeFiles: networkDefinition.unit_test_code_files.map((unit_test_code_file) => ({ fileName: unit_test_code_file.file_name, fileContent: unit_test_code_file.file_content, })), version: networkDefinition.version, }; }; export const productModuleDefinitionToNetwork = ( localDefinition: CLIProductModuleDefinition, ): NetworkProductModuleDefinitionRequest => { return { root_config: localDefinition.rootConfig, readme_markdown: localDefinition.readmeMarkdown, documents: { policy_schedule_html: localDefinition.documents.policyScheduleHtml, welcome_letter_html: localDefinition.documents.welcomeLetterHtml, anniversary_letter_html: localDefinition.documents.anniversaryLetterHtml, quote_summary_html: localDefinition.documents.quoteSummaryHtml, terms_pdf_base_64: localDefinition.documents.termsPdfBase64, member_certificate_html: localDefinition.documents.memberCertificateHtml, certificate_html: localDefinition.documents.certificateHtml, invoice_template_html: localDefinition.documents.invoiceTemplateHtml, supplementary_terms: localDefinition.documents.supplementaryTerms.map((file) => ({ type: file.type, pdf_base_64: file.pdfBase64, })), }, workflows: { quote_schema: localDefinition.workflows.quoteSchema, application_schema: localDefinition.workflows.applicationSchema, claims_blocks: localDefinition.workflows.claimsBlocks, alteration_hooks: localDefinition.workflows.alterationHooks, application_alteration_hooks: localDefinition.workflows.applicationAlterationHooks, ...(localDefinition.workflows.memberAlterationHooks.length > 0 ? { member_alteration_hooks: localDefinition.workflows.memberAlterationHooks } : {}), product_module_embed_config: localDefinition.workflows.productModuleEmbedConfig, }, api_docs_schema: coerceApiDocsSchema(localDefinition.apiDocsSchema, 'local'), code_files: localDefinition.codeFiles.map((codeFile) => ({ file_content: codeFile.fileContent, file_name: codeFile.fileName, })), source_code_files: localDefinition.sourceCodeFiles?.map((sourceCodeFile) => ({ file_content: sourceCodeFile.fileContent, file_name: sourceCodeFile.fileName, })), unit_test_code_files: localDefinition.unitTestCodeFiles.map((unitTestCodeFile) => ({ file_content: unitTestCodeFile.fileContent, file_name: unitTestCodeFile.fileName, })), }; };