@friggframework/devtools 2.0.0--canary.474.082077e.0 → 2.0.0--canary.474.4793186.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.
@@ -0,0 +1,318 @@
1
+ /**
2
+ * AWSResourceImporter - AWS CloudFormation Resource Import Adapter
3
+ *
4
+ * Infrastructure Adapter - Hexagonal Architecture
5
+ *
6
+ * Implements IResourceImporter port for AWS CloudFormation.
7
+ * Handles resource import operations using CloudFormation change sets.
8
+ *
9
+ * Lazy-loads AWS SDK to minimize cold start time and memory usage.
10
+ */
11
+
12
+ const IResourceImporter = require('../../application/ports/IResourceImporter');
13
+
14
+ // Lazy-loaded AWS SDK CloudFormation client
15
+ let CloudFormationClient,
16
+ CreateChangeSetCommand,
17
+ DescribeChangeSetCommand,
18
+ ExecuteChangeSetCommand,
19
+ GetTemplateCommand;
20
+
21
+ /**
22
+ * Lazy load CloudFormation SDK
23
+ */
24
+ function loadCloudFormation() {
25
+ if (!CloudFormationClient) {
26
+ const cfModule = require('@aws-sdk/client-cloudformation');
27
+ CloudFormationClient = cfModule.CloudFormationClient;
28
+ CreateChangeSetCommand = cfModule.CreateChangeSetCommand;
29
+ DescribeChangeSetCommand = cfModule.DescribeChangeSetCommand;
30
+ ExecuteChangeSetCommand = cfModule.ExecuteChangeSetCommand;
31
+ GetTemplateCommand = cfModule.GetTemplateCommand;
32
+ }
33
+ }
34
+
35
+ class AWSResourceImporter extends IResourceImporter {
36
+ /**
37
+ * Resource types that support import
38
+ * Maps CloudFormation resource type to identifier property
39
+ * @private
40
+ */
41
+ static IMPORTABLE_TYPES = {
42
+ 'AWS::EC2::VPC': 'VpcId',
43
+ 'AWS::EC2::Subnet': 'SubnetId',
44
+ 'AWS::EC2::SecurityGroup': 'GroupId',
45
+ 'AWS::EC2::RouteTable': 'RouteTableId',
46
+ 'AWS::RDS::DBCluster': 'DBClusterIdentifier',
47
+ 'AWS::KMS::Key': 'KeyId',
48
+ };
49
+
50
+ /**
51
+ * Create AWS Resource Importer
52
+ *
53
+ * @param {Object} [config={}]
54
+ * @param {string} [config.region] - AWS region (defaults to AWS_REGION env var)
55
+ */
56
+ constructor(config = {}) {
57
+ super();
58
+ this.region = config.region || process.env.AWS_REGION || 'us-east-1';
59
+ this.client = null;
60
+ }
61
+
62
+ /**
63
+ * Get or create CloudFormation client
64
+ * @private
65
+ */
66
+ _getClient() {
67
+ if (!this.client) {
68
+ loadCloudFormation();
69
+ this.client = new CloudFormationClient({ region: this.region });
70
+ }
71
+ return this.client;
72
+ }
73
+
74
+ /**
75
+ * Check if a resource type supports import
76
+ */
77
+ async supportsImport(resourceType) {
78
+ return resourceType in AWSResourceImporter.IMPORTABLE_TYPES;
79
+ }
80
+
81
+ /**
82
+ * Get the identifier property for a resource type
83
+ */
84
+ async getIdentifierProperty(resourceType) {
85
+ if (!(await this.supportsImport(resourceType))) {
86
+ throw new Error(`Resource type ${resourceType} does not support import`);
87
+ }
88
+
89
+ return AWSResourceImporter.IMPORTABLE_TYPES[resourceType];
90
+ }
91
+
92
+ /**
93
+ * Validate that a resource can be imported
94
+ */
95
+ async validateImport({ resourceType, physicalId, region }) {
96
+ const canImport = await this.supportsImport(resourceType);
97
+
98
+ if (!canImport) {
99
+ return {
100
+ canImport: false,
101
+ reason: `Resource type ${resourceType} is not supported for import`,
102
+ warnings: [],
103
+ };
104
+ }
105
+
106
+ // Add resource-specific warnings
107
+ const warnings = [];
108
+ if (resourceType === 'AWS::RDS::DBCluster') {
109
+ warnings.push(
110
+ 'Ensure DBCluster has required properties (Engine, MasterUsername, etc.)'
111
+ );
112
+ }
113
+
114
+ return {
115
+ canImport: true,
116
+ reason: '',
117
+ warnings,
118
+ };
119
+ }
120
+
121
+ /**
122
+ * Import a single resource into a stack
123
+ */
124
+ async importResource({ stackIdentifier, logicalId, resourceType, physicalId, properties }) {
125
+ // Validate resource type
126
+ if (!(await this.supportsImport(resourceType))) {
127
+ throw new Error(`Resource type ${resourceType} does not support import`);
128
+ }
129
+
130
+ const client = this._getClient();
131
+
132
+ // Get identifier property
133
+ const identifierProperty = await this.getIdentifierProperty(resourceType);
134
+
135
+ // Create change set for import
136
+ const changeSetName = `import-${logicalId}-${Date.now()}`;
137
+
138
+ const createChangeSetCommand = new CreateChangeSetCommand({
139
+ StackName: stackIdentifier.stackName,
140
+ ChangeSetName: changeSetName,
141
+ ChangeSetType: 'IMPORT',
142
+ ResourcesToImport: [
143
+ {
144
+ ResourceType: resourceType,
145
+ LogicalResourceId: logicalId,
146
+ ResourceIdentifier: {
147
+ [identifierProperty]: physicalId,
148
+ },
149
+ },
150
+ ],
151
+ TemplateBody: JSON.stringify({
152
+ Resources: {
153
+ [logicalId]: {
154
+ Type: resourceType,
155
+ Properties: properties,
156
+ },
157
+ },
158
+ }),
159
+ });
160
+
161
+ const createResponse = await client.send(createChangeSetCommand);
162
+
163
+ // Execute the change set
164
+ const executeCommand = new ExecuteChangeSetCommand({
165
+ ChangeSetName: changeSetName,
166
+ StackName: stackIdentifier.stackName,
167
+ });
168
+
169
+ await client.send(executeCommand);
170
+
171
+ return {
172
+ operationId: createResponse.Id,
173
+ status: 'IN_PROGRESS',
174
+ message: `Resource import initiated via change set ${createResponse.Id}`,
175
+ };
176
+ }
177
+
178
+ /**
179
+ * Import multiple resources into a stack in a single operation
180
+ */
181
+ async importMultipleResources({ stackIdentifier, resources }) {
182
+ const client = this._getClient();
183
+
184
+ // Filter to only supported resources
185
+ const supportedResources = [];
186
+ const unsupportedResources = [];
187
+
188
+ for (const resource of resources) {
189
+ if (await this.supportsImport(resource.resourceType)) {
190
+ supportedResources.push(resource);
191
+ } else {
192
+ unsupportedResources.push(resource);
193
+ }
194
+ }
195
+
196
+ if (supportedResources.length === 0) {
197
+ return {
198
+ operationId: null,
199
+ status: 'FAILED',
200
+ importedCount: 0,
201
+ failedCount: resources.length,
202
+ message: 'No supported resources to import',
203
+ details: [],
204
+ };
205
+ }
206
+
207
+ // Build resources to import
208
+ const resourcesToImport = [];
209
+ const templateResources = {};
210
+
211
+ for (const resource of supportedResources) {
212
+ const identifierProperty = await this.getIdentifierProperty(resource.resourceType);
213
+
214
+ resourcesToImport.push({
215
+ ResourceType: resource.resourceType,
216
+ LogicalResourceId: resource.logicalId,
217
+ ResourceIdentifier: {
218
+ [identifierProperty]: resource.physicalId,
219
+ },
220
+ });
221
+
222
+ templateResources[resource.logicalId] = {
223
+ Type: resource.resourceType,
224
+ Properties: resource.properties,
225
+ };
226
+ }
227
+
228
+ // Create change set for import
229
+ const changeSetName = `import-multi-${Date.now()}`;
230
+
231
+ const createChangeSetCommand = new CreateChangeSetCommand({
232
+ StackName: stackIdentifier.stackName,
233
+ ChangeSetName: changeSetName,
234
+ ChangeSetType: 'IMPORT',
235
+ ResourcesToImport: resourcesToImport,
236
+ TemplateBody: JSON.stringify({
237
+ Resources: templateResources,
238
+ }),
239
+ });
240
+
241
+ const createResponse = await client.send(createChangeSetCommand);
242
+
243
+ // Execute the change set
244
+ const executeCommand = new ExecuteChangeSetCommand({
245
+ ChangeSetName: changeSetName,
246
+ StackName: stackIdentifier.stackName,
247
+ });
248
+
249
+ await client.send(executeCommand);
250
+
251
+ return {
252
+ operationId: createResponse.Id,
253
+ status: 'IN_PROGRESS',
254
+ importedCount: supportedResources.length,
255
+ failedCount: unsupportedResources.length,
256
+ message: `Import operation initiated for ${supportedResources.length} resources`,
257
+ details: [],
258
+ };
259
+ }
260
+
261
+ /**
262
+ * Get status of an import operation
263
+ */
264
+ async getImportStatus(operationId) {
265
+ const client = this._getClient();
266
+
267
+ const command = new DescribeChangeSetCommand({
268
+ ChangeSetName: operationId,
269
+ });
270
+
271
+ const response = await client.send(command);
272
+
273
+ // Map CloudFormation status to our status
274
+ let status = 'IN_PROGRESS';
275
+ let progress = 0;
276
+
277
+ if (response.ExecutionStatus === 'EXECUTE_COMPLETE') {
278
+ status = 'COMPLETE';
279
+ progress = 100;
280
+ } else if (response.Status === 'FAILED' || response.ExecutionStatus === 'EXECUTE_FAILED') {
281
+ status = 'FAILED';
282
+ progress = 0;
283
+ } else if (response.Status === 'CREATE_COMPLETE') {
284
+ status = 'IN_PROGRESS';
285
+ progress = 50;
286
+ } else if (response.Status === 'CREATE_PENDING') {
287
+ status = 'IN_PROGRESS';
288
+ progress = 25;
289
+ }
290
+
291
+ return {
292
+ operationId,
293
+ status,
294
+ progress,
295
+ message: response.StatusReason || '',
296
+ completedTime: status === 'COMPLETE' ? response.CreationTime : null,
297
+ };
298
+ }
299
+
300
+ /**
301
+ * Generate CloudFormation template snippet for an imported resource
302
+ */
303
+ async generateTemplateSnippet({ logicalId, resourceType, properties }) {
304
+ // Validate resource type
305
+ if (!(await this.supportsImport(resourceType))) {
306
+ throw new Error(`Resource type ${resourceType} does not support import`);
307
+ }
308
+
309
+ return {
310
+ [logicalId]: {
311
+ Type: resourceType,
312
+ Properties: properties,
313
+ },
314
+ };
315
+ }
316
+ }
317
+
318
+ module.exports = AWSResourceImporter;
@@ -0,0 +1,398 @@
1
+ /**
2
+ * Tests for AWSResourceImporter Adapter
3
+ *
4
+ * Tests CloudFormation resource import operations using mocked AWS SDK
5
+ */
6
+
7
+ const AWSResourceImporter = require('./aws-resource-importer');
8
+ const StackIdentifier = require('../../domain/value-objects/stack-identifier');
9
+
10
+ // Mock AWS SDK
11
+ jest.mock('@aws-sdk/client-cloudformation', () => ({
12
+ CloudFormationClient: jest.fn(),
13
+ CreateChangeSetCommand: jest.fn(),
14
+ DescribeChangeSetCommand: jest.fn(),
15
+ ExecuteChangeSetCommand: jest.fn(),
16
+ GetTemplateCommand: jest.fn(),
17
+ }));
18
+
19
+ describe('AWSResourceImporter', () => {
20
+ let importer;
21
+ let mockSend;
22
+
23
+ beforeEach(() => {
24
+ jest.clearAllMocks();
25
+
26
+ mockSend = jest.fn();
27
+ const { CloudFormationClient } = require('@aws-sdk/client-cloudformation');
28
+ CloudFormationClient.mockImplementation(() => ({ send: mockSend }));
29
+
30
+ importer = new AWSResourceImporter({ region: 'us-east-1' });
31
+ });
32
+
33
+ describe('supportsImport', () => {
34
+ it('should return true for VPC', async () => {
35
+ const supports = await importer.supportsImport('AWS::EC2::VPC');
36
+ expect(supports).toBe(true);
37
+ });
38
+
39
+ it('should return true for RDS DBCluster', async () => {
40
+ const supports = await importer.supportsImport('AWS::RDS::DBCluster');
41
+ expect(supports).toBe(true);
42
+ });
43
+
44
+ it('should return true for KMS Key', async () => {
45
+ const supports = await importer.supportsImport('AWS::KMS::Key');
46
+ expect(supports).toBe(true);
47
+ });
48
+
49
+ it('should return false for unsupported type', async () => {
50
+ const supports = await importer.supportsImport('AWS::Lambda::Function');
51
+ expect(supports).toBe(false);
52
+ });
53
+ });
54
+
55
+ describe('getIdentifierProperty', () => {
56
+ it('should return VpcId for VPC', async () => {
57
+ const prop = await importer.getIdentifierProperty('AWS::EC2::VPC');
58
+ expect(prop).toBe('VpcId');
59
+ });
60
+
61
+ it('should return SubnetId for Subnet', async () => {
62
+ const prop = await importer.getIdentifierProperty('AWS::EC2::Subnet');
63
+ expect(prop).toBe('SubnetId');
64
+ });
65
+
66
+ it('should return DBClusterIdentifier for RDS DBCluster', async () => {
67
+ const prop = await importer.getIdentifierProperty('AWS::RDS::DBCluster');
68
+ expect(prop).toBe('DBClusterIdentifier');
69
+ });
70
+
71
+ it('should throw error for unsupported type', async () => {
72
+ await expect(
73
+ importer.getIdentifierProperty('AWS::Lambda::Function')
74
+ ).rejects.toThrow('Resource type AWS::Lambda::Function does not support import');
75
+ });
76
+ });
77
+
78
+ describe('validateImport', () => {
79
+ it('should validate VPC import', async () => {
80
+ const result = await importer.validateImport({
81
+ resourceType: 'AWS::EC2::VPC',
82
+ physicalId: 'vpc-123',
83
+ region: 'us-east-1',
84
+ });
85
+
86
+ expect(result).toEqual({
87
+ canImport: true,
88
+ reason: '',
89
+ warnings: [],
90
+ });
91
+ });
92
+
93
+ it('should fail validation for unsupported type', async () => {
94
+ const result = await importer.validateImport({
95
+ resourceType: 'AWS::Lambda::Function',
96
+ physicalId: 'my-function',
97
+ region: 'us-east-1',
98
+ });
99
+
100
+ expect(result.canImport).toBe(false);
101
+ expect(result.reason).toContain('not supported');
102
+ });
103
+
104
+ it('should warn about missing required properties', async () => {
105
+ const result = await importer.validateImport({
106
+ resourceType: 'AWS::RDS::DBCluster',
107
+ physicalId: 'my-cluster',
108
+ region: 'us-east-1',
109
+ });
110
+
111
+ expect(result.canImport).toBe(true);
112
+ expect(result.warnings).toContain(
113
+ 'Ensure DBCluster has required properties (Engine, MasterUsername, etc.)'
114
+ );
115
+ });
116
+ });
117
+
118
+ describe('importResource', () => {
119
+ it('should import a single VPC resource', async () => {
120
+ const stackIdentifier = new StackIdentifier({
121
+ stackName: 'my-app-prod',
122
+ region: 'us-east-1',
123
+ });
124
+
125
+ // Mock CreateChangeSet
126
+ mockSend.mockResolvedValueOnce({
127
+ Id: 'changeset-123',
128
+ StackId: 'arn:aws:cloudformation:us-east-1:123456789012:stack/my-app-prod/guid',
129
+ });
130
+
131
+ // Mock ExecuteChangeSet
132
+ mockSend.mockResolvedValueOnce({});
133
+
134
+ const result = await importer.importResource({
135
+ stackIdentifier,
136
+ logicalId: 'ImportedVPC',
137
+ resourceType: 'AWS::EC2::VPC',
138
+ physicalId: 'vpc-123',
139
+ properties: {
140
+ CidrBlock: '10.0.0.0/16',
141
+ EnableDnsSupport: true,
142
+ },
143
+ });
144
+
145
+ expect(result).toEqual({
146
+ operationId: 'changeset-123',
147
+ status: 'IN_PROGRESS',
148
+ message: 'Resource import initiated via change set changeset-123',
149
+ });
150
+
151
+ expect(mockSend).toHaveBeenCalledTimes(2);
152
+ });
153
+
154
+ it('should throw error for unsupported resource type', async () => {
155
+ const stackIdentifier = new StackIdentifier({
156
+ stackName: 'my-app-prod',
157
+ region: 'us-east-1',
158
+ });
159
+
160
+ await expect(
161
+ importer.importResource({
162
+ stackIdentifier,
163
+ logicalId: 'MyFunction',
164
+ resourceType: 'AWS::Lambda::Function',
165
+ physicalId: 'my-function',
166
+ properties: {},
167
+ })
168
+ ).rejects.toThrow('Resource type AWS::Lambda::Function does not support import');
169
+ });
170
+
171
+ it('should handle import failure', async () => {
172
+ const stackIdentifier = new StackIdentifier({
173
+ stackName: 'my-app-prod',
174
+ region: 'us-east-1',
175
+ });
176
+
177
+ const error = new Error('Resource already exists in stack');
178
+ error.name = 'AlreadyExistsException';
179
+ mockSend.mockRejectedValue(error);
180
+
181
+ await expect(
182
+ importer.importResource({
183
+ stackIdentifier,
184
+ logicalId: 'ExistingVPC',
185
+ resourceType: 'AWS::EC2::VPC',
186
+ physicalId: 'vpc-123',
187
+ properties: { CidrBlock: '10.0.0.0/16' },
188
+ })
189
+ ).rejects.toThrow('Resource already exists in stack');
190
+ });
191
+ });
192
+
193
+ describe('importMultipleResources', () => {
194
+ it('should import multiple resources in single operation', async () => {
195
+ const stackIdentifier = new StackIdentifier({
196
+ stackName: 'my-app-prod',
197
+ region: 'us-east-1',
198
+ });
199
+
200
+ const resources = [
201
+ {
202
+ logicalId: 'ImportedVPC',
203
+ resourceType: 'AWS::EC2::VPC',
204
+ physicalId: 'vpc-123',
205
+ properties: { CidrBlock: '10.0.0.0/16' },
206
+ },
207
+ {
208
+ logicalId: 'ImportedSubnet',
209
+ resourceType: 'AWS::EC2::Subnet',
210
+ physicalId: 'subnet-456',
211
+ properties: { VpcId: 'vpc-123', CidrBlock: '10.0.1.0/24' },
212
+ },
213
+ ];
214
+
215
+ // Mock CreateChangeSet
216
+ mockSend.mockResolvedValueOnce({
217
+ Id: 'changeset-multi-123',
218
+ StackId: 'arn:aws:cloudformation:us-east-1:123456789012:stack/my-app-prod/guid',
219
+ });
220
+
221
+ // Mock ExecuteChangeSet
222
+ mockSend.mockResolvedValueOnce({});
223
+
224
+ const result = await importer.importMultipleResources({
225
+ stackIdentifier,
226
+ resources,
227
+ });
228
+
229
+ expect(result).toEqual({
230
+ operationId: 'changeset-multi-123',
231
+ status: 'IN_PROGRESS',
232
+ importedCount: 2,
233
+ failedCount: 0,
234
+ message: 'Import operation initiated for 2 resources',
235
+ details: [],
236
+ });
237
+ });
238
+
239
+ it('should filter out unsupported resource types', async () => {
240
+ const stackIdentifier = new StackIdentifier({
241
+ stackName: 'my-app-prod',
242
+ region: 'us-east-1',
243
+ });
244
+
245
+ const resources = [
246
+ {
247
+ logicalId: 'ImportedVPC',
248
+ resourceType: 'AWS::EC2::VPC',
249
+ physicalId: 'vpc-123',
250
+ properties: { CidrBlock: '10.0.0.0/16' },
251
+ },
252
+ {
253
+ logicalId: 'UnsupportedFunction',
254
+ resourceType: 'AWS::Lambda::Function',
255
+ physicalId: 'my-function',
256
+ properties: {},
257
+ },
258
+ ];
259
+
260
+ // Mock CreateChangeSet (only for VPC)
261
+ mockSend.mockResolvedValueOnce({
262
+ Id: 'changeset-filtered-123',
263
+ StackId: 'arn:aws:cloudformation:us-east-1:123456789012:stack/my-app-prod/guid',
264
+ });
265
+
266
+ // Mock ExecuteChangeSet
267
+ mockSend.mockResolvedValueOnce({});
268
+
269
+ const result = await importer.importMultipleResources({
270
+ stackIdentifier,
271
+ resources,
272
+ });
273
+
274
+ expect(result.importedCount).toBe(1);
275
+ expect(result.failedCount).toBe(1);
276
+ });
277
+ });
278
+
279
+ describe('getImportStatus', () => {
280
+ it('should get status of in-progress import', async () => {
281
+ mockSend.mockResolvedValue({
282
+ Status: 'CREATE_PENDING',
283
+ ExecutionStatus: 'AVAILABLE',
284
+ StatusReason: 'Change set created',
285
+ });
286
+
287
+ const status = await importer.getImportStatus('changeset-123');
288
+
289
+ expect(status).toEqual({
290
+ operationId: 'changeset-123',
291
+ status: 'IN_PROGRESS',
292
+ progress: 25,
293
+ message: 'Change set created',
294
+ completedTime: null,
295
+ });
296
+ });
297
+
298
+ it('should get status of completed import', async () => {
299
+ const completedTime = new Date('2024-01-15T10:30:00Z');
300
+
301
+ mockSend.mockResolvedValue({
302
+ Status: 'CREATE_COMPLETE',
303
+ ExecutionStatus: 'EXECUTE_COMPLETE',
304
+ StatusReason: 'Import completed successfully',
305
+ CreationTime: completedTime,
306
+ });
307
+
308
+ const status = await importer.getImportStatus('changeset-123');
309
+
310
+ expect(status.status).toBe('COMPLETE');
311
+ expect(status.progress).toBe(100);
312
+ expect(status.completedTime).toEqual(completedTime);
313
+ });
314
+
315
+ it('should get status of failed import', async () => {
316
+ mockSend.mockResolvedValue({
317
+ Status: 'FAILED',
318
+ ExecutionStatus: 'EXECUTE_FAILED',
319
+ StatusReason: 'Resource already exists',
320
+ });
321
+
322
+ const status = await importer.getImportStatus('changeset-123');
323
+
324
+ expect(status.status).toBe('FAILED');
325
+ expect(status.message).toContain('already exists');
326
+ });
327
+ });
328
+
329
+ describe('generateTemplateSnippet', () => {
330
+ it('should generate VPC template snippet', async () => {
331
+ const snippet = await importer.generateTemplateSnippet({
332
+ logicalId: 'ImportedVPC',
333
+ resourceType: 'AWS::EC2::VPC',
334
+ properties: {
335
+ CidrBlock: '10.0.0.0/16',
336
+ EnableDnsSupport: true,
337
+ EnableDnsHostnames: true,
338
+ },
339
+ });
340
+
341
+ expect(snippet).toEqual({
342
+ ImportedVPC: {
343
+ Type: 'AWS::EC2::VPC',
344
+ Properties: {
345
+ CidrBlock: '10.0.0.0/16',
346
+ EnableDnsSupport: true,
347
+ EnableDnsHostnames: true,
348
+ },
349
+ },
350
+ });
351
+ });
352
+
353
+ it('should generate RDS DBCluster template snippet', async () => {
354
+ const snippet = await importer.generateTemplateSnippet({
355
+ logicalId: 'ImportedCluster',
356
+ resourceType: 'AWS::RDS::DBCluster',
357
+ properties: {
358
+ Engine: 'aurora-postgresql',
359
+ EngineVersion: '13.7',
360
+ MasterUsername: 'admin',
361
+ },
362
+ });
363
+
364
+ expect(snippet).toEqual({
365
+ ImportedCluster: {
366
+ Type: 'AWS::RDS::DBCluster',
367
+ Properties: {
368
+ Engine: 'aurora-postgresql',
369
+ EngineVersion: '13.7',
370
+ MasterUsername: 'admin',
371
+ },
372
+ },
373
+ });
374
+ });
375
+
376
+ it('should throw error for unsupported resource type', async () => {
377
+ await expect(
378
+ importer.generateTemplateSnippet({
379
+ logicalId: 'MyFunction',
380
+ resourceType: 'AWS::Lambda::Function',
381
+ properties: {},
382
+ })
383
+ ).rejects.toThrow('Resource type AWS::Lambda::Function does not support import');
384
+ });
385
+ });
386
+
387
+ describe('constructor', () => {
388
+ it('should create instance with default region', () => {
389
+ const imp = new AWSResourceImporter();
390
+ expect(imp).toBeInstanceOf(AWSResourceImporter);
391
+ });
392
+
393
+ it('should create instance with custom region', () => {
394
+ const imp = new AWSResourceImporter({ region: 'eu-west-1' });
395
+ expect(imp).toBeInstanceOf(AWSResourceImporter);
396
+ });
397
+ });
398
+ });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@friggframework/devtools",
3
3
  "prettier": "@friggframework/prettier-config",
4
- "version": "2.0.0--canary.474.082077e.0",
4
+ "version": "2.0.0--canary.474.4793186.0",
5
5
  "dependencies": {
6
6
  "@aws-sdk/client-ec2": "^3.835.0",
7
7
  "@aws-sdk/client-kms": "^3.835.0",
@@ -11,8 +11,8 @@
11
11
  "@babel/eslint-parser": "^7.18.9",
12
12
  "@babel/parser": "^7.25.3",
13
13
  "@babel/traverse": "^7.25.3",
14
- "@friggframework/schemas": "2.0.0--canary.474.082077e.0",
15
- "@friggframework/test": "2.0.0--canary.474.082077e.0",
14
+ "@friggframework/schemas": "2.0.0--canary.474.4793186.0",
15
+ "@friggframework/test": "2.0.0--canary.474.4793186.0",
16
16
  "@hapi/boom": "^10.0.1",
17
17
  "@inquirer/prompts": "^5.3.8",
18
18
  "axios": "^1.7.2",
@@ -34,8 +34,8 @@
34
34
  "serverless-http": "^2.7.0"
35
35
  },
36
36
  "devDependencies": {
37
- "@friggframework/eslint-config": "2.0.0--canary.474.082077e.0",
38
- "@friggframework/prettier-config": "2.0.0--canary.474.082077e.0",
37
+ "@friggframework/eslint-config": "2.0.0--canary.474.4793186.0",
38
+ "@friggframework/prettier-config": "2.0.0--canary.474.4793186.0",
39
39
  "aws-sdk-client-mock": "^4.1.0",
40
40
  "aws-sdk-client-mock-jest": "^4.1.0",
41
41
  "jest": "^30.1.3",
@@ -70,5 +70,5 @@
70
70
  "publishConfig": {
71
71
  "access": "public"
72
72
  },
73
- "gitHead": "082077ef7bba084fdeaaee60a08f9039542bff40"
73
+ "gitHead": "47931865212a71b5e52aaf06bc9c1e49dbb3171e"
74
74
  }