@aws-amplify/amplify-util-uibuilder 1.3.0-rc.c36fa71f5.0 → 1.3.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 +14 -1
- package/lib/clients/amplify-studio-client.d.ts +17 -0
- package/lib/clients/amplify-studio-client.d.ts.map +1 -0
- package/lib/clients/amplify-studio-client.js +145 -0
- package/lib/clients/amplify-studio-client.js.map +1 -0
- package/lib/clients/index.d.ts +2 -0
- package/lib/clients/index.d.ts.map +1 -0
- package/lib/clients/index.js +9 -0
- package/lib/clients/index.js.map +1 -0
- package/lib/commands/cloneComponentsFromEnv.d.ts +1 -1
- package/lib/commands/cloneComponentsFromEnv.d.ts.map +1 -1
- package/lib/commands/cloneComponentsFromEnv.js +15 -31
- package/lib/commands/cloneComponentsFromEnv.js.map +1 -1
- package/lib/commands/generateComponents.d.ts.map +1 -1
- package/lib/commands/generateComponents.js +14 -12
- package/lib/commands/generateComponents.js.map +1 -1
- package/lib/commands/utils/{createUiBuilderComponent.d.ts → codegenResources.d.ts} +5 -3
- package/lib/commands/utils/codegenResources.d.ts.map +1 -0
- package/lib/commands/utils/{createUiBuilderComponent.js → codegenResources.js} +26 -4
- package/lib/commands/utils/codegenResources.js.map +1 -0
- package/lib/commands/utils/environmentHelpers.d.ts +5 -0
- package/lib/commands/utils/environmentHelpers.d.ts.map +1 -0
- package/lib/commands/utils/environmentHelpers.js +25 -0
- package/lib/commands/utils/environmentHelpers.js.map +1 -0
- package/lib/commands/utils/index.d.ts +8 -0
- package/lib/commands/utils/index.d.ts.map +1 -0
- package/lib/commands/utils/index.js +20 -0
- package/lib/commands/utils/index.js.map +1 -0
- package/lib/commands/utils/syncAmplifyUiBuilderComponents.d.ts +9 -18
- package/lib/commands/utils/syncAmplifyUiBuilderComponents.d.ts.map +1 -1
- package/lib/commands/utils/syncAmplifyUiBuilderComponents.js +8 -80
- package/lib/commands/utils/syncAmplifyUiBuilderComponents.js.map +1 -1
- package/lib/index.d.ts +3 -3
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +5 -5
- package/lib/index.js.map +1 -1
- package/package.json +14 -6
- package/src/__tests__/cloneComponentsFromEnv.test.ts +33 -26
- package/src/__tests__/createUiBuilderComponent.test.ts +14 -13
- package/src/__tests__/generateComponents.test.ts +40 -30
- package/src/__tests__/getAmplifyDataSchema.test.ts +102 -0
- package/src/__tests__/syncAmplifyUiComponents.test.ts +55 -33
- package/src/clients/amplify-studio-client.ts +161 -0
- package/src/clients/index.ts +1 -0
- package/src/commands/cloneComponentsFromEnv.ts +26 -29
- package/src/commands/generateComponents.ts +19 -12
- package/src/commands/utils/{createUiBuilderComponent.ts → codegenResources.ts} +39 -11
- package/src/commands/utils/environmentHelpers.ts +32 -0
- package/src/commands/utils/index.ts +7 -0
- package/src/commands/utils/syncAmplifyUiBuilderComponents.ts +38 -81
- package/src/index.ts +15 -5
- package/tsconfig.tsbuildinfo +1 -1
- package/lib/commands/utils/amplifyUiBuilderService.d.ts +0 -5
- package/lib/commands/utils/amplifyUiBuilderService.d.ts.map +0 -1
- package/lib/commands/utils/amplifyUiBuilderService.js +0 -20
- package/lib/commands/utils/amplifyUiBuilderService.js.map +0 -1
- package/lib/commands/utils/createUiBuilderComponent.d.ts.map +0 -1
- package/lib/commands/utils/createUiBuilderComponent.js.map +0 -1
- package/src/commands/utils/amplifyUiBuilderService.ts +0 -22
|
@@ -1,24 +1,29 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AmplifyCategories, AmplifySupportedService, stateManager } from 'amplify-cli-core'; // eslint-disable-line import/no-extraneous-dependencies
|
|
2
|
+
import aws from 'aws-sdk'; // eslint-disable-line import/no-extraneous-dependencies
|
|
2
3
|
import {
|
|
3
4
|
generateUiBuilderComponents,
|
|
5
|
+
generateUiBuilderThemes,
|
|
4
6
|
getEnvName,
|
|
5
7
|
getAppId,
|
|
6
|
-
listUiBuilderComponents,
|
|
7
|
-
listUiBuilderThemes,
|
|
8
8
|
resolveAppId,
|
|
9
|
-
|
|
10
|
-
} from '../
|
|
11
|
-
import
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
9
|
+
} from '../commands/utils';
|
|
10
|
+
import { AmplifyStudioClient } from '../clients';
|
|
11
|
+
import * as createUiBuilderComponentDependency from '../commands/utils/codegenResources';
|
|
12
|
+
|
|
13
|
+
jest.mock('amplify-cli-core', () => ({
|
|
14
|
+
...jest.requireActual('amplify-cli-core'),
|
|
15
|
+
FeatureFlags: {
|
|
16
|
+
getBoolean: () => false,
|
|
17
|
+
getNumber: () => 0,
|
|
18
|
+
},
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
const awsMock = aws as any;
|
|
22
|
+
const stateManagerMock = stateManager as any;
|
|
23
|
+
const createUiBuilderComponentDependencyMock = createUiBuilderComponentDependency as any;
|
|
17
24
|
describe('should sync amplify ui builder components', () => {
|
|
18
|
-
let context:
|
|
25
|
+
let context: any;
|
|
19
26
|
beforeEach(() => {
|
|
20
|
-
CLICore.FeatureFlags.getBoolean = () => false;
|
|
21
|
-
CLICore.FeatureFlags.getNumber = () => 0;
|
|
22
27
|
context = {
|
|
23
28
|
exeInfo: {
|
|
24
29
|
projectConfig: {
|
|
@@ -43,7 +48,18 @@ describe('should sync amplify ui builder components', () => {
|
|
|
43
48
|
},
|
|
44
49
|
};
|
|
45
50
|
|
|
46
|
-
|
|
51
|
+
stateManagerMock.getMeta = jest.fn(() => ({
|
|
52
|
+
[AmplifyCategories.API]: {
|
|
53
|
+
MyResourceName: {
|
|
54
|
+
service: AmplifySupportedService.APPSYNC,
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
providers: {
|
|
58
|
+
awscloudformation: { AmplifyAppId: 'testAppId' },
|
|
59
|
+
},
|
|
60
|
+
}));
|
|
61
|
+
|
|
62
|
+
awsMock.AmplifyUIBuilder = jest.fn(() => ({
|
|
47
63
|
exportComponents: jest.fn(() => ({
|
|
48
64
|
promise: jest.fn(() => ({
|
|
49
65
|
entities: [
|
|
@@ -68,8 +84,8 @@ describe('should sync amplify ui builder components', () => {
|
|
|
68
84
|
})),
|
|
69
85
|
}));
|
|
70
86
|
|
|
71
|
-
|
|
72
|
-
|
|
87
|
+
createUiBuilderComponentDependencyMock.createUiBuilderComponent = jest.fn();
|
|
88
|
+
createUiBuilderComponentDependencyMock.createUiBuilderTheme = jest.fn();
|
|
73
89
|
});
|
|
74
90
|
|
|
75
91
|
it('pulls components from aws-sdk and passes them to createUiBuilderComponent', () => {
|
|
@@ -77,14 +93,14 @@ describe('should sync amplify ui builder components', () => {
|
|
|
77
93
|
});
|
|
78
94
|
|
|
79
95
|
it('does not throw an error when createUiBuilderComponent fails', () => {
|
|
80
|
-
|
|
96
|
+
createUiBuilderComponentDependencyMock.createUiBuilderComponent = jest.fn(() => {
|
|
81
97
|
throw new Error('ahhh!');
|
|
82
98
|
});
|
|
83
99
|
expect(async () => generateUiBuilderComponents(context, [])).not.toThrow();
|
|
84
100
|
});
|
|
85
101
|
|
|
86
102
|
it('does not throw an error when createUiBuilderThemes fails', () => {
|
|
87
|
-
|
|
103
|
+
createUiBuilderComponentDependencyMock.createUiBuilderComponent = jest.fn(() => {
|
|
88
104
|
throw new Error('ahhh!');
|
|
89
105
|
});
|
|
90
106
|
expect(async () => generateUiBuilderThemes(context, [])).not.toThrow();
|
|
@@ -93,20 +109,26 @@ describe('should sync amplify ui builder components', () => {
|
|
|
93
109
|
it('can getAmplifyUIBuilderService', async () => {
|
|
94
110
|
process.env.UI_BUILDER_ENDPOINT = 'https://mock-endpoint.com';
|
|
95
111
|
process.env.UI_BUILDER_REGION = 'mock-region';
|
|
96
|
-
const
|
|
97
|
-
expect(Object.keys(
|
|
98
|
-
|
|
112
|
+
const client = await AmplifyStudioClient.setClientInfo(context);
|
|
113
|
+
expect(Object.keys(client)).toEqual(expect.arrayContaining([
|
|
114
|
+
'listComponents',
|
|
115
|
+
'listThemes',
|
|
116
|
+
'getModels',
|
|
117
|
+
'createComponent',
|
|
118
|
+
]));
|
|
99
119
|
});
|
|
100
|
-
it('can
|
|
101
|
-
const
|
|
120
|
+
it('can list themes', async () => {
|
|
121
|
+
const client = await AmplifyStudioClient.setClientInfo(context);
|
|
122
|
+
const themes = await client.listThemes();
|
|
102
123
|
expect(themes.entities).toHaveLength(1);
|
|
103
124
|
});
|
|
104
125
|
it('can listUiBuilderComponents', async () => {
|
|
105
|
-
const
|
|
126
|
+
const client = await AmplifyStudioClient.setClientInfo(context);
|
|
127
|
+
const components = await client.listThemes();
|
|
106
128
|
expect(components.entities).toHaveLength(1);
|
|
107
129
|
});
|
|
108
130
|
it('can getAppId', async () => {
|
|
109
|
-
const appId =
|
|
131
|
+
const appId = getAppId(context);
|
|
110
132
|
expect(appId).toBe('testAppId');
|
|
111
133
|
});
|
|
112
134
|
it('can getEnvName', () => {
|
|
@@ -114,34 +136,34 @@ describe('should sync amplify ui builder components', () => {
|
|
|
114
136
|
expect(envName).toBe('testEnvName');
|
|
115
137
|
});
|
|
116
138
|
it('can resolveAppId', async () => {
|
|
117
|
-
|
|
118
|
-
const appId = await resolveAppId(context);
|
|
139
|
+
const appId = resolveAppId();
|
|
119
140
|
expect(appId).toBe('testAppId');
|
|
120
141
|
});
|
|
121
142
|
it('can throw on getAppId', async () => {
|
|
122
143
|
context.input.options.appId = null;
|
|
123
144
|
context.amplify.invokePluginMethod = () => null;
|
|
124
|
-
|
|
145
|
+
stateManagerMock.getMeta = () => ({});
|
|
146
|
+
expect(() => getAppId(context)).toThrowError();
|
|
125
147
|
});
|
|
126
148
|
it('can generate ui builder components', async () => {
|
|
127
|
-
|
|
149
|
+
createUiBuilderComponentDependencyMock.createUiBuilderComponent = jest.fn().mockImplementation(() => ({}));
|
|
128
150
|
const components = generateUiBuilderComponents(context, [{}, {}]);
|
|
129
151
|
expect(components.every(component => component.resultType === 'SUCCESS')).toBeTruthy();
|
|
130
152
|
});
|
|
131
153
|
it('can handle failed generation generate ui builder components', async () => {
|
|
132
|
-
|
|
154
|
+
createUiBuilderComponentDependencyMock.createUiBuilderComponent = jest.fn().mockImplementation(() => {
|
|
133
155
|
throw new Error('ahh!');
|
|
134
156
|
});
|
|
135
157
|
const components = generateUiBuilderComponents(context, [{}, {}]);
|
|
136
158
|
expect(components.every(component => component.resultType === 'FAILURE')).toBeTruthy();
|
|
137
159
|
});
|
|
138
160
|
it('can generate ui builder themes', async () => {
|
|
139
|
-
|
|
161
|
+
createUiBuilderComponentDependencyMock.createUiBuilderTheme = jest.fn().mockImplementation(() => ({}));
|
|
140
162
|
const themes = generateUiBuilderThemes(context, [{}, {}]);
|
|
141
163
|
expect(themes.every(theme => theme.resultType === 'SUCCESS')).toBeTruthy();
|
|
142
164
|
});
|
|
143
165
|
it('can handle failed generation generate ui builder themes', async () => {
|
|
144
|
-
|
|
166
|
+
createUiBuilderComponentDependencyMock.createUiBuilderTheme = jest.fn().mockImplementation(() => {
|
|
145
167
|
throw new Error('ahh!');
|
|
146
168
|
});
|
|
147
169
|
const themes = generateUiBuilderThemes(context, [{}, {}]);
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import type { $TSContext } from 'amplify-cli-core';
|
|
2
|
+
import type { ServiceConfigurationOptions } from 'aws-sdk/lib/service';
|
|
3
|
+
import type { CreateComponentData, Component, Theme } from 'aws-sdk/clients/amplifyuibuilder';
|
|
4
|
+
import { AmplifyUIBuilder, AmplifyBackend } from 'aws-sdk';
|
|
5
|
+
import { printer } from 'amplify-prompts';
|
|
6
|
+
import { getAppId, getEnvName } from '../commands/utils/environmentHelpers';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Builds the Amplify Backend Client
|
|
10
|
+
*/
|
|
11
|
+
const buildAmplifyBackendClient = (awsConfigInfo: ServiceConfigurationOptions): AmplifyBackend => {
|
|
12
|
+
const awsConfig = { ...awsConfigInfo };
|
|
13
|
+
if (process.env.AMPLIFY_BACKEND_ENDPOINT) {
|
|
14
|
+
awsConfig.endpoint = process.env.AMPLIFY_BACKEND_ENDPOINT;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (process.env.AMPLIFY_BACKEND_REGION) {
|
|
18
|
+
awsConfig.region = process.env.AMPLIFY_BACKEND_REGION;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return new AmplifyBackend(awsConfig);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Builds the Amplify UIBuilder Client
|
|
26
|
+
*/
|
|
27
|
+
const buildAmplifyUiBuilderClient = (awsConfigInfo: ServiceConfigurationOptions): AmplifyUIBuilder => {
|
|
28
|
+
const awsConfig = { ...awsConfigInfo };
|
|
29
|
+
if (process.env.UI_BUILDER_ENDPOINT) {
|
|
30
|
+
awsConfig.endpoint = process.env.UI_BUILDER_ENDPOINT;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (process.env.UI_BUILDER_REGION) {
|
|
34
|
+
awsConfig.region = process.env.UI_BUILDER_REGION;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return new AmplifyUIBuilder(awsConfig);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* used for Amplify Studio related clients
|
|
42
|
+
*/
|
|
43
|
+
export default class AmplifyStudioClient {
|
|
44
|
+
#amplifyUiBuilder: AmplifyUIBuilder;
|
|
45
|
+
#amplifyBackend: AmplifyBackend;
|
|
46
|
+
#appId: string;
|
|
47
|
+
#envName: string;
|
|
48
|
+
/**
|
|
49
|
+
* Used to configure the AWS Amplify clients.
|
|
50
|
+
*/
|
|
51
|
+
static async setClientInfo(context: $TSContext, envName?: string, appId?: string): Promise<AmplifyStudioClient> {
|
|
52
|
+
const resolvedEnvName = getEnvName(context, envName);
|
|
53
|
+
const resolvedAppId = getAppId(context, appId);
|
|
54
|
+
const awsConfigInfo = (await context.amplify.invokePluginMethod(context, 'awscloudformation', undefined, 'loadConfigurationForEnv', [
|
|
55
|
+
context,
|
|
56
|
+
resolvedEnvName,
|
|
57
|
+
resolvedAppId,
|
|
58
|
+
])) as ServiceConfigurationOptions;
|
|
59
|
+
return new AmplifyStudioClient(awsConfigInfo, resolvedAppId, resolvedEnvName);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/*
|
|
63
|
+
* Builds the clients
|
|
64
|
+
*/
|
|
65
|
+
constructor(awsConfigInfo: ServiceConfigurationOptions, appId: string, envName: string) {
|
|
66
|
+
this.#amplifyUiBuilder = buildAmplifyUiBuilderClient(awsConfigInfo);
|
|
67
|
+
this.#amplifyBackend = buildAmplifyBackendClient(awsConfigInfo);
|
|
68
|
+
this.#appId = appId;
|
|
69
|
+
this.#envName = envName;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* listComponents from Studio
|
|
74
|
+
*/
|
|
75
|
+
listComponents = async (envName?: string, appId?: string): Promise<{ entities: Component[] }> => {
|
|
76
|
+
const environmentName = envName || this.#envName;
|
|
77
|
+
const resolvedAppId = appId || this.#appId;
|
|
78
|
+
try {
|
|
79
|
+
let nextToken: string | undefined;
|
|
80
|
+
const uiBuilderComponents: Component[] = [];
|
|
81
|
+
do {
|
|
82
|
+
const response = await this.#amplifyUiBuilder
|
|
83
|
+
.exportComponents({
|
|
84
|
+
appId: resolvedAppId,
|
|
85
|
+
environmentName,
|
|
86
|
+
nextToken,
|
|
87
|
+
})
|
|
88
|
+
.promise();
|
|
89
|
+
uiBuilderComponents.push(...response.entities);
|
|
90
|
+
nextToken = response.nextToken;
|
|
91
|
+
} while (nextToken);
|
|
92
|
+
printer.debug(JSON.stringify(uiBuilderComponents, null, 2));
|
|
93
|
+
return { entities: uiBuilderComponents };
|
|
94
|
+
} catch (e) {
|
|
95
|
+
printer.debug(e);
|
|
96
|
+
throw e;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Returns all the UI Builder themes from the app
|
|
102
|
+
*/
|
|
103
|
+
listThemes = async (envName?: string, appId?: string): Promise<{ entities: Theme[] }> => {
|
|
104
|
+
const environmentName = envName || this.#envName;
|
|
105
|
+
const resolvedAppId = appId || this.#appId;
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
let nextToken: string | undefined;
|
|
109
|
+
const uiBuilderThemes: Theme[] = [];
|
|
110
|
+
do {
|
|
111
|
+
const response = await this.#amplifyUiBuilder
|
|
112
|
+
.exportThemes({
|
|
113
|
+
appId: resolvedAppId,
|
|
114
|
+
environmentName,
|
|
115
|
+
nextToken,
|
|
116
|
+
})
|
|
117
|
+
.promise();
|
|
118
|
+
uiBuilderThemes.push(...response.entities);
|
|
119
|
+
nextToken = response.nextToken;
|
|
120
|
+
} while (nextToken);
|
|
121
|
+
printer.debug(JSON.stringify(uiBuilderThemes, null, 2));
|
|
122
|
+
return { entities: uiBuilderThemes };
|
|
123
|
+
} catch (e) {
|
|
124
|
+
printer.debug(e);
|
|
125
|
+
throw e;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
createComponent = async (component: CreateComponentData, envName?: string, appId?: string): Promise<Component | undefined> => {
|
|
130
|
+
const environmentName = envName || this.#envName;
|
|
131
|
+
const resolvedAppId = appId || this.#appId;
|
|
132
|
+
try {
|
|
133
|
+
const response = await this.#amplifyUiBuilder.createComponent({
|
|
134
|
+
appId: resolvedAppId,
|
|
135
|
+
environmentName,
|
|
136
|
+
componentToCreate: component,
|
|
137
|
+
}).promise();
|
|
138
|
+
return response.entity;
|
|
139
|
+
} catch (err) {
|
|
140
|
+
printer.debug(err.toString());
|
|
141
|
+
throw err;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
getModels = async (resourceName: string, envName?: string, appId?: string): Promise<string | undefined> => {
|
|
146
|
+
try {
|
|
147
|
+
const environmentName = envName || this.#envName;
|
|
148
|
+
const resolvedAppId = appId || this.#appId;
|
|
149
|
+
const { Models } = await this.#amplifyBackend.getBackendAPIModels({
|
|
150
|
+
AppId: resolvedAppId,
|
|
151
|
+
BackendEnvironmentName: environmentName,
|
|
152
|
+
ResourceName: resourceName,
|
|
153
|
+
})
|
|
154
|
+
.promise();
|
|
155
|
+
return Models;
|
|
156
|
+
} catch (e) {
|
|
157
|
+
printer.debug(e);
|
|
158
|
+
throw new Error(`Models not found in AmplifyBackend:GetBackendAPIModels response: ${e.message}`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as AmplifyStudioClient } from './amplify-studio-client';
|
|
@@ -1,21 +1,25 @@
|
|
|
1
|
-
import { extractArgs } from './utils/extractArgs';
|
|
2
|
-
import { listUiBuilderComponents } from './utils/syncAmplifyUiBuilderComponents';
|
|
3
1
|
import { printer } from 'amplify-prompts';
|
|
4
2
|
import { $TSContext } from 'amplify-cli-core';
|
|
5
|
-
import {
|
|
3
|
+
import { extractArgs } from './utils';
|
|
4
|
+
import { AmplifyStudioClient } from '../clients';
|
|
6
5
|
|
|
7
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Clones the components from the source to new environment
|
|
8
|
+
*/
|
|
9
|
+
export const run = async (context: $TSContext): Promise<void> => {
|
|
8
10
|
printer.debug('Running create components command in amplify-util-uibuilder');
|
|
9
11
|
const args = extractArgs(context);
|
|
10
12
|
const sourceEnvName = args.sourceEnvName ? args.sourceEnvName : context.exeInfo.sourceEnvName;
|
|
11
13
|
const newEnvName = args.newEnvName ? args.newEnvName : context.exeInfo.localEnvInfo.envName;
|
|
14
|
+
const appId = args.appId ?? context.exeInfo.teamProviderInfo[sourceEnvName].awscloudformation.AmplifyAppId;
|
|
15
|
+
const studioClient = await AmplifyStudioClient.setClientInfo(context, sourceEnvName, appId);
|
|
12
16
|
|
|
13
|
-
const existingComponents = await
|
|
17
|
+
const [existingComponents, existingComponentsNewEnv] = await Promise.all([
|
|
18
|
+
studioClient.listComponents(sourceEnvName), studioClient.listComponents(newEnvName)]);
|
|
14
19
|
if (existingComponents.entities.length === 0) {
|
|
15
20
|
printer.debug(`${existingComponents.entities.length} components exist in source env. Skipping creation of local components.`);
|
|
16
21
|
return;
|
|
17
22
|
}
|
|
18
|
-
const existingComponentsNewEnv = await listUiBuilderComponents(context, newEnvName);
|
|
19
23
|
|
|
20
24
|
if (existingComponentsNewEnv.entities.length > 0) {
|
|
21
25
|
printer.debug(
|
|
@@ -24,35 +28,28 @@ export async function run(context: $TSContext) {
|
|
|
24
28
|
return;
|
|
25
29
|
}
|
|
26
30
|
|
|
27
|
-
const environmentName = args.environmentName ?? context.exeInfo.localEnvInfo.envName;
|
|
28
|
-
const appId = args.appId ?? context.exeInfo.teamProviderInfo[environmentName].awscloudformation.AmplifyAppId;
|
|
29
|
-
const amplifyUIBuilder = await getAmplifyUIBuilderService(context, environmentName, appId);
|
|
30
31
|
const components = existingComponents.entities;
|
|
31
32
|
if (!components.length) {
|
|
32
33
|
printer.debug(`No UIBuilder components found in app ${appId} from env ${sourceEnvName}. Skipping component clone process.`);
|
|
33
34
|
return;
|
|
34
35
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
})
|
|
51
|
-
.promise();
|
|
52
|
-
});
|
|
53
|
-
await Promise.all(promises);
|
|
36
|
+
for (let i = 0; i < components.length; i++) {
|
|
37
|
+
const {
|
|
38
|
+
appId: _appId, // eslint-disable-line @typescript-eslint/no-unused-vars
|
|
39
|
+
environmentName, // eslint-disable-line @typescript-eslint/no-unused-vars
|
|
40
|
+
id, // eslint-disable-line @typescript-eslint/no-unused-vars
|
|
41
|
+
createdAt, // eslint-disable-line @typescript-eslint/no-unused-vars
|
|
42
|
+
modifiedAt, // eslint-disable-line @typescript-eslint/no-unused-vars
|
|
43
|
+
...componentCreateData
|
|
44
|
+
} = components[i];
|
|
45
|
+
await studioClient.createComponent(
|
|
46
|
+
componentCreateData,
|
|
47
|
+
newEnvName,
|
|
48
|
+
appId,
|
|
49
|
+
);
|
|
50
|
+
}
|
|
54
51
|
|
|
55
52
|
printer.info(
|
|
56
53
|
`Successfully cloned ${components.length} UIBuilder components in app ${appId} from env ${sourceEnvName} to env ${newEnvName}.`,
|
|
57
54
|
);
|
|
58
|
-
}
|
|
55
|
+
};
|
|
@@ -1,35 +1,38 @@
|
|
|
1
1
|
import { StudioComponent } from '@aws-amplify/codegen-ui';
|
|
2
|
-
import { printer } from 'amplify-prompts';
|
|
3
2
|
import ora from 'ora';
|
|
3
|
+
import { printer } from 'amplify-prompts';
|
|
4
4
|
import { $TSContext } from 'amplify-cli-core';
|
|
5
|
-
import {
|
|
6
|
-
import { shouldRenderComponents } from './utils/shouldRenderComponents';
|
|
5
|
+
import { AmplifyStudioClient } from '../clients';
|
|
7
6
|
import {
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
notifyMissingPackages,
|
|
8
|
+
shouldRenderComponents,
|
|
10
9
|
generateUiBuilderComponents,
|
|
11
10
|
generateUiBuilderThemes,
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
getAmplifyDataSchema,
|
|
12
|
+
generateAmplifyUiBuilderIndexFile,
|
|
13
|
+
} from './utils';
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Pulls ui components from Studio backend and generates the code in the user's file system
|
|
17
17
|
*/
|
|
18
18
|
export const run = async (context: $TSContext): Promise<void> => {
|
|
19
|
-
printer.debug('Running generate components command in amplify-util-uibuilder');
|
|
20
19
|
if (!(await shouldRenderComponents(context))) {
|
|
21
20
|
return;
|
|
22
21
|
}
|
|
23
22
|
const spinner = ora('');
|
|
24
23
|
try {
|
|
25
|
-
const
|
|
24
|
+
const studioClient = await AmplifyStudioClient.setClientInfo(context);
|
|
25
|
+
const [componentSchemas, themeSchemas, dataSchema] = await Promise.all([
|
|
26
|
+
studioClient.listComponents(),
|
|
27
|
+
studioClient.listThemes(),
|
|
28
|
+
getAmplifyDataSchema(studioClient),
|
|
29
|
+
]);
|
|
26
30
|
if (componentSchemas.entities.length === 0 && themeSchemas.entities.length === 0) {
|
|
27
31
|
printer.debug('Skipping UI component generation since none are found.');
|
|
28
32
|
return;
|
|
29
33
|
}
|
|
30
|
-
|
|
31
34
|
spinner.start('Generating UI components...');
|
|
32
|
-
const generatedComponentResults = generateUiBuilderComponents(context, componentSchemas.entities);
|
|
35
|
+
const generatedComponentResults = generateUiBuilderComponents(context, componentSchemas.entities, dataSchema);
|
|
33
36
|
const generatedThemeResults = generateUiBuilderThemes(context, themeSchemas.entities);
|
|
34
37
|
|
|
35
38
|
generateAmplifyUiBuilderIndexFile(context, [
|
|
@@ -50,7 +53,11 @@ export const run = async (context: $TSContext): Promise<void> => {
|
|
|
50
53
|
|
|
51
54
|
const invalidComponentNames = componentSchemas.entities.filter(component => !component.schemaVersion).map(component => component.name);
|
|
52
55
|
if (invalidComponentNames.length) {
|
|
53
|
-
printer.warn(
|
|
56
|
+
printer.warn(
|
|
57
|
+
`The components ${invalidComponentNames.join(
|
|
58
|
+
', ',
|
|
59
|
+
)} were synced with an older version of Amplify Studio. Please re-sync your components with Figma to get latest features and changes.`, // eslint-disable-line spellcheck/spell-checker
|
|
60
|
+
);
|
|
54
61
|
}
|
|
55
62
|
|
|
56
63
|
notifyMissingPackages(context);
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/* eslint-disable spellcheck/spell-checker*/
|
|
2
|
+
// You have a misspelled word: Renderer on Identifier
|
|
1
3
|
import {
|
|
2
4
|
StudioTemplateRendererManager,
|
|
3
5
|
StudioComponent,
|
|
@@ -6,6 +8,8 @@ import {
|
|
|
6
8
|
StudioTemplateRenderer,
|
|
7
9
|
FrameworkOutputManager,
|
|
8
10
|
RenderTextComponentResponse,
|
|
11
|
+
GenericDataSchema,
|
|
12
|
+
getGenericFromDataStore,
|
|
9
13
|
} from '@aws-amplify/codegen-ui';
|
|
10
14
|
import {
|
|
11
15
|
AmplifyRenderer,
|
|
@@ -15,10 +19,12 @@ import {
|
|
|
15
19
|
ScriptTarget,
|
|
16
20
|
ScriptKind,
|
|
17
21
|
} from '@aws-amplify/codegen-ui-react';
|
|
18
|
-
|
|
19
22
|
import { printer } from 'amplify-prompts';
|
|
20
|
-
import {
|
|
23
|
+
import {
|
|
24
|
+
$TSContext, AmplifyCategories, AmplifySupportedService, stateManager,
|
|
25
|
+
} from 'amplify-cli-core';
|
|
21
26
|
import { getUiBuilderComponentsPath } from './getUiBuilderComponentsPath';
|
|
27
|
+
import { AmplifyStudioClient } from '../../clients';
|
|
22
28
|
|
|
23
29
|
const config = {
|
|
24
30
|
module: ModuleKind.ES2020,
|
|
@@ -30,15 +36,10 @@ const config = {
|
|
|
30
36
|
/**
|
|
31
37
|
* Writes component file to the work space
|
|
32
38
|
*/
|
|
33
|
-
export const createUiBuilderComponent = (context: $TSContext, schema: StudioComponent): StudioComponent => {
|
|
39
|
+
export const createUiBuilderComponent = (context: $TSContext, schema: StudioComponent, dataSchema?: GenericDataSchema): StudioComponent => {
|
|
34
40
|
const uiBuilderComponentsPath = getUiBuilderComponentsPath(context);
|
|
35
41
|
const rendererFactory = new StudioTemplateRendererFactory(
|
|
36
|
-
(component: StudioComponent) => new AmplifyRenderer(component as StudioComponent, config)
|
|
37
|
-
unknown,
|
|
38
|
-
StudioComponent,
|
|
39
|
-
FrameworkOutputManager<unknown>,
|
|
40
|
-
RenderTextComponentResponse
|
|
41
|
-
>,
|
|
42
|
+
(component: StudioComponent) => new AmplifyRenderer(component as StudioComponent, config, dataSchema),
|
|
42
43
|
);
|
|
43
44
|
|
|
44
45
|
const outputPathDir = uiBuilderComponentsPath;
|
|
@@ -57,7 +58,7 @@ export const createUiBuilderComponent = (context: $TSContext, schema: StudioComp
|
|
|
57
58
|
export const createUiBuilderTheme = (context: $TSContext, schema: StudioTheme): StudioTheme => {
|
|
58
59
|
const uiBuilderComponentsPath = getUiBuilderComponentsPath(context);
|
|
59
60
|
const rendererFactory = new StudioTemplateRendererFactory(
|
|
60
|
-
(component: StudioTheme) => new ReactThemeStudioTemplateRenderer(component, config) as unknown as StudioTemplateRenderer<
|
|
61
|
+
(component: StudioTheme) => (new ReactThemeStudioTemplateRenderer(component, config) as unknown) as StudioTemplateRenderer<
|
|
61
62
|
unknown,
|
|
62
63
|
StudioTheme,
|
|
63
64
|
FrameworkOutputManager<unknown>,
|
|
@@ -88,7 +89,7 @@ export const createUiBuilderTheme = (context: $TSContext, schema: StudioTheme):
|
|
|
88
89
|
export const generateAmplifyUiBuilderIndexFile = (context: $TSContext, schemas: StudioComponent[]): RenderTextComponentResponse => {
|
|
89
90
|
const uiBuilderComponentsPath = getUiBuilderComponentsPath(context);
|
|
90
91
|
const rendererFactory = new StudioTemplateRendererFactory(
|
|
91
|
-
(component: StudioComponent[]) => new ReactIndexStudioTemplateRenderer(component, config) as unknown as StudioTemplateRenderer<
|
|
92
|
+
(component: StudioComponent[]) => (new ReactIndexStudioTemplateRenderer(component, config) as unknown) as StudioTemplateRenderer<
|
|
92
93
|
unknown,
|
|
93
94
|
StudioComponent[],
|
|
94
95
|
FrameworkOutputManager<unknown>,
|
|
@@ -110,3 +111,30 @@ export const generateAmplifyUiBuilderIndexFile = (context: $TSContext, schemas:
|
|
|
110
111
|
throw e;
|
|
111
112
|
}
|
|
112
113
|
};
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* If models are available, they will be populated in the models field of the returned object.
|
|
117
|
+
* If they're not available, it will return undefined
|
|
118
|
+
*/
|
|
119
|
+
export const getAmplifyDataSchema = async (
|
|
120
|
+
studioClient: AmplifyStudioClient,
|
|
121
|
+
): Promise<GenericDataSchema | undefined> => {
|
|
122
|
+
try {
|
|
123
|
+
const meta = stateManager.getMeta();
|
|
124
|
+
const resourceName = Object.entries(meta[AmplifyCategories.API]).find(
|
|
125
|
+
([, value]) => (value as { service: string }).service === AmplifySupportedService.APPSYNC,
|
|
126
|
+
)?.[0];
|
|
127
|
+
if (resourceName) {
|
|
128
|
+
const model = await studioClient.getModels(resourceName);
|
|
129
|
+
if (model) {
|
|
130
|
+
const source = model.replace(model.substring(0, model.indexOf(`{`) - 1), ``).replace(/;/g, ``);
|
|
131
|
+
return getGenericFromDataStore(JSON.parse(source));
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
printer.debug(`Provided ResourceName: ${resourceName} did not yield Models.`);
|
|
135
|
+
return undefined;
|
|
136
|
+
} catch (error) {
|
|
137
|
+
printer.debug(error.toString());
|
|
138
|
+
return undefined;
|
|
139
|
+
}
|
|
140
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { $TSContext, stateManager } from 'amplify-cli-core';
|
|
2
|
+
import { extractArgs } from './extractArgs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Get's the environment name from the given name or the context
|
|
6
|
+
*/
|
|
7
|
+
export const getEnvName = (context: $TSContext, envName?: string): string => {
|
|
8
|
+
const args = extractArgs(context);
|
|
9
|
+
return envName || args?.environmentName || context.exeInfo.localEnvInfo.envName;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Returns the appId from the context
|
|
14
|
+
*/
|
|
15
|
+
export const resolveAppId = (): string | undefined => {
|
|
16
|
+
const meta = stateManager.getMeta();
|
|
17
|
+
return meta?.providers?.awscloudformation?.AmplifyAppId;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Returns the appId or throws an error if it's not found.
|
|
22
|
+
*/
|
|
23
|
+
export const getAppId = (context: $TSContext, appId?: string): string => {
|
|
24
|
+
const resolvedAppId = appId || extractArgs(context).appId || resolveAppId();
|
|
25
|
+
|
|
26
|
+
if (!resolvedAppId) {
|
|
27
|
+
throw new Error(
|
|
28
|
+
'Unable to sync Studio components since appId could not be determined. This can happen when you hit the soft limit of number of apps that you can have in Amplify console.',
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
return resolvedAppId;
|
|
32
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * from './codegenResources';
|
|
2
|
+
export * from './environmentHelpers';
|
|
3
|
+
export * from './extractArgs';
|
|
4
|
+
export * from './getUiBuilderComponentsPath';
|
|
5
|
+
export * from './notifyMissingPackages';
|
|
6
|
+
export * from './shouldRenderComponents';
|
|
7
|
+
export * from './syncAmplifyUiBuilderComponents';
|