Skip to content

Commit 70a3883

Browse files
committed
feat: add command to show ros template
Signed-off-by: seven <[email protected]>
1 parent af91ea7 commit 70a3883

File tree

7 files changed

+359
-3
lines changed

7 files changed

+359
-3
lines changed

src/commands/index.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { lang } from '../lang';
55
import { logger, getVersion } from '../common';
66
import { validate } from './validate';
77
import { deploy } from './deploy';
8+
import { template } from './template';
89

910
const program = new Command();
1011

@@ -48,8 +49,17 @@ program
4849
{},
4950
)
5051
.action(async (stackName, { file, parameter, stage }) => {
51-
logger.debug('log command info');
5252
await deploy(stackName, { location: file, parameters: parameter, stage });
5353
});
5454

55+
program
56+
.command('template <stackName>')
57+
.description('print ROS template')
58+
.option('-f, --file <path>', 'specify the yaml file')
59+
.option('-s, --stage <stage>', 'specify the stage')
60+
.option('-t, --format <type>', 'output content type (JSON or YAML)', 'JSON')
61+
.action((stackName, { format, file, stage }) => {
62+
template(stackName, { format, location: file, stage });
63+
});
64+
5565
program.parse();

src/commands/template.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { TemplateFormat } from '../types';
2+
import yaml from 'yaml';
3+
import { generateStackTemplate } from '../stack/deploy';
4+
import { constructActionContext, logger } from '../common';
5+
import { parseYaml } from '../stack';
6+
7+
export const template = (
8+
stackName: string,
9+
options: { format: TemplateFormat; location: string; stage: string | undefined },
10+
) => {
11+
const context = constructActionContext({ ...options, stackName });
12+
const iac = parseYaml(context.iacLocation);
13+
const { template } = generateStackTemplate(stackName, iac, context);
14+
15+
const output =
16+
options.format === TemplateFormat.JSON
17+
? JSON.stringify(template, null, 2)
18+
: yaml.stringify(template);
19+
20+
logger.info(`\n${output}`);
21+
};

src/common/actionContext.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export const constructActionContext = (config?: {
2323
iacLocation: (() => {
2424
const projectRoot = path.resolve(process.cwd());
2525
return config?.location
26-
? path.resolve(projectRoot, config?.location)
26+
? path.resolve(projectRoot, config.location)
2727
: path.resolve(projectRoot, 'serverlessinsight.yml') ||
2828
path.resolve(projectRoot, 'serverlessInsight.yml') ||
2929
path.resolve(projectRoot, 'ServerlessInsight.yml') ||

src/stack/deploy.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@ import { ActionContext, ServerlessIac } from '../types';
33
import { logger, rosStackDeploy } from '../common';
44
import { IacStack } from './iacStack';
55

6-
const generateStackTemplate = (stackName: string, iac: ServerlessIac, context: ActionContext) => {
6+
export const generateStackTemplate = (
7+
stackName: string,
8+
iac: ServerlessIac,
9+
context: ActionContext,
10+
) => {
711
const app = new ros.App();
812
new IacStack(app, iac, context);
913

src/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,8 @@ export type ActionContext = {
7979
parameters?: Array<{ key: string; value: string }>;
8080
tags?: Array<{ key: string; value: string }>;
8181
};
82+
83+
export enum TemplateFormat {
84+
YAML = 'YAML',
85+
JSON = 'JSON',
86+
}

tests/commands/template.test.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { template } from '../../src/commands/template';
2+
import { TemplateFormat } from '../../src/types';
3+
import { jsonTemplate, yamlTemplate } from '../fixtures/templateFixture';
4+
5+
const mockedLogger = jest.fn();
6+
jest.mock('../../src/common/logger', () => ({
7+
logger: { info: (...args: unknown[]) => mockedLogger(...args), debug: jest.fn() },
8+
}));
9+
const stackName = 'printTemplateStack';
10+
const location = 'tests/fixtures/serverless-insight.yml';
11+
12+
describe('Unit test for template command', () => {
13+
beforeEach(() => {
14+
mockedLogger.mockRestore();
15+
});
16+
it('should print the template in JSON format by default', () => {
17+
const options = {
18+
format: TemplateFormat.JSON,
19+
location,
20+
stage: undefined,
21+
};
22+
23+
template(stackName, options);
24+
25+
expect(mockedLogger).toHaveBeenCalledWith(`\n${JSON.stringify(jsonTemplate, null, 2)}`);
26+
});
27+
28+
it('should print the template in YAML format when specified', () => {
29+
const options = { format: TemplateFormat.YAML, location, stage: undefined };
30+
31+
template(stackName, options);
32+
33+
expect(mockedLogger).toHaveBeenCalledWith(yamlTemplate);
34+
});
35+
});

tests/fixtures/templateFixture.ts

Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
1+
export const jsonTemplate = {
2+
Description: 'insight-poc stack',
3+
Metadata: {
4+
'ALIYUN::ROS::Interface': {
5+
TemplateTags: ['Create by ROS CDK'],
6+
},
7+
},
8+
ROSTemplateFormatVersion: '2015-09-01',
9+
Parameters: {
10+
region: {
11+
Type: 'String',
12+
Default: 'cn-hangzhou',
13+
},
14+
testv: {
15+
Type: 'String',
16+
Default: 'testVarValue',
17+
},
18+
handler: {
19+
Type: 'String',
20+
Default: 'index.handler',
21+
},
22+
},
23+
Mappings: {
24+
stages: {
25+
default: {
26+
region: {
27+
Ref: 'region',
28+
},
29+
node_env: 'default',
30+
},
31+
dev: {
32+
region: {
33+
Ref: 'region',
34+
},
35+
node_env: 'development',
36+
},
37+
prod: {
38+
region: 'cn-shanghai',
39+
},
40+
},
41+
},
42+
Resources: {
43+
insight_poc_fn: {
44+
Type: 'ALIYUN::FC3::Function',
45+
Properties: {
46+
FunctionName: 'insight-poc-fn',
47+
Handler: {
48+
Ref: 'handler',
49+
},
50+
Runtime: 'nodejs18',
51+
Code: {
52+
ZipFile:
53+
'UEsDBBQAAAAAADiNNVkAAAAAAAAAAAAAAAAJACAAYXJ0aWZhY3QvVVQNAAfdlO5m7pTuZt2U7mZ1eAsAAQT1AQAABBQAAABQSwMEFAAIAAgA44w1WQAAAAAAAAAAngAAABEAIABhcnRpZmFjdC9pbmRleC5qc1VUDQAHO5TuZt6U7mbdlO5mdXgLAAEE9QEAAAQUAAAAhYuxCsJAEAX7fMV2uUDIDwR7rS2sz8uaBB+7srsJAfHf5cDebhhm+HiphQ9LlglsdKLHJiVWFUq8s0RPRSX4qJCBey7Pjt4NVe0KHqBzaq9sOxvY/SK+zkvQmQGlmxqmthtr/7uTbEBP/5fP2HwBUEsHCG5M74NvAAAAngAAAFBLAQIUAxQAAAAAADiNNVkAAAAAAAAAAAAAAAAJACAAAAAAAAAAAADtQQAAAABhcnRpZmFjdC9VVA0AB92U7mbulO5m3ZTuZnV4CwABBPUBAAAEFAAAAFBLAQIUAxQACAAIAOOMNVluTO+DbwAAAJ4AAAARACAAAAAAAAAAAACkgUcAAABhcnRpZmFjdC9pbmRleC5qc1VUDQAHO5TuZt6U7mbdlO5mdXgLAAEE9QEAAAQUAAAAUEsFBgAAAAACAAIAtgAAABUBAAAAAA==',
54+
},
55+
EnvironmentVariables: {
56+
NODE_ENV: {
57+
'Fn::FindInMap': ['stages', 'default', 'node_env'],
58+
},
59+
TEST_VAR: {
60+
Ref: 'testv',
61+
},
62+
TEST_VAR_EXTRA: {
63+
'Fn::Sub': 'abcds-${testv}-andyou',
64+
},
65+
},
66+
MemorySize: 512,
67+
Timeout: 10,
68+
},
69+
},
70+
'insight-poc_role': {
71+
Type: 'ALIYUN::RAM::Role',
72+
Properties: {
73+
AssumeRolePolicyDocument: {
74+
Version: '1',
75+
Statement: [
76+
{
77+
Action: 'sts:AssumeRole',
78+
Effect: 'Allow',
79+
Principal: {
80+
Service: ['apigateway.aliyuncs.com'],
81+
},
82+
},
83+
],
84+
},
85+
RoleName: 'insight-poc-gateway-access-role',
86+
Description: 'insight-poc role',
87+
Policies: [
88+
{
89+
PolicyName: 'insight-poc-policy',
90+
PolicyDocument: {
91+
Version: '1',
92+
Statement: [
93+
{
94+
Action: ['fc:InvokeFunction'],
95+
Resource: ['*'],
96+
Effect: 'Allow',
97+
},
98+
],
99+
},
100+
},
101+
],
102+
},
103+
},
104+
'insight-poc_apigroup': {
105+
Type: 'ALIYUN::ApiGateway::Group',
106+
Properties: {
107+
GroupName: 'insight-poc_apigroup',
108+
Tags: [
109+
{
110+
Value: 'ServerlessInsight',
111+
Key: 'iac-provider',
112+
},
113+
{
114+
Value: 'geek-fun',
115+
Key: 'owner',
116+
},
117+
],
118+
},
119+
},
120+
gateway_event_api_get__api_hello: {
121+
Type: 'ALIYUN::ApiGateway::Api',
122+
Properties: {
123+
ApiName: 'insight-poc-gateway_api_get__api_hello',
124+
GroupId: {
125+
'Fn::GetAtt': ['insight-poc_apigroup', 'GroupId'],
126+
},
127+
RequestConfig: {
128+
RequestPath: '/api/hello',
129+
RequestMode: 'PASSTHROUGH',
130+
RequestProtocol: 'HTTP',
131+
RequestHttpMethod: 'GET',
132+
},
133+
ServiceConfig: {
134+
FunctionComputeConfig: {
135+
FcVersion: '3.0',
136+
FcRegionId: 'cn-hangzhou',
137+
RoleArn: {
138+
'Fn::GetAtt': ['insight-poc_role', 'Arn'],
139+
},
140+
FunctionName: {
141+
'Fn::GetAtt': ['insight_poc_fn', 'FunctionName'],
142+
},
143+
},
144+
ServiceProtocol: 'FunctionCompute',
145+
},
146+
Visibility: 'PRIVATE',
147+
ResultSample: 'ServerlessInsight resultSample',
148+
ResultType: 'JSON',
149+
Tags: [
150+
{
151+
Value: 'ServerlessInsight',
152+
Key: 'iac-provider',
153+
},
154+
{
155+
Value: 'geek-fun',
156+
Key: 'owner',
157+
},
158+
],
159+
},
160+
},
161+
},
162+
};
163+
164+
export const yamlTemplate = `
165+
Description: insight-poc stack
166+
Metadata:
167+
ALIYUN::ROS::Interface:
168+
TemplateTags:
169+
- Create by ROS CDK
170+
ROSTemplateFormatVersion: 2015-09-01
171+
Parameters:
172+
region:
173+
Type: String
174+
Default: cn-hangzhou
175+
testv:
176+
Type: String
177+
Default: testVarValue
178+
handler:
179+
Type: String
180+
Default: index.handler
181+
Mappings:
182+
stages:
183+
default:
184+
region:
185+
Ref: region
186+
node_env: default
187+
dev:
188+
region:
189+
Ref: region
190+
node_env: development
191+
prod:
192+
region: cn-shanghai
193+
Resources:
194+
insight_poc_fn:
195+
Type: ALIYUN::FC3::Function
196+
Properties:
197+
FunctionName: insight-poc-fn
198+
Handler:
199+
Ref: handler
200+
Runtime: nodejs18
201+
Code:
202+
ZipFile: UEsDBBQAAAAAADiNNVkAAAAAAAAAAAAAAAAJACAAYXJ0aWZhY3QvVVQNAAfdlO5m7pTuZt2U7mZ1eAsAAQT1AQAABBQAAABQSwMEFAAIAAgA44w1WQAAAAAAAAAAngAAABEAIABhcnRpZmFjdC9pbmRleC5qc1VUDQAHO5TuZt6U7mbdlO5mdXgLAAEE9QEAAAQUAAAAhYuxCsJAEAX7fMV2uUDIDwR7rS2sz8uaBB+7srsJAfHf5cDebhhm+HiphQ9LlglsdKLHJiVWFUq8s0RPRSX4qJCBey7Pjt4NVe0KHqBzaq9sOxvY/SK+zkvQmQGlmxqmthtr/7uTbEBP/5fP2HwBUEsHCG5M74NvAAAAngAAAFBLAQIUAxQAAAAAADiNNVkAAAAAAAAAAAAAAAAJACAAAAAAAAAAAADtQQAAAABhcnRpZmFjdC9VVA0AB92U7mbulO5m3ZTuZnV4CwABBPUBAAAEFAAAAFBLAQIUAxQACAAIAOOMNVluTO+DbwAAAJ4AAAARACAAAAAAAAAAAACkgUcAAABhcnRpZmFjdC9pbmRleC5qc1VUDQAHO5TuZt6U7mbdlO5mdXgLAAEE9QEAAAQUAAAAUEsFBgAAAAACAAIAtgAAABUBAAAAAA==
203+
EnvironmentVariables:
204+
NODE_ENV:
205+
Fn::FindInMap:
206+
- stages
207+
- default
208+
- node_env
209+
TEST_VAR:
210+
Ref: testv
211+
TEST_VAR_EXTRA:
212+
Fn::Sub: abcds-\${testv}-andyou
213+
MemorySize: 512
214+
Timeout: 10
215+
insight-poc_role:
216+
Type: ALIYUN::RAM::Role
217+
Properties:
218+
AssumeRolePolicyDocument:
219+
Version: "1"
220+
Statement:
221+
- Action: sts:AssumeRole
222+
Effect: Allow
223+
Principal:
224+
Service:
225+
- apigateway.aliyuncs.com
226+
RoleName: insight-poc-gateway-access-role
227+
Description: insight-poc role
228+
Policies:
229+
- PolicyName: insight-poc-policy
230+
PolicyDocument:
231+
Version: "1"
232+
Statement:
233+
- Action:
234+
- fc:InvokeFunction
235+
Resource:
236+
- "*"
237+
Effect: Allow
238+
insight-poc_apigroup:
239+
Type: ALIYUN::ApiGateway::Group
240+
Properties:
241+
GroupName: insight-poc_apigroup
242+
Tags:
243+
- Value: ServerlessInsight
244+
Key: iac-provider
245+
- Value: geek-fun
246+
Key: owner
247+
gateway_event_api_get__api_hello:
248+
Type: ALIYUN::ApiGateway::Api
249+
Properties:
250+
ApiName: insight-poc-gateway_api_get__api_hello
251+
GroupId:
252+
Fn::GetAtt:
253+
- insight-poc_apigroup
254+
- GroupId
255+
RequestConfig:
256+
RequestPath: /api/hello
257+
RequestMode: PASSTHROUGH
258+
RequestProtocol: HTTP
259+
RequestHttpMethod: GET
260+
ServiceConfig:
261+
FunctionComputeConfig:
262+
FcVersion: "3.0"
263+
FcRegionId: cn-hangzhou
264+
RoleArn:
265+
Fn::GetAtt:
266+
- insight-poc_role
267+
- Arn
268+
FunctionName:
269+
Fn::GetAtt:
270+
- insight_poc_fn
271+
- FunctionName
272+
ServiceProtocol: FunctionCompute
273+
Visibility: PRIVATE
274+
ResultSample: ServerlessInsight resultSample
275+
ResultType: JSON
276+
Tags:
277+
- Value: ServerlessInsight
278+
Key: iac-provider
279+
- Value: geek-fun
280+
Key: owner
281+
`;

0 commit comments

Comments
 (0)