Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions apps/sim/lib/api/contracts/tools/aws/lambda-create-alias.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,16 @@ const CreateAliasSchema = z.object({
.string()
.min(1, 'functionName is required')
.max(256, 'functionName cannot exceed 256 characters'),
aliasName: z.string().min(1, 'aliasName is required'),
aliasName: z
.string()
.min(1, 'aliasName is required')
.max(128, 'aliasName cannot exceed 128 characters')
.regex(
/^(?![0-9]+$)[a-zA-Z0-9-_]+$/,
'aliasName may only contain letters, numbers, hyphens, and underscores, and cannot be all digits'
),
aliasFunctionVersion: z.string().min(1, 'aliasFunctionVersion is required'),
description: z.string().optional(),
description: z.string().max(256, 'description cannot exceed 256 characters').optional(),
additionalVersionWeights: z
.record(
z.string().regex(/^[0-9]+$/, 'routing keys must be published version numbers'),
Expand All @@ -27,6 +34,10 @@ const CreateAliasSchema = z.object({
.min(0, 'a routing weight cannot be negative')
.max(1, 'a routing weight cannot exceed 1')
)
.refine(
(weights) => Object.keys(weights).length <= 1,
'additionalVersionWeights routes to a single second version, so it accepts at most one entry'
)
.optional(),
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,16 +54,26 @@ const CreateEventSourceMappingSchema = z
documentDbFullDocument: z.enum(['UpdateLookup', 'Default']).optional(),
amazonManagedKafkaConsumerGroupId: z.string().optional(),
selfManagedKafkaConsumerGroupId: z.string().optional(),
selfManagedKafkaBootstrapServers: z.array(z.string()).optional(),
selfManagedKafkaBootstrapServers: z
.array(z.string().min(1, 'a bootstrap server cannot be empty'))
.optional(),
})
.superRefine((value, ctx) => {
if (!value.eventSourceArn && !value.selfManagedKafkaBootstrapServers?.length) {
const hasBootstrapServers = Boolean(value.selfManagedKafkaBootstrapServers?.length)
if (!value.eventSourceArn && !hasBootstrapServers) {
ctx.addIssue({
code: 'custom',
path: ['eventSourceArn'],
message:
'An event source is required: provide eventSourceArn, or selfManagedKafkaBootstrapServers for a self-managed Kafka cluster',
})
} else if (value.eventSourceArn && hasBootstrapServers) {
Comment thread
waleedlatif1 marked this conversation as resolved.
ctx.addIssue({
code: 'custom',
path: ['selfManagedKafkaBootstrapServers'],
message:
'A mapping has one event source: provide eventSourceArn, or selfManagedKafkaBootstrapServers, not both',
})
}
if (value.startingPosition === 'AT_TIMESTAMP' && !value.startingPositionTimestamp) {
ctx.addIssue({
Expand Down
50 changes: 33 additions & 17 deletions apps/sim/lib/api/contracts/tools/aws/lambda-create-function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const CreateFunctionSchema = z
s3ObjectVersion: z.string().optional(),
imageUri: z.string().optional(),
sourceKmsKeyArn: z.string().optional(),
description: z.string().optional(),
description: z.string().max(256, 'description cannot exceed 256 characters').optional(),
functionTimeout: z.number().int().min(1).max(900).optional(),
memorySize: z.number().int().min(128).max(32768).optional(),
ephemeralStorageSize: z.number().int().min(512).max(10240).optional(),
Expand All @@ -48,36 +48,41 @@ const CreateFunctionSchema = z
logGroup: z.string().optional(),
})
.superRefine((value, ctx) => {
const hasAnyZipField = Boolean(
value.s3Bucket || value.s3Key || value.s3ObjectVersion || value.sourceKmsKeyArn
Comment thread
waleedlatif1 marked this conversation as resolved.
)
const hasS3 = Boolean(value.s3Bucket && value.s3Key)
if (!hasS3 && !value.imageUri) {
if (value.imageUri && hasAnyZipField) {
ctx.addIssue({
code: 'custom',
path: ['s3Bucket'],
path: ['imageUri'],
message:
'A code source is required: provide s3Bucket and s3Key for a .zip package, or imageUri for a container image',
'Provide either a .zip package (s3Bucket, s3Key, s3ObjectVersion, sourceKmsKeyArn) or imageUri, not both',
})
return
}
if (hasS3 && value.imageUri) {
if (!value.imageUri && !hasS3) {
ctx.addIssue({
code: 'custom',
path: ['imageUri'],
message: 'Provide either an S3 package or imageUri, not both',
path: hasAnyZipField ? ['s3Key'] : ['s3Bucket'],
message: hasAnyZipField
? 's3Bucket and s3Key must be provided together for a .zip package'
: 'A code source is required: provide s3Bucket and s3Key for a .zip package, or imageUri for a container image',
})
return
}
if (value.packageType === 'Image' && hasS3) {
if (value.imageUri && value.packageType !== 'Image') {
ctx.addIssue({
code: 'custom',
path: ['imageUri'],
message: 'packageType Image requires imageUri, not an S3 package',
path: ['packageType'],
message: 'packageType must be Image when imageUri is set',
})
}
if (value.packageType === 'Zip' && value.imageUri) {
if (hasS3 && value.packageType === 'Image') {
ctx.addIssue({
code: 'custom',
path: ['s3Bucket'],
message: 'packageType Zip requires an S3 package, not imageUri',
path: ['imageUri'],
message: 'packageType Image requires imageUri, not an S3 package',
})
}
if (hasS3) {
Expand All @@ -96,15 +101,26 @@ const CreateFunctionSchema = z
})
}
}
const hasSubnets = value.vpcSubnetIds !== undefined
const hasSecurityGroups = value.vpcSecurityGroupIds !== undefined
if (hasSubnets !== hasSecurityGroups) {
const subnetIds = value.vpcSubnetIds
const securityGroupIds = value.vpcSecurityGroupIds
if ((subnetIds === undefined) !== (securityGroupIds === undefined)) {
ctx.addIssue({
code: 'custom',
path: [hasSubnets ? 'vpcSecurityGroupIds' : 'vpcSubnetIds'],
path: [subnetIds === undefined ? 'vpcSubnetIds' : 'vpcSecurityGroupIds'],
message:
'vpcSubnetIds and vpcSecurityGroupIds must be supplied together: send both lists to attach a VPC, or both empty to detach',
})
} else if (
subnetIds !== undefined &&
securityGroupIds !== undefined &&
(subnetIds.length === 0) !== (securityGroupIds.length === 0)
) {
ctx.addIssue({
code: 'custom',
path: [subnetIds.length === 0 ? 'vpcSubnetIds' : 'vpcSecurityGroupIds'],
message:
'vpcSubnetIds and vpcSecurityGroupIds must both be empty to detach, or both be populated to attach',
})
}
})

Expand Down
9 changes: 8 additions & 1 deletion apps/sim/lib/api/contracts/tools/aws/lambda-delete-alias.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,14 @@ const DeleteAliasSchema = z.object({
.string()
.min(1, 'functionName is required')
.max(256, 'functionName cannot exceed 256 characters'),
aliasName: z.string().min(1, 'aliasName is required'),
aliasName: z
.string()
.min(1, 'aliasName is required')
.max(128, 'aliasName cannot exceed 128 characters')
.regex(
/^(?![0-9]+$)[a-zA-Z0-9-_]+$/,
'aliasName may only contain letters, numbers, hyphens, and underscores, and cannot be all digits'
),
})

const DeleteAliasResponseSchema = lambdaMessageResponseSchema
Expand Down
9 changes: 8 additions & 1 deletion apps/sim/lib/api/contracts/tools/aws/lambda-get-alias.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,14 @@ const GetAliasSchema = z.object({
.string()
.min(1, 'functionName is required')
.max(256, 'functionName cannot exceed 256 characters'),
aliasName: z.string().min(1, 'aliasName is required'),
aliasName: z
.string()
.min(1, 'aliasName is required')
.max(128, 'aliasName cannot exceed 128 characters')
.regex(
/^(?![0-9]+$)[a-zA-Z0-9-_]+$/,
'aliasName may only contain letters, numbers, hyphens, and underscores, and cannot be all digits'
),
})

const GetAliasResponseSchema = z.object({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,14 @@ import { defineRouteContract } from '@/lib/api/contracts/types'

const GetLayerVersionSchema = z.object({
...lambdaConnectionFields,
layerName: z.string().min(1, 'layerName is required'),
layerName: z
.string()
.min(1, 'layerName is required')
.max(140, 'layerName cannot exceed 140 characters')
.regex(
/^(arn:[a-zA-Z0-9-]+:lambda:[a-zA-Z0-9-]+:d{12}:layer:[a-zA-Z0-9-_]+)$|^[a-zA-Z0-9-_]+$/,
Comment thread
waleedlatif1 marked this conversation as resolved.
Outdated
Comment thread
waleedlatif1 marked this conversation as resolved.
Outdated
'layerName must be a layer name or a layer ARN'
),
versionNumber: z.number().int().min(1, 'versionNumber must be at least 1'),
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ import { defineRouteContract } from '@/lib/api/contracts/types'
const ListEventSourceMappingsSchema = z.object({
...lambdaConnectionFields,
...lambdaPaginationFields,
functionName: z.string().optional(),
functionName: z
.string()
.min(1, 'functionName cannot be empty')
.max(256, 'functionName cannot exceed 256 characters')
.optional(),
eventSourceArn: z.string().optional(),
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,14 @@ import { defineRouteContract } from '@/lib/api/contracts/types'
const ListLayerVersionsSchema = z.object({
...lambdaConnectionFields,
...lambdaSmallPaginationFields,
layerName: z.string().min(1, 'layerName is required'),
layerName: z
.string()
.min(1, 'layerName is required')
.max(140, 'layerName cannot exceed 140 characters')
.regex(
/^(arn:[a-zA-Z0-9-]+:lambda:[a-zA-Z0-9-]+:d{12}:layer:[a-zA-Z0-9-_]+)$|^[a-zA-Z0-9-_]+$/,
'layerName must be a layer name or a layer ARN'
),
compatibleRuntime: z.string().optional(),
compatibleArchitecture: z.enum(['x86_64', 'arm64']).optional(),
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const PublishVersionSchema = z.object({
.min(1, 'functionName is required')
.max(256, 'functionName cannot exceed 256 characters'),
codeSha256: z.string().optional(),
description: z.string().optional(),
description: z.string().max(256, 'description cannot exceed 256 characters').optional(),
revisionId: z.string().optional(),
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,14 @@ const RemovePermissionSchema = z.object({
.string()
.min(1, 'functionName is required')
.max(256, 'functionName cannot exceed 256 characters'),
statementId: z.string().min(1, 'statementId is required'),
statementId: z
.string()
.min(1, 'statementId is required')
.max(100, 'statementId cannot exceed 100 characters')
.regex(
/^[a-zA-Z0-9-_.]+$/,
'statementId may only contain letters, numbers, hyphens, underscores, and dots'
),
qualifier: z
.string()
.min(1, 'qualifier cannot be empty')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ import { defineRouteContract } from '@/lib/api/contracts/types'
const UntagResourceSchema = z.object({
...lambdaConnectionFields,
resourceArn: z.string().min(1, 'resourceArn is required'),
tagKeys: z.array(z.string()).min(1, 'tagKeys must contain at least one key'),
tagKeys: z
.array(z.string().min(1, 'a tag key cannot be empty'))
.min(1, 'tagKeys must contain at least one key'),
})

const UntagResourceResponseSchema = lambdaMessageResponseSchema
Expand Down
15 changes: 13 additions & 2 deletions apps/sim/lib/api/contracts/tools/aws/lambda-update-alias.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,16 @@ const UpdateAliasSchema = z.object({
.string()
.min(1, 'functionName is required')
.max(256, 'functionName cannot exceed 256 characters'),
aliasName: z.string().min(1, 'aliasName is required'),
aliasName: z
.string()
.min(1, 'aliasName is required')
.max(128, 'aliasName cannot exceed 128 characters')
.regex(
/^(?![0-9]+$)[a-zA-Z0-9-_]+$/,
'aliasName may only contain letters, numbers, hyphens, and underscores, and cannot be all digits'
),
aliasFunctionVersion: z.string().optional(),
description: z.string().optional(),
description: z.string().max(256, 'description cannot exceed 256 characters').optional(),
additionalVersionWeights: z
.record(
z.string().regex(/^[0-9]+$/, 'routing keys must be published version numbers'),
Expand All @@ -27,6 +34,10 @@ const UpdateAliasSchema = z.object({
.min(0, 'a routing weight cannot be negative')
.max(1, 'a routing weight cannot exceed 1')
)
.refine(
(weights) => Object.keys(weights).length <= 1,
'additionalVersionWeights routes to a single second version, so it accepts at most one entry'
)
.optional(),
revisionId: z.string().optional(),
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ import { defineRouteContract } from '@/lib/api/contracts/types'
const UpdateEventSourceMappingSchema = z.object({
...lambdaConnectionFields,
uuid: z.string().min(1, 'uuid is required'),
functionName: z.string().optional(),
functionName: z
.string()
.min(1, 'functionName cannot be empty')
.max(256, 'functionName cannot exceed 256 characters')
.optional(),
enabled: z.boolean().optional(),
batchSize: z.number().int().min(1).max(10000).optional(),
maximumBatchingWindowInSeconds: z.number().int().min(0).max(300).optional(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,21 +31,26 @@ const UpdateFunctionCodeSchema = z
revisionId: z.string().optional(),
})
.superRefine((value, ctx) => {
const hasAnyZipField = Boolean(
value.s3Bucket || value.s3Key || value.s3ObjectVersion || value.sourceKmsKeyArn
)
const hasS3 = Boolean(value.s3Bucket && value.s3Key)
if (!hasS3 && !value.imageUri) {
if (value.imageUri && hasAnyZipField) {
ctx.addIssue({
code: 'custom',
path: ['s3Bucket'],
path: ['imageUri'],
message:
'A code source is required: provide s3Bucket and s3Key for a .zip package, or imageUri for a container image',
'Provide either a .zip package (s3Bucket, s3Key, s3ObjectVersion, sourceKmsKeyArn) or imageUri, not both',
})
return
}
if (hasS3 && value.imageUri) {
if (!value.imageUri && !hasS3) {
ctx.addIssue({
code: 'custom',
path: ['imageUri'],
message: 'Provide either an S3 package or imageUri, not both',
path: hasAnyZipField ? ['s3Key'] : ['s3Bucket'],
message: hasAnyZipField
? 's3Bucket and s3Key must be provided together for a .zip package'
: 'A code source is required: provide s3Bucket and s3Key for a .zip package, or imageUri for a container image',
})
}
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const UpdateFunctionConfigurationSchema = z
role: z.string().optional(),
runtime: z.string().optional(),
handler: z.string().optional(),
description: z.string().optional(),
description: z.string().max(256, 'description cannot exceed 256 characters').optional(),
functionTimeout: z.number().int().min(1).max(900).optional(),
memorySize: z.number().int().min(128).max(32768).optional(),
ephemeralStorageSize: z.number().int().min(512).max(10240).optional(),
Expand All @@ -37,15 +37,26 @@ const UpdateFunctionConfigurationSchema = z
revisionId: z.string().optional(),
})
.superRefine((value, ctx) => {
const hasSubnets = value.vpcSubnetIds !== undefined
const hasSecurityGroups = value.vpcSecurityGroupIds !== undefined
if (hasSubnets !== hasSecurityGroups) {
const subnetIds = value.vpcSubnetIds
const securityGroupIds = value.vpcSecurityGroupIds
if ((subnetIds === undefined) !== (securityGroupIds === undefined)) {
ctx.addIssue({
code: 'custom',
path: [hasSubnets ? 'vpcSecurityGroupIds' : 'vpcSubnetIds'],
path: [subnetIds === undefined ? 'vpcSubnetIds' : 'vpcSecurityGroupIds'],
message:
'vpcSubnetIds and vpcSecurityGroupIds must be supplied together: send both lists to attach a VPC, or both empty to detach',
})
} else if (
subnetIds !== undefined &&
securityGroupIds !== undefined &&
(subnetIds.length === 0) !== (securityGroupIds.length === 0)
) {
ctx.addIssue({
code: 'custom',
path: [subnetIds.length === 0 ? 'vpcSubnetIds' : 'vpcSecurityGroupIds'],
message:
'vpcSubnetIds and vpcSecurityGroupIds must both be empty to detach, or both be populated to attach',
})
}
})

Expand Down
Loading
Loading