@aws-amplify/amplify-util-uibuilder 1.5.0 → 1.6.0-cdkv2.1
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 +24 -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 +12 -2
- package/lib/commands/utils/codegenResources.d.ts.map +1 -1
- package/lib/commands/utils/codegenResources.js +70 -3
- 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 +36 -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 +21 -3
- package/src/__tests__/generateComponents.test.ts +43 -15
- package/src/__tests__/getAmplifyDataSchema.test.ts +51 -19
- package/src/__tests__/shouldRenderComponents.test.ts +51 -4
- package/src/__tests__/syncAmplifyUiComponents.test.ts +113 -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 +108 -4
- 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 +76 -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,19 @@ 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,
|
|
21
26
|
} from '@aws-amplify/codegen-ui-react';
|
|
22
27
|
import { printer } from 'amplify-prompts';
|
|
23
28
|
import {
|
|
@@ -83,15 +88,46 @@ export const createUiBuilderTheme = (context: $TSContext, schema: StudioTheme):
|
|
|
83
88
|
}
|
|
84
89
|
};
|
|
85
90
|
|
|
91
|
+
/**
|
|
92
|
+
* Writes form file to the work space
|
|
93
|
+
*/
|
|
94
|
+
export const createUiBuilderForm = (context: $TSContext, schema: StudioForm, dataSchema?: GenericDataSchema): StudioForm => {
|
|
95
|
+
const uiBuilderComponentsPath = getUiBuilderComponentsPath(context);
|
|
96
|
+
const rendererFactory = new StudioTemplateRendererFactory(
|
|
97
|
+
(form: StudioForm) => (new AmplifyFormRenderer(form, dataSchema, config) as unknown) as StudioTemplateRenderer<
|
|
98
|
+
unknown,
|
|
99
|
+
StudioForm,
|
|
100
|
+
FrameworkOutputManager<unknown>,
|
|
101
|
+
RenderTextComponentResponse
|
|
102
|
+
>,
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
const outputPathDir = uiBuilderComponentsPath;
|
|
106
|
+
|
|
107
|
+
const rendererManager = new StudioTemplateRendererManager(rendererFactory, {
|
|
108
|
+
outputPathDir,
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
try {
|
|
112
|
+
rendererManager.renderSchemaToTemplate(schema);
|
|
113
|
+
return schema;
|
|
114
|
+
} catch (e) {
|
|
115
|
+
printer.debug(e);
|
|
116
|
+
printer.debug('Skipping invalid form with schema');
|
|
117
|
+
printer.debug(JSON.stringify(schema, null, 2));
|
|
118
|
+
throw e;
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
|
|
86
122
|
/**
|
|
87
123
|
* Writes index file to the work space
|
|
88
124
|
*/
|
|
89
|
-
export const generateAmplifyUiBuilderIndexFile = (context: $TSContext, schemas:
|
|
125
|
+
export const generateAmplifyUiBuilderIndexFile = (context: $TSContext, schemas: StudioSchema[]): void => {
|
|
90
126
|
const uiBuilderComponentsPath = getUiBuilderComponentsPath(context);
|
|
91
127
|
const rendererFactory = new StudioTemplateRendererFactory(
|
|
92
|
-
(
|
|
128
|
+
(schema: StudioSchema[]) => (new ReactIndexStudioTemplateRenderer(schema, config) as unknown) as StudioTemplateRenderer<
|
|
93
129
|
unknown,
|
|
94
|
-
|
|
130
|
+
StudioSchema[],
|
|
95
131
|
FrameworkOutputManager<unknown>,
|
|
96
132
|
RenderTextComponentResponse
|
|
97
133
|
>,
|
|
@@ -104,7 +140,50 @@ export const generateAmplifyUiBuilderIndexFile = (context: $TSContext, schemas:
|
|
|
104
140
|
});
|
|
105
141
|
|
|
106
142
|
try {
|
|
107
|
-
|
|
143
|
+
rendererManager.renderSchemaToTemplate(schemas);
|
|
144
|
+
} catch (e) {
|
|
145
|
+
printer.debug(e);
|
|
146
|
+
printer.debug('Failed to generate component index file');
|
|
147
|
+
throw e;
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
type UtilFileChecks = {
|
|
152
|
+
hasForms: boolean;
|
|
153
|
+
hasViews: boolean;
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Writes utils file to the work space
|
|
158
|
+
*/
|
|
159
|
+
export const generateAmplifyUiBuilderUtilFile = (context: $TSContext, { hasForms, hasViews }: UtilFileChecks): void => {
|
|
160
|
+
const uiBuilderComponentsPath = getUiBuilderComponentsPath(context);
|
|
161
|
+
const rendererFactory = new StudioTemplateRendererFactory(
|
|
162
|
+
(utils: UtilTemplateType[]) => (new ReactUtilsStudioTemplateRenderer(utils, config)),
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
const outputPathDir = uiBuilderComponentsPath;
|
|
166
|
+
|
|
167
|
+
const rendererManager = new StudioTemplateRendererManager(rendererFactory, {
|
|
168
|
+
outputPathDir,
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
const utils = new Set<UtilTemplateType>();
|
|
172
|
+
|
|
173
|
+
if (hasForms) {
|
|
174
|
+
utils.add('validation');
|
|
175
|
+
utils.add('formatter');
|
|
176
|
+
utils.add('fetchByPath');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (hasViews) {
|
|
180
|
+
utils.add('formatter');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
try {
|
|
184
|
+
if (utils.size) {
|
|
185
|
+
rendererManager.renderSchemaToTemplate([...utils]);
|
|
186
|
+
}
|
|
108
187
|
} catch (e) {
|
|
109
188
|
printer.debug(e);
|
|
110
189
|
printer.debug('Failed to generate component index file');
|
|
@@ -119,6 +198,9 @@ export const generateAmplifyUiBuilderIndexFile = (context: $TSContext, schemas:
|
|
|
119
198
|
export const getAmplifyDataSchema = async (
|
|
120
199
|
studioClient: AmplifyStudioClient,
|
|
121
200
|
): Promise<GenericDataSchema | undefined> => {
|
|
201
|
+
if (!studioClient.isGraphQLSupported) {
|
|
202
|
+
return undefined;
|
|
203
|
+
}
|
|
122
204
|
try {
|
|
123
205
|
const meta = stateManager.getMeta();
|
|
124
206
|
const resourceName = Object.entries(meta[AmplifyCategories.API]).find(
|
|
@@ -138,3 +220,25 @@ export const getAmplifyDataSchema = async (
|
|
|
138
220
|
return undefined;
|
|
139
221
|
}
|
|
140
222
|
};
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* generates base create/update froms from names
|
|
226
|
+
*/
|
|
227
|
+
export const generateBaseForms = (modelMap: {[model: string]: Set<'create' | 'update'>}): StudioForm[] => {
|
|
228
|
+
const getSchema = (name: string, type: 'create' | 'update') : StudioForm => ({
|
|
229
|
+
name: `${name}${type === 'create' ? 'CreateForm' : 'UpdateForm'}`,
|
|
230
|
+
formActionType: type,
|
|
231
|
+
dataType: { dataSourceType: 'DataStore', dataTypeName: name },
|
|
232
|
+
fields: {},
|
|
233
|
+
sectionalElements: {},
|
|
234
|
+
style: {},
|
|
235
|
+
cta: {},
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
const schemas : StudioForm[] = [];
|
|
239
|
+
|
|
240
|
+
Object.entries(modelMap).forEach(([name, set]) => {
|
|
241
|
+
set.forEach(type => schemas.push(getSchema(name, type)));
|
|
242
|
+
});
|
|
243
|
+
return schemas;
|
|
244
|
+
};
|
|
@@ -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
|
|
|
@@ -1,34 +1,46 @@
|
|
|
1
|
+
/* eslint-disable prefer-arrow/prefer-arrow-functions */
|
|
1
2
|
import { $TSContext } from 'amplify-cli-core';
|
|
2
3
|
import { printer } from 'amplify-prompts';
|
|
3
|
-
|
|
4
|
+
import { AmplifyStudioClient } from '../../clients';
|
|
5
|
+
import { getAppId } from './environmentHelpers';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* process to decide if we should render components
|
|
9
|
+
*/
|
|
10
|
+
export const shouldRenderComponents = async (context: $TSContext): Promise<boolean> => {
|
|
4
11
|
if (process.env.FORCE_RENDER) {
|
|
5
12
|
printer.debug('Forcing component render since environment variable flag is set.');
|
|
6
13
|
return true;
|
|
7
14
|
}
|
|
8
15
|
if (context?.input?.options?.['no-codegen']) {
|
|
9
|
-
printer.debug('Not pulling components because --no-codegen flag is set');
|
|
16
|
+
printer.debug('Not pulling components because --no-codegen flag is set.');
|
|
10
17
|
return false;
|
|
11
18
|
}
|
|
12
19
|
|
|
13
20
|
if (!context?.exeInfo?.projectConfig) {
|
|
14
|
-
printer.debug('Not pulling components because there is no projectConfig set for this project');
|
|
21
|
+
printer.debug('Not pulling components because there is no projectConfig set for this project.');
|
|
15
22
|
return false;
|
|
16
23
|
}
|
|
17
24
|
|
|
18
25
|
if (!context.exeInfo.projectConfig.providers.includes('awscloudformation')) {
|
|
19
|
-
printer.debug('Not pulling components because there is no "awscloudformation" provider');
|
|
26
|
+
printer.debug('Not pulling components because there is no "awscloudformation" provider.');
|
|
20
27
|
return false;
|
|
21
28
|
}
|
|
22
29
|
|
|
23
30
|
if (context.exeInfo.projectConfig.frontend !== 'javascript') {
|
|
24
|
-
printer.debug('Not pulling components because this project is not configured as a javascript frontend');
|
|
31
|
+
printer.debug('Not pulling components because this project is not configured as a javascript frontend.');
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (context.exeInfo.projectConfig.javascript.framework !== 'react') {
|
|
36
|
+
printer.debug('Not pulling components because this project is not configured with the "react" framework.');
|
|
25
37
|
return false;
|
|
26
38
|
}
|
|
27
39
|
|
|
28
|
-
if (context
|
|
29
|
-
printer.debug('Not pulling components because this project is not
|
|
40
|
+
if (!(await AmplifyStudioClient.isAmplifyApp(context, getAppId(context)))) {
|
|
41
|
+
printer.debug('Not pulling components because this project is not Amplify Studio enabled.');
|
|
30
42
|
return false;
|
|
31
43
|
}
|
|
32
44
|
|
|
33
45
|
return true;
|
|
34
|
-
}
|
|
46
|
+
};
|