@aws-amplify/amplify-util-uibuilder 1.5.0 → 1.6.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +43 -0
- package/amplify-plugin.json +10 -3
- package/lib/clients/amplify-studio-client.d.ts +13 -2
- package/lib/clients/amplify-studio-client.d.ts.map +1 -1
- package/lib/clients/amplify-studio-client.js +85 -17
- package/lib/clients/amplify-studio-client.js.map +1 -1
- package/lib/commands/generateComponents.d.ts.map +1 -1
- package/lib/commands/generateComponents.js +33 -15
- package/lib/commands/generateComponents.js.map +1 -1
- package/lib/commands/help.d.ts +1 -1
- package/lib/commands/help.d.ts.map +1 -1
- package/lib/commands/help.js +2 -2
- package/lib/commands/help.js.map +1 -1
- package/lib/commands/utils/codegenResources.d.ts +14 -3
- package/lib/commands/utils/codegenResources.d.ts.map +1 -1
- package/lib/commands/utils/codegenResources.js +72 -5
- package/lib/commands/utils/codegenResources.js.map +1 -1
- package/lib/commands/utils/featureFlags.d.ts +2 -0
- package/lib/commands/utils/featureFlags.d.ts.map +1 -0
- package/lib/commands/utils/featureFlags.js +35 -0
- package/lib/commands/utils/featureFlags.js.map +1 -0
- package/lib/commands/utils/getUiBuilderComponentsPath.d.ts.map +1 -1
- package/lib/commands/utils/getUiBuilderComponentsPath.js.map +1 -1
- package/lib/commands/utils/shouldRenderComponents.d.ts +1 -1
- package/lib/commands/utils/shouldRenderComponents.d.ts.map +1 -1
- package/lib/commands/utils/shouldRenderComponents.js +14 -8
- package/lib/commands/utils/shouldRenderComponents.js.map +1 -1
- package/lib/commands/utils/syncAmplifyUiBuilderComponents.d.ts +12 -20
- package/lib/commands/utils/syncAmplifyUiBuilderComponents.d.ts.map +1 -1
- package/lib/commands/utils/syncAmplifyUiBuilderComponents.js +51 -3
- package/lib/commands/utils/syncAmplifyUiBuilderComponents.js.map +1 -1
- package/lib/commands/version.d.ts +1 -1
- package/lib/commands/version.d.ts.map +1 -1
- package/lib/commands/version.js +2 -2
- package/lib/commands/version.js.map +1 -1
- package/lib/event-handlers/handle-PostEnvAdd.d.ts +1 -1
- package/lib/event-handlers/handle-PostEnvAdd.d.ts.map +1 -1
- package/lib/event-handlers/handle-PostEnvAdd.js +4 -6
- package/lib/event-handlers/handle-PostEnvAdd.js.map +1 -1
- package/lib/event-handlers/handle-PostPull.d.ts +1 -1
- package/lib/event-handlers/handle-PostPull.d.ts.map +1 -1
- package/lib/event-handlers/handle-PostPull.js +4 -3
- package/lib/event-handlers/handle-PostPull.js.map +1 -1
- package/lib/event-handlers/handle-PostPush.d.ts +3 -0
- package/lib/event-handlers/handle-PostPush.d.ts.map +1 -0
- package/lib/event-handlers/handle-PostPush.js +15 -0
- package/lib/event-handlers/handle-PostPush.js.map +1 -0
- package/package.json +8 -7
- package/src/__tests__/cloneComponentsFromEnv.test.ts +17 -6
- package/src/__tests__/createUiBuilderComponent.test.ts +26 -3
- package/src/__tests__/generateComponents.test.ts +44 -15
- package/src/__tests__/getAmplifyDataSchema.test.ts +51 -19
- package/src/__tests__/shouldRenderComponents.test.ts +51 -4
- package/src/__tests__/syncAmplifyUiComponents.test.ts +126 -8
- package/src/__tests__/utils.ts +70 -0
- package/src/clients/amplify-studio-client.ts +157 -69
- package/src/commands/generateComponents.ts +43 -16
- package/src/commands/help.ts +5 -2
- package/src/commands/utils/codegenResources.ts +115 -6
- package/src/commands/utils/featureFlags.ts +62 -0
- package/src/commands/utils/getUiBuilderComponentsPath.ts +7 -2
- package/src/commands/utils/shouldRenderComponents.ts +20 -8
- package/src/commands/utils/syncAmplifyUiBuilderComponents.ts +96 -29
- package/src/commands/version.ts +5 -2
- package/src/event-handlers/handle-PostEnvAdd.ts +8 -6
- package/src/event-handlers/handle-PostPull.ts +7 -3
- package/src/event-handlers/handle-PostPush.ts +15 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/src/__tests__/handleEvents.test.ts +0 -21
|
@@ -1,9 +1,20 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { $TSContext, CloudformationProviderFacade } from 'amplify-cli-core';
|
|
2
2
|
import type { ServiceConfigurationOptions } from 'aws-sdk/lib/service';
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
CreateComponentData, Component, Theme, Form,
|
|
5
|
+
} from 'aws-sdk/clients/amplifyuibuilder';
|
|
4
6
|
import { AmplifyUIBuilder, AmplifyBackend } from 'aws-sdk';
|
|
5
7
|
import { printer } from 'amplify-prompts';
|
|
6
8
|
import { getAppId, getEnvName } from '../commands/utils/environmentHelpers';
|
|
9
|
+
import { getTransformerVersion } from '../commands/utils/featureFlags';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* studio client metadata
|
|
13
|
+
*/
|
|
14
|
+
export type StudioMetadata = {
|
|
15
|
+
autoGenerateForms: boolean;
|
|
16
|
+
autoGenerateViews: boolean;
|
|
17
|
+
};
|
|
7
18
|
|
|
8
19
|
/**
|
|
9
20
|
* Builds the Amplify Backend Client
|
|
@@ -45,6 +56,24 @@ export default class AmplifyStudioClient {
|
|
|
45
56
|
#amplifyBackend: AmplifyBackend;
|
|
46
57
|
#appId: string;
|
|
47
58
|
#envName: string;
|
|
59
|
+
metadata: StudioMetadata;
|
|
60
|
+
isGraphQLSupported = false;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* static function meant to check if given appId is studio enabled
|
|
64
|
+
*/
|
|
65
|
+
static isAmplifyApp = async (context: $TSContext, appId: string): Promise<boolean> => {
|
|
66
|
+
try {
|
|
67
|
+
const { isAdminApp } = await CloudformationProviderFacade.isAmplifyAdminApp(context, appId);
|
|
68
|
+
return isAdminApp;
|
|
69
|
+
} catch (err) {
|
|
70
|
+
// return false is admin app failed check
|
|
71
|
+
// this means we wont run codegen-ui
|
|
72
|
+
printer.debug(`Failed admin app check: ${err.message}`);
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
48
77
|
/**
|
|
49
78
|
* Used to configure the AWS Amplify clients.
|
|
50
79
|
*/
|
|
@@ -56,19 +85,55 @@ export default class AmplifyStudioClient {
|
|
|
56
85
|
resolvedEnvName,
|
|
57
86
|
resolvedAppId,
|
|
58
87
|
])) as ServiceConfigurationOptions;
|
|
59
|
-
|
|
88
|
+
|
|
89
|
+
const client = new AmplifyStudioClient(awsConfigInfo, resolvedAppId, resolvedEnvName);
|
|
90
|
+
|
|
91
|
+
if ((await getTransformerVersion()) === 2) {
|
|
92
|
+
await client.loadMetadata();
|
|
93
|
+
client.isGraphQLSupported = true;
|
|
94
|
+
} else {
|
|
95
|
+
client.isGraphQLSupported = false;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return client;
|
|
60
99
|
}
|
|
61
100
|
|
|
62
101
|
/*
|
|
63
|
-
|
|
64
|
-
|
|
102
|
+
* Builds the clients
|
|
103
|
+
*/
|
|
65
104
|
constructor(awsConfigInfo: ServiceConfigurationOptions, appId: string, envName: string) {
|
|
66
105
|
this.#amplifyUiBuilder = buildAmplifyUiBuilderClient(awsConfigInfo);
|
|
67
106
|
this.#amplifyBackend = buildAmplifyBackendClient(awsConfigInfo);
|
|
68
107
|
this.#appId = appId;
|
|
69
108
|
this.#envName = envName;
|
|
109
|
+
this.metadata = {
|
|
110
|
+
autoGenerateForms: false,
|
|
111
|
+
autoGenerateViews: false,
|
|
112
|
+
};
|
|
70
113
|
}
|
|
71
114
|
|
|
115
|
+
/**
|
|
116
|
+
* loads metadata from appId/envName
|
|
117
|
+
*/
|
|
118
|
+
loadMetadata = async (envName?: string, appId?: string): Promise<void> => {
|
|
119
|
+
const environmentName = envName || this.#envName;
|
|
120
|
+
const resolvedAppId = appId || this.#appId;
|
|
121
|
+
try {
|
|
122
|
+
const response = await this.#amplifyUiBuilder
|
|
123
|
+
.getMetadata({
|
|
124
|
+
appId: resolvedAppId,
|
|
125
|
+
environmentName,
|
|
126
|
+
})
|
|
127
|
+
.promise();
|
|
128
|
+
this.metadata = {
|
|
129
|
+
autoGenerateForms: response.features?.autoGenerateForms === 'true',
|
|
130
|
+
autoGenerateViews: response.features?.autoGenerateViews === 'true',
|
|
131
|
+
};
|
|
132
|
+
} catch (err) {
|
|
133
|
+
throw new Error(`Failed to load metadata: ${err.message}`);
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
|
|
72
137
|
/**
|
|
73
138
|
* listComponents from Studio
|
|
74
139
|
*/
|
|
@@ -91,71 +156,94 @@ export default class AmplifyStudioClient {
|
|
|
91
156
|
} while (nextToken);
|
|
92
157
|
printer.debug(JSON.stringify(uiBuilderComponents, null, 2));
|
|
93
158
|
return { entities: uiBuilderComponents };
|
|
94
|
-
} catch (
|
|
95
|
-
|
|
96
|
-
throw e;
|
|
159
|
+
} catch (err) {
|
|
160
|
+
throw new Error(`Failed to list components: ${err.message}`);
|
|
97
161
|
}
|
|
98
|
-
}
|
|
162
|
+
};
|
|
99
163
|
|
|
100
164
|
/**
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
165
|
+
* Returns all the UI Builder themes from the app
|
|
166
|
+
*/
|
|
167
|
+
listThemes = async (envName?: string, appId?: string): Promise<{ entities: Theme[] }> => {
|
|
168
|
+
const environmentName = envName || this.#envName;
|
|
169
|
+
const resolvedAppId = appId || this.#appId;
|
|
170
|
+
|
|
171
|
+
try {
|
|
172
|
+
let nextToken: string | undefined;
|
|
173
|
+
const uiBuilderThemes: Theme[] = [];
|
|
174
|
+
do {
|
|
175
|
+
const response = await this.#amplifyUiBuilder
|
|
176
|
+
.exportThemes({
|
|
177
|
+
appId: resolvedAppId,
|
|
178
|
+
environmentName,
|
|
179
|
+
nextToken,
|
|
180
|
+
})
|
|
181
|
+
.promise();
|
|
182
|
+
uiBuilderThemes.push(...response.entities);
|
|
183
|
+
nextToken = response.nextToken;
|
|
184
|
+
} while (nextToken);
|
|
185
|
+
printer.debug(JSON.stringify(uiBuilderThemes, null, 2));
|
|
186
|
+
return { entities: uiBuilderThemes };
|
|
187
|
+
} catch (err) {
|
|
188
|
+
throw new Error(`Failed to list themes: ${err.message}`);
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
listForms = async (envName?: string, appId?: string): Promise<{ entities: Form[] }> => {
|
|
193
|
+
const environmentName = envName || this.#envName;
|
|
194
|
+
const resolvedAppId = appId || this.#appId;
|
|
195
|
+
try {
|
|
196
|
+
let nextToken: string | undefined;
|
|
197
|
+
const uibuilderForms: Form[] = [];
|
|
198
|
+
do {
|
|
199
|
+
const response = await this.#amplifyUiBuilder
|
|
200
|
+
.exportForms({
|
|
201
|
+
appId: resolvedAppId,
|
|
202
|
+
environmentName,
|
|
203
|
+
nextToken,
|
|
204
|
+
})
|
|
205
|
+
.promise();
|
|
206
|
+
uibuilderForms.push(...response.entities);
|
|
207
|
+
nextToken = response.nextToken;
|
|
208
|
+
} while (nextToken);
|
|
209
|
+
printer.debug(JSON.stringify(uibuilderForms, null, 2));
|
|
210
|
+
return { entities: uibuilderForms };
|
|
211
|
+
} catch (err) {
|
|
212
|
+
throw new Error(`Failed to list forms: ${err.message}`);
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
createComponent = async (component: CreateComponentData, envName?: string, appId?: string): Promise<Component | undefined> => {
|
|
217
|
+
const environmentName = envName || this.#envName;
|
|
218
|
+
const resolvedAppId = appId || this.#appId;
|
|
219
|
+
try {
|
|
220
|
+
const response = await this.#amplifyUiBuilder
|
|
221
|
+
.createComponent({
|
|
222
|
+
appId: resolvedAppId,
|
|
223
|
+
environmentName,
|
|
224
|
+
componentToCreate: component,
|
|
225
|
+
})
|
|
226
|
+
.promise();
|
|
227
|
+
return response.entity;
|
|
228
|
+
} catch (err) {
|
|
229
|
+
throw new Error(`Failed to create component: ${err.message}`);
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
getModels = async (resourceName: string, envName?: string, appId?: string): Promise<string | undefined> => {
|
|
234
|
+
try {
|
|
235
|
+
const environmentName = envName || this.#envName;
|
|
236
|
+
const resolvedAppId = appId || this.#appId;
|
|
237
|
+
const { Models } = await this.#amplifyBackend
|
|
238
|
+
.getBackendAPIModels({
|
|
239
|
+
AppId: resolvedAppId,
|
|
240
|
+
BackendEnvironmentName: environmentName,
|
|
241
|
+
ResourceName: resourceName,
|
|
242
|
+
})
|
|
243
|
+
.promise();
|
|
244
|
+
return Models;
|
|
245
|
+
} catch (err) {
|
|
246
|
+
throw new Error(`Models not found in AmplifyBackend:GetBackendAPIModels response: ${err.message}`);
|
|
247
|
+
}
|
|
248
|
+
};
|
|
161
249
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
/* eslint-disable spellcheck/spell-checker */
|
|
2
|
+
import { StudioSchema } from '@aws-amplify/codegen-ui';
|
|
2
3
|
import ora from 'ora';
|
|
3
4
|
import { printer } from 'amplify-prompts';
|
|
4
5
|
import { $TSContext } from 'amplify-cli-core';
|
|
@@ -10,6 +11,8 @@ import {
|
|
|
10
11
|
generateUiBuilderThemes,
|
|
11
12
|
getAmplifyDataSchema,
|
|
12
13
|
generateAmplifyUiBuilderIndexFile,
|
|
14
|
+
generateUiBuilderForms,
|
|
15
|
+
generateAmplifyUiBuilderUtilFile,
|
|
13
16
|
} from './utils';
|
|
14
17
|
|
|
15
18
|
/**
|
|
@@ -22,36 +25,60 @@ export const run = async (context: $TSContext): Promise<void> => {
|
|
|
22
25
|
const spinner = ora('');
|
|
23
26
|
try {
|
|
24
27
|
const studioClient = await AmplifyStudioClient.setClientInfo(context);
|
|
25
|
-
const [componentSchemas, themeSchemas, dataSchema] = await Promise.all([
|
|
28
|
+
const [componentSchemas, themeSchemas, formSchemas, dataSchema] = await Promise.all([
|
|
26
29
|
studioClient.listComponents(),
|
|
27
30
|
studioClient.listThemes(),
|
|
31
|
+
studioClient.listForms(),
|
|
28
32
|
getAmplifyDataSchema(studioClient),
|
|
29
33
|
]);
|
|
30
|
-
|
|
34
|
+
|
|
35
|
+
const nothingWouldAutogen = !dataSchema || !studioClient.metadata.autoGenerateForms || !studioClient.isGraphQLSupported;
|
|
36
|
+
|
|
37
|
+
if (nothingWouldAutogen && [componentSchemas, themeSchemas, formSchemas].every(group => !group.entities.length)) {
|
|
31
38
|
printer.debug('Skipping UI component generation since none are found.');
|
|
32
39
|
return;
|
|
33
40
|
}
|
|
34
41
|
spinner.start('Generating UI components...');
|
|
35
|
-
const generatedComponentResults = generateUiBuilderComponents(context, componentSchemas.entities, dataSchema);
|
|
36
|
-
const generatedThemeResults = generateUiBuilderThemes(context, themeSchemas.entities);
|
|
37
42
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
43
|
+
const generatedResults = {
|
|
44
|
+
component: generateUiBuilderComponents(context, componentSchemas.entities, dataSchema),
|
|
45
|
+
theme: generateUiBuilderThemes(context, themeSchemas.entities),
|
|
46
|
+
form: generateUiBuilderForms(
|
|
47
|
+
context, formSchemas.entities, dataSchema, studioClient.metadata.autoGenerateForms && studioClient.isGraphQLSupported,
|
|
48
|
+
),
|
|
49
|
+
};
|
|
42
50
|
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
51
|
+
const successfulSchemas: StudioSchema[] = [];
|
|
52
|
+
let hasSuccessfulForm = false;
|
|
53
|
+
const failedResponseNames: string[] = [];
|
|
54
|
+
|
|
55
|
+
Object.entries(generatedResults).forEach(([key, results]) => {
|
|
56
|
+
results.forEach(result => {
|
|
57
|
+
if (result.resultType === 'SUCCESS') {
|
|
58
|
+
successfulSchemas.push(result.schema);
|
|
59
|
+
if (key === 'form') {
|
|
60
|
+
hasSuccessfulForm = true;
|
|
61
|
+
}
|
|
62
|
+
} else {
|
|
63
|
+
failedResponseNames.push(result.schemaName);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
});
|
|
47
67
|
|
|
48
|
-
|
|
49
|
-
|
|
68
|
+
generateAmplifyUiBuilderIndexFile(context, successfulSchemas);
|
|
69
|
+
|
|
70
|
+
generateAmplifyUiBuilderUtilFile(context, { hasForms: hasSuccessfulForm, hasViews: false });
|
|
71
|
+
|
|
72
|
+
if (failedResponseNames.length > 0) {
|
|
73
|
+
spinner.fail(`Failed to sync the following components: ${failedResponseNames.join(', ')}`);
|
|
50
74
|
} else {
|
|
51
75
|
spinner.succeed('Synced UI components.');
|
|
52
76
|
}
|
|
53
77
|
|
|
54
|
-
const invalidComponentNames =
|
|
78
|
+
const invalidComponentNames = [
|
|
79
|
+
...componentSchemas.entities.filter(component => !component.schemaVersion).map(component => component.name),
|
|
80
|
+
...formSchemas.entities.filter(form => !form.schemaVersion).map(form => form.name),
|
|
81
|
+
];
|
|
55
82
|
if (invalidComponentNames.length) {
|
|
56
83
|
printer.warn(
|
|
57
84
|
`The components ${invalidComponentNames.join(
|
package/src/commands/help.ts
CHANGED
|
@@ -10,14 +10,20 @@ import {
|
|
|
10
10
|
RenderTextComponentResponse,
|
|
11
11
|
GenericDataSchema,
|
|
12
12
|
getGenericFromDataStore,
|
|
13
|
+
StudioForm,
|
|
14
|
+
StudioSchema,
|
|
13
15
|
} from '@aws-amplify/codegen-ui';
|
|
14
16
|
import {
|
|
15
17
|
AmplifyRenderer,
|
|
18
|
+
AmplifyFormRenderer,
|
|
16
19
|
ReactThemeStudioTemplateRenderer,
|
|
17
20
|
ReactIndexStudioTemplateRenderer,
|
|
18
21
|
ModuleKind,
|
|
19
22
|
ScriptTarget,
|
|
20
23
|
ScriptKind,
|
|
24
|
+
UtilTemplateType,
|
|
25
|
+
ReactUtilsStudioTemplateRenderer,
|
|
26
|
+
ReactThemeStudioTemplateRendererOptions,
|
|
21
27
|
} from '@aws-amplify/codegen-ui-react';
|
|
22
28
|
import { printer } from 'amplify-prompts';
|
|
23
29
|
import {
|
|
@@ -55,10 +61,14 @@ export const createUiBuilderComponent = (context: $TSContext, schema: StudioComp
|
|
|
55
61
|
/**
|
|
56
62
|
* Writes theme file to the work space
|
|
57
63
|
*/
|
|
58
|
-
export const createUiBuilderTheme = (
|
|
64
|
+
export const createUiBuilderTheme = (
|
|
65
|
+
context: $TSContext,
|
|
66
|
+
schema: StudioTheme,
|
|
67
|
+
options?: ReactThemeStudioTemplateRendererOptions,
|
|
68
|
+
): StudioTheme => {
|
|
59
69
|
const uiBuilderComponentsPath = getUiBuilderComponentsPath(context);
|
|
60
70
|
const rendererFactory = new StudioTemplateRendererFactory(
|
|
61
|
-
(component: StudioTheme) => (new ReactThemeStudioTemplateRenderer(component, config) as unknown) as StudioTemplateRenderer<
|
|
71
|
+
(component: StudioTheme) => (new ReactThemeStudioTemplateRenderer(component, config, options) as unknown) as StudioTemplateRenderer<
|
|
62
72
|
unknown,
|
|
63
73
|
StudioTheme,
|
|
64
74
|
FrameworkOutputManager<unknown>,
|
|
@@ -83,15 +93,46 @@ export const createUiBuilderTheme = (context: $TSContext, schema: StudioTheme):
|
|
|
83
93
|
}
|
|
84
94
|
};
|
|
85
95
|
|
|
96
|
+
/**
|
|
97
|
+
* Writes form file to the work space
|
|
98
|
+
*/
|
|
99
|
+
export const createUiBuilderForm = (context: $TSContext, schema: StudioForm, dataSchema?: GenericDataSchema): StudioForm => {
|
|
100
|
+
const uiBuilderComponentsPath = getUiBuilderComponentsPath(context);
|
|
101
|
+
const rendererFactory = new StudioTemplateRendererFactory(
|
|
102
|
+
(form: StudioForm) => (new AmplifyFormRenderer(form, dataSchema, config) as unknown) as StudioTemplateRenderer<
|
|
103
|
+
unknown,
|
|
104
|
+
StudioForm,
|
|
105
|
+
FrameworkOutputManager<unknown>,
|
|
106
|
+
RenderTextComponentResponse
|
|
107
|
+
>,
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
const outputPathDir = uiBuilderComponentsPath;
|
|
111
|
+
|
|
112
|
+
const rendererManager = new StudioTemplateRendererManager(rendererFactory, {
|
|
113
|
+
outputPathDir,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
try {
|
|
117
|
+
rendererManager.renderSchemaToTemplate(schema);
|
|
118
|
+
return schema;
|
|
119
|
+
} catch (e) {
|
|
120
|
+
printer.debug(e);
|
|
121
|
+
printer.debug('Skipping invalid form with schema');
|
|
122
|
+
printer.debug(JSON.stringify(schema, null, 2));
|
|
123
|
+
throw e;
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
|
|
86
127
|
/**
|
|
87
128
|
* Writes index file to the work space
|
|
88
129
|
*/
|
|
89
|
-
export const generateAmplifyUiBuilderIndexFile = (context: $TSContext, schemas:
|
|
130
|
+
export const generateAmplifyUiBuilderIndexFile = (context: $TSContext, schemas: StudioSchema[]): void => {
|
|
90
131
|
const uiBuilderComponentsPath = getUiBuilderComponentsPath(context);
|
|
91
132
|
const rendererFactory = new StudioTemplateRendererFactory(
|
|
92
|
-
(
|
|
133
|
+
(schema: StudioSchema[]) => (new ReactIndexStudioTemplateRenderer(schema, config) as unknown) as StudioTemplateRenderer<
|
|
93
134
|
unknown,
|
|
94
|
-
|
|
135
|
+
StudioSchema[],
|
|
95
136
|
FrameworkOutputManager<unknown>,
|
|
96
137
|
RenderTextComponentResponse
|
|
97
138
|
>,
|
|
@@ -104,7 +145,50 @@ export const generateAmplifyUiBuilderIndexFile = (context: $TSContext, schemas:
|
|
|
104
145
|
});
|
|
105
146
|
|
|
106
147
|
try {
|
|
107
|
-
|
|
148
|
+
rendererManager.renderSchemaToTemplate(schemas);
|
|
149
|
+
} catch (e) {
|
|
150
|
+
printer.debug(e);
|
|
151
|
+
printer.debug('Failed to generate component index file');
|
|
152
|
+
throw e;
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
type UtilFileChecks = {
|
|
157
|
+
hasForms: boolean;
|
|
158
|
+
hasViews: boolean;
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Writes utils file to the work space
|
|
163
|
+
*/
|
|
164
|
+
export const generateAmplifyUiBuilderUtilFile = (context: $TSContext, { hasForms, hasViews }: UtilFileChecks): void => {
|
|
165
|
+
const uiBuilderComponentsPath = getUiBuilderComponentsPath(context);
|
|
166
|
+
const rendererFactory = new StudioTemplateRendererFactory(
|
|
167
|
+
(utils: UtilTemplateType[]) => (new ReactUtilsStudioTemplateRenderer(utils, config)),
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
const outputPathDir = uiBuilderComponentsPath;
|
|
171
|
+
|
|
172
|
+
const rendererManager = new StudioTemplateRendererManager(rendererFactory, {
|
|
173
|
+
outputPathDir,
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
const utils = new Set<UtilTemplateType>();
|
|
177
|
+
|
|
178
|
+
if (hasForms) {
|
|
179
|
+
utils.add('validation');
|
|
180
|
+
utils.add('formatter');
|
|
181
|
+
utils.add('fetchByPath');
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (hasViews) {
|
|
185
|
+
utils.add('formatter');
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
try {
|
|
189
|
+
if (utils.size) {
|
|
190
|
+
rendererManager.renderSchemaToTemplate([...utils]);
|
|
191
|
+
}
|
|
108
192
|
} catch (e) {
|
|
109
193
|
printer.debug(e);
|
|
110
194
|
printer.debug('Failed to generate component index file');
|
|
@@ -119,6 +203,9 @@ export const generateAmplifyUiBuilderIndexFile = (context: $TSContext, schemas:
|
|
|
119
203
|
export const getAmplifyDataSchema = async (
|
|
120
204
|
studioClient: AmplifyStudioClient,
|
|
121
205
|
): Promise<GenericDataSchema | undefined> => {
|
|
206
|
+
if (!studioClient.isGraphQLSupported) {
|
|
207
|
+
return undefined;
|
|
208
|
+
}
|
|
122
209
|
try {
|
|
123
210
|
const meta = stateManager.getMeta();
|
|
124
211
|
const resourceName = Object.entries(meta[AmplifyCategories.API]).find(
|
|
@@ -138,3 +225,25 @@ export const getAmplifyDataSchema = async (
|
|
|
138
225
|
return undefined;
|
|
139
226
|
}
|
|
140
227
|
};
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* generates base create/update froms from names
|
|
231
|
+
*/
|
|
232
|
+
export const generateBaseForms = (modelMap: {[model: string]: Set<'create' | 'update'>}): StudioForm[] => {
|
|
233
|
+
const getSchema = (name: string, type: 'create' | 'update') : StudioForm => ({
|
|
234
|
+
name: `${name}${type === 'create' ? 'CreateForm' : 'UpdateForm'}`,
|
|
235
|
+
formActionType: type,
|
|
236
|
+
dataType: { dataSourceType: 'DataStore', dataTypeName: name },
|
|
237
|
+
fields: {},
|
|
238
|
+
sectionalElements: {},
|
|
239
|
+
style: {},
|
|
240
|
+
cta: {},
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
const schemas : StudioForm[] = [];
|
|
244
|
+
|
|
245
|
+
Object.entries(modelMap).forEach(([name, set]) => {
|
|
246
|
+
set.forEach(type => schemas.push(getSchema(name, type)));
|
|
247
|
+
});
|
|
248
|
+
return schemas;
|
|
249
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/* eslint-disable spellcheck/spell-checker */
|
|
2
|
+
import {
|
|
3
|
+
FeatureFlags, pathManager, stateManager,
|
|
4
|
+
} from 'amplify-cli-core';
|
|
5
|
+
import { printer } from 'amplify-prompts';
|
|
6
|
+
|
|
7
|
+
// This is copy-pasta from the category api until they can expose the method to plugins
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Shorthand for Feature flag retrieval.
|
|
11
|
+
*/
|
|
12
|
+
const useExperimentalPipelinedTransformerFF = (): boolean => FeatureFlags.getBoolean('graphQLTransformer.useExperimentalPipelinedTransformer');
|
|
13
|
+
const transformerVersionFF = (): number => FeatureFlags.getNumber('graphQLTransformer.transformerVersion');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Inspect feature flags for the project and determine if this particular project should be using graphql v2 or v1.
|
|
17
|
+
* Coerces feature flags into a sane state if they are out of, and throws error on invalid configuration.
|
|
18
|
+
*/
|
|
19
|
+
export const getTransformerVersion = async (): Promise<number> => {
|
|
20
|
+
if (useExperimentalPipelinedTransformerFF() === false) {
|
|
21
|
+
return 1;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (isLegacyFeatureFlagConfiguration()) {
|
|
25
|
+
await migrateToTransformerVersionFeatureFlag();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const transformerVersion = transformerVersionFF();
|
|
29
|
+
if (transformerVersion !== 1 && transformerVersion !== 2) {
|
|
30
|
+
throw new Error(`Invalid value specified for transformerVersion: '${transformerVersion}'`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return transformerVersion;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Return whether or not the project is configured with legacy pipelined transformer feature flags, and may need to be updated.
|
|
38
|
+
*/
|
|
39
|
+
const isLegacyFeatureFlagConfiguration = (): boolean => useExperimentalPipelinedTransformerFF() && transformerVersionFF() === 1;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Update project feature flags and alert the user if they have a deprecated set of feature flags.
|
|
43
|
+
*/
|
|
44
|
+
const migrateToTransformerVersionFeatureFlag = async (): Promise<void> => {
|
|
45
|
+
const projectPath = pathManager.findProjectRoot() ?? process.cwd();
|
|
46
|
+
|
|
47
|
+
const config = stateManager.getCLIJSON(projectPath, undefined, {
|
|
48
|
+
throwIfNotExist: false,
|
|
49
|
+
preserveComments: true,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// eslint-disable-next-line spellcheck/spell-checker
|
|
53
|
+
config.features.graphqltransformer.transformerversion = 2;
|
|
54
|
+
stateManager.setCLIJSON(projectPath, config);
|
|
55
|
+
await FeatureFlags.reloadValues();
|
|
56
|
+
// eslint-disable-next-line spellcheck/spell-checker
|
|
57
|
+
printer.warn(
|
|
58
|
+
`\nThe project is configured with 'transformerVersion': ${transformerVersionFF()}, but 'useExperimentalPipelinedTransformer': ${useExperimentalPipelinedTransformerFF()}. Setting the 'transformerVersion': ${
|
|
59
|
+
config.features.graphqltransformer.transformerversion
|
|
60
|
+
}. 'useExperimentalPipelinedTransformer' is deprecated.`,
|
|
61
|
+
);
|
|
62
|
+
};
|
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
2
|
import fs from 'fs-extra';
|
|
3
|
-
import { extractArgs } from './extractArgs';
|
|
4
3
|
import { $TSContext } from 'amplify-cli-core';
|
|
4
|
+
import { extractArgs } from './extractArgs';
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
/**
|
|
7
|
+
*
|
|
8
|
+
* locates components path directory
|
|
9
|
+
*/
|
|
10
|
+
export const getUiBuilderComponentsPath = (context: $TSContext): string => {
|
|
7
11
|
const args = extractArgs(context);
|
|
8
12
|
const srcDir = args.srcDir ? args.srcDir : context.exeInfo.projectConfig.javascript.config.SourceDir;
|
|
9
13
|
const uiBuilderComponentsPath = path.resolve(path.join('.', srcDir, 'ui-components'));
|
|
10
14
|
|
|
11
15
|
if (!fs.existsSync(uiBuilderComponentsPath)) {
|
|
16
|
+
// eslint-disable-next-line spellcheck/spell-checker
|
|
12
17
|
fs.mkdirpSync(uiBuilderComponentsPath);
|
|
13
18
|
}
|
|
14
19
|
|