@geek-fun/serverlessinsight 0.4.0 → 0.5.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.
Files changed (160) hide show
  1. package/.gitattributes +1 -0
  2. package/README.md +108 -8
  3. package/README.zh-CN.md +52 -8
  4. package/dist/package.json +37 -35
  5. package/dist/src/commands/deploy.js +17 -7
  6. package/dist/src/commands/destroy.js +27 -4
  7. package/dist/src/commands/forceUnlock.js +61 -0
  8. package/dist/src/commands/index.js +86 -14
  9. package/dist/src/commands/local.js +10 -1
  10. package/dist/src/commands/plan.js +33 -0
  11. package/dist/src/commands/template.js +3 -1
  12. package/dist/src/commands/validate.js +2 -1
  13. package/dist/src/common/aliyunClient/apigwOperations.js +652 -0
  14. package/dist/src/common/aliyunClient/dnsOperations.js +90 -0
  15. package/dist/src/common/aliyunClient/ecsOperations.js +141 -0
  16. package/dist/src/common/aliyunClient/esOperations.js +219 -0
  17. package/dist/src/common/aliyunClient/fc3Operations.js +270 -0
  18. package/dist/src/common/aliyunClient/index.js +141 -0
  19. package/dist/src/common/aliyunClient/nasOperations.js +233 -0
  20. package/dist/src/common/aliyunClient/ossOperations.js +237 -0
  21. package/dist/src/common/aliyunClient/ramOperations.js +205 -0
  22. package/dist/src/common/aliyunClient/rdsOperations.js +206 -0
  23. package/dist/src/common/aliyunClient/slsOperations.js +218 -0
  24. package/dist/src/common/aliyunClient/tablestoreOperations.js +199 -0
  25. package/dist/src/common/aliyunClient/types.js +2 -0
  26. package/dist/src/common/constants.js +7 -1
  27. package/dist/src/common/context.js +32 -14
  28. package/dist/src/common/credentials.js +39 -0
  29. package/dist/src/common/dependencyGraph/graph.js +280 -0
  30. package/dist/src/common/dependencyGraph/index.js +18 -0
  31. package/dist/src/common/dependencyGraph/types.js +2 -0
  32. package/dist/src/common/fileUtils.js +16 -0
  33. package/dist/src/common/hashUtils.js +121 -0
  34. package/dist/src/common/iacHelper.js +25 -97
  35. package/dist/src/common/imsClient.js +4 -0
  36. package/dist/src/common/index.js +7 -2
  37. package/dist/src/common/lockManager.js +212 -0
  38. package/dist/src/common/logger.js +89 -6
  39. package/dist/src/common/providerEnum.js +2 -3
  40. package/dist/src/common/runtimeMapper.js +160 -0
  41. package/dist/src/common/scfClient.js +84 -0
  42. package/dist/src/common/stateManager.js +107 -0
  43. package/dist/src/common/tencentClient/cosOperations.js +287 -0
  44. package/dist/src/common/tencentClient/esOperations.js +156 -0
  45. package/dist/src/common/tencentClient/index.js +116 -0
  46. package/dist/src/common/tencentClient/scfOperations.js +141 -0
  47. package/dist/src/common/tencentClient/tdsqlcOperations.js +211 -0
  48. package/dist/src/common/tencentClient/types.js +17 -0
  49. package/dist/src/lang/en.js +254 -0
  50. package/dist/src/lang/index.js +28 -8
  51. package/dist/src/lang/zh-CN.js +229 -0
  52. package/dist/src/parser/bucketParser.js +25 -12
  53. package/dist/src/parser/databaseParser.js +14 -10
  54. package/dist/src/parser/functionParser.js +19 -6
  55. package/dist/src/parser/parseUtils.js +74 -0
  56. package/dist/src/parser/tableParser.js +19 -17
  57. package/dist/src/stack/aliyunStack/apigwExecutor.js +84 -0
  58. package/dist/src/stack/aliyunStack/apigwPlanner.js +118 -0
  59. package/dist/src/stack/aliyunStack/apigwResource.js +339 -0
  60. package/dist/src/stack/aliyunStack/apigwTypes.js +125 -0
  61. package/dist/src/stack/aliyunStack/databaseExecutor.js +112 -0
  62. package/dist/src/stack/aliyunStack/databasePlanner.js +128 -0
  63. package/dist/src/stack/aliyunStack/databaseResource.js +228 -0
  64. package/dist/src/stack/aliyunStack/deployer.js +133 -0
  65. package/dist/src/stack/aliyunStack/destroyer.js +114 -0
  66. package/dist/src/stack/aliyunStack/esServerlessTypes.js +141 -0
  67. package/dist/src/stack/aliyunStack/fc3Executor.js +91 -0
  68. package/dist/src/stack/aliyunStack/fc3Planner.js +77 -0
  69. package/dist/src/stack/aliyunStack/fc3Resource.js +511 -0
  70. package/dist/src/stack/aliyunStack/fc3Types.js +76 -0
  71. package/dist/src/stack/aliyunStack/index.js +40 -0
  72. package/dist/src/stack/aliyunStack/ossExecutor.js +91 -0
  73. package/dist/src/stack/aliyunStack/ossPlanner.js +76 -0
  74. package/dist/src/stack/aliyunStack/ossResource.js +196 -0
  75. package/dist/src/stack/aliyunStack/ossTypes.js +50 -0
  76. package/dist/src/stack/aliyunStack/planner.js +37 -0
  77. package/dist/src/stack/aliyunStack/rdsTypes.js +217 -0
  78. package/dist/src/stack/aliyunStack/tablestoreExecutor.js +92 -0
  79. package/dist/src/stack/aliyunStack/tablestorePlanner.js +94 -0
  80. package/dist/src/stack/aliyunStack/tablestoreResource.js +120 -0
  81. package/dist/src/stack/aliyunStack/tablestoreTypes.js +77 -0
  82. package/dist/src/stack/bucketTypes.js +17 -0
  83. package/dist/src/stack/deploy.js +24 -77
  84. package/dist/src/stack/localStack/bucket.js +11 -6
  85. package/dist/src/stack/localStack/event.js +10 -5
  86. package/dist/src/stack/localStack/function.js +13 -7
  87. package/dist/src/stack/localStack/functionRunner.js +1 -1
  88. package/dist/src/stack/localStack/localServer.js +7 -6
  89. package/dist/src/stack/scfStack/cosExecutor.js +91 -0
  90. package/dist/src/stack/scfStack/cosPlanner.js +76 -0
  91. package/dist/src/stack/scfStack/cosResource.js +126 -0
  92. package/dist/src/stack/scfStack/cosTypes.js +46 -0
  93. package/dist/src/stack/scfStack/deployer.js +91 -0
  94. package/dist/src/stack/scfStack/destroyer.js +88 -0
  95. package/dist/src/stack/scfStack/esServerlessExecutor.js +105 -0
  96. package/dist/src/stack/scfStack/esServerlessPlanner.js +86 -0
  97. package/dist/src/stack/scfStack/esServerlessResource.js +94 -0
  98. package/dist/src/stack/scfStack/esServerlessTypes.js +48 -0
  99. package/dist/src/stack/scfStack/index.js +35 -0
  100. package/dist/src/stack/scfStack/planner.js +91 -0
  101. package/dist/src/stack/scfStack/scfExecutor.js +91 -0
  102. package/dist/src/stack/scfStack/scfPlanner.js +78 -0
  103. package/dist/src/stack/scfStack/scfResource.js +216 -0
  104. package/dist/src/stack/scfStack/scfTypes.js +41 -0
  105. package/dist/src/stack/scfStack/tdsqlcExecutor.js +105 -0
  106. package/dist/src/stack/scfStack/tdsqlcPlanner.js +90 -0
  107. package/dist/src/stack/scfStack/tdsqlcResource.js +146 -0
  108. package/dist/src/stack/scfStack/tdsqlcTypes.js +59 -0
  109. package/dist/src/types/domains/lock.js +2 -0
  110. package/dist/src/types/domains/resolvable.js +2 -0
  111. package/dist/src/types/domains/state.js +19 -0
  112. package/dist/src/types/index.js +4 -0
  113. package/dist/src/validator/bucketSchema.js +4 -10
  114. package/dist/src/validator/databaseSchema.js +36 -36
  115. package/dist/src/validator/eventSchema.js +3 -2
  116. package/dist/src/validator/functionSchema.js +51 -46
  117. package/dist/src/validator/iacSchema.js +52 -3
  118. package/dist/src/validator/rootSchema.js +47 -1
  119. package/dist/src/validator/tableschema.js +9 -8
  120. package/dist/src/validator/templateRefSchema.js +23 -0
  121. package/dist/tsconfig.tsbuildinfo +1 -1
  122. package/package.json +37 -35
  123. package/samples/README_TENCENT_COS.md +486 -0
  124. package/samples/README_TENCENT_SCF.md +272 -0
  125. package/samples/aliyun-poc-api.yml +1 -1
  126. package/samples/aliyun-poc-bucket.yml +0 -1
  127. package/samples/aliyun-poc-domain.yml +0 -1
  128. package/samples/aliyun-poc-es.yml +14 -13
  129. package/samples/aliyun-poc-rds.yml +0 -2
  130. package/samples/aliyun-poc-table.yml +1 -3
  131. package/samples/tencent-poc-cos.yml +20 -0
  132. package/samples/tencent-poc-scf.yml +36 -0
  133. package/dist/src/commands/index.d.ts +0 -2
  134. package/dist/src/common/index.d.ts +0 -11
  135. package/dist/src/common/rosAssets.js +0 -178
  136. package/dist/src/common/rosClient.js +0 -198
  137. package/dist/src/index.d.ts +0 -1
  138. package/dist/src/lang/index.d.ts +0 -3
  139. package/dist/src/parser/index.d.ts +0 -3
  140. package/dist/src/stack/index.d.ts +0 -1
  141. package/dist/src/stack/localStack/index.d.ts +0 -5
  142. package/dist/src/stack/rfsStack/index.d.ts +0 -9
  143. package/dist/src/stack/rosStack/bootstrap.js +0 -187
  144. package/dist/src/stack/rosStack/bucket.js +0 -127
  145. package/dist/src/stack/rosStack/database.js +0 -313
  146. package/dist/src/stack/rosStack/event.js +0 -143
  147. package/dist/src/stack/rosStack/function.js +0 -259
  148. package/dist/src/stack/rosStack/index.d.ts +0 -7
  149. package/dist/src/stack/rosStack/index.js +0 -75
  150. package/dist/src/stack/rosStack/stage.js +0 -46
  151. package/dist/src/stack/rosStack/table.js +0 -95
  152. package/dist/src/stack/rosStack/tag.js +0 -11
  153. package/dist/src/stack/rosStack/vars.js +0 -49
  154. package/dist/src/types/index.d.ts +0 -55
  155. package/dist/src/types/localStack/index.d.ts +0 -81
  156. package/dist/src/validator/index.d.ts +0 -1
  157. package/layers/si-bootstrap-sdk/Dockerfile-aliyuncli +0 -12
  158. package/layers/si-bootstrap-sdk/README.md +0 -1
  159. package/layers/si-bootstrap-sdk/package-lock.json +0 -875
  160. package/layers/si-bootstrap-sdk/package.json +0 -33
@@ -0,0 +1,118 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateApigwPlan = void 0;
4
+ const aliyunClient_1 = require("../../common/aliyunClient");
5
+ const apigwTypes_1 = require("./apigwTypes");
6
+ const stateManager_1 = require("../../common/stateManager");
7
+ const hashUtils_1 = require("../../common/hashUtils");
8
+ const planEventDeletion = (logicalId, definition) => ({
9
+ logicalId,
10
+ action: 'delete',
11
+ resourceType: 'ALIYUN_APIGW',
12
+ changes: { before: definition },
13
+ });
14
+ /**
15
+ * Generate plan for API Gateway events
16
+ */
17
+ const generateApigwPlan = async (context, state, events, serviceName) => {
18
+ // If no events, check if we need to delete existing ones
19
+ if (!events || events.length === 0) {
20
+ const allStates = (0, stateManager_1.getAllResources)(state);
21
+ const items = Object.entries(allStates)
22
+ .filter(([logicalId]) => logicalId.startsWith('events.'))
23
+ .map(([logicalId, resourceState]) => planEventDeletion(logicalId, resourceState.definition));
24
+ return { items };
25
+ }
26
+ const desiredLogicalIds = new Set(events.map((e) => `events.${e.key}`));
27
+ const eventItems = await Promise.all(events.map(async (event) => {
28
+ const logicalId = `events.${event.key}`;
29
+ const currentState = (0, stateManager_1.getResource)(state, logicalId);
30
+ const groupConfig = (0, apigwTypes_1.eventToApigwGroupConfig)(event, serviceName);
31
+ const groupDefinition = (0, apigwTypes_1.extractApigwGroupDefinition)(groupConfig);
32
+ const client = (0, aliyunClient_1.createAliyunClient)(context);
33
+ // Build desired definition including triggers
34
+ const desiredDefinition = {
35
+ ...groupDefinition,
36
+ triggers: event.triggers.map((t) => ({
37
+ method: t.method,
38
+ path: t.path,
39
+ backend: t.backend,
40
+ })),
41
+ domain: event.domain
42
+ ? {
43
+ domainName: event.domain.domain_name,
44
+ certificateName: event.domain.certificate_name,
45
+ }
46
+ : null,
47
+ };
48
+ if (!currentState) {
49
+ // No state exists, check if resource exists remotely
50
+ try {
51
+ const remoteGroup = await client.apigw.findApiGroupByName(groupConfig.groupName);
52
+ if (remoteGroup) {
53
+ // Resource exists remotely but not in state - this is drift
54
+ // We should update (import) it rather than try to create again
55
+ return {
56
+ logicalId,
57
+ action: 'update',
58
+ resourceType: 'ALIYUN_APIGW',
59
+ changes: { after: desiredDefinition },
60
+ drifted: true,
61
+ };
62
+ }
63
+ }
64
+ catch {
65
+ // Ignore errors when checking remote
66
+ }
67
+ return {
68
+ logicalId,
69
+ action: 'create',
70
+ resourceType: 'ALIYUN_APIGW',
71
+ changes: { after: desiredDefinition },
72
+ };
73
+ }
74
+ // State exists, check for changes
75
+ try {
76
+ const groupInstance = currentState.instances.find((i) => i.type === 'ALIYUN_APIGW_GROUP');
77
+ if (groupInstance) {
78
+ const remoteGroup = await client.apigw.getApiGroup(groupInstance.id);
79
+ if (!remoteGroup) {
80
+ // Resource in state but not remotely - needs recreate
81
+ return {
82
+ logicalId,
83
+ action: 'create',
84
+ resourceType: 'ALIYUN_APIGW',
85
+ changes: { before: currentState.definition, after: desiredDefinition },
86
+ drifted: true,
87
+ };
88
+ }
89
+ }
90
+ const currentDefinition = currentState.definition || {};
91
+ const definitionChanged = !(0, hashUtils_1.attributesEqual)(currentDefinition, desiredDefinition);
92
+ if (definitionChanged) {
93
+ return {
94
+ logicalId,
95
+ action: 'update',
96
+ resourceType: 'ALIYUN_APIGW',
97
+ changes: { before: currentDefinition, after: desiredDefinition },
98
+ };
99
+ }
100
+ return { logicalId, action: 'noop', resourceType: 'ALIYUN_APIGW' };
101
+ }
102
+ catch {
103
+ return {
104
+ logicalId,
105
+ action: 'create',
106
+ resourceType: 'ALIYUN_APIGW',
107
+ changes: { before: currentState.definition, after: desiredDefinition },
108
+ };
109
+ }
110
+ }));
111
+ // Find resources in state that are no longer in config
112
+ const allStates = (0, stateManager_1.getAllResources)(state);
113
+ const deletionItems = Object.entries(allStates)
114
+ .filter(([logicalId]) => logicalId.startsWith('events.') && !desiredLogicalIds.has(logicalId))
115
+ .map(([logicalId, resourceState]) => planEventDeletion(logicalId, resourceState.definition));
116
+ return { items: [...eventItems, ...deletionItems] };
117
+ };
118
+ exports.generateApigwPlan = generateApigwPlan;
@@ -0,0 +1,339 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.deleteApigwResource = exports.updateApigwResource = exports.readApigwResourceByName = exports.readApigwResource = exports.createApigwResource = void 0;
4
+ const aliyunClient_1 = require("../../common/aliyunClient");
5
+ const apigwTypes_1 = require("./apigwTypes");
6
+ const stateManager_1 = require("../../common/stateManager");
7
+ const logger_1 = require("../../common/logger");
8
+ const lang_1 = require("../../lang");
9
+ const buildApigwGroupInstanceFromProvider = (info, region) => {
10
+ return {
11
+ type: 'ALIYUN_APIGW_GROUP',
12
+ arn: `arn:acs:apigateway:${region}:group/${info.groupId}`,
13
+ id: info.groupId ?? '',
14
+ groupId: info.groupId ?? null,
15
+ groupName: info.groupName ?? null,
16
+ description: info.description ?? null,
17
+ basePath: info.basePath ?? null,
18
+ subDomain: info.subDomain ?? null,
19
+ instanceId: info.instanceId ?? null,
20
+ instanceType: info.instanceType ?? null,
21
+ status: info.status ?? null,
22
+ createdTime: info.createdTime ?? null,
23
+ modifiedTime: info.modifiedTime ?? null,
24
+ billingStatus: info.billingStatus ?? null,
25
+ illegalStatus: info.illegalStatus ?? null,
26
+ trafficLimit: info.trafficLimit ?? null,
27
+ };
28
+ };
29
+ const buildApigwApiInstanceFromProvider = (info, region, groupId) => {
30
+ return {
31
+ type: 'ALIYUN_APIGW_API',
32
+ arn: `arn:acs:apigateway:${region}:group/${groupId}/api/${info.apiId}`,
33
+ id: info.apiId ?? '',
34
+ apiId: info.apiId ?? null,
35
+ apiName: info.apiName ?? null,
36
+ groupId: info.groupId ?? null,
37
+ groupName: info.groupName ?? null,
38
+ description: info.description ?? null,
39
+ visibility: info.visibility ?? null,
40
+ authType: info.authType ?? null,
41
+ requestConfig: info.requestConfig ?? null,
42
+ serviceConfig: info.serviceConfig ?? null,
43
+ resultType: info.resultType ?? null,
44
+ createdTime: info.createdTime ?? null,
45
+ modifiedTime: info.modifiedTime ?? null,
46
+ };
47
+ };
48
+ const buildApigwDeploymentInstance = (groupId, apiId, stageName, region) => {
49
+ return {
50
+ type: 'ALIYUN_APIGW_DEPLOYMENT',
51
+ arn: `arn:acs:apigateway:${region}:group/${groupId}/api/${apiId}/deployment/${stageName}`,
52
+ id: `${groupId}/${apiId}/${stageName}`,
53
+ groupId,
54
+ apiId,
55
+ stageName,
56
+ };
57
+ };
58
+ const createApigwResource = async (context, event, serviceName, roleArn, state) => {
59
+ const logicalId = `events.${event.key}`;
60
+ const client = (0, aliyunClient_1.createAliyunClient)(context);
61
+ const groupConfig = (0, apigwTypes_1.eventToApigwGroupConfig)(event, serviceName);
62
+ let groupId;
63
+ try {
64
+ const existingGroup = await client.apigw.findApiGroupByName(groupConfig.groupName);
65
+ if (existingGroup?.groupId) {
66
+ logger_1.logger.info(lang_1.lang.__('APIGW_GROUP_FOUND_REUSING', { groupName: groupConfig.groupName }));
67
+ groupId = existingGroup.groupId;
68
+ }
69
+ else {
70
+ groupId = await client.apigw.createApiGroup(groupConfig);
71
+ }
72
+ }
73
+ catch (error) {
74
+ logger_1.logger.debug(`Could not find existing group, creating new: ${error}`);
75
+ groupId = await client.apigw.createApiGroup(groupConfig);
76
+ }
77
+ // Get group info for state
78
+ const groupInfo = await client.apigw.getApiGroup(groupId);
79
+ if (!groupInfo) {
80
+ throw new Error(`Failed to get API group info after creation: ${groupId}`);
81
+ }
82
+ const instances = [
83
+ buildApigwGroupInstanceFromProvider(groupInfo, context.region),
84
+ ];
85
+ const groupDefinition = (0, apigwTypes_1.extractApigwGroupDefinition)(groupConfig);
86
+ const partialResourceState = {
87
+ mode: 'managed',
88
+ region: context.region,
89
+ definition: {
90
+ ...groupDefinition,
91
+ triggers: event.triggers.map((t) => ({
92
+ method: t.method,
93
+ path: t.path,
94
+ backend: t.backend,
95
+ })),
96
+ domain: event.domain
97
+ ? {
98
+ domainName: event.domain.domain_name,
99
+ certificateName: event.domain.certificate_name,
100
+ }
101
+ : null,
102
+ },
103
+ instances,
104
+ lastUpdated: new Date().toISOString(),
105
+ };
106
+ state = (0, stateManager_1.setResource)(state, logicalId, partialResourceState);
107
+ // Create APIs and deployments for each trigger
108
+ for (const trigger of event.triggers) {
109
+ const apiConfig = (0, apigwTypes_1.triggerToApigwApiConfig)(event, trigger, groupId, serviceName, context.region, roleArn);
110
+ const apiId = await client.apigw.createApi(apiConfig);
111
+ // Get API info for state
112
+ const apiInfo = await client.apigw.getApi(groupId, apiId);
113
+ if (apiInfo) {
114
+ instances.push(buildApigwApiInstanceFromProvider(apiInfo, context.region, groupId));
115
+ }
116
+ // Deploy API to RELEASE stage
117
+ const deploymentConfig = {
118
+ groupId,
119
+ apiId,
120
+ stageName: 'RELEASE',
121
+ description: `${serviceName} API deployment for ${trigger.method} ${trigger.path}`,
122
+ };
123
+ await client.apigw.deployApi(deploymentConfig);
124
+ instances.push(buildApigwDeploymentInstance(groupId, apiId, 'RELEASE', context.region));
125
+ const updatedResourceState = {
126
+ mode: 'managed',
127
+ region: context.region,
128
+ definition: {
129
+ ...groupDefinition,
130
+ triggers: event.triggers.map((t) => ({
131
+ method: t.method,
132
+ path: t.path,
133
+ backend: t.backend,
134
+ })),
135
+ domain: event.domain
136
+ ? {
137
+ domainName: event.domain.domain_name,
138
+ certificateName: event.domain.certificate_name,
139
+ }
140
+ : null,
141
+ },
142
+ instances,
143
+ lastUpdated: new Date().toISOString(),
144
+ };
145
+ state = (0, stateManager_1.setResource)(state, logicalId, updatedResourceState);
146
+ }
147
+ if (event.domain) {
148
+ try {
149
+ state = await client.apigw.bindCustomDomain({
150
+ groupId,
151
+ domainName: event.domain.domain_name,
152
+ certificateName: event.domain.certificate_name,
153
+ certificateBody: event.domain.certificate_body,
154
+ certificatePrivateKey: event.domain.certificate_private_key,
155
+ }, state, logicalId);
156
+ }
157
+ catch (error) {
158
+ logger_1.logger.error(lang_1.lang.__('APIGW_DOMAIN_BINDING_FAILED', { error: String(error) }));
159
+ logger_1.logger.info(lang_1.lang.__('APIGW_GROUP_APIS_CREATED_DOMAIN_FAILED'));
160
+ logger_1.logger.info(lang_1.lang.__('APIGW_STATE_SAVED_RETRY'));
161
+ return state;
162
+ }
163
+ }
164
+ // Update final state with all instances (group + APIs + deployments)
165
+ const finalResourceState = {
166
+ mode: 'managed',
167
+ region: context.region,
168
+ definition: {
169
+ ...groupDefinition,
170
+ triggers: event.triggers.map((t) => ({
171
+ method: t.method,
172
+ path: t.path,
173
+ backend: t.backend,
174
+ })),
175
+ domain: event.domain
176
+ ? {
177
+ domainName: event.domain.domain_name,
178
+ certificateName: event.domain.certificate_name,
179
+ }
180
+ : null,
181
+ },
182
+ instances,
183
+ lastUpdated: new Date().toISOString(),
184
+ };
185
+ return (0, stateManager_1.setResource)(state, logicalId, finalResourceState);
186
+ };
187
+ exports.createApigwResource = createApigwResource;
188
+ const readApigwResource = async (context, groupId) => {
189
+ const client = (0, aliyunClient_1.createAliyunClient)(context);
190
+ return await client.apigw.getApiGroup(groupId);
191
+ };
192
+ exports.readApigwResource = readApigwResource;
193
+ const readApigwResourceByName = async (context, groupName) => {
194
+ const client = (0, aliyunClient_1.createAliyunClient)(context);
195
+ return await client.apigw.findApiGroupByName(groupName);
196
+ };
197
+ exports.readApigwResourceByName = readApigwResourceByName;
198
+ const updateApigwResource = async (context, event, serviceName, roleArn, state) => {
199
+ const logicalId = `events.${event.key}`;
200
+ const existingState = (0, stateManager_1.getResource)(state, logicalId);
201
+ const client = (0, aliyunClient_1.createAliyunClient)(context);
202
+ if (!existingState) {
203
+ return (0, exports.createApigwResource)(context, event, serviceName, roleArn, state);
204
+ }
205
+ const existingInstances = existingState.instances;
206
+ const groupInstance = existingInstances.find((i) => i.type === 'ALIYUN_APIGW_GROUP');
207
+ if (!groupInstance) {
208
+ return (0, exports.createApigwResource)(context, event, serviceName, roleArn, state);
209
+ }
210
+ const groupId = groupInstance.id;
211
+ const groupConfig = (0, apigwTypes_1.eventToApigwGroupConfig)(event, serviceName);
212
+ await client.apigw.updateApiGroup(groupId, groupConfig);
213
+ const groupInfo = await client.apigw.getApiGroup(groupId);
214
+ if (!groupInfo) {
215
+ throw new Error(`Failed to get API group info after update: ${groupId}`);
216
+ }
217
+ const instances = [
218
+ buildApigwGroupInstanceFromProvider(groupInfo, context.region),
219
+ ];
220
+ const existingApis = existingInstances.filter((i) => i.type === 'ALIYUN_APIGW_API');
221
+ const neededApiKeys = new Set();
222
+ for (const trigger of event.triggers) {
223
+ const apiConfig = (0, apigwTypes_1.triggerToApigwApiConfig)(event, trigger, groupId, serviceName, context.region, roleArn);
224
+ const apiKey = (0, apigwTypes_1.generateApiKey)(trigger.method, trigger.path);
225
+ neededApiKeys.add(apiKey);
226
+ const existingApi = existingApis.find((a) => {
227
+ return a.id && a.apiName === apiConfig.apiName;
228
+ });
229
+ let apiId;
230
+ if (existingApi) {
231
+ apiId = existingApi.id;
232
+ await client.apigw.updateApi(apiId, apiConfig);
233
+ }
234
+ else {
235
+ apiId = await client.apigw.createApi(apiConfig);
236
+ }
237
+ const apiInfo = await client.apigw.getApi(groupId, apiId);
238
+ if (apiInfo) {
239
+ instances.push(buildApigwApiInstanceFromProvider(apiInfo, context.region, groupId));
240
+ }
241
+ const deploymentConfig = {
242
+ groupId,
243
+ apiId,
244
+ stageName: 'RELEASE',
245
+ description: `${serviceName} API deployment for ${trigger.method} ${trigger.path}`,
246
+ };
247
+ await client.apigw.deployApi(deploymentConfig);
248
+ instances.push(buildApigwDeploymentInstance(groupId, apiId, 'RELEASE', context.region));
249
+ }
250
+ for (const existingApi of existingApis) {
251
+ const apiInfo = await client.apigw.getApi(groupId, existingApi.id);
252
+ if (apiInfo) {
253
+ const isNeeded = event.triggers.some((t) => {
254
+ const expectedName = (0, apigwTypes_1.triggerToApigwApiConfig)(event, t, groupId, serviceName, context.region, roleArn).apiName;
255
+ return apiInfo.apiName === expectedName;
256
+ });
257
+ if (!isNeeded) {
258
+ try {
259
+ await client.apigw.abolishApi(groupId, existingApi.id, 'RELEASE');
260
+ }
261
+ catch {
262
+ // API might not be deployed
263
+ }
264
+ await client.apigw.deleteApi(groupId, existingApi.id);
265
+ }
266
+ }
267
+ }
268
+ if (event.domain) {
269
+ state = await client.apigw.bindCustomDomain({
270
+ groupId,
271
+ domainName: event.domain.domain_name,
272
+ certificateName: event.domain.certificate_name,
273
+ certificateBody: event.domain.certificate_body,
274
+ certificatePrivateKey: event.domain.certificate_private_key,
275
+ }, state, logicalId);
276
+ }
277
+ const groupDefinition = (0, apigwTypes_1.extractApigwGroupDefinition)(groupConfig);
278
+ const resourceState = {
279
+ mode: 'managed',
280
+ region: context.region,
281
+ definition: {
282
+ ...groupDefinition,
283
+ triggers: event.triggers.map((t) => ({
284
+ method: t.method,
285
+ path: t.path,
286
+ backend: t.backend,
287
+ })),
288
+ domain: event.domain
289
+ ? {
290
+ domainName: event.domain.domain_name,
291
+ certificateName: event.domain.certificate_name,
292
+ }
293
+ : null,
294
+ },
295
+ instances,
296
+ lastUpdated: new Date().toISOString(),
297
+ };
298
+ return (0, stateManager_1.setResource)(state, logicalId, resourceState);
299
+ };
300
+ exports.updateApigwResource = updateApigwResource;
301
+ const deleteApigwResource = async (context, logicalId, state) => {
302
+ const existingState = (0, stateManager_1.getResource)(state, logicalId);
303
+ const client = (0, aliyunClient_1.createAliyunClient)(context);
304
+ if (!existingState) {
305
+ return state;
306
+ }
307
+ const existingInstances = existingState.instances;
308
+ const groupInstance = existingInstances.find((i) => i.type === 'ALIYUN_APIGW_GROUP');
309
+ if (!groupInstance) {
310
+ return (0, stateManager_1.removeResource)(state, logicalId);
311
+ }
312
+ const groupId = groupInstance.id;
313
+ const deployments = existingInstances.filter((i) => i.type === 'ALIYUN_APIGW_DEPLOYMENT');
314
+ for (const deployment of deployments) {
315
+ try {
316
+ await client.apigw.abolishApi(deployment.groupId || groupId, deployment.apiId, deployment.stageName || 'RELEASE');
317
+ }
318
+ catch {
319
+ // Deployment might already be abolished
320
+ }
321
+ }
322
+ const apis = existingInstances.filter((i) => i.type === 'ALIYUN_APIGW_API');
323
+ for (const api of apis) {
324
+ try {
325
+ await client.apigw.deleteApi(groupId, api.id);
326
+ }
327
+ catch {
328
+ // API might already be deleted
329
+ }
330
+ }
331
+ try {
332
+ await client.apigw.deleteApiGroup(groupId);
333
+ }
334
+ catch {
335
+ // Group might already be deleted
336
+ }
337
+ return (0, stateManager_1.removeResource)(state, logicalId);
338
+ };
339
+ exports.deleteApigwResource = deleteApigwResource;
@@ -0,0 +1,125 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.extractApigwDeploymentDefinition = exports.extractApigwApiDefinition = exports.extractApigwGroupDefinition = exports.triggerToApigwApiConfig = exports.generateApiKey = exports.eventToApigwGroupConfig = void 0;
4
+ const common_1 = require("../../common");
5
+ /**
6
+ * Convert EventDomain to API Gateway group config
7
+ */
8
+ const eventToApigwGroupConfig = (event, serviceName) => {
9
+ return {
10
+ groupName: `${serviceName}-agw-group`.replace(/_/g, '-'),
11
+ description: `API Gateway group for ${serviceName}`,
12
+ };
13
+ };
14
+ exports.eventToApigwGroupConfig = eventToApigwGroupConfig;
15
+ /**
16
+ * Generate a unique API key from method and path
17
+ * Uses URL encoding to preserve path structure and avoid collisions
18
+ */
19
+ const generateApiKey = (method, path) => {
20
+ // Replace slashes with double underscores to preserve path structure
21
+ // Replace other non-alphanumeric chars with single underscore
22
+ const sanitizedPath = path
23
+ .replace(/\//g, '__')
24
+ .replace(/[^a-zA-Z0-9_]/g, '_')
25
+ .replace(/^__/, '') // Remove leading double underscore
26
+ .replace(/__$/, ''); // Remove trailing double underscore
27
+ return `${method}_${sanitizedPath}`;
28
+ };
29
+ exports.generateApiKey = generateApiKey;
30
+ /**
31
+ * Resolves a function reference like ${functions.xxx} to the actual function name
32
+ */
33
+ const resolveFunctionReference = (backendRef) => {
34
+ // Get IAC from context
35
+ const context = (0, common_1.getContext)();
36
+ if (!context.iac) {
37
+ common_1.logger.warn(`Cannot resolve function reference ${backendRef}: IAC not available in context`);
38
+ return backendRef;
39
+ }
40
+ // Use getIacDefinition to resolve function references
41
+ const functionDef = (0, common_1.getIacDefinition)(context.iac, backendRef);
42
+ if (!functionDef) {
43
+ // Not a function reference or function not found, return as is
44
+ common_1.logger.warn(`Function reference ${backendRef} could not be resolved from IAC`);
45
+ return backendRef;
46
+ }
47
+ const functionName = functionDef.name;
48
+ common_1.logger.info(`Resolved function reference ${backendRef} to function name: ${functionName}`);
49
+ return functionName;
50
+ };
51
+ /**
52
+ * Convert EventDomain trigger to API Gateway API config
53
+ */
54
+ const triggerToApigwApiConfig = (event, trigger, groupId, serviceName, region, roleArn) => {
55
+ const method = trigger.method;
56
+ const path = trigger.path;
57
+ const backend = trigger.backend;
58
+ const resolvedFunctionName = resolveFunctionReference(backend);
59
+ const apiKey = (0, exports.generateApiKey)(method, path);
60
+ return {
61
+ groupId,
62
+ apiName: `${event.name}-agw-api-${apiKey}`.replace(/_/g, '-'),
63
+ visibility: 'PRIVATE',
64
+ authType: 'ANONYMOUS',
65
+ requestConfig: {
66
+ requestProtocol: 'HTTP',
67
+ requestHttpMethod: method,
68
+ requestPath: path,
69
+ requestMode: 'PASSTHROUGH',
70
+ },
71
+ serviceConfig: {
72
+ serviceProtocol: 'FunctionCompute',
73
+ functionComputeConfig: {
74
+ fcRegionId: region,
75
+ functionName: resolvedFunctionName,
76
+ roleArn,
77
+ fcVersion: '3.0',
78
+ method,
79
+ },
80
+ },
81
+ resultType: 'PASSTHROUGH',
82
+ resultSample: 'ServerlessInsight resultSample',
83
+ };
84
+ };
85
+ exports.triggerToApigwApiConfig = triggerToApigwApiConfig;
86
+ /**
87
+ * Extract definition from API Gateway group config for state comparison
88
+ */
89
+ const extractApigwGroupDefinition = (config) => {
90
+ return {
91
+ groupName: config.groupName,
92
+ description: config.description ?? null,
93
+ basePath: config.basePath ?? null,
94
+ };
95
+ };
96
+ exports.extractApigwGroupDefinition = extractApigwGroupDefinition;
97
+ /**
98
+ * Extract definition from API Gateway API config for state comparison
99
+ */
100
+ const extractApigwApiDefinition = (config) => {
101
+ return {
102
+ apiName: config.apiName,
103
+ groupId: config.groupId,
104
+ visibility: config.visibility,
105
+ authType: config.authType,
106
+ requestConfig: config.requestConfig,
107
+ serviceConfig: {
108
+ serviceProtocol: config.serviceConfig.serviceProtocol,
109
+ functionComputeConfig: config.serviceConfig.functionComputeConfig ?? null,
110
+ },
111
+ resultType: config.resultType ?? null,
112
+ };
113
+ };
114
+ exports.extractApigwApiDefinition = extractApigwApiDefinition;
115
+ /**
116
+ * Extract definition from deployment config for state comparison
117
+ */
118
+ const extractApigwDeploymentDefinition = (config) => {
119
+ return {
120
+ groupId: config.groupId,
121
+ apiId: config.apiId,
122
+ stageName: config.stageName,
123
+ };
124
+ };
125
+ exports.extractApigwDeploymentDefinition = extractApigwDeploymentDefinition;