Skip to content

Commit 292940f

Browse files
authored
Merge pull request bigcommerce#3 from jcorona48/feature/download-widget
Feature/download widget
2 parents 7c501d6 + b11c256 commit 292940f

File tree

6 files changed

+293
-1
lines changed

6 files changed

+293
-1
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#!/usr/bin/env node
2+
import path from 'path';
3+
4+
import { Command } from 'commander';
5+
import inquirer from 'inquirer';
6+
7+
import { getAllWidgets } from '../../services/api/widget';
8+
import downloadWidgetTemplate from '../../services/widgetTemplate/download';
9+
10+
const widgetTemplateDownload = () => {
11+
const program = new Command('download');
12+
13+
return program
14+
.description('Select your widget template to download')
15+
.usage('select name')
16+
.action(() => {
17+
const widgetDir = path.resolve('.');
18+
getAllWidgets().then((widgets) => {
19+
const question = [
20+
{
21+
type: 'list',
22+
messages: 'Select your widget template to download',
23+
name: 'widgetTemplate',
24+
choices: widgets.map((widget) => ({
25+
name: widget.name,
26+
value: widget,
27+
})),
28+
},
29+
];
30+
inquirer.prompt(question).then((answer) => {
31+
downloadWidgetTemplate(answer.widgetTemplate, widgetDir);
32+
});
33+
});
34+
});
35+
};
36+
37+
export default widgetTemplateDownload;

src/cli/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import createStarterTemplate from './create/starterTemplate';
66
import start from './run/start';
77
import widgetTemplatePublish from './deployment/widgetTemplatePublish';
88
import widgetTemplateDelete from './delete/widgetTemplateRemove';
9+
import widgetTemplateDownload from './download/widgetTemplateDownload';
910
import validateCommands from './run/validate';
1011
import init from './run/init';
1112

@@ -20,6 +21,7 @@ cli
2021
.addCommand(validateCommands())
2122
.addCommand(createStarterTemplate())
2223
.addCommand(widgetTemplatePublish())
23-
.addCommand(widgetTemplateDelete());
24+
.addCommand(widgetTemplateDelete())
25+
.addCommand(widgetTemplateDownload());
2426

2527
cli.parse(process.argv);

src/services/api/widget.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@ import Axios, { AxiosResponse } from 'axios';
22

33
import AUTH_CONFIG from '../auth/authConfig';
44
import { WidgetConfiguration } from '../schema/schemaParser/schemaParser';
5+
import { Widget } from '../../types';
56

67
export const widgetApi = {
78
widgetPreviewRender: `${AUTH_CONFIG.apiPath}/content/widget-templates/preview`,
89
widgetTemplatePublish: `${AUTH_CONFIG.apiPath}/content/widget-templates`,
910
widgetTemplateDelete: `${AUTH_CONFIG.apiPath}/content/widget-templates`,
11+
widgetTemplateDownload: `${AUTH_CONFIG.apiPath}/content/widget-templates`,
1012
};
1113

1214
interface WidgetPreviewRenderResponse {
@@ -81,3 +83,18 @@ export const deleteWidget = (uuid: string): Promise<boolean> => new Promise((res
8183
))
8284
.catch((error) => reject(error));
8385
});
86+
87+
export const getAllWidgets = (): Promise<Widget[]> => new Promise((resolve, reject) => {
88+
Axios({
89+
method: 'get',
90+
headers: {
91+
Accept: 'application/json',
92+
'Content-Type': 'application/json',
93+
'X-Auth-Client': AUTH_CONFIG.authId,
94+
'X-Auth-Token': AUTH_CONFIG.authToken,
95+
},
96+
url: widgetApi.widgetTemplatePublish,
97+
})
98+
.then(({ data: { data } }) => resolve(data))
99+
.catch((error) => reject(error));
100+
});
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { log, messages } from '../../messages';
2+
import { Widget } from '../../types';
3+
4+
import createDownloadedWidgetTemplate from './widgetTemplateDownload/createDownloadedTemplate';
5+
6+
const downloadWidgetTemplate = async (widgetObject: Widget, widgetTemplateDir: string) => {
7+
try {
8+
if (!widgetObject) {
9+
log.error(messages.widgetRemove.failure);
10+
return;
11+
}
12+
createDownloadedWidgetTemplate.generate(widgetObject, widgetTemplateDir);
13+
} catch {
14+
log.error(messages.widgetRemove.failure);
15+
}
16+
};
17+
18+
export default downloadWidgetTemplate;
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
#!/usr/bin/env node
2+
3+
import {
4+
existsSync, mkdirSync, rmdirSync, writeFileSync,
5+
} from 'fs';
6+
7+
import { log, messages } from '../../../messages';
8+
import { Widget } from '../../../types';
9+
10+
export class CreateDownloadedTemplate {
11+
// Name of the widget template supplied by the user
12+
_widgetTemplateName: string;
13+
14+
// Name of the widget template directory created with the widget template name
15+
_widgetTemplateDir: string;
16+
17+
_widget: Widget;
18+
19+
// Name of the configuration file
20+
_configFile = 'config.json';
21+
22+
// Name of the schema file
23+
_schemaFile = 'schema.json';
24+
25+
// Name of the html template
26+
_templateFile = 'widget.html';
27+
28+
_queryFile = 'query.graphql';
29+
30+
_queryParamsFile = 'queryParams.json';
31+
32+
_queryParamsBuilderFile = 'queryParamsBuilder.json';
33+
34+
_schemaTranslationsFile = 'schema_translations.json';
35+
36+
_uuidFile = 'widget.yml';
37+
38+
constructor(widget: Widget, templateName: string) {
39+
this._widget = widget;
40+
this._widgetTemplateDir = `${templateName}/${widget.name}`;
41+
}
42+
43+
createDirectory() {
44+
try {
45+
if (!existsSync(this._widgetTemplateDir)) {
46+
mkdirSync(this._widgetTemplateDir);
47+
log.success(messages.createWidgetTemplate.createSuccess(this._widget.name));
48+
}
49+
} catch {
50+
throw new Error(messages.createWidgetTemplate.createError(this._widget.name));
51+
}
52+
53+
return this;
54+
}
55+
56+
createSchemaFile() {
57+
const configPath = `${this._widgetTemplateDir}/${this._schemaFile}`;
58+
if (!this._widget.schema) return this;
59+
const schema = JSON.stringify(this._widget.schema, null, 2);
60+
try {
61+
writeFileSync(configPath, schema);
62+
log.success(messages.createWidgetTemplate.createSuccess(this._schemaFile, configPath));
63+
} catch {
64+
throw new Error(messages.createWidgetTemplate.createError(this._schemaFile, configPath));
65+
}
66+
67+
return this;
68+
}
69+
70+
createTemplateFile() {
71+
const templatePath = `${this._widgetTemplateDir}/${this._templateFile}`;
72+
if (!this._widget.template) return this;
73+
const downloadedHtmlTemplate = this._widget.template;
74+
try {
75+
writeFileSync(templatePath, downloadedHtmlTemplate);
76+
log.success(messages.createWidgetTemplate.createSuccess(this._templateFile, templatePath));
77+
} catch {
78+
throw new Error(messages.createWidgetTemplate.createError(this._templateFile, templatePath));
79+
}
80+
81+
return this;
82+
}
83+
84+
createUuidFile() {
85+
const uuidPath = `${this._widgetTemplateDir}/${this._uuidFile}`;
86+
if (!this._widget.uuid) return this;
87+
const downloadedUuid = this._widget.uuid;
88+
try {
89+
writeFileSync(uuidPath, downloadedUuid);
90+
log.success(messages.createWidgetTemplate.createSuccess(this._uuidFile, uuidPath));
91+
} catch {
92+
throw new Error(messages.createWidgetTemplate.createError(this._uuidFile, uuidPath));
93+
}
94+
95+
return this;
96+
}
97+
98+
createQueryFile() {
99+
const queryPath = `${this._widgetTemplateDir}/${this._queryFile}`;
100+
if (!this._widget.storefront_api_query) return this;
101+
const downloadedQuery = this._widget.storefront_api_query;
102+
try {
103+
writeFileSync(queryPath, downloadedQuery);
104+
log.success(messages.createWidgetTemplate.createSuccess(this._queryFile, queryPath));
105+
} catch {
106+
throw new Error(messages.createWidgetTemplate.createError(this._queryFile, queryPath));
107+
}
108+
109+
return this;
110+
}
111+
112+
createSchemaTranslationsFile() {
113+
const schemaTranslationsPath = `${this._widgetTemplateDir}/${this._schemaTranslationsFile}`;
114+
if (!this._widget.schema_translations || Object.keys(this._widget.schema_translations).length === 0) return;
115+
const downloadedSchemaTranslations = JSON.stringify(this._widget.schema_translations, null, 2);
116+
try {
117+
writeFileSync(schemaTranslationsPath, downloadedSchemaTranslations);
118+
log.success(messages.createWidgetTemplate.createSuccess(this._schemaTranslationsFile, schemaTranslationsPath)); // eslint-disable-line max-len
119+
} catch {
120+
throw new Error(messages.createWidgetTemplate.createError(this._schemaTranslationsFile, schemaTranslationsPath)); // eslint-disable-line max-len
121+
}
122+
}
123+
124+
removeDirectory() {
125+
try {
126+
if (existsSync(this._widgetTemplateDir)) {
127+
rmdirSync(this._widgetTemplateDir, { recursive: true });
128+
log.success(messages.createWidgetTemplate.removeSuccess(this._widgetTemplateDir));
129+
}
130+
} catch {
131+
log.error(messages.createWidgetTemplate.removeError(this._widgetTemplateDir));
132+
}
133+
}
134+
}
135+
136+
const generate = (widget: Widget, widgetTemplateName: string) => {
137+
const blankTemplate = new CreateDownloadedTemplate(widget, widgetTemplateName);
138+
139+
try {
140+
blankTemplate
141+
.createDirectory()
142+
.createSchemaFile()
143+
.createTemplateFile()
144+
.createQueryFile()
145+
.createUuidFile()
146+
.createSchemaTranslationsFile();
147+
} catch (e) {
148+
blankTemplate.removeDirectory();
149+
throw e;
150+
}
151+
};
152+
153+
const createDownloadedWidgetTemplate = {
154+
generate,
155+
};
156+
157+
export default createDownloadedWidgetTemplate;

src/types.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,65 @@ export interface JqueryElement extends JQuery<HTMLElement> {
2626
slick(options?: object | string): void;
2727
}
2828

29+
export interface WidgetResponse {
30+
data: Widget[];
31+
}
32+
33+
export interface Widget {
34+
uuid: string;
35+
name: string;
36+
schema: WidgetResponseSchema[];
37+
template: string;
38+
date_created: Date;
39+
date_modified: Date;
40+
kind: string;
41+
storefront_api_query: string;
42+
icon_name: string;
43+
template_engine: string;
44+
client_rerender: boolean;
45+
current_version_uuid: string;
46+
channel_id: number;
47+
schema_translations: object;
48+
}
49+
50+
export interface WidgetResponseSchema {
51+
type: string;
52+
label: string;
53+
id?: string;
54+
defaultCount?: number;
55+
entryLabel?: string;
56+
schema?: SchemaSchema[];
57+
sections?: FluffySection[];
58+
}
59+
60+
export interface SchemaSchema {
61+
type: string;
62+
label: string;
63+
sections: PurpleSection[];
64+
}
65+
66+
export interface PurpleSection {
67+
settings: PurpleSetting[];
68+
}
69+
70+
export interface PurpleSetting {
71+
type: string;
72+
label: string;
73+
id: string;
74+
default: string;
75+
}
76+
77+
export interface FluffySection {
78+
label: string;
79+
settings: FluffySetting[];
80+
}
81+
82+
export interface FluffySetting {
83+
type: string;
84+
label: string;
85+
id: string;
86+
default?: boolean | string;
87+
placeholder?: string;
88+
}
89+
2990
export default WidgetFileType;

0 commit comments

Comments
 (0)