Skip to content

Commit 23d98a0

Browse files
update keyAttributes and add unit tests
1 parent f08bb42 commit 23d98a0

File tree

5 files changed

+337
-5
lines changed

5 files changed

+337
-5
lines changed

packages/@aws-cdk/aws-applicationsignals-alpha/lib/slo/constants.ts

+14-1
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,20 @@ export enum KeyAttributeType {
119119
/**
120120
* External service
121121
*/
122-
REMOTE_SERVICE = 'REMOTE_SERVICE'
122+
REMOTE_SERVICE = 'REMOTE_SERVICE',
123+
124+
/**
125+
* Resource
126+
*/
127+
128+
RESOURCE = 'RESOURCE',
129+
130+
/**
131+
* AWS managed Resource
132+
*/
133+
134+
AWS_RESOURCE = 'AWS::RESOURCE'
135+
123136
}
124137

125138
/**

packages/@aws-cdk/aws-applicationsignals-alpha/lib/slo/keyAttributes.ts

+49-4
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,43 @@ export class KeyAttributes {
1313
}
1414

1515
private validateProps(props: KeyAttributesProps) {
16-
if (!props.name && !props.environment && !props.type) {
17-
throw new Error('name or type or environment is required for Application Signals service');
16+
if (!props.type) {
17+
throw new Error('Type is required for Application Signals service');
18+
}
19+
20+
if ([KeyAttributeType.SERVICE, KeyAttributeType.REMOTE_SERVICE, KeyAttributeType.AWS_SERVICE].includes(props.type)) {
21+
if (!props.name || !props.environment) {
22+
throw new Error('Name and Environment are required for Service types');
23+
}
24+
}
25+
26+
if ([KeyAttributeType.RESOURCE, KeyAttributeType.AWS_RESOURCE].includes(props.type)) {
27+
if (props.name) {
28+
throw new Error('Name is not allowed for Resource types');
29+
}
30+
if (props.resourceType === undefined) {
31+
throw new Error('ResourceType is required for Resource types');
32+
}
1833
}
1934
}
2035

36+
2137
public bind(): { [key: string]: string } {
22-
return {
38+
const attributes: { [key: string]: string } = {
2339
Type: this.props.type,
2440
Name: this.props.name,
2541
Environment: this.props.environment,
26-
...(this.props.identifier && { Identifier: this.props.identifier }),
2742
};
43+
44+
if (this.props.resourceType) {
45+
attributes.ResourceType = this.props.resourceType;
46+
}
47+
48+
if (this.props.identifier) {
49+
attributes.Identifier = this.props.identifier;
50+
}
51+
52+
return attributes;
2853
}
2954

3055
/**
@@ -56,4 +81,24 @@ export class KeyAttributes {
5681
...props,
5782
});
5883
}
84+
85+
/**
86+
* Creates key attributes for a resource
87+
*/
88+
public static resource(props: Omit<KeyAttributesProps, 'type'>): KeyAttributes {
89+
return new KeyAttributes({
90+
type: KeyAttributeType.RESOURCE,
91+
...props,
92+
});
93+
}
94+
95+
/**
96+
* Creates key attributes for an AWS resource
97+
*/
98+
public static awsResource(props: Omit<KeyAttributesProps, 'type'>): KeyAttributes {
99+
return new KeyAttributes({
100+
type: KeyAttributeType.AWS_RESOURCE,
101+
...props,
102+
});
103+
}
59104
}

packages/@aws-cdk/aws-applicationsignals-alpha/lib/slo/slo.ts

+3
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,9 @@ export interface KeyAttributesProps {
117117
* @default - no identifier
118118
*/
119119
readonly identifier?: string;
120+
121+
readonly resourceType?: string;
122+
120123
}
121124

122125
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import {Interval} from '../../lib/slo/interval'
2+
import {CalendarIntervalProps, IntervalProps} from '../../lib';
3+
import {DurationUnit} from "../../lib/slo/constants";
4+
5+
describe('Interval', () => {
6+
describe('calendar()', () => {
7+
it('should create a calendar interval with valid props', () => {
8+
const props: CalendarIntervalProps = {
9+
unit: DurationUnit.MONTH,
10+
duration: 1
11+
};
12+
13+
const interval = Interval.calendar(props);
14+
expect(interval).toBeDefined();
15+
expect(interval.bind).toBeDefined();
16+
});
17+
18+
it('should create a calendar interval with different time units', () => {
19+
const units = [
20+
DurationUnit.DAY,
21+
DurationUnit.MONTH
22+
];
23+
24+
units.forEach(unit => {
25+
const props: CalendarIntervalProps = {
26+
unit: unit,
27+
duration: 1
28+
};
29+
const interval = Interval.calendar(props);
30+
expect(interval).toBeDefined();
31+
});
32+
});
33+
34+
it('should handle different duration values', () => {
35+
const durations = [1, 7, 14, 30];
36+
37+
durations.forEach(duration => {
38+
const props: CalendarIntervalProps = {
39+
unit: DurationUnit.DAY,
40+
duration
41+
};
42+
const interval = Interval.calendar(props);
43+
expect(interval).toBeDefined();
44+
});
45+
});
46+
});
47+
48+
describe('rolling()', () => {
49+
it('should create a rolling interval with valid props', () => {
50+
const props: IntervalProps = {
51+
duration: 24,
52+
unit: DurationUnit.HOUR
53+
};
54+
55+
const interval = Interval.rolling(props);
56+
expect(interval).toBeDefined();
57+
expect(interval.bind).toBeDefined();
58+
});
59+
60+
it('should handle different duration values', () => {
61+
const durations = [1, 7, 14, 31];
62+
63+
durations.forEach(duration => {
64+
const props: CalendarIntervalProps = {
65+
unit: DurationUnit.DAY,
66+
duration
67+
};
68+
const interval = Interval.calendar(props);
69+
expect(interval).toBeDefined();
70+
});
71+
});
72+
});
73+
74+
describe('bind()', () => {
75+
it('should return the correct structure for calendar intervals', () => {
76+
const props: CalendarIntervalProps = {
77+
unit: DurationUnit.MONTH,
78+
duration: 1
79+
};
80+
81+
const interval = Interval.calendar(props);
82+
const bound = interval.bind();
83+
expect(bound).toBeDefined();
84+
85+
});
86+
87+
it('should return the correct structure for rolling intervals', () => {
88+
const props: IntervalProps = {
89+
duration: 24,
90+
unit: DurationUnit.HOUR
91+
};
92+
93+
const interval = Interval.rolling(props);
94+
const bound = interval.bind();
95+
expect(bound).toBeDefined();
96+
});
97+
});
98+
99+
describe('error cases', () => {
100+
it('should handle invalid calendar interval props', () => {
101+
expect(() => {
102+
Interval.calendar({
103+
unit: DurationUnit.DAY,
104+
duration: -1
105+
}).bind()
106+
}).toThrow();
107+
});
108+
109+
it('should handle invalid rolling interval props', () => {
110+
expect(() => {
111+
Interval.rolling({
112+
duration: -1,
113+
unit: DurationUnit.HOUR
114+
} as any).bind()
115+
}).toThrow();
116+
});
117+
118+
it('should handle missing required properties', () => {
119+
expect(() => {
120+
Interval.calendar({
121+
unit: DurationUnit.DAY,
122+
duration: 0
123+
} as CalendarIntervalProps).bind();
124+
}).toThrow('Duration must be greater than 0');
125+
126+
expect(() => {
127+
Interval.rolling({
128+
unit: DurationUnit.HOUR,
129+
duration: 0
130+
} as IntervalProps).bind();
131+
}).toThrow('Duration must be greater than 0');
132+
});
133+
});
134+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
import { KeyAttributes } from '../../lib/slo/keyAttributes';
2+
import { KeyAttributeType } from '../../lib/slo/constants';
3+
import { KeyAttributesProps } from "../../lib";
4+
5+
describe('KeyAttributes', () => {
6+
describe('constructor validation', () => {
7+
test('throws error when type is missing', () => {
8+
expect(() => {
9+
new KeyAttributes({} as KeyAttributesProps);
10+
}).toThrow('Type is required for Application Signals service');
11+
});
12+
13+
describe('Service type validation', () => {
14+
const serviceTypes = [
15+
KeyAttributeType.SERVICE,
16+
KeyAttributeType.REMOTE_SERVICE,
17+
KeyAttributeType.AWS_SERVICE,
18+
];
19+
20+
serviceTypes.forEach(type => {
21+
test(`requires name and environment for ${type}`, () => {
22+
expect(() => {
23+
new KeyAttributes({
24+
type,
25+
name: undefined,
26+
environment: undefined
27+
} as any);
28+
}).toThrow('Name and Environment are required for Service types');
29+
30+
expect(() => {
31+
new KeyAttributes({
32+
type,
33+
name: 'test',
34+
environment: undefined
35+
} as any);
36+
}).toThrow('Name and Environment are required for Service types');
37+
38+
expect(() => {
39+
new KeyAttributes({
40+
type,
41+
name: undefined,
42+
environment: 'prod'
43+
} as any);
44+
}).toThrow('Name and Environment are required for Service types');
45+
});
46+
47+
test(`accepts valid configuration for ${type}`, () => {
48+
const props: KeyAttributesProps = {
49+
type,
50+
name: 'test',
51+
environment: 'prod'
52+
};
53+
expect(() => {
54+
new KeyAttributes(props);
55+
}).not.toThrow();
56+
});
57+
});
58+
});
59+
60+
describe('Resource type validation', () => {
61+
const resourceTypes = [
62+
KeyAttributeType.RESOURCE,
63+
KeyAttributeType.AWS_RESOURCE,
64+
];
65+
66+
resourceTypes.forEach(type => {
67+
test(`validates ${type} requirements`, () => {
68+
expect(() => {
69+
new KeyAttributes({
70+
type,
71+
name: 'test',
72+
resourceType: 'someResource',
73+
environment: 'prod'
74+
} as any);
75+
}).toThrow('Name is not allowed for Resource types');
76+
77+
expect(() => {
78+
new KeyAttributes({
79+
type,
80+
resourceType: undefined
81+
} as any);
82+
}).toThrow('ResourceType is required for Resource types');
83+
});
84+
85+
test(`accepts valid configuration for ${type}`, () => {
86+
const props = {
87+
type,
88+
resourceType: 'someResource'
89+
} as KeyAttributesProps;
90+
expect(() => {
91+
new KeyAttributes(props);
92+
}).not.toThrow();
93+
});
94+
});
95+
});
96+
});
97+
98+
describe('bind method', () => {
99+
test('returns correct attributes for service type', () => {
100+
const keyAttributes = new KeyAttributes({
101+
type: KeyAttributeType.SERVICE,
102+
name: 'testService',
103+
environment: 'beta'
104+
});
105+
expect(keyAttributes.bind()).toEqual({
106+
Type: KeyAttributeType.SERVICE,
107+
Name: 'testService',
108+
Environment: 'beta'
109+
});
110+
});
111+
112+
test('returns correct attributes for resource type', () => {
113+
const keyAttributes = new KeyAttributes({
114+
type: KeyAttributeType.RESOURCE,
115+
resourceType: 'database'
116+
} as KeyAttributesProps);
117+
expect(keyAttributes.bind()).toEqual({
118+
Type: KeyAttributeType.RESOURCE,
119+
ResourceType: 'database'
120+
});
121+
});
122+
});
123+
124+
describe('static factory methods', () => {
125+
test('service creates correct configuration', () => {
126+
const keyAttributes = KeyAttributes.service({
127+
name: 'testService',
128+
environment: 'production'
129+
});
130+
expect(keyAttributes.bind()).toEqual({
131+
Type: KeyAttributeType.SERVICE,
132+
Name: 'testService',
133+
Environment: 'production'
134+
});
135+
});
136+
});
137+
});

0 commit comments

Comments
 (0)