diff --git a/src/cli/run/validate.ts b/src/cli/run/validate.ts index 1f408c4..b750aa3 100644 --- a/src/cli/run/validate.ts +++ b/src/cli/run/validate.ts @@ -6,12 +6,13 @@ import { Command } from 'commander'; import validateSchema from '../../services/schema/schemaValidator/validateSchema'; import validateQueryParamsBuilder from '../../services/query/queryParamsBuilderValidator/validateQueryParamsBuilder'; -import validateTranslation from '../../services/translation/validate'; +import validateTranslation from '../../services/translation/translationValidator/validateTranslation'; const helperText = ` Usage: - --validate-schema sample-widget/schema.json - --validate-query-params sample-widget/queryParamsBuilder.json + widget-builder validate --schema sample-widget + widget-builder validate --query-params sample-widget + widget-builder validate --translation sample-widget `; const validateCommands = () => { @@ -24,12 +25,13 @@ const validateCommands = () => { .option('--translation', 'validates schema_translation.json file') .addHelpText('afterAll', helperText) .action((name, options) => { - const directory = path.resolve('.'); - if (options.validateSchema) { + const directory = path.resolve('.', name); + + if (options.schema) { validateSchema(directory); } - if (options.validateQueryParamsBuilder) { + if (options.queryParams) { validateQueryParamsBuilder(directory); } diff --git a/src/config.ts b/src/config.ts index abdf37a..a992a75 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,2 +1,3 @@ export const port: string = process.env.WIDGET_BUILDER_PORT || '8080'; export const host: string = process.env.WIDGET_BUILDER_HOST || 'http://localhost'; +export const channelId = process.env.WIDGET_BUILDER_CHANNEL_ID ? parseInt(process.env.WIDGET_BUILDER_CHANNEL_ID, 10) : 1; diff --git a/src/server/index.ts b/src/server/index.ts index fcd385d..80a1c67 100755 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -16,6 +16,7 @@ import generateQueryParams from '../services/query/generateQueryParams'; import validateQueryParamsBuilder from '../services/query/queryParamsBuilderValidator/validateQueryParamsBuilder'; import renderWidget from '../services/widgetRenderer/widgetRenderer'; import generateConfig from '../services/widgetConfig/generateConfig'; +import validateTranslation from '../services/translation/translationValidator/validateTranslation'; const BUILDER_ADDRESS = `${host}:${port}`; @@ -62,6 +63,14 @@ function setupFileWatcher({ directory, sockets, options }: Watcher) { break; + case WidgetFileType.TRANSLATION: + validateTranslation(directory); + + liveReload({ + directory, sockets, fileEvent, filePath, options, + }); + break; + case WidgetFileType.META: // We are not currently handling this file type break; diff --git a/src/services/api/widget.ts b/src/services/api/widget.ts index e6d1933..3180dea 100644 --- a/src/services/api/widget.ts +++ b/src/services/api/widget.ts @@ -6,6 +6,7 @@ import { WidgetConfiguration } from '../schema/schemaParser/schemaParser'; export const widgetApi = { widgetPreviewRender: `${AUTH_CONFIG.apiPath}/content/widget-templates/preview`, widgetTemplatePublish: `${AUTH_CONFIG.apiPath}/content/widget-templates`, + widgetTemplateList: `${AUTH_CONFIG.apiPath}/content/widget-templates`, }; interface WidgetPreviewRenderResponse { @@ -22,6 +23,7 @@ export interface WidgetPreviewRenderRequest { storefront_api_query: string; storefront_api_query_params: object; channel_id: number; + schema_translations?: string; } export function getWidget(data: WidgetPreviewRenderRequest): Promise { @@ -64,3 +66,52 @@ export const publishWidget = ( .then(({ data: { data } }) => resolve(data)) .catch(error => reject(error)); }); + +export const getWidgetTemplate = ( + name: string +): Promise => new Promise((resolve, reject) => + getAllTemplates() + .then((data) => { + const match = data.find( + template => name === template.name, + ); + + resolve(match?.uuid || ''); + }) + .catch(error => reject(error))) + +export interface WidgetTemplateResult { + name: string; + schema: any[]; + template: string; + storefront_api_query: string; + uuid: string; + kind: string; + date_created: string; + date_modified: string; + current_version_uuid: string; + icon_name: string; + } + +const getAllTemplates = async (page: number = 1): Promise => { + let listResults: WidgetTemplateResult[] = []; + let done = false; + + while (!done) { + const { data } = (await Axios({ + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + 'X-Auth-Client': AUTH_CONFIG.authId, + 'X-Auth-Token': AUTH_CONFIG.authToken, + }, + url: `${widgetApi.widgetTemplateList}?limit=250&page=${page}`, + })).data; + + done = data.length === 0; + page++; + listResults = listResults.concat(data); + } + + return listResults; +} diff --git a/src/services/translation/translationLoader/translationLoader.test.ts b/src/services/translation/translationLoader/translationLoader.test.ts index 3de2f2c..777cf9e 100644 --- a/src/services/translation/translationLoader/translationLoader.test.ts +++ b/src/services/translation/translationLoader/translationLoader.test.ts @@ -1,8 +1,6 @@ import fs from 'fs'; -import { messages } from '../../../messages'; - -import translationLoader, { handleSchemaLoader } from './translationLoader'; +import translationLoader, { handleSchemaLoader, translationDefaultPayload } from './translationLoader'; const schemaData = fs.readFileSync('src/services/__fixtures__/schema_translations.json', 'utf8').toString(); @@ -33,7 +31,7 @@ describe('Schema Loader', () => { it('should return with no data', () => { const result = translationLoader('dummyPath'); - expect(result).rejects.toEqual(messages.invalidTranslationSchema()); + expect(result).resolves.toEqual(translationDefaultPayload); }); }); }); diff --git a/src/services/translation/translationLoader/translationLoader.ts b/src/services/translation/translationLoader/translationLoader.ts index ffee814..f0fc03d 100644 --- a/src/services/translation/translationLoader/translationLoader.ts +++ b/src/services/translation/translationLoader/translationLoader.ts @@ -3,17 +3,20 @@ import * as fs from 'fs'; import WidgetFileType, { FileLoaderResponse } from '../../../types'; import { messages } from '../../../messages'; -export function handleSchemaLoader(error: Error | null, schemaData: string): FileLoaderResponse { - const payload = { - type: WidgetFileType.TRANSLATION, - data: '', - }; +export const translationDefaultPayload = { + type: WidgetFileType.TRANSLATION, + data: '{}', +}; +export function handleSchemaLoader(error: Error | null, schemaData: string): FileLoaderResponse { if (schemaData && !error) { - payload.data = schemaData; + return { + ...translationDefaultPayload, + data: schemaData, + } } - return payload; + return translationDefaultPayload; } export default function translationLoader(widgetDir: string): Promise { @@ -21,7 +24,11 @@ export default function translationLoader(widgetDir: string): Promise { + (error: NodeJS.ErrnoException, schemaData: string) => { + if (error && error.code === 'ENOENT') { + resolve(translationDefaultPayload); + } + const schemaResults = handleSchemaLoader(error, schemaData); if (!schemaResults.data) { diff --git a/src/services/translation/validate.ts b/src/services/translation/translationValidator/validateTranslation.ts similarity index 62% rename from src/services/translation/validate.ts rename to src/services/translation/translationValidator/validateTranslation.ts index 277c6a0..89addbd 100644 --- a/src/services/translation/validate.ts +++ b/src/services/translation/translationValidator/validateTranslation.ts @@ -1,8 +1,8 @@ -import { FileLoaderResponse } from '../../types'; -import { log } from '../../messages'; +import { FileLoaderResponse } from '../../../types'; +import { log } from '../../../messages'; -import TranslationValidator from './translationValidator/translationValidator'; -import translationLoader from './translationLoader/translationLoader'; +import TranslationValidator from './translationValidator'; +import translationLoader from '../translationLoader/translationLoader'; export default function validateTranslation(directory: string) { return translationLoader(directory) diff --git a/src/services/widgetRenderer/widgetRenderer.test.ts b/src/services/widgetRenderer/widgetRenderer.test.ts index af46cfa..98ad07a 100644 --- a/src/services/widgetRenderer/widgetRenderer.test.ts +++ b/src/services/widgetRenderer/widgetRenderer.test.ts @@ -5,6 +5,7 @@ import WidgetFileType, { FileLoaderResponse } from '../../types'; import { generateRenderPayloadFromFileLoaderResults } from './widgetRenderer'; const configurationData = fs.readFileSync('src/services/__fixtures__/config.json', 'utf8').toString(); +const translationsData = fs.readFileSync('src/services/__fixtures__/schema_translations.json', 'utf8').toString(); const htmlData = fs.readFileSync('src/services/__fixtures__/widget.html', 'utf8').toString(); const query = fs.readFileSync('src/services/__fixtures__/query.graphql', 'utf8').toString(); const queryParams = fs.readFileSync('src/services/__fixtures__/queryParams.json', 'utf8').toString(); @@ -26,6 +27,10 @@ const fileLoaderResponseData: FileLoaderResponse[] = [ type: WidgetFileType.QUERY_PARAMS, data: queryParams, }, + { + type: WidgetFileType.TRANSLATION, + data: translationsData, + }, ]; describe('Widget Renderer', () => { @@ -38,6 +43,7 @@ describe('Widget Renderer', () => { widget_uuid, storefront_api_query, storefront_api_query_params, + schema_translations, } = generateRenderPayloadFromFileLoaderResults(fileLoaderResponseData); @@ -47,5 +53,6 @@ describe('Widget Renderer', () => { expect(storefront_api_query_params).toEqual(JSON.parse(queryParams)); expect(placement_uuid).not.toBeNull(); expect(widget_uuid).not.toBeNull(); + expect(schema_translations).toEqual(JSON.parse(translationsData)); }); }); diff --git a/src/services/widgetRenderer/widgetRenderer.ts b/src/services/widgetRenderer/widgetRenderer.ts index ecaeba5..fb0c6d0 100644 --- a/src/services/widgetRenderer/widgetRenderer.ts +++ b/src/services/widgetRenderer/widgetRenderer.ts @@ -6,8 +6,8 @@ import widgetTemplateLoader from '../widgetTemplate/widgetTemplateLoader/widgetT import widgetConfigLoader from '../widgetConfig/widgetConfigLoader/widgetConfigLoader'; import queryLoader from '../query/queryLoader/queryLoader'; import queryParamsLoader from '../query/queryParamsLoader/queryParamsLoader'; - -const channelId = process.env.WIDGET_BUILDER_CHANNEL_ID ? parseInt(process.env.WIDGET_BUILDER_CHANNEL_ID, 10) : 1; +import { channelId } from '../../config'; +import translationsLoader from '../translation/translationLoader/translationLoader'; const getInitialRenderingPayload = (): WidgetPreviewRenderRequest => ({ widget_configuration: {}, @@ -17,6 +17,7 @@ const getInitialRenderingPayload = (): WidgetPreviewRenderRequest => ({ storefront_api_query: '', storefront_api_query_params: {}, channel_id: channelId, + schema_translations: '', }); export function generateRenderPayloadFromFileLoaderResults(results: FileLoaderResponse[]): WidgetPreviewRenderRequest { @@ -40,6 +41,10 @@ export function generateRenderPayloadFromFileLoaderResults(results: FileLoaderRe return { ...acc, storefront_api_query_params: JSON.parse(data) }; } + if (type === WidgetFileType.TRANSLATION) { + return { ...acc, schema_translations: JSON.parse(data) }; + } + return acc; }, getInitialRenderingPayload(), ); @@ -51,6 +56,7 @@ export default function renderWidget(widgetDir: string): Promise { widgetConfigLoader(widgetDir), queryLoader(widgetDir), queryParamsLoader(widgetDir), + translationsLoader(widgetDir), ]).then( (results: FileLoaderResponse[]) => getWidget( generateRenderPayloadFromFileLoaderResults(results), diff --git a/src/services/widgetTemplate/publish.ts b/src/services/widgetTemplate/publish.ts index d84351b..422c38c 100644 --- a/src/services/widgetTemplate/publish.ts +++ b/src/services/widgetTemplate/publish.ts @@ -1,12 +1,14 @@ import { log, messages } from '../../messages'; import queryLoader from '../query/queryLoader/queryLoader'; import queryParamsLoader from '../query/queryParamsLoader/queryParamsLoader'; -import { publishWidget } from '../api/widget'; +import { publishWidget, getWidgetTemplate } from '../api/widget'; import WidgetFileType, { FileLoaderResponse } from '../../types'; import schemaLoader from '../schema/schemaLoader/schemaLoader'; +import { channelId } from '../../config'; +import translationsLoader from '../translation/translationLoader/translationLoader'; import widgetTemplateLoader from './widgetTemplateLoader/widgetTemplateLoader'; -import track from './track'; + interface CreateWidgetTemplateReq { name: string; @@ -14,6 +16,7 @@ interface CreateWidgetTemplateReq { template: string; storefront_api_query: string; channel_id: number; + schema_translations?: string; } const widgetTemplatePayload = (widgetName: string): CreateWidgetTemplateReq => ({ @@ -21,15 +24,17 @@ const widgetTemplatePayload = (widgetName: string): CreateWidgetTemplateReq => ( schema: [], template: '', storefront_api_query: '', - channel_id: 1, + channel_id: channelId, + schema_translations: '', }); const publishWidgetTemplate = async (widgetName: string, widgetTemplateDir: string) => { - const widgetTemplateUuid = track.isTracked(widgetTemplateDir); + const widgetTemplateUuid = await getWidgetTemplate(widgetName); try { const widgetConfiguration = await Promise.all([ widgetTemplateLoader(widgetTemplateDir), + translationsLoader(widgetTemplateDir), schemaLoader(widgetTemplateDir), queryLoader(widgetTemplateDir), queryParamsLoader(widgetTemplateDir), @@ -49,14 +54,17 @@ const publishWidgetTemplate = async (widgetName: string, widgetTemplateDir: stri return { ...acc, storefront_api_query: data }; } + if (type === WidgetFileType.TRANSLATION) { + return { ...acc, schema_translations: data }; + } + return acc; }, widgetTemplatePayload(widgetName), )); - const { uuid } = await publishWidget(widgetConfiguration, widgetTemplateUuid); + await publishWidget(widgetConfiguration, widgetTemplateUuid); if (!widgetTemplateUuid) { - track.startTracking(widgetTemplateDir, uuid); log.success(messages.widgetRelease.success(widgetName)); } else { log.success(`Successfully updated ${widgetName}`);