From 10e761044d64fb204472a7629415746d1d3bc693 Mon Sep 17 00:00:00 2001
From: Darren Belding <darren.belding@space48.com>
Date: Tue, 5 Apr 2022 12:43:36 +0100
Subject: [PATCH] Add translations support to start and publish commands

---
 src/cli/run/validate.ts                       |  2 +-
 src/server/index.ts                           |  9 ++++++++
 src/services/api/widget.ts                    |  1 +
 .../translationLoader.test.ts                 |  6 ++---
 .../translationLoader/translationLoader.ts    | 23 ++++++++++++-------
 .../validateTranslation.ts}                   |  8 +++----
 .../widgetRenderer/widgetRenderer.test.ts     |  7 ++++++
 src/services/widgetRenderer/widgetRenderer.ts |  7 ++++++
 src/services/widgetTemplate/publish.ts        |  8 +++++++
 9 files changed, 54 insertions(+), 17 deletions(-)
 rename src/services/translation/{validate.ts => translationValidator/validateTranslation.ts} (62%)

diff --git a/src/cli/run/validate.ts b/src/cli/run/validate.ts
index 1f408c4..c2a7b6e 100644
--- a/src/cli/run/validate.ts
+++ b/src/cli/run/validate.ts
@@ -6,7 +6,7 @@ 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:
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..a1bd7b5 100644
--- a/src/services/api/widget.ts
+++ b/src/services/api/widget.ts
@@ -22,6 +22,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<string> {
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<FileLoaderResponse> {
@@ -21,7 +24,11 @@ export default function translationLoader(widgetDir: string): Promise<FileLoader
         fs.readFile(
             `${widgetDir}/${WidgetFileType.TRANSLATION}`,
             'utf8',
-            (error: Error, schemaData: string) => {
+            (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..79e6b22 100644
--- a/src/services/widgetRenderer/widgetRenderer.ts
+++ b/src/services/widgetRenderer/widgetRenderer.ts
@@ -6,6 +6,7 @@ import widgetTemplateLoader from '../widgetTemplate/widgetTemplateLoader/widgetT
 import widgetConfigLoader from '../widgetConfig/widgetConfigLoader/widgetConfigLoader';
 import queryLoader from '../query/queryLoader/queryLoader';
 import queryParamsLoader from '../query/queryParamsLoader/queryParamsLoader';
+import translationsLoader from '../translation/translationLoader/translationLoader';
 
 const channelId = process.env.WIDGET_BUILDER_CHANNEL_ID ? parseInt(process.env.WIDGET_BUILDER_CHANNEL_ID, 10) : 1;
 
@@ -17,6 +18,7 @@ const getInitialRenderingPayload = (): WidgetPreviewRenderRequest => ({
     storefront_api_query: '',
     storefront_api_query_params: {},
     channel_id: channelId,
+    schema_translations: '',
 });
 
 export function generateRenderPayloadFromFileLoaderResults(results: FileLoaderResponse[]): WidgetPreviewRenderRequest {
@@ -40,6 +42,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 +57,7 @@ export default function renderWidget(widgetDir: string): Promise<string> {
         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..b6e88f5 100644
--- a/src/services/widgetTemplate/publish.ts
+++ b/src/services/widgetTemplate/publish.ts
@@ -6,6 +6,7 @@ import WidgetFileType, { FileLoaderResponse } from '../../types';
 import schemaLoader from '../schema/schemaLoader/schemaLoader';
 
 import widgetTemplateLoader from './widgetTemplateLoader/widgetTemplateLoader';
+import translationsLoader from '../translation/translationLoader/translationLoader';
 import track from './track';
 
 interface CreateWidgetTemplateReq {
@@ -14,6 +15,7 @@ interface CreateWidgetTemplateReq {
     template: string;
     storefront_api_query: string;
     channel_id: number;
+    schema_translations?: string;
 }
 
 const widgetTemplatePayload = (widgetName: string): CreateWidgetTemplateReq => ({
@@ -22,6 +24,7 @@ const widgetTemplatePayload = (widgetName: string): CreateWidgetTemplateReq => (
     template: '',
     storefront_api_query: '',
     channel_id: 1,
+    schema_translations: ''
 });
 
 const publishWidgetTemplate = async (widgetName: string, widgetTemplateDir: string) => {
@@ -30,6 +33,7 @@ const publishWidgetTemplate = async (widgetName: string, widgetTemplateDir: stri
     try {
         const widgetConfiguration = await Promise.all([
             widgetTemplateLoader(widgetTemplateDir),
+            translationsLoader(widgetTemplateDir),
             schemaLoader(widgetTemplateDir),
             queryLoader(widgetTemplateDir),
             queryParamsLoader(widgetTemplateDir),
@@ -49,6 +53,10 @@ 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),
         ));