Skip to content

Commit d9cd185

Browse files
committed
feat: add tests for s3 request & response templates
1 parent 51a024a commit d9cd185

File tree

3 files changed

+282
-9
lines changed

3 files changed

+282
-9
lines changed

lib/apiGateway/schema.js

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -254,15 +254,18 @@ const proxiesSchemas = {
254254
.valid('GetObject', 'PutObject', 'DeleteObject')
255255
.required(),
256256
bucket: stringOrRef.required(),
257-
// don't accept a key when requestParameters has a 'integration.request.path.object' property
258-
key: Joi.when('requestParameters', {
259-
is: requestParameters
260-
.keys({ 'integration.request.path.object': Joi.string().required() })
261-
.required(),
262-
then: Joi.forbidden(),
263-
otherwise: Joi.when('request', {
264-
is: request.required(),
265-
then: key,
257+
// key is
258+
// - optional when using a request mapping template
259+
// - forbidden if requestParameter has a 'integration.request.path.object' property
260+
// - otherwise required
261+
key: Joi.when('request', {
262+
is: request.required(),
263+
then: key,
264+
otherwise: Joi.when('requestParameters', {
265+
is: requestParameters
266+
.keys({ 'integration.request.path.object': Joi.string().required() })
267+
.required(),
268+
then: Joi.forbidden(),
266269
otherwise: key.required()
267270
})
268271
}),

lib/apiGateway/validate.test.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1283,6 +1283,20 @@ describe('#validateServiceProxies()', () => {
12831283
'integration.request.header.cache-control': "'public, max-age=31536000, immutable'"
12841284
})
12851285
})
1286+
1287+
it('should not throw if request template is defined and key is not defined', () => {
1288+
shouldSucceed('request', { template: {} }, 'key')
1289+
})
1290+
1291+
it('should not throw if request template is defined and key is defined', () => {
1292+
shouldSucceed('request', { template: {} })
1293+
})
1294+
1295+
it('should not throw if response template is defined', () => {
1296+
shouldSucceed('response', { template: {} })
1297+
shouldSucceed('response', { template: { clientError: 'not empty' } })
1298+
shouldSucceed('response', { template: { success: 'a', clientError: 'b', serverError: 'c' } })
1299+
})
12861300
})
12871301

12881302
describe('sns', () => {

lib/package/s3/compileMethodsToS3.test.js

Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1050,4 +1050,260 @@ describe('#compileMethodsToS3()', () => {
10501050
}
10511051
})
10521052
})
1053+
1054+
it('should create corresponding resources when s3 GetObject proxy is given with request template', () => {
1055+
serverlessApigatewayServiceProxy.validated = {
1056+
events: [
1057+
{
1058+
serviceName: 's3',
1059+
http: {
1060+
path: '/{item}',
1061+
method: 'get',
1062+
pathOverride: '{folder}/{item}',
1063+
bucket: {
1064+
Ref: 'MyBucket'
1065+
},
1066+
action: 'GetObject',
1067+
auth: { authorizationType: 'NONE' },
1068+
requestParameters: {
1069+
'integration.request.path.item': 'method.request.path.item'
1070+
},
1071+
request: {
1072+
template: {
1073+
'foo/bar': 'foo the bars!',
1074+
'bar/foo': 'bar the foos!'
1075+
}
1076+
}
1077+
}
1078+
}
1079+
]
1080+
}
1081+
serverlessApigatewayServiceProxy.apiGatewayRestApiLogicalId = 'ApiGatewayRestApi'
1082+
serverlessApigatewayServiceProxy.apiGatewayResources = {
1083+
'/{item}': {
1084+
name: 'Item',
1085+
resourceLogicalId: 'ApiGatewayPathOverrideS3'
1086+
}
1087+
}
1088+
1089+
serverlessApigatewayServiceProxy.compileMethodsToS3()
1090+
expect(serverless.service.provider.compiledCloudFormationTemplate.Resources).to.deep.equal({
1091+
ApiGatewayMethodItemGet: {
1092+
Type: 'AWS::ApiGateway::Method',
1093+
Properties: {
1094+
HttpMethod: 'GET',
1095+
AuthorizationType: 'NONE',
1096+
AuthorizationScopes: undefined,
1097+
AuthorizerId: undefined,
1098+
ApiKeyRequired: false,
1099+
ResourceId: { Ref: 'ApiGatewayPathOverrideS3' },
1100+
RestApiId: { Ref: 'ApiGatewayRestApi' },
1101+
RequestParameters: {
1102+
'method.request.path.item': true
1103+
},
1104+
Integration: {
1105+
Type: 'AWS',
1106+
IntegrationHttpMethod: 'GET',
1107+
Credentials: { 'Fn::GetAtt': ['ApigatewayToS3Role', 'Arn'] },
1108+
Uri: {
1109+
'Fn::Sub': [
1110+
'arn:${AWS::Partition}:apigateway:${AWS::Region}:s3:path/{bucket}/{folder}/{item}',
1111+
{}
1112+
]
1113+
},
1114+
PassthroughBehavior: 'NEVER',
1115+
RequestParameters: {
1116+
'integration.request.path.bucket': {
1117+
'Fn::Sub': [
1118+
"'${bucket}'",
1119+
{
1120+
bucket: {
1121+
Ref: 'MyBucket'
1122+
}
1123+
}
1124+
]
1125+
},
1126+
'integration.request.path.item': 'method.request.path.item'
1127+
},
1128+
RequestTemplates: {
1129+
'bar/foo': 'bar the foos!',
1130+
'foo/bar': 'foo the bars!'
1131+
},
1132+
IntegrationResponses: [
1133+
{
1134+
StatusCode: 400,
1135+
SelectionPattern: '4\\d{2}',
1136+
ResponseParameters: {},
1137+
ResponseTemplates: {}
1138+
},
1139+
{
1140+
StatusCode: 500,
1141+
SelectionPattern: '5\\d{2}',
1142+
ResponseParameters: {},
1143+
ResponseTemplates: {}
1144+
},
1145+
{
1146+
StatusCode: 200,
1147+
SelectionPattern: '2\\d{2}',
1148+
ResponseParameters: {
1149+
'method.response.header.Content-Type': 'integration.response.header.Content-Type',
1150+
'method.response.header.content-type': 'integration.response.header.content-type'
1151+
},
1152+
ResponseTemplates: {}
1153+
}
1154+
]
1155+
},
1156+
MethodResponses: [
1157+
{
1158+
ResponseParameters: {
1159+
'method.response.header.Content-Type': true,
1160+
'method.response.header.content-type': true
1161+
},
1162+
ResponseModels: {},
1163+
StatusCode: 200
1164+
},
1165+
{
1166+
ResponseParameters: {},
1167+
ResponseModels: {},
1168+
StatusCode: 400
1169+
},
1170+
{
1171+
ResponseParameters: {},
1172+
ResponseModels: {},
1173+
StatusCode: 500
1174+
}
1175+
]
1176+
}
1177+
}
1178+
})
1179+
})
1180+
1181+
it('should create corresponding resources when s3 GetObject proxy is given with response templates', () => {
1182+
serverlessApigatewayServiceProxy.validated = {
1183+
events: [
1184+
{
1185+
serviceName: 's3',
1186+
http: {
1187+
path: '/{myKey}',
1188+
method: 'get',
1189+
action: 'GetObject',
1190+
auth: { authorizationType: 'NONE' },
1191+
bucket: {
1192+
Ref: 'MyBucket'
1193+
},
1194+
key: {
1195+
pathParam: 'myKey'
1196+
},
1197+
response: {
1198+
template: {
1199+
success: '{"message": "all good"}',
1200+
clientError: '{"message": "Your fault"}',
1201+
serverError: '{"message": "My fault"}'
1202+
}
1203+
}
1204+
}
1205+
}
1206+
]
1207+
}
1208+
serverlessApigatewayServiceProxy.apiGatewayRestApiLogicalId = 'ApiGatewayRestApi'
1209+
serverlessApigatewayServiceProxy.apiGatewayResources = {
1210+
'/{myKey}': {
1211+
name: 'MyKey',
1212+
resourceLogicalId: 'ApiGatewayPathOverrideS3'
1213+
}
1214+
}
1215+
1216+
serverlessApigatewayServiceProxy.compileMethodsToS3()
1217+
expect(serverless.service.provider.compiledCloudFormationTemplate.Resources).to.deep.equal({
1218+
ApiGatewayMethodMyKeyGet: {
1219+
Type: 'AWS::ApiGateway::Method',
1220+
Properties: {
1221+
HttpMethod: 'GET',
1222+
AuthorizationType: 'NONE',
1223+
AuthorizationScopes: undefined,
1224+
AuthorizerId: undefined,
1225+
ApiKeyRequired: false,
1226+
ResourceId: { Ref: 'ApiGatewayPathOverrideS3' },
1227+
RestApiId: { Ref: 'ApiGatewayRestApi' },
1228+
RequestParameters: {
1229+
'method.request.path.myKey': true
1230+
},
1231+
Integration: {
1232+
Type: 'AWS',
1233+
IntegrationHttpMethod: 'GET',
1234+
Credentials: { 'Fn::GetAtt': ['ApigatewayToS3Role', 'Arn'] },
1235+
Uri: {
1236+
'Fn::Sub': [
1237+
'arn:${AWS::Partition}:apigateway:${AWS::Region}:s3:path/{bucket}/{object}',
1238+
{}
1239+
]
1240+
},
1241+
PassthroughBehavior: 'WHEN_NO_MATCH',
1242+
RequestParameters: {
1243+
'integration.request.path.bucket': {
1244+
'Fn::Sub': [
1245+
"'${bucket}'",
1246+
{
1247+
bucket: {
1248+
Ref: 'MyBucket'
1249+
}
1250+
}
1251+
]
1252+
},
1253+
'integration.request.path.object': 'method.request.path.myKey'
1254+
},
1255+
IntegrationResponses: [
1256+
{
1257+
StatusCode: 400,
1258+
SelectionPattern: '4\\d{2}',
1259+
ResponseParameters: {},
1260+
ResponseTemplates: {
1261+
'application/json': '{"message": "Your fault"}'
1262+
}
1263+
},
1264+
{
1265+
StatusCode: 500,
1266+
SelectionPattern: '5\\d{2}',
1267+
ResponseParameters: {},
1268+
ResponseTemplates: {
1269+
'application/json': '{"message": "My fault"}'
1270+
}
1271+
},
1272+
{
1273+
StatusCode: 200,
1274+
SelectionPattern: '2\\d{2}',
1275+
ResponseParameters: {
1276+
'method.response.header.Content-Type': 'integration.response.header.Content-Type',
1277+
'method.response.header.content-type': 'integration.response.header.content-type'
1278+
},
1279+
ResponseTemplates: {
1280+
'application/json': '{"message": "all good"}'
1281+
}
1282+
}
1283+
]
1284+
},
1285+
MethodResponses: [
1286+
{
1287+
ResponseParameters: {
1288+
'method.response.header.Content-Type': true,
1289+
'method.response.header.content-type': true
1290+
},
1291+
ResponseModels: {},
1292+
StatusCode: 200
1293+
},
1294+
{
1295+
ResponseParameters: {},
1296+
ResponseModels: {},
1297+
StatusCode: 400
1298+
},
1299+
{
1300+
ResponseParameters: {},
1301+
ResponseModels: {},
1302+
StatusCode: 500
1303+
}
1304+
]
1305+
}
1306+
}
1307+
})
1308+
})
10531309
})

0 commit comments

Comments
 (0)