@aws-amplify/amplify-util-uibuilder 1.6.2 → 1.6.3-graphql15.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/API.md +19 -0
- package/CHANGELOG.md +13 -0
- package/amplify-plugin.json +3 -1
- package/lib/clients/amplify-studio-client.d.ts +1 -0
- package/lib/clients/amplify-studio-client.d.ts.map +1 -1
- package/lib/clients/amplify-studio-client.js +13 -0
- package/lib/clients/amplify-studio-client.js.map +1 -1
- package/lib/commands/cloneComponentsFromEnv.js +2 -2
- package/lib/commands/cloneComponentsFromEnv.js.map +1 -1
- package/lib/commands/generateComponents.d.ts +1 -1
- package/lib/commands/generateComponents.d.ts.map +1 -1
- package/lib/commands/generateComponents.js +15 -3
- package/lib/commands/generateComponents.js.map +1 -1
- package/lib/commands/utils/codegenResources.d.ts.map +1 -1
- package/lib/commands/utils/codegenResources.js +3 -1
- package/lib/commands/utils/codegenResources.js.map +1 -1
- package/lib/commands/utils/detachedFormCleanup.d.ts +11 -0
- package/lib/commands/utils/detachedFormCleanup.d.ts.map +1 -0
- package/lib/commands/utils/detachedFormCleanup.js +47 -0
- package/lib/commands/utils/detachedFormCleanup.js.map +1 -0
- package/lib/commands/utils/index.d.ts +1 -0
- package/lib/commands/utils/index.d.ts.map +1 -1
- package/lib/commands/utils/index.js +1 -0
- package/lib/commands/utils/index.js.map +1 -1
- package/lib/commands/utils/notifyMissingPackages.js +2 -2
- package/lib/commands/utils/notifyMissingPackages.js.map +1 -1
- package/lib/commands/utils/shouldRenderComponents.d.ts.map +1 -1
- package/lib/commands/utils/shouldRenderComponents.js +7 -5
- package/lib/commands/utils/shouldRenderComponents.js.map +1 -1
- package/lib/commands/utils/syncAmplifyUiBuilderComponents.d.ts.map +1 -1
- package/lib/commands/utils/syncAmplifyUiBuilderComponents.js +3 -1
- package/lib/commands/utils/syncAmplifyUiBuilderComponents.js.map +1 -1
- package/lib/event-handlers/handle-PostPull.js +1 -1
- package/lib/event-handlers/handle-PostPull.js.map +1 -1
- package/lib/event-handlers/handle-PostPush.js +1 -1
- package/lib/event-handlers/handle-PostPush.js.map +1 -1
- package/lib/event-handlers/handle-PrePush.d.ts +3 -0
- package/lib/event-handlers/handle-PrePush.d.ts.map +1 -0
- package/lib/event-handlers/handle-PrePush.js +15 -0
- package/lib/event-handlers/handle-PrePush.js.map +1 -0
- package/lib/utils/prePushHandler.d.ts +3 -0
- package/lib/utils/prePushHandler.d.ts.map +1 -0
- package/lib/utils/prePushHandler.js +40 -0
- package/lib/utils/prePushHandler.js.map +1 -0
- package/package.json +12 -9
- package/src/__tests__/createUiBuilderComponent.test.ts +4 -0
- package/src/__tests__/detachedFormCleanup.test.ts +78 -0
- package/src/__tests__/generateComponents.test.ts +13 -11
- package/src/__tests__/handleEvents.test.ts +27 -0
- package/src/__tests__/prePushHandler.test.ts +105 -0
- package/src/__tests__/shouldRenderComponents.test.ts +48 -35
- package/src/__tests__/syncAmplifyUiComponents.test.ts +3 -3
- package/src/clients/amplify-studio-client.ts +13 -0
- package/src/commands/cloneComponentsFromEnv.ts +1 -1
- package/src/commands/generateComponents.ts +30 -4
- package/src/commands/utils/codegenResources.ts +3 -1
- package/src/commands/utils/detachedFormCleanup.ts +58 -0
- package/src/commands/utils/index.ts +1 -0
- package/src/commands/utils/notifyMissingPackages.ts +2 -2
- package/src/commands/utils/shouldRenderComponents.ts +7 -5
- package/src/commands/utils/syncAmplifyUiBuilderComponents.ts +4 -1
- package/src/event-handlers/handle-PostPull.ts +1 -1
- package/src/event-handlers/handle-PostPush.ts +1 -1
- package/src/event-handlers/handle-PrePush.ts +15 -0
- package/src/utils/prePushHandler.ts +55 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { StudioForm, StudioSchema } from '@aws-amplify/codegen-ui';
|
|
2
|
+
import { Form } from 'aws-sdk/clients/amplifyuibuilder';
|
|
3
|
+
import { AmplifyStudioClient } from '../../clients';
|
|
4
|
+
import { printer } from 'amplify-prompts';
|
|
5
|
+
import asyncPool from 'tiny-async-pool';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Does the form's reference DataStore type exist in the list of models
|
|
9
|
+
*/
|
|
10
|
+
export const isFormDetachedFromModel = (schema: StudioForm | Form, models: Set<string>): boolean => {
|
|
11
|
+
if (schema.dataType.dataSourceType === 'DataStore' && !models.has(schema.dataType.dataTypeName)) {
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
return false;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* A form schema will be customized if fields, style, sectionalElements objects aren't empty
|
|
19
|
+
* @param schema The schema to test
|
|
20
|
+
* @returns boolean
|
|
21
|
+
*/
|
|
22
|
+
export const isFormSchemaCustomized = (schema: StudioForm | Form): boolean => {
|
|
23
|
+
const { fields, style, sectionalElements } = schema;
|
|
24
|
+
// Fields and sectionalElements just need an empty check
|
|
25
|
+
if (!isEmpty({ ...fields, ...sectionalElements })) {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Check style
|
|
30
|
+
// If not empty, at least one style must be defined
|
|
31
|
+
return Object.values(style).some(styleConfig => styleConfig !== undefined);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Is the schema a StudioForm
|
|
36
|
+
*/
|
|
37
|
+
export const isStudioForm = (schema: StudioSchema | undefined): schema is StudioForm => {
|
|
38
|
+
if (!schema) return false;
|
|
39
|
+
return 'formActionType' in schema;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const deleteDetachedForms = async (detachedForms: {id: string, name: string}[], studioClient: AmplifyStudioClient) => {
|
|
43
|
+
const deleteForm = async ({id, name}: {id: string, name: string}) => {
|
|
44
|
+
try {
|
|
45
|
+
// Try to delete the form but don't stop any other operations if it fails
|
|
46
|
+
await studioClient.deleteForm(id);
|
|
47
|
+
return {status: 'SUCCESS', message: `Deleted detached form ${name}`};
|
|
48
|
+
} catch (error) {
|
|
49
|
+
return {status: 'FAIL', message: `Failed to delete detached form ${name}`}
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
for await (const status of asyncPool(5, detachedForms, deleteForm)) {
|
|
53
|
+
printer.debug(status.message);
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
58
|
+
const isEmpty = (obj?: any): boolean => Object.keys(obj).length === 0;
|
|
@@ -33,11 +33,11 @@ export const notifyMissingPackages = (context: $TSContext): void => {
|
|
|
33
33
|
const packageIsInstalled = Object.keys(packageJson.dependencies).includes(dependency.dependencyName);
|
|
34
34
|
if (!packageIsInstalled) {
|
|
35
35
|
printer.warn(
|
|
36
|
-
`UIBuilder components
|
|
36
|
+
`UIBuilder components require "${dependency.dependencyName}" that is not in your package.json. Run \`npm install "${dependency.dependencyName}@${dependency.supportedSemVerPattern}"\`. ${dependency.reason}`,
|
|
37
37
|
);
|
|
38
38
|
} else if (!rangeSubset(packageJson.dependencies[dependency.dependencyName], dependency.supportedSemVerPattern)) {
|
|
39
39
|
printer.warn(
|
|
40
|
-
`UIBuilder components
|
|
40
|
+
`UIBuilder components require version "${dependency.supportedSemVerPattern}" of "${
|
|
41
41
|
dependency.dependencyName
|
|
42
42
|
}". You currently are on version "${packageJson.dependencies[dependency.dependencyName]}". Run \`npm install "${
|
|
43
43
|
dependency.dependencyName
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* eslint-disable prefer-arrow/prefer-arrow-functions */
|
|
2
|
-
import { $TSContext } from 'amplify-cli-core';
|
|
2
|
+
import { $TSContext, stateManager } from 'amplify-cli-core';
|
|
3
3
|
import { printer } from 'amplify-prompts';
|
|
4
4
|
import { AmplifyStudioClient } from '../../clients';
|
|
5
5
|
import { getAppId } from './environmentHelpers';
|
|
@@ -8,6 +8,8 @@ import { getAppId } from './environmentHelpers';
|
|
|
8
8
|
* process to decide if we should render components
|
|
9
9
|
*/
|
|
10
10
|
export const shouldRenderComponents = async (context: $TSContext): Promise<boolean> => {
|
|
11
|
+
const projectConfig = stateManager.getProjectConfig();
|
|
12
|
+
|
|
11
13
|
if (process.env.FORCE_RENDER) {
|
|
12
14
|
printer.debug('Forcing component render since environment variable flag is set.');
|
|
13
15
|
return true;
|
|
@@ -17,22 +19,22 @@ export const shouldRenderComponents = async (context: $TSContext): Promise<boole
|
|
|
17
19
|
return false;
|
|
18
20
|
}
|
|
19
21
|
|
|
20
|
-
if (!
|
|
22
|
+
if (!projectConfig) {
|
|
21
23
|
printer.debug('Not pulling components because there is no projectConfig set for this project.');
|
|
22
24
|
return false;
|
|
23
25
|
}
|
|
24
26
|
|
|
25
|
-
if (!
|
|
27
|
+
if (!projectConfig.providers.includes('awscloudformation')) {
|
|
26
28
|
printer.debug('Not pulling components because there is no "awscloudformation" provider.');
|
|
27
29
|
return false;
|
|
28
30
|
}
|
|
29
31
|
|
|
30
|
-
if (
|
|
32
|
+
if (projectConfig.frontend !== 'javascript') {
|
|
31
33
|
printer.debug('Not pulling components because this project is not configured as a javascript frontend.');
|
|
32
34
|
return false;
|
|
33
35
|
}
|
|
34
36
|
|
|
35
|
-
if (
|
|
37
|
+
if (projectConfig.javascript.framework !== 'react') {
|
|
36
38
|
printer.debug('Not pulling components because this project is not configured with the "react" framework.');
|
|
37
39
|
return false;
|
|
38
40
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// es-lint-disable-file spellcheck/spell-checker
|
|
1
2
|
import { printer } from 'amplify-prompts';
|
|
2
3
|
import { $TSContext } from 'amplify-cli-core';
|
|
3
4
|
import {
|
|
@@ -120,7 +121,9 @@ export const generateUiBuilderForms = (
|
|
|
120
121
|
} catch (e) {
|
|
121
122
|
printer.debug(`Failure caught processing ${schema.name}`);
|
|
122
123
|
printer.debug(e);
|
|
123
|
-
return {
|
|
124
|
+
return {
|
|
125
|
+
resultType: 'FAILURE', schemaName: schema.name, schema, error: e,
|
|
126
|
+
};
|
|
124
127
|
}
|
|
125
128
|
};
|
|
126
129
|
const formResults = formSchemas.map((schema: StudioForm) => {
|
|
@@ -7,7 +7,7 @@ import { run as generateComponents } from '../commands/generateComponents';
|
|
|
7
7
|
*/
|
|
8
8
|
export const run = async (context: $TSContext): Promise<void> => {
|
|
9
9
|
try {
|
|
10
|
-
await generateComponents(context);
|
|
10
|
+
await generateComponents(context, 'PostPull');
|
|
11
11
|
} catch (e) {
|
|
12
12
|
// Swallow all errors from the uibuilder plugin
|
|
13
13
|
printer.debug(e);
|
|
@@ -7,7 +7,7 @@ import { run as generateComponents } from '../commands/generateComponents';
|
|
|
7
7
|
*/
|
|
8
8
|
export const run = async (context: $TSContext): Promise<void> => {
|
|
9
9
|
try {
|
|
10
|
-
await generateComponents(context);
|
|
10
|
+
await generateComponents(context, 'PostPush');
|
|
11
11
|
} catch (e) {
|
|
12
12
|
// Swallow all errors from the uibuilder plugin
|
|
13
13
|
printer.debug(e);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { $TSContext } from 'amplify-cli-core';
|
|
2
|
+
import { printer } from 'amplify-prompts';
|
|
3
|
+
import { prePushHandler } from '../utils/prePushHandler';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The code to run before a push.
|
|
7
|
+
*/
|
|
8
|
+
export const run = async (context: $TSContext): Promise<void> => {
|
|
9
|
+
try {
|
|
10
|
+
await prePushHandler(context);
|
|
11
|
+
} catch (e) {
|
|
12
|
+
// Swallow all errors from the uibuilder plugin
|
|
13
|
+
printer.debug(e);
|
|
14
|
+
}
|
|
15
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { $TSContext } from 'amplify-cli-core';
|
|
2
|
+
import type { Schema } from '@aws-amplify/datastore';
|
|
3
|
+
import { printer } from 'amplify-prompts';
|
|
4
|
+
import AmplifyUIBuilder from 'aws-sdk/clients/amplifyuibuilder';
|
|
5
|
+
import { AmplifyStudioClient } from '../clients';
|
|
6
|
+
import { isFormDetachedFromModel, isFormSchemaCustomized, shouldRenderComponents } from '../commands/utils';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Handles showing a warning for detached forms pre-push.
|
|
10
|
+
*/
|
|
11
|
+
export const prePushHandler = async (context: $TSContext): Promise<void> => {
|
|
12
|
+
if (!(await shouldRenderComponents(context))) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
const studioClient = await AmplifyStudioClient.setClientInfo(context);
|
|
17
|
+
const [formSchemas, localSchema] = await Promise.all<[Promise<{
|
|
18
|
+
entities: AmplifyUIBuilder.Form[];
|
|
19
|
+
}>, Promise<Schema>]>([
|
|
20
|
+
studioClient.listForms(),
|
|
21
|
+
context.amplify.invokePluginMethod(
|
|
22
|
+
context,
|
|
23
|
+
'codegen',
|
|
24
|
+
undefined,
|
|
25
|
+
'getModelIntrospection',
|
|
26
|
+
[context],
|
|
27
|
+
),
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
if (!localSchema) {
|
|
31
|
+
printer.debug('Local schema not found');
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
printDetachedFormsWarning(formSchemas, localSchema);
|
|
35
|
+
} catch (error) {
|
|
36
|
+
printer.debug(error);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const printDetachedFormsWarning = (formSchemas: { entities: AmplifyUIBuilder.Form[] }, localSchema: Schema): void => {
|
|
41
|
+
const modelNames = new Set(Object.keys(localSchema.models));
|
|
42
|
+
const detachedCustomForms: string[] = [];
|
|
43
|
+
|
|
44
|
+
formSchemas.entities.forEach(form => {
|
|
45
|
+
// Only show warnings for schema's that have customization, otherwise it's the same as
|
|
46
|
+
// an autogenerated form.
|
|
47
|
+
if (isFormDetachedFromModel(form, modelNames) && isFormSchemaCustomized(form)) {
|
|
48
|
+
detachedCustomForms.push(form.name);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
if (detachedCustomForms.length > 0) {
|
|
53
|
+
printer.warn(`The following form${detachedCustomForms.length === 1 ? '' : 's'} will no longer be available because the connected data model no longer exists: ${detachedCustomForms.join(', ')}`);
|
|
54
|
+
}
|
|
55
|
+
};
|