Skip to content
14 changes: 8 additions & 6 deletions src/cli/run/validate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = () => {
Expand All @@ -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);
}

Expand Down
1 change: 1 addition & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
@@ -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;
9 changes: 9 additions & 0 deletions src/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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}`;

Expand Down Expand Up @@ -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;
Expand Down
51 changes: 51 additions & 0 deletions src/services/api/widget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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<string> {
Expand Down Expand Up @@ -64,3 +66,52 @@ export const publishWidget = (
.then(({ data: { data } }) => resolve(data))
.catch(error => reject(error));
});

export const getWidgetTemplate = (
name: string
): Promise<string> => 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<WidgetTemplateResult[]> => {
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;
}
Original file line number Diff line number Diff line change
@@ -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();

Expand Down Expand Up @@ -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);
});
});
});
23 changes: 15 additions & 8 deletions src/services/translation/translationLoader/translationLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,32 @@ 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> {
return new Promise((resolve, reject) => {
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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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)
Expand Down
7 changes: 7 additions & 0 deletions src/services/widgetRenderer/widgetRenderer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -26,6 +27,10 @@ const fileLoaderResponseData: FileLoaderResponse[] = [
type: WidgetFileType.QUERY_PARAMS,
data: queryParams,
},
{
type: WidgetFileType.TRANSLATION,
data: translationsData,
},
];

describe('Widget Renderer', () => {
Expand All @@ -38,6 +43,7 @@ describe('Widget Renderer', () => {
widget_uuid,
storefront_api_query,
storefront_api_query_params,
schema_translations,
} = generateRenderPayloadFromFileLoaderResults(fileLoaderResponseData);


Expand All @@ -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));
});
});
10 changes: 8 additions & 2 deletions src/services/widgetRenderer/widgetRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: {},
Expand All @@ -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 {
Expand All @@ -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(),
);
Expand All @@ -51,6 +56,7 @@ export default function renderWidget(widgetDir: string): Promise<string> {
widgetConfigLoader(widgetDir),
queryLoader(widgetDir),
queryParamsLoader(widgetDir),
translationsLoader(widgetDir),
]).then(
(results: FileLoaderResponse[]) => getWidget(
generateRenderPayloadFromFileLoaderResults(results),
Expand Down
20 changes: 14 additions & 6 deletions src/services/widgetTemplate/publish.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,40 @@
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;
schema: object;
template: string;
storefront_api_query: string;
channel_id: number;
schema_translations?: string;
}

const widgetTemplatePayload = (widgetName: string): CreateWidgetTemplateReq => ({
name: widgetName,
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),
Expand All @@ -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}`);
Expand Down