Skip to content

Commit 15a0209

Browse files
committed
refactor(validation-fixes): add partitionKey and template validation
1 parent 973a693 commit 15a0209

File tree

2 files changed

+247
-4
lines changed

2 files changed

+247
-4
lines changed

lib/apiGateway/schema.js

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,23 @@ const key = Joi.alternatives().try([
102102
)
103103
])
104104

105+
const partitionKey = Joi.alternatives().try([
106+
Joi.string(),
107+
Joi.object()
108+
.keys({
109+
pathParam: Joi.string(),
110+
queryStringParam: Joi.string(),
111+
bodyParam: Joi.string()
112+
})
113+
.xor('pathParam', 'queryStringParam', 'bodyParam')
114+
.error(
115+
customErrorBuilder(
116+
'object.xor',
117+
'key must contain "pathParam" or "queryStringParam" or "bodyParam" and only one'
118+
)
119+
)
120+
])
121+
105122
const stringOrGetAtt = (propertyName, attributeName) => {
106123
return Joi.alternatives().try([
107124
Joi.string(),
@@ -124,11 +141,15 @@ const stringOrGetAtt = (propertyName, attributeName) => {
124141
])
125142
}
126143

144+
const request = Joi.object({
145+
template: Joi.object().required()
146+
})
147+
127148
const allowedProxies = ['kinesis', 'sqs', 's3', 'sns']
128149

129150
const proxiesSchemas = {
130151
kinesis: Joi.object({
131-
kinesis: proxy.append({ streamName: stringOrRef.required() })
152+
kinesis: proxy.append({ streamName: stringOrRef.required(), partitionKey, request })
132153
}),
133154
s3: Joi.object({
134155
s3: proxy.append({
@@ -140,7 +161,7 @@ const proxiesSchemas = {
140161
})
141162
}),
142163
sns: Joi.object({
143-
sns: proxy.append({ topicName: stringOrGetAtt('topicName', 'TopicName').required() })
164+
sns: proxy.append({ topicName: stringOrGetAtt('topicName', 'TopicName').required(), request })
144165
}),
145166
sqs: Joi.object({
146167
sqs: proxy.append({

lib/apiGateway/validate.test.js

Lines changed: 224 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -678,7 +678,7 @@ describe('#validateServiceProxies()', () => {
678678
]
679679
}
680680

681-
serverlessApigatewayServiceProxy.validateServiceProxies()
681+
expect(() => serverlessApigatewayServiceProxy.validateServiceProxies()).to.not.throw()
682682
})
683683

684684
it('should not throw error if streamName is an AWS intrinsic Ref function', () => {
@@ -694,7 +694,172 @@ describe('#validateServiceProxies()', () => {
694694
]
695695
}
696696

697-
serverlessApigatewayServiceProxy.validateServiceProxies()
697+
expect(() => serverlessApigatewayServiceProxy.validateServiceProxies()).to.not.throw()
698+
})
699+
700+
it('should not throw error if partitionKey is a string', () => {
701+
serverlessApigatewayServiceProxy.serverless.service.custom = {
702+
apiGatewayServiceProxies: [
703+
{
704+
kinesis: {
705+
streamName: 'yourStream',
706+
path: 'kinesis',
707+
method: 'post',
708+
partitionKey: 'partitionKey'
709+
}
710+
}
711+
]
712+
}
713+
714+
expect(() => serverlessApigatewayServiceProxy.validateServiceProxies()).to.not.throw()
715+
})
716+
717+
it('should not throw error if partitionKey is a pathParam', () => {
718+
serverlessApigatewayServiceProxy.serverless.service.custom = {
719+
apiGatewayServiceProxies: [
720+
{
721+
kinesis: {
722+
streamName: 'yourStream',
723+
path: 'kinesis',
724+
method: 'post',
725+
partitionKey: { pathParam: 'partitionKey' }
726+
}
727+
}
728+
]
729+
}
730+
731+
expect(() => serverlessApigatewayServiceProxy.validateServiceProxies()).to.not.throw()
732+
})
733+
734+
it('should not throw error if partitionKey is a queryStringParam', () => {
735+
serverlessApigatewayServiceProxy.serverless.service.custom = {
736+
apiGatewayServiceProxies: [
737+
{
738+
kinesis: {
739+
streamName: 'yourStream',
740+
path: 'kinesis',
741+
method: 'post',
742+
partitionKey: { queryStringParam: 'partitionKey' }
743+
}
744+
}
745+
]
746+
}
747+
748+
expect(() => serverlessApigatewayServiceProxy.validateServiceProxies()).to.not.throw()
749+
})
750+
751+
it('should not throw error if partitionKey is a bodyParam', () => {
752+
serverlessApigatewayServiceProxy.serverless.service.custom = {
753+
apiGatewayServiceProxies: [
754+
{
755+
kinesis: {
756+
streamName: 'yourStream',
757+
path: 'kinesis',
758+
method: 'post',
759+
partitionKey: { bodyParam: 'partitionKey' }
760+
}
761+
}
762+
]
763+
}
764+
765+
expect(() => serverlessApigatewayServiceProxy.validateServiceProxies()).to.not.throw()
766+
})
767+
768+
it('should throw error if partitionKey is not a string or an object', () => {
769+
serverlessApigatewayServiceProxy.serverless.service.custom = {
770+
apiGatewayServiceProxies: [
771+
{
772+
kinesis: {
773+
streamName: 'yourStream',
774+
path: 'kinesis',
775+
method: 'post',
776+
partitionKey: []
777+
}
778+
}
779+
]
780+
}
781+
782+
expect(() => serverlessApigatewayServiceProxy.validateServiceProxies()).to.throw(
783+
serverless.classes.Error,
784+
'child "kinesis" fails because [child "partitionKey" fails because ["partitionKey" must be a string, "partitionKey" must be an object]]'
785+
)
786+
})
787+
788+
it('should throw error if partitionKey is not a valid param', () => {
789+
serverlessApigatewayServiceProxy.serverless.service.custom = {
790+
apiGatewayServiceProxies: [
791+
{
792+
kinesis: {
793+
streamName: 'yourStream',
794+
path: 'kinesis',
795+
method: 'post',
796+
partitionKey: { xxx: 'partitionKey' }
797+
}
798+
}
799+
]
800+
}
801+
802+
expect(() => serverlessApigatewayServiceProxy.validateServiceProxies()).to.throw(
803+
serverless.classes.Error,
804+
'child "kinesis" fails because [child "partitionKey" fails because ["partitionKey" must be a string, "xxx" is not allowed, "partitionKey" must contain at least one of [pathParam, queryStringParam, bodyParam]]]'
805+
)
806+
})
807+
808+
it('should throw error if request is missing the template property', () => {
809+
serverlessApigatewayServiceProxy.serverless.service.custom = {
810+
apiGatewayServiceProxies: [
811+
{
812+
kinesis: {
813+
streamName: 'yourStream',
814+
path: 'kinesis',
815+
method: 'post',
816+
request: { xxx: { 'application/json': 'mappingTemplate' } }
817+
}
818+
}
819+
]
820+
}
821+
822+
expect(() => serverlessApigatewayServiceProxy.validateServiceProxies()).to.throw(
823+
serverless.classes.Error,
824+
'child "kinesis" fails because [child "request" fails because [child "template" fails because ["template" is required]]]'
825+
)
826+
})
827+
828+
it('should throw error if request is not a mapping template object', () => {
829+
serverlessApigatewayServiceProxy.serverless.service.custom = {
830+
apiGatewayServiceProxies: [
831+
{
832+
kinesis: {
833+
streamName: 'yourStream',
834+
path: 'kinesis',
835+
method: 'post',
836+
request: { template: [] }
837+
}
838+
}
839+
]
840+
}
841+
842+
expect(() => serverlessApigatewayServiceProxy.validateServiceProxies()).to.throw(
843+
serverless.classes.Error,
844+
'child "kinesis" fails because [child "request" fails because [child "template" fails because ["template" must be an object]]]'
845+
)
846+
})
847+
848+
it('should not throw error if request is a mapping template object', () => {
849+
serverlessApigatewayServiceProxy.serverless.service.custom = {
850+
apiGatewayServiceProxies: [
851+
{
852+
kinesis: {
853+
streamName: 'yourStream',
854+
path: 'kinesis',
855+
method: 'post',
856+
request: { template: { 'application/json': 'mappingTemplate' } }
857+
}
858+
}
859+
]
860+
}
861+
862+
expect(() => serverlessApigatewayServiceProxy.validateServiceProxies()).to.not.throw()
698863
})
699864
})
700865

@@ -959,6 +1124,63 @@ describe('#validateServiceProxies()', () => {
9591124

9601125
expect(() => serverlessApigatewayServiceProxy.validateServiceProxies()).to.not.throw()
9611126
})
1127+
1128+
it('should throw error if request is missing the template property', () => {
1129+
serverlessApigatewayServiceProxy.serverless.service.custom = {
1130+
apiGatewayServiceProxies: [
1131+
{
1132+
sns: {
1133+
topicName: 'topicName',
1134+
path: 'sns',
1135+
method: 'post',
1136+
request: { xxx: { 'application/json': 'mappingTemplate' } }
1137+
}
1138+
}
1139+
]
1140+
}
1141+
1142+
expect(() => serverlessApigatewayServiceProxy.validateServiceProxies()).to.throw(
1143+
serverless.classes.Error,
1144+
'child "sns" fails because [child "request" fails because [child "template" fails because ["template" is required]]]'
1145+
)
1146+
})
1147+
1148+
it('should throw error if request is not a mapping template object', () => {
1149+
serverlessApigatewayServiceProxy.serverless.service.custom = {
1150+
apiGatewayServiceProxies: [
1151+
{
1152+
sns: {
1153+
topicName: 'topicName',
1154+
path: 'sns',
1155+
method: 'post',
1156+
request: { template: [] }
1157+
}
1158+
}
1159+
]
1160+
}
1161+
1162+
expect(() => serverlessApigatewayServiceProxy.validateServiceProxies()).to.throw(
1163+
serverless.classes.Error,
1164+
'child "sns" fails because [child "request" fails because [child "template" fails because ["template" must be an object]]]'
1165+
)
1166+
})
1167+
1168+
it('should not throw error if request is a mapping template object', () => {
1169+
serverlessApigatewayServiceProxy.serverless.service.custom = {
1170+
apiGatewayServiceProxies: [
1171+
{
1172+
sns: {
1173+
topicName: 'topicName',
1174+
path: 'sns',
1175+
method: 'post',
1176+
request: { template: { 'application/json': 'mappingTemplate' } }
1177+
}
1178+
}
1179+
]
1180+
}
1181+
1182+
expect(() => serverlessApigatewayServiceProxy.validateServiceProxies()).to.not.throw()
1183+
})
9621184
})
9631185

9641186
describe('sqs', () => {

0 commit comments

Comments
 (0)