From 9417d0a52431bf699447d9369c23896eeb48a429 Mon Sep 17 00:00:00 2001 From: Josh Kellendonk Date: Tue, 2 Mar 2021 10:40:51 -0700 Subject: [PATCH 01/19] feat: add missing route match features --- packages/@aws-cdk/aws-appmesh/README.md | 25 +++ .../@aws-cdk/aws-appmesh/lib/route-spec.ts | 177 ++++++++++++++++++ .../aws-appmesh/test/integ.mesh.expected.json | 100 ++++++++-- .../@aws-cdk/aws-appmesh/test/integ.mesh.ts | 34 ++++ .../@aws-cdk/aws-appmesh/test/test.route.ts | 175 +++++++++++++++++ 5 files changed, 499 insertions(+), 12 deletions(-) diff --git a/packages/@aws-cdk/aws-appmesh/README.md b/packages/@aws-cdk/aws-appmesh/README.md index c400cbb0af05d..5857a9447083a 100644 --- a/packages/@aws-cdk/aws-appmesh/README.md +++ b/packages/@aws-cdk/aws-appmesh/README.md @@ -298,6 +298,31 @@ router.addRoute('route-http', { }); ``` +Add an HTTP2 route that matches based on method, scheme and header: + +```ts +router.addRoute('route-http', { + routeSpec: appmesh.RouteSpec.http2({ + weightedTargets: [ + { + virtualNode: node, + }, + ], + match: { + prefixPath: '/path-to-app', + method: appmesh.HttpRouteMatchMethod.GET, + scheme: appmesh.HttpRouteMatchScheme.HTTPS, + headers: [ + { + name: 'Content-Type', + match: appmesh.HeaderMatchMethod.exact('text/html'), + }, + ] + }, + }), +}); +``` + Add a single route with multiple targets and split traffic 50/50 ```ts diff --git a/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts b/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts index 74b16976b69ca..9d5b26bb82118 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts @@ -34,6 +34,170 @@ export interface HttpRouteMatch { * and you want the route to match requests to my-service.local/metrics, your prefix should be /metrics. */ readonly prefixPath: string; + + /** + * Specifies the client request headers to match on + * @default - do not match on headers + */ + readonly headers?: HttpRouteHeader[]; + + /** + * The client request method to match on. Specify only one. + * @default - do not match on request method + */ + readonly method?: HttpRouteMatchMethod; + + /** + * The client request scheme to match on. Specify only one. Applicable only for HTTP2 routes. + * @default - do not match on HTTP2 scheme + */ + readonly scheme?: HttpRouteMatchScheme; +} + +/** + * Supported request matching methods. + */ +export enum HttpRouteMatchMethod { + /** + * GET request + */ + GET = 'GET', + /** + * HEAD request + */ + HEAD = 'HEAD', + /** + * POST request + */ + POST = 'POST', + /** + * PUT request + */ + PUT = 'PUT', + /** + * DELETE request + */ + DELETE = 'DELETE', + /** + * CONNECT request + */ + CONNECT = 'CONNECT', + /** + * OPTIONS request + */ + OPTIONS = 'OPTIONS', + /** + * TRACE request + */ + TRACE = 'TRACE', + /** + * PATCH request + */ + PATCH = 'PATCH', +} + +/** + * Supported :scheme options for HTTP2 + */ +export enum HttpRouteMatchScheme { + /** + * Match HTTP requests + */ + HTTP = 'http', + + /** + * Match HTTPS requests + */ + HTTPS = 'https', +} + +/** + * An object that represents the HTTP header in the request to match. + */ +export interface HttpRouteHeader { + /** + * Specify `true` to match anything except the match criteria. + * @default false + */ + readonly invert?: boolean; + + /** + * The method to use to match the header. + * @default - no match method + */ + readonly match?: IHeaderMatchMethod; + + /** + * A name for the HTTP header in the client request that will be matched on. + * @example 'content-type' + */ + readonly name: string; +} + +/** + * Match method. + */ +export interface IHeaderMatchMethod { + /** + * Produce the match method. + */ + renderMatch(): CfnRoute.HeaderMatchMethodProperty; +} +/** + * Used to generate header matching methods. + */ +export abstract class HeaderMatchMethod { + /** + * The value sent by the client must match the specified value exactly. + */ + static exact(exact: string): IHeaderMatchMethod { + return { + renderMatch: () => ({ exact }), + }; + } + + /** + * The value sent by the client must begin with the specified characters. + */ + static prefix(prefix: string): IHeaderMatchMethod { + return { + renderMatch: () => ({ prefix }), + }; + } + + /** + * The value sent by the client must end with the specified characters. + */ + static suffix(suffix: string): IHeaderMatchMethod { + return { + renderMatch: () => ({ suffix }), + }; + } + + /** + * The value sent by the client must include the specified characters. + */ + static regex(regex: string): IHeaderMatchMethod { + return { + renderMatch: () => ({ regex }), + }; + } + + /** + * Match on a numeric range of values. + * @param start Match on values starting at and including this value + * @param end Match on values up to but not including this value + */ + static range(start: number, end: number): IHeaderMatchMethod { + return { + renderMatch: () => ({ + range: { + start, + end, + }, + }), + }; + } } /** @@ -216,12 +380,25 @@ class HttpRouteSpec extends RouteSpec { if (prefixPath[0] != '/') { throw new Error(`Prefix Path must start with \'/\', got: ${prefixPath}`); } + + let headers: CfnRoute.HttpRouteHeaderProperty[] | undefined; + if (this.match?.headers) { + headers = this.match.headers.map(header => ({ + name: header.name, + invert: header.invert, + match: header.match?.renderMatch(), + })); + } + const httpConfig: CfnRoute.HttpRouteProperty = { action: { weightedTargets: renderWeightedTargets(this.weightedTargets), }, match: { prefix: prefixPath, + headers: headers, + method: this.match?.method, + scheme: this.match?.scheme, }, timeout: renderTimeout(this.timeout), }; diff --git a/packages/@aws-cdk/aws-appmesh/test/integ.mesh.expected.json b/packages/@aws-cdk/aws-appmesh/test/integ.mesh.expected.json index 5f4a9ca206725..d3b9961d72ba4 100644 --- a/packages/@aws-cdk/aws-appmesh/test/integ.mesh.expected.json +++ b/packages/@aws-cdk/aws-appmesh/test/integ.mesh.expected.json @@ -497,7 +497,6 @@ "MeshName" ] }, - "RouteName": "route-1", "Spec": { "HttpRoute": { "Action": { @@ -533,7 +532,8 @@ "meshrouter81B8087E", "VirtualRouterName" ] - } + }, + "RouteName": "route-1" } }, "meshrouterroute2486D9DEF": { @@ -545,7 +545,6 @@ "MeshName" ] }, - "RouteName": "route-2", "Spec": { "HttpRoute": { "Action": { @@ -581,7 +580,8 @@ "meshrouter81B8087E", "VirtualRouterName" ] - } + }, + "RouteName": "route-2" } }, "meshrouterroute3BD0FA22F": { @@ -593,7 +593,6 @@ "MeshName" ] }, - "RouteName": "route-3", "Spec": { "TcpRoute": { "Action": { @@ -622,7 +621,84 @@ "meshrouter81B8087E", "VirtualRouterName" ] - } + }, + "RouteName": "route-3" + } + }, + "meshrouterroute417D3F5B5": { + "Type": "AWS::AppMesh::Route", + "Properties": { + "MeshName": { + "Fn::GetAtt": [ + "meshACDFE68E", + "MeshName" + ] + }, + "Spec": { + "Http2Route": { + "Action": { + "WeightedTargets": [ + { + "VirtualNode": { + "Fn::GetAtt": [ + "meshnode3D2A19CF2", + "VirtualNodeName" + ] + }, + "Weight": 1 + } + ] + }, + "Match": { + "Headers": [ + { + "Match": { + "Exact": "text/html" + }, + "Name": "Content-Type" + }, + { + "Match": { + "Prefix": "text/" + }, + "Name": "Content-Type" + }, + { + "Invert": true, + "Match": { + "Suffix": "/plain" + }, + "Name": "Content-Type" + }, + { + "Match": { + "Regex": ".*" + }, + "Name": "Content-Type" + }, + { + "Match": { + "Range": { + "End": 5, + "Start": 1 + } + }, + "Name": "Max-Forwards" + } + ], + "Method": "GET", + "Prefix": "/", + "Scheme": "https" + } + } + }, + "VirtualRouterName": { + "Fn::GetAtt": [ + "meshrouter81B8087E", + "VirtualRouterName" + ] + }, + "RouteName": "route-4" } }, "meshnode726C787D": { @@ -832,7 +908,6 @@ "meshgateway1gateway1routehttpE8D6F433": { "Type": "AWS::AppMesh::GatewayRoute", "Properties": { - "GatewayRouteName": "meshstackmeshgateway1gateway1routehttpBA921D42", "MeshName": { "Fn::GetAtt": [ "meshACDFE68E", @@ -863,13 +938,13 @@ "meshgateway1B02387E8", "VirtualGatewayName" ] - } + }, + "GatewayRouteName": "meshstackmeshgateway1gateway1routehttpBA921D42" } }, "meshgateway1gateway1routehttp2FD69C306": { "Type": "AWS::AppMesh::GatewayRoute", "Properties": { - "GatewayRouteName": "meshstackmeshgateway1gateway1routehttp255781963", "MeshName": { "Fn::GetAtt": [ "meshACDFE68E", @@ -900,13 +975,13 @@ "meshgateway1B02387E8", "VirtualGatewayName" ] - } + }, + "GatewayRouteName": "meshstackmeshgateway1gateway1routehttp255781963" } }, "meshgateway1gateway1routegrpc76486062": { "Type": "AWS::AppMesh::GatewayRoute", "Properties": { - "GatewayRouteName": "meshstackmeshgateway1gateway1routegrpcCD4D891D", "MeshName": { "Fn::GetAtt": [ "meshACDFE68E", @@ -942,7 +1017,8 @@ "meshgateway1B02387E8", "VirtualGatewayName" ] - } + }, + "GatewayRouteName": "meshstackmeshgateway1gateway1routegrpcCD4D891D" } }, "service6D174F83": { diff --git a/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts b/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts index 90e54586f7f51..102896ca7e2c3 100644 --- a/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts +++ b/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts @@ -140,6 +140,40 @@ router.addRoute('route-3', { }), }); +router.addRoute('route-4', { + routeSpec: appmesh.RouteSpec.http2({ + weightedTargets: [{ virtualNode: node3 }], + match: { + prefixPath: '/', + method: appmesh.HttpRouteMatchMethod.GET, + scheme: appmesh.HttpRouteMatchScheme.HTTPS, + headers: [ + { + name: 'Content-Type', + match: appmesh.HeaderMatchMethod.exact('text/html'), + }, + { + name: 'Content-Type', + match: appmesh.HeaderMatchMethod.prefix('text/'), + }, + { + name: 'Content-Type', + invert: true, + match: appmesh.HeaderMatchMethod.suffix('/plain'), + }, + { + name: 'Content-Type', + match: appmesh.HeaderMatchMethod.regex('.*'), + }, + { + name: 'Max-Forwards', + match: appmesh.HeaderMatchMethod.range(1, 5), + }, + ], + }, + }), +}); + const gateway = mesh.addVirtualGateway('gateway1', { accessLog: appmesh.AccessLog.fromFilePath('/dev/stdout'), virtualGatewayName: 'gateway1', diff --git a/packages/@aws-cdk/aws-appmesh/test/test.route.ts b/packages/@aws-cdk/aws-appmesh/test/test.route.ts index cb5c92e6464cf..1eddfa1f020de 100644 --- a/packages/@aws-cdk/aws-appmesh/test/test.route.ts +++ b/packages/@aws-cdk/aws-appmesh/test/test.route.ts @@ -282,6 +282,181 @@ export = { })); test.done(); }, + + 'should match routes based on headers'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const mesh = new appmesh.Mesh(stack, 'mesh', { + meshName: 'test-mesh', + }); + + const router = new appmesh.VirtualRouter(stack, 'router', { + mesh, + }); + + const virtualNode = mesh.addVirtualNode('test-node', { + serviceDiscovery: appmesh.ServiceDiscovery.dns('test'), + listeners: [appmesh.VirtualNodeListener.http()], + }); + + // WHEN + router.addRoute('route', { + routeSpec: appmesh.RouteSpec.http2({ + weightedTargets: [{ virtualNode }], + match: { + prefixPath: '/', + headers: [ + { + name: 'Content-Type', + match: appmesh.HeaderMatchMethod.exact('text/html'), + }, + { + name: 'Content-Type', + match: appmesh.HeaderMatchMethod.prefix('text/'), + }, + { + name: 'Content-Type', + invert: true, + match: appmesh.HeaderMatchMethod.suffix('/plain'), + }, + { + name: 'Content-Type', + match: appmesh.HeaderMatchMethod.regex('.*'), + }, + { + name: 'Max-Forwards', + match: appmesh.HeaderMatchMethod.range(1, 5), + }, + ], + }, + }), + }); + + // THEN + expect(stack).to(haveResourceLike('AWS::AppMesh::Route', { + Spec: { + Http2Route: { + Match: { + Prefix: '/', + Headers: [ + { + Name: 'Content-Type', + Match: { Exact: 'text/html' }, + }, + { + Name: 'Content-Type', + Match: { Prefix: 'text/' }, + }, + { + Name: 'Content-Type', + Invert: true, + Match: { Suffix: '/plain' }, + }, + { + Name: 'Content-Type', + Match: { Regex: '.*' }, + }, + { + Name: 'Max-Forwards', + Match: { + Range: { + End: 5, + Start: 1, + }, + }, + }, + ], + }, + }, + }, + })); + + test.done(); + }, + + 'should match routes based on method'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const mesh = new appmesh.Mesh(stack, 'mesh', { + meshName: 'test-mesh', + }); + + const router = new appmesh.VirtualRouter(stack, 'router', { + mesh, + }); + + const virtualNode = mesh.addVirtualNode('test-node', { + serviceDiscovery: appmesh.ServiceDiscovery.dns('test'), + listeners: [appmesh.VirtualNodeListener.http()], + }); + + // WHEN + router.addRoute('route', { + routeSpec: appmesh.RouteSpec.http2({ + weightedTargets: [{ virtualNode }], + match: { + prefixPath: '/', + method: appmesh.HttpRouteMatchMethod.GET, + }, + }), + }); + + // THEN + expect(stack).to(haveResourceLike('AWS::AppMesh::Route', { + Spec: { + Http2Route: { + Match: { + Prefix: '/', + Method: 'GET', + }, + }, + }, + })); + + test.done(); + }, + + 'should match routes based on scheme'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const mesh = new appmesh.Mesh(stack, 'mesh', { + meshName: 'test-mesh', + }); + + const router = new appmesh.VirtualRouter(stack, 'router', { + mesh, + }); + + const virtualNode = mesh.addVirtualNode('test-node', { + serviceDiscovery: appmesh.ServiceDiscovery.dns('test'), + listeners: [appmesh.VirtualNodeListener.http()], + }); + + // WHEN + router.addRoute('route', { + routeSpec: appmesh.RouteSpec.http2({ + weightedTargets: [{ virtualNode }], + match: { + prefixPath: '/', + scheme: appmesh.HttpRouteMatchScheme.HTTP, + }, + }), + }); + + // THEN + expect(stack).to(haveResourceLike('AWS::AppMesh::Route', { + Spec: { + Http2Route: { + Match: { + Prefix: '/', + Scheme: 'http', + }, + }, + }, + })); + + test.done(); + }, }, 'Can import Routes using an ARN'(test: Test) { From 40cd42a94a308004e33db19bd3c6e1635b28a426 Mon Sep 17 00:00:00 2001 From: Josh Kellendonk Date: Tue, 2 Mar 2021 10:59:20 -0700 Subject: [PATCH 02/19] fix: use a more sensible example --- packages/@aws-cdk/aws-appmesh/README.md | 6 +++--- .../aws-appmesh/test/integ.mesh.expected.json | 8 ++++---- packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts | 8 ++++---- packages/@aws-cdk/aws-appmesh/test/test.route.ts | 12 ++++++------ 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/packages/@aws-cdk/aws-appmesh/README.md b/packages/@aws-cdk/aws-appmesh/README.md index 5857a9447083a..a916d320318d2 100644 --- a/packages/@aws-cdk/aws-appmesh/README.md +++ b/packages/@aws-cdk/aws-appmesh/README.md @@ -301,7 +301,7 @@ router.addRoute('route-http', { Add an HTTP2 route that matches based on method, scheme and header: ```ts -router.addRoute('route-http', { +router.addRoute('route-http2', { routeSpec: appmesh.RouteSpec.http2({ weightedTargets: [ { @@ -310,12 +310,12 @@ router.addRoute('route-http', { ], match: { prefixPath: '/path-to-app', - method: appmesh.HttpRouteMatchMethod.GET, + method: appmesh.HttpRouteMatchMethod.POST, scheme: appmesh.HttpRouteMatchScheme.HTTPS, headers: [ { name: 'Content-Type', - match: appmesh.HeaderMatchMethod.exact('text/html'), + match: appmesh.HeaderMatchMethod.exact('application/json'), }, ] }, diff --git a/packages/@aws-cdk/aws-appmesh/test/integ.mesh.expected.json b/packages/@aws-cdk/aws-appmesh/test/integ.mesh.expected.json index d3b9961d72ba4..70be21c0443c0 100644 --- a/packages/@aws-cdk/aws-appmesh/test/integ.mesh.expected.json +++ b/packages/@aws-cdk/aws-appmesh/test/integ.mesh.expected.json @@ -653,20 +653,20 @@ "Headers": [ { "Match": { - "Exact": "text/html" + "Exact": "application/json" }, "Name": "Content-Type" }, { "Match": { - "Prefix": "text/" + "Prefix": "application/" }, "Name": "Content-Type" }, { "Invert": true, "Match": { - "Suffix": "/plain" + "Suffix": "/x-www-form-urlencoded" }, "Name": "Content-Type" }, @@ -686,7 +686,7 @@ "Name": "Max-Forwards" } ], - "Method": "GET", + "Method": "POST", "Prefix": "/", "Scheme": "https" } diff --git a/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts b/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts index 102896ca7e2c3..aa1645e07f4f3 100644 --- a/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts +++ b/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts @@ -145,21 +145,21 @@ router.addRoute('route-4', { weightedTargets: [{ virtualNode: node3 }], match: { prefixPath: '/', - method: appmesh.HttpRouteMatchMethod.GET, + method: appmesh.HttpRouteMatchMethod.POST, scheme: appmesh.HttpRouteMatchScheme.HTTPS, headers: [ { name: 'Content-Type', - match: appmesh.HeaderMatchMethod.exact('text/html'), + match: appmesh.HeaderMatchMethod.exact('application/json'), }, { name: 'Content-Type', - match: appmesh.HeaderMatchMethod.prefix('text/'), + match: appmesh.HeaderMatchMethod.prefix('application/'), }, { name: 'Content-Type', invert: true, - match: appmesh.HeaderMatchMethod.suffix('/plain'), + match: appmesh.HeaderMatchMethod.suffix('/x-www-form-urlencoded'), }, { name: 'Content-Type', diff --git a/packages/@aws-cdk/aws-appmesh/test/test.route.ts b/packages/@aws-cdk/aws-appmesh/test/test.route.ts index 1eddfa1f020de..07d88f3a13f3f 100644 --- a/packages/@aws-cdk/aws-appmesh/test/test.route.ts +++ b/packages/@aws-cdk/aws-appmesh/test/test.route.ts @@ -308,16 +308,16 @@ export = { headers: [ { name: 'Content-Type', - match: appmesh.HeaderMatchMethod.exact('text/html'), + match: appmesh.HeaderMatchMethod.exact('application/json'), }, { name: 'Content-Type', - match: appmesh.HeaderMatchMethod.prefix('text/'), + match: appmesh.HeaderMatchMethod.prefix('application/'), }, { name: 'Content-Type', invert: true, - match: appmesh.HeaderMatchMethod.suffix('/plain'), + match: appmesh.HeaderMatchMethod.suffix('/json+foobar'), }, { name: 'Content-Type', @@ -341,16 +341,16 @@ export = { Headers: [ { Name: 'Content-Type', - Match: { Exact: 'text/html' }, + Match: { Exact: 'application/json' }, }, { Name: 'Content-Type', - Match: { Prefix: 'text/' }, + Match: { Prefix: 'application/' }, }, { Name: 'Content-Type', Invert: true, - Match: { Suffix: '/plain' }, + Match: { Suffix: '/json+foobar' }, }, { Name: 'Content-Type', From 612de8dbbdd5564b62d4b223e189581f7e777c97 Mon Sep 17 00:00:00 2001 From: Josh Kellendonk Date: Tue, 2 Mar 2021 10:59:41 -0700 Subject: [PATCH 03/19] refactor: renderMatch -> renderMatchMethod --- packages/@aws-cdk/aws-appmesh/lib/route-spec.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts b/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts index 9d5b26bb82118..3b343affe21af 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts @@ -141,8 +141,9 @@ export interface IHeaderMatchMethod { /** * Produce the match method. */ - renderMatch(): CfnRoute.HeaderMatchMethodProperty; + renderMatchMethod(): CfnRoute.HeaderMatchMethodProperty; } + /** * Used to generate header matching methods. */ @@ -152,7 +153,7 @@ export abstract class HeaderMatchMethod { */ static exact(exact: string): IHeaderMatchMethod { return { - renderMatch: () => ({ exact }), + renderMatchMethod: () => ({ exact }), }; } @@ -161,7 +162,7 @@ export abstract class HeaderMatchMethod { */ static prefix(prefix: string): IHeaderMatchMethod { return { - renderMatch: () => ({ prefix }), + renderMatchMethod: () => ({ prefix }), }; } @@ -170,7 +171,7 @@ export abstract class HeaderMatchMethod { */ static suffix(suffix: string): IHeaderMatchMethod { return { - renderMatch: () => ({ suffix }), + renderMatchMethod: () => ({ suffix }), }; } @@ -179,7 +180,7 @@ export abstract class HeaderMatchMethod { */ static regex(regex: string): IHeaderMatchMethod { return { - renderMatch: () => ({ regex }), + renderMatchMethod: () => ({ regex }), }; } @@ -190,7 +191,7 @@ export abstract class HeaderMatchMethod { */ static range(start: number, end: number): IHeaderMatchMethod { return { - renderMatch: () => ({ + renderMatchMethod: () => ({ range: { start, end, @@ -386,7 +387,7 @@ class HttpRouteSpec extends RouteSpec { headers = this.match.headers.map(header => ({ name: header.name, invert: header.invert, - match: header.match?.renderMatch(), + match: header.match?.renderMatchMethod(), })); } From d930e921a80dbb9c2971ca0be4b8f0618909beec Mon Sep 17 00:00:00 2001 From: Josh Kellendonk Date: Tue, 2 Mar 2021 11:25:31 -0700 Subject: [PATCH 04/19] feat: add routespec priority --- .../@aws-cdk/aws-appmesh/lib/route-spec.ts | 48 +++++++++++- packages/@aws-cdk/aws-appmesh/lib/route.ts | 1 + .../aws-appmesh/test/integ.mesh.expected.json | 39 ++++++++++ .../@aws-cdk/aws-appmesh/test/integ.mesh.ts | 7 ++ .../@aws-cdk/aws-appmesh/test/test.route.ts | 74 +++++++++++++++++++ 5 files changed, 166 insertions(+), 3 deletions(-) diff --git a/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts b/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts index 3b343affe21af..f7e029ece9dc5 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts @@ -211,10 +211,23 @@ export interface GrpcRouteMatch { readonly serviceName: string; } +/** + * Base options for all route specs. + */ +export interface RouteSpecOptionsBase { + /** + * The priority for the route. Routes are matched based on the specified + * value, where 0 is the highest priority. + * + * @default - no particular priority + */ + readonly priority?: number; +} + /** * Properties specific for HTTP Based Routes */ -export interface HttpRouteSpecOptions { +export interface HttpRouteSpecOptions extends RouteSpecOptionsBase { /** * The criterion for determining a request match for this Route * @@ -238,7 +251,7 @@ export interface HttpRouteSpecOptions { /** * Properties specific for a TCP Based Routes */ -export interface TcpRouteSpecOptions { +export interface TcpRouteSpecOptions extends RouteSpecOptionsBase { /** * List of targets that traffic is routed to when a request matches the route */ @@ -255,7 +268,7 @@ export interface TcpRouteSpecOptions { /** * Properties specific for a GRPC Based Routes */ -export interface GrpcRouteSpecOptions { +export interface GrpcRouteSpecOptions extends RouteSpecOptionsBase { /** * The criterion for determining a request match for this Route */ @@ -305,6 +318,14 @@ export interface RouteSpecConfig { * @default - no tcp spec */ readonly tcpRouteSpec?: CfnRoute.TcpRouteProperty; + + /** + * The priority for the route. Routes are matched based on the specified + * value, where 0 is the highest priority. + * + * @default - no particular priority + */ + readonly priority?: number; } /** @@ -348,6 +369,11 @@ export abstract class RouteSpec { } class HttpRouteSpec extends RouteSpec { + /** + * The priority for the route. + */ + public readonly priority?: number; + /** * Type of route you are creating */ @@ -374,6 +400,7 @@ class HttpRouteSpec extends RouteSpec { this.match = props.match; this.weightedTargets = props.weightedTargets; this.timeout = props.timeout; + this.priority = props.priority; } public bind(_scope: Construct): RouteSpecConfig { @@ -404,6 +431,7 @@ class HttpRouteSpec extends RouteSpec { timeout: renderTimeout(this.timeout), }; return { + priority: this.priority, httpRouteSpec: this.protocol === Protocol.HTTP ? httpConfig : undefined, http2RouteSpec: this.protocol === Protocol.HTTP2 ? httpConfig : undefined, }; @@ -411,6 +439,11 @@ class HttpRouteSpec extends RouteSpec { } class TcpRouteSpec extends RouteSpec { + /** + * The priority for the route. + */ + public readonly priority?: number; + /* * List of targets that traffic is routed to when a request matches the route */ @@ -425,10 +458,12 @@ class TcpRouteSpec extends RouteSpec { super(); this.weightedTargets = props.weightedTargets; this.timeout = props.timeout; + this.priority = props.priority; } public bind(_scope: Construct): RouteSpecConfig { return { + priority: this.priority, tcpRouteSpec: { action: { weightedTargets: renderWeightedTargets(this.weightedTargets), @@ -440,6 +475,11 @@ class TcpRouteSpec extends RouteSpec { } class GrpcRouteSpec extends RouteSpec { + /** + * The priority for the route. + */ + public readonly priority?: number; + public readonly weightedTargets: WeightedTarget[]; public readonly match: GrpcRouteMatch; public readonly timeout?: GrpcTimeout; @@ -449,10 +489,12 @@ class GrpcRouteSpec extends RouteSpec { this.weightedTargets = props.weightedTargets; this.match = props.match; this.timeout = props.timeout; + this.priority = props.priority; } public bind(_scope: Construct): RouteSpecConfig { return { + priority: this.priority, grpcRouteSpec: { action: { weightedTargets: renderWeightedTargets(this.weightedTargets), diff --git a/packages/@aws-cdk/aws-appmesh/lib/route.ts b/packages/@aws-cdk/aws-appmesh/lib/route.ts index 7b9bd2aeb94d2..7800e3e08e53f 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/route.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/route.ts @@ -126,6 +126,7 @@ export class Route extends cdk.Resource implements IRoute { httpRoute: spec.httpRouteSpec, http2Route: spec.http2RouteSpec, grpcRoute: spec.grpcRouteSpec, + priority: spec.priority, }, }); diff --git a/packages/@aws-cdk/aws-appmesh/test/integ.mesh.expected.json b/packages/@aws-cdk/aws-appmesh/test/integ.mesh.expected.json index 70be21c0443c0..b0e88ba32eb2a 100644 --- a/packages/@aws-cdk/aws-appmesh/test/integ.mesh.expected.json +++ b/packages/@aws-cdk/aws-appmesh/test/integ.mesh.expected.json @@ -701,6 +701,45 @@ "RouteName": "route-4" } }, + "meshrouterroute53F46B0FE": { + "Type": "AWS::AppMesh::Route", + "Properties": { + "MeshName": { + "Fn::GetAtt": [ + "meshACDFE68E", + "MeshName" + ] + }, + "Spec": { + "Http2Route": { + "Action": { + "WeightedTargets": [ + { + "VirtualNode": { + "Fn::GetAtt": [ + "meshnode2092BA426", + "VirtualNodeName" + ] + }, + "Weight": 1 + } + ] + }, + "Match": { + "Prefix": "/" + } + }, + "Priority": 10 + }, + "VirtualRouterName": { + "Fn::GetAtt": [ + "meshrouter81B8087E", + "VirtualRouterName" + ] + }, + "RouteName": "route-5" + } + }, "meshnode726C787D": { "Type": "AWS::AppMesh::VirtualNode", "Properties": { diff --git a/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts b/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts index aa1645e07f4f3..3d2c0de7820ac 100644 --- a/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts +++ b/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts @@ -174,6 +174,13 @@ router.addRoute('route-4', { }), }); +router.addRoute('route-5', { + routeSpec: appmesh.RouteSpec.http2({ + priority: 10, + weightedTargets: [{ virtualNode: node2 }], + }), +}); + const gateway = mesh.addVirtualGateway('gateway1', { accessLog: appmesh.AccessLog.fromFilePath('/dev/stdout'), virtualGatewayName: 'gateway1', diff --git a/packages/@aws-cdk/aws-appmesh/test/test.route.ts b/packages/@aws-cdk/aws-appmesh/test/test.route.ts index 07d88f3a13f3f..909d3cd0d2002 100644 --- a/packages/@aws-cdk/aws-appmesh/test/test.route.ts +++ b/packages/@aws-cdk/aws-appmesh/test/test.route.ts @@ -457,6 +457,80 @@ export = { test.done(); }, + + 'should allow route priority'(test: Test) { + // GIVEN + const stack = new cdk.Stack(); + const mesh = new appmesh.Mesh(stack, 'mesh', { + meshName: 'test-mesh', + }); + + const router = new appmesh.VirtualRouter(stack, 'router', { + mesh, + }); + + const virtualNode = mesh.addVirtualNode('test-node', { + serviceDiscovery: appmesh.ServiceDiscovery.dns('test'), + listeners: [appmesh.VirtualNodeListener.http()], + }); + + // WHEN + router.addRoute('http2', { + routeSpec: appmesh.RouteSpec.http2({ + priority: 0, + weightedTargets: [{ virtualNode }], + }), + }); + router.addRoute('http', { + routeSpec: appmesh.RouteSpec.http({ + priority: 10, + weightedTargets: [{ virtualNode }], + }), + }); + router.addRoute('grpc', { + routeSpec: appmesh.RouteSpec.grpc({ + priority: 20, + weightedTargets: [{ virtualNode }], + match: { + serviceName: 'test', + }, + }), + }); + router.addRoute('tcp', { + routeSpec: appmesh.RouteSpec.tcp({ + priority: 30, + weightedTargets: [{ virtualNode }], + }), + }); + + // THEN + expect(stack).to(haveResourceLike('AWS::AppMesh::Route', { + Spec: { + Priority: 0, + Http2Route: {}, + }, + })); + expect(stack).to(haveResourceLike('AWS::AppMesh::Route', { + Spec: { + Priority: 10, + HttpRoute: {}, + }, + })); + expect(stack).to(haveResourceLike('AWS::AppMesh::Route', { + Spec: { + Priority: 20, + GrpcRoute: {}, + }, + })); + expect(stack).to(haveResourceLike('AWS::AppMesh::Route', { + Spec: { + Priority: 30, + TcpRoute: {}, + }, + })); + + test.done(); + }, }, 'Can import Routes using an ARN'(test: Test) { From 5766d5474426c0673a9673884b73cab1d4bcb3b5 Mon Sep 17 00:00:00 2001 From: Josh Kellendonk Date: Tue, 2 Mar 2021 19:17:15 -0700 Subject: [PATCH 05/19] style: enum spacing --- packages/@aws-cdk/aws-appmesh/lib/route-spec.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts b/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts index f7e029ece9dc5..4ecdd11323150 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts @@ -62,34 +62,42 @@ export enum HttpRouteMatchMethod { * GET request */ GET = 'GET', + /** * HEAD request */ HEAD = 'HEAD', + /** * POST request */ POST = 'POST', + /** * PUT request */ PUT = 'PUT', + /** * DELETE request */ DELETE = 'DELETE', + /** * CONNECT request */ CONNECT = 'CONNECT', + /** * OPTIONS request */ OPTIONS = 'OPTIONS', + /** * TRACE request */ TRACE = 'TRACE', + /** * PATCH request */ From 82e8096dae83f24f4b84a9d09693d3f71dd10095 Mon Sep 17 00:00:00 2001 From: Josh Kellendonk Date: Wed, 3 Mar 2021 20:41:44 -0700 Subject: [PATCH 06/19] implement review changes --- .../@aws-cdk/aws-appmesh/lib/route-spec.ts | 150 ++++++++++++------ .../aws-appmesh/test/integ.mesh.expected.json | 50 +++++- .../@aws-cdk/aws-appmesh/test/integ.mesh.ts | 31 ++-- .../@aws-cdk/aws-appmesh/test/test.route.ts | 71 ++++++--- 4 files changed, 201 insertions(+), 101 deletions(-) diff --git a/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts b/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts index 4ecdd11323150..32cdcba611292 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts @@ -37,18 +37,21 @@ export interface HttpRouteMatch { /** * Specifies the client request headers to match on + * * @default - do not match on headers */ - readonly headers?: HttpRouteHeader[]; + readonly headers?: HeaderMatch[]; /** * The client request method to match on. Specify only one. + * * @default - do not match on request method */ readonly method?: HttpRouteMatchMethod; /** * The client request scheme to match on. Specify only one. Applicable only for HTTP2 routes. + * * @default - do not match on HTTP2 scheme */ readonly scheme?: HttpRouteMatchScheme; @@ -120,91 +123,131 @@ export enum HttpRouteMatchScheme { } /** - * An object that represents the HTTP header in the request to match. + * Configuration for `HeaderMatch` */ -export interface HttpRouteHeader { +export interface HeaderMatchConfig { /** - * Specify `true` to match anything except the match criteria. - * @default false + * The Http header name */ - readonly invert?: boolean; + readonly name: string; /** - * The method to use to match the header. - * @default - no match method + * Invert the matching condition. + * + * @default false */ - readonly match?: IHeaderMatchMethod; + readonly invert?: boolean; /** - * A name for the HTTP header in the client request that will be matched on. - * @example 'content-type' + * The match property. */ - readonly name: string; + readonly matchProperty: CfnRoute.HeaderMatchMethodProperty; } /** - * Match method. + * Used to generate header matching methods. */ -export interface IHeaderMatchMethod { +export abstract class HeaderMatch { /** - * Produce the match method. + * The value sent by the client must match the specified value exactly. */ - renderMatchMethod(): CfnRoute.HeaderMatchMethodProperty; -} + static valueIs(name: string, exact: string): HeaderMatch { + return new HeaderMatchImpl(name, false, { exact }); + } -/** - * Used to generate header matching methods. - */ -export abstract class HeaderMatchMethod { /** - * The value sent by the client must match the specified value exactly. + * The value sent by the client must not match the specified value exactly. */ - static exact(exact: string): IHeaderMatchMethod { - return { - renderMatchMethod: () => ({ exact }), - }; + static valueIsNot(name: string, exact: string): HeaderMatch { + return new HeaderMatchImpl(name, true, { exact }); } /** - * The value sent by the client must begin with the specified characters. + * The value sent by the client must start with the specified characters. */ - static prefix(prefix: string): IHeaderMatchMethod { - return { - renderMatchMethod: () => ({ prefix }), - }; + static valueStartsWith(name: string, prefix: string): HeaderMatch { + return new HeaderMatchImpl(name, false, { prefix }); + } + + /** + * The value sent by the client must not start with the specified characters. + */ + static valueDoesNotStartWith(name: string, prefix: string): HeaderMatch { + return new HeaderMatchImpl(name, true, { prefix }); } /** * The value sent by the client must end with the specified characters. */ - static suffix(suffix: string): IHeaderMatchMethod { - return { - renderMatchMethod: () => ({ suffix }), - }; + static valueEndsWith(name: string, suffix: string): HeaderMatch { + return new HeaderMatchImpl(name, false, { suffix }); + } + + /** + * The value sent by the client must not end with the specified characters. + */ + static valueDoesNotEndWith(name: string, suffix: string): HeaderMatch { + return new HeaderMatchImpl(name, true, { suffix }); } /** * The value sent by the client must include the specified characters. */ - static regex(regex: string): IHeaderMatchMethod { - return { - renderMatchMethod: () => ({ regex }), - }; + static valueMatchesRegex(name: string, regex: string): HeaderMatch { + return new HeaderMatchImpl(name, false, { regex }); } /** - * Match on a numeric range of values. + * The value sent by the client must not include the specified characters. + */ + static valueDoesNotMatchRegex(name: string, regex: string): HeaderMatch { + return new HeaderMatchImpl(name, true, { regex }); + } + + /** + * The value sent by the client must be in a range of values * @param start Match on values starting at and including this value * @param end Match on values up to but not including this value */ - static range(start: number, end: number): IHeaderMatchMethod { + static valuesIsInRange(name: string, start: number, end: number): HeaderMatch { + return new HeaderMatchImpl(name, false, { + range: { + start, + end, + }, + }); + } + + /** + * The value sent by the client must not be in a range of values + * @param start Match on values starting at and including this value + * @param end Match on values up to but not including this value + */ + static valuesIsNotInRange(name: string, start: number, end: number): HeaderMatch { + return new HeaderMatchImpl(name, true, { + range: { + start, + end, + }, + }); + } + + /** + * Returns the header match configuration. + */ + abstract bind(scope: Construct): HeaderMatchConfig; +} + +class HeaderMatchImpl extends HeaderMatch { + constructor(private readonly name: string, private readonly invert: boolean, private readonly matchProperty: CfnRoute.HeaderMatchMethodProperty) { + super(); + } + + bind(_scope: Construct): HeaderMatchConfig { return { - renderMatchMethod: () => ({ - range: { - start, - end, - }, - }), + name: this.name, + invert: this.invert, + matchProperty: this.matchProperty, }; } } @@ -419,11 +462,14 @@ class HttpRouteSpec extends RouteSpec { let headers: CfnRoute.HttpRouteHeaderProperty[] | undefined; if (this.match?.headers) { - headers = this.match.headers.map(header => ({ - name: header.name, - invert: header.invert, - match: header.match?.renderMatchMethod(), - })); + headers = this.match.headers.map(header => { + const config = header.bind(_scope); + return { + name: config.name, + invert: config.invert, + match: config.matchProperty, + }; + }); } const httpConfig: CfnRoute.HttpRouteProperty = { diff --git a/packages/@aws-cdk/aws-appmesh/test/integ.mesh.expected.json b/packages/@aws-cdk/aws-appmesh/test/integ.mesh.expected.json index b0e88ba32eb2a..a5dd273c7e074 100644 --- a/packages/@aws-cdk/aws-appmesh/test/integ.mesh.expected.json +++ b/packages/@aws-cdk/aws-appmesh/test/integ.mesh.expected.json @@ -652,38 +652,80 @@ "Match": { "Headers": [ { + "Invert": false, "Match": { "Exact": "application/json" }, "Name": "Content-Type" }, { + "Invert": false, "Match": { - "Prefix": "application/" + "Prefix": "application/json" + }, + "Name": "Content-Type" + }, + { + "Invert": false, + "Match": { + "Suffix": "application/json" + }, + "Name": "Content-Type" + }, + { + "Invert": false, + "Match": { + "Regex": "application/.*" + }, + "Name": "Content-Type" + }, + { + "Invert": false, + "Match": { + "Range": { + "End": 5, + "Start": 1 + } }, "Name": "Content-Type" }, { "Invert": true, "Match": { - "Suffix": "/x-www-form-urlencoded" + "Exact": "application/json" }, "Name": "Content-Type" }, { + "Invert": true, "Match": { - "Regex": ".*" + "Prefix": "application/json" }, "Name": "Content-Type" }, { + "Invert": true, + "Match": { + "Suffix": "application/json" + }, + "Name": "Content-Type" + }, + { + "Invert": true, + "Match": { + "Regex": "application/.*" + }, + "Name": "Content-Type" + }, + { + "Invert": true, "Match": { "Range": { "End": 5, "Start": 1 } }, - "Name": "Max-Forwards" + "Name": "Content-Type" } ], "Method": "POST", diff --git a/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts b/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts index 3d2c0de7820ac..09b45f1403b6e 100644 --- a/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts +++ b/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts @@ -148,27 +148,16 @@ router.addRoute('route-4', { method: appmesh.HttpRouteMatchMethod.POST, scheme: appmesh.HttpRouteMatchScheme.HTTPS, headers: [ - { - name: 'Content-Type', - match: appmesh.HeaderMatchMethod.exact('application/json'), - }, - { - name: 'Content-Type', - match: appmesh.HeaderMatchMethod.prefix('application/'), - }, - { - name: 'Content-Type', - invert: true, - match: appmesh.HeaderMatchMethod.suffix('/x-www-form-urlencoded'), - }, - { - name: 'Content-Type', - match: appmesh.HeaderMatchMethod.regex('.*'), - }, - { - name: 'Max-Forwards', - match: appmesh.HeaderMatchMethod.range(1, 5), - }, + appmesh.HeaderMatch.valueIs('Content-Type', 'application/json'), + appmesh.HeaderMatch.valueStartsWith('Content-Type', 'application/json'), + appmesh.HeaderMatch.valueEndsWith('Content-Type', 'application/json'), + appmesh.HeaderMatch.valueMatchesRegex('Content-Type', 'application/.*'), + appmesh.HeaderMatch.valuesIsInRange('Content-Type', 1, 5), + appmesh.HeaderMatch.valueIsNot('Content-Type', 'application/json'), + appmesh.HeaderMatch.valueDoesNotStartWith('Content-Type', 'application/json'), + appmesh.HeaderMatch.valueDoesNotEndWith('Content-Type', 'application/json'), + appmesh.HeaderMatch.valueDoesNotMatchRegex('Content-Type', 'application/.*'), + appmesh.HeaderMatch.valuesIsNotInRange('Content-Type', 1, 5), ], }, }), diff --git a/packages/@aws-cdk/aws-appmesh/test/test.route.ts b/packages/@aws-cdk/aws-appmesh/test/test.route.ts index 909d3cd0d2002..0a8ee2179f179 100644 --- a/packages/@aws-cdk/aws-appmesh/test/test.route.ts +++ b/packages/@aws-cdk/aws-appmesh/test/test.route.ts @@ -306,27 +306,16 @@ export = { match: { prefixPath: '/', headers: [ - { - name: 'Content-Type', - match: appmesh.HeaderMatchMethod.exact('application/json'), - }, - { - name: 'Content-Type', - match: appmesh.HeaderMatchMethod.prefix('application/'), - }, - { - name: 'Content-Type', - invert: true, - match: appmesh.HeaderMatchMethod.suffix('/json+foobar'), - }, - { - name: 'Content-Type', - match: appmesh.HeaderMatchMethod.regex('.*'), - }, - { - name: 'Max-Forwards', - match: appmesh.HeaderMatchMethod.range(1, 5), - }, + appmesh.HeaderMatch.valueIs('Content-Type', 'application/json'), + appmesh.HeaderMatch.valueIsNot('Content-Type', 'text/html'), + appmesh.HeaderMatch.valueStartsWith('Content-Type', 'application/'), + appmesh.HeaderMatch.valueDoesNotStartWith('Content-Type', 'text/'), + appmesh.HeaderMatch.valueEndsWith('Content-Type', '/json'), + appmesh.HeaderMatch.valueDoesNotEndWith('Content-Type', '/json+foobar'), + appmesh.HeaderMatch.valueMatchesRegex('Content-Type', 'application/.*'), + appmesh.HeaderMatch.valueDoesNotMatchRegex('Content-Type', 'text/.*'), + appmesh.HeaderMatch.valuesIsInRange('Max-Forward', 1, 5), + appmesh.HeaderMatch.valuesIsNotInRange('Max-Forward', 1, 5), ], }, }), @@ -340,30 +329,64 @@ export = { Prefix: '/', Headers: [ { - Name: 'Content-Type', + Invert: false, Match: { Exact: 'application/json' }, + Name: 'Content-Type', }, { + Invert: true, + Match: { Exact: 'text/html' }, Name: 'Content-Type', + }, + { + Invert: false, Match: { Prefix: 'application/' }, + Name: 'Content-Type', + }, + { + Invert: true, + Match: { Prefix: 'text/' }, + Name: 'Content-Type', }, { + Invert: false, + Match: { Suffix: '/json' }, Name: 'Content-Type', + }, + { Invert: true, Match: { Suffix: '/json+foobar' }, + Name: 'Content-Type', + }, + { + Invert: false, + Match: { Regex: 'application/.*' }, + Name: 'Content-Type', }, { + Invert: true, + Match: { Regex: 'text/.*' }, Name: 'Content-Type', - Match: { Regex: '.*' }, }, { - Name: 'Max-Forwards', + Invert: false, + Match: { + Range: { + End: 5, + Start: 1, + }, + }, + Name: 'Max-Forward', + }, + { + Invert: true, Match: { Range: { End: 5, Start: 1, }, }, + Name: 'Max-Forward', }, ], }, From 5258a65c4da9cd22c2906242bea20162a9abdfb6 Mon Sep 17 00:00:00 2001 From: Josh Kellendonk Date: Wed, 3 Mar 2021 20:44:07 -0700 Subject: [PATCH 07/19] update the readme example --- packages/@aws-cdk/aws-appmesh/README.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/@aws-cdk/aws-appmesh/README.md b/packages/@aws-cdk/aws-appmesh/README.md index a916d320318d2..15f9fc73f14f1 100644 --- a/packages/@aws-cdk/aws-appmesh/README.md +++ b/packages/@aws-cdk/aws-appmesh/README.md @@ -313,10 +313,7 @@ router.addRoute('route-http2', { method: appmesh.HttpRouteMatchMethod.POST, scheme: appmesh.HttpRouteMatchScheme.HTTPS, headers: [ - { - name: 'Content-Type', - match: appmesh.HeaderMatchMethod.exact('application/json'), - }, + appmesh.HeaderMatch.valueIs('Content-Type', 'application/json'), ] }, }), From 68c417137a4c70095df8a7404caff5c9d063442c Mon Sep 17 00:00:00 2001 From: Josh Kellendonk Date: Thu, 4 Mar 2021 12:02:01 -0700 Subject: [PATCH 08/19] docs: add call-out for header logical and --- packages/@aws-cdk/aws-appmesh/README.md | 4 +++- packages/@aws-cdk/aws-appmesh/lib/route-spec.ts | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/aws-appmesh/README.md b/packages/@aws-cdk/aws-appmesh/README.md index 15f9fc73f14f1..6b228021c900b 100644 --- a/packages/@aws-cdk/aws-appmesh/README.md +++ b/packages/@aws-cdk/aws-appmesh/README.md @@ -313,7 +313,9 @@ router.addRoute('route-http2', { method: appmesh.HttpRouteMatchMethod.POST, scheme: appmesh.HttpRouteMatchScheme.HTTPS, headers: [ - appmesh.HeaderMatch.valueIs('Content-Type', 'application/json'), + // All specified headers must match for the route to match. + appmesh.HeaderMatch.valueStartsWith('Content-Type', 'application/json'), + appmesh.HeaderMatch.valueIsNot('Content-Type', 'application/json+unsupported'), ] }, }), diff --git a/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts b/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts index 32cdcba611292..bd50c4a71518a 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts @@ -36,7 +36,8 @@ export interface HttpRouteMatch { readonly prefixPath: string; /** - * Specifies the client request headers to match on + * Specifies the client request headers to match on. All specified headers + * must match for the route to match. * * @default - do not match on headers */ From 6f0473cf454b2215d3131dc85012c331385a30c9 Mon Sep 17 00:00:00 2001 From: Josh Kellendonk Date: Mon, 8 Mar 2021 18:59:14 -0700 Subject: [PATCH 09/19] docs: http request method review changes --- packages/@aws-cdk/aws-appmesh/lib/route-spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts b/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts index bd50c4a71518a..359669f538868 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts @@ -44,14 +44,14 @@ export interface HttpRouteMatch { readonly headers?: HeaderMatch[]; /** - * The client request method to match on. Specify only one. + * The HTTP client request method to match on. * * @default - do not match on request method */ readonly method?: HttpRouteMatchMethod; /** - * The client request scheme to match on. Specify only one. Applicable only for HTTP2 routes. + * The client request scheme to match on. Applicable only for HTTP2 routes. * * @default - do not match on HTTP2 scheme */ From 44ea443a361ed67c5f759a93ceac0dfae7cd1c5a Mon Sep 17 00:00:00 2001 From: Josh Kellendonk Date: Mon, 8 Mar 2021 19:06:04 -0700 Subject: [PATCH 10/19] refactor: scheme -> protocol --- packages/@aws-cdk/aws-appmesh/lib/route-spec.ts | 8 ++++---- packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts | 2 +- packages/@aws-cdk/aws-appmesh/test/test.route.ts | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts b/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts index 359669f538868..137c633f59625 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts @@ -51,11 +51,11 @@ export interface HttpRouteMatch { readonly method?: HttpRouteMatchMethod; /** - * The client request scheme to match on. Applicable only for HTTP2 routes. + * The client request protocol to match on. Applicable only for HTTP2 routes. * - * @default - do not match on HTTP2 scheme + * @default - do not match on HTTP2 request protocol */ - readonly scheme?: HttpRouteMatchScheme; + readonly protocol?: HttpRouteMatchScheme; } /** @@ -481,7 +481,7 @@ class HttpRouteSpec extends RouteSpec { prefix: prefixPath, headers: headers, method: this.match?.method, - scheme: this.match?.scheme, + scheme: this.match?.protocol, }, timeout: renderTimeout(this.timeout), }; diff --git a/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts b/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts index 09b45f1403b6e..6878e19fd9a0e 100644 --- a/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts +++ b/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts @@ -146,7 +146,7 @@ router.addRoute('route-4', { match: { prefixPath: '/', method: appmesh.HttpRouteMatchMethod.POST, - scheme: appmesh.HttpRouteMatchScheme.HTTPS, + protocol: appmesh.HttpRouteMatchScheme.HTTPS, headers: [ appmesh.HeaderMatch.valueIs('Content-Type', 'application/json'), appmesh.HeaderMatch.valueStartsWith('Content-Type', 'application/json'), diff --git a/packages/@aws-cdk/aws-appmesh/test/test.route.ts b/packages/@aws-cdk/aws-appmesh/test/test.route.ts index 0a8ee2179f179..c87c775d76f6b 100644 --- a/packages/@aws-cdk/aws-appmesh/test/test.route.ts +++ b/packages/@aws-cdk/aws-appmesh/test/test.route.ts @@ -461,7 +461,7 @@ export = { weightedTargets: [{ virtualNode }], match: { prefixPath: '/', - scheme: appmesh.HttpRouteMatchScheme.HTTP, + protocol: appmesh.HttpRouteMatchScheme.HTTP, }, }), }); From 5ba27a6ea8e53c47dbfd85a75daac531873449d4 Mon Sep 17 00:00:00 2001 From: Josh Kellendonk Date: Mon, 8 Mar 2021 19:12:29 -0700 Subject: [PATCH 11/19] docs: improve wording for HttpRouteMatchMethod --- packages/@aws-cdk/aws-appmesh/lib/route-spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts b/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts index 137c633f59625..c0cee3f2e33aa 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts @@ -59,7 +59,7 @@ export interface HttpRouteMatch { } /** - * Supported request matching methods. + * Supported values for matching routes based on the HTTP request method */ export enum HttpRouteMatchMethod { /** From 17c703da3a3edef2dfa50fe1ca5c64bb7c73206b Mon Sep 17 00:00:00 2001 From: Josh Kellendonk Date: Mon, 8 Mar 2021 19:13:12 -0700 Subject: [PATCH 12/19] refactor: HeaderMatchConfig.name -> headerName --- packages/@aws-cdk/aws-appmesh/lib/route-spec.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts b/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts index c0cee3f2e33aa..e24f44277d14f 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts @@ -130,7 +130,7 @@ export interface HeaderMatchConfig { /** * The Http header name */ - readonly name: string; + readonly headerName: string; /** * Invert the matching condition. @@ -246,7 +246,7 @@ class HeaderMatchImpl extends HeaderMatch { bind(_scope: Construct): HeaderMatchConfig { return { - name: this.name, + headerName: this.name, invert: this.invert, matchProperty: this.matchProperty, }; @@ -466,7 +466,7 @@ class HttpRouteSpec extends RouteSpec { headers = this.match.headers.map(header => { const config = header.bind(_scope); return { - name: config.name, + name: config.headerName, invert: config.invert, match: config.matchProperty, }; From 700cd83ff02b8d814e19d0f7aac8ef2c30bcdfb8 Mon Sep 17 00:00:00 2001 From: Josh Kellendonk Date: Mon, 8 Mar 2021 19:23:19 -0700 Subject: [PATCH 13/19] refactor: name -> headerName for all factory methods, add params to docs --- .../@aws-cdk/aws-appmesh/lib/route-spec.ts | 66 ++++++++++++------- 1 file changed, 44 insertions(+), 22 deletions(-) diff --git a/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts b/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts index e24f44277d14f..58e2a1e3a89ef 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts @@ -151,67 +151,84 @@ export interface HeaderMatchConfig { export abstract class HeaderMatch { /** * The value sent by the client must match the specified value exactly. + * @param headerName Test against the value of this HTTP request header + * @param exact The exact value to test against */ - static valueIs(name: string, exact: string): HeaderMatch { - return new HeaderMatchImpl(name, false, { exact }); + static valueIs(headerName: string, exact: string): HeaderMatch { + return new HeaderMatchImpl(headerName, false, { exact }); } /** * The value sent by the client must not match the specified value exactly. + * @param headerName Test against the value of this HTTP request header + * @param exact The exact value to test against */ - static valueIsNot(name: string, exact: string): HeaderMatch { - return new HeaderMatchImpl(name, true, { exact }); + static valueIsNot(headerName: string, exact: string): HeaderMatch { + return new HeaderMatchImpl(headerName, true, { exact }); } /** * The value sent by the client must start with the specified characters. + * @param headerName Test against the value of this HTTP request header + * @param prefix The prefix to test against */ - static valueStartsWith(name: string, prefix: string): HeaderMatch { - return new HeaderMatchImpl(name, false, { prefix }); + static valueStartsWith(headerName: string, prefix: string): HeaderMatch { + return new HeaderMatchImpl(headerName, false, { prefix }); } /** * The value sent by the client must not start with the specified characters. + * @param headerName Test against the value of this HTTP request header + * @param prefix The prefix to test against */ - static valueDoesNotStartWith(name: string, prefix: string): HeaderMatch { - return new HeaderMatchImpl(name, true, { prefix }); + static valueDoesNotStartWith(headerName: string, prefix: string): HeaderMatch { + return new HeaderMatchImpl(headerName, true, { prefix }); } /** * The value sent by the client must end with the specified characters. + * @param headerName Test against the value of this HTTP request header + * @param suffix The suffix to test against */ - static valueEndsWith(name: string, suffix: string): HeaderMatch { - return new HeaderMatchImpl(name, false, { suffix }); + static valueEndsWith(headerName: string, suffix: string): HeaderMatch { + return new HeaderMatchImpl(headerName, false, { suffix }); } /** * The value sent by the client must not end with the specified characters. + * @param headerName Test against the value of this HTTP request header + * @param suffix The suffix to test against */ - static valueDoesNotEndWith(name: string, suffix: string): HeaderMatch { - return new HeaderMatchImpl(name, true, { suffix }); + static valueDoesNotEndWith(headerName: string, suffix: string): HeaderMatch { + return new HeaderMatchImpl(headerName, true, { suffix }); } /** * The value sent by the client must include the specified characters. + * @param headerName Test against the value of this HTTP request header + * @param regex The regex to test against */ - static valueMatchesRegex(name: string, regex: string): HeaderMatch { - return new HeaderMatchImpl(name, false, { regex }); + static valueMatchesRegex(headerName: string, regex: string): HeaderMatch { + return new HeaderMatchImpl(headerName, false, { regex }); } /** * The value sent by the client must not include the specified characters. + * @param headerName Test against the value of this HTTP request header + * @param regex The regex to test against */ - static valueDoesNotMatchRegex(name: string, regex: string): HeaderMatch { - return new HeaderMatchImpl(name, true, { regex }); + static valueDoesNotMatchRegex(headerName: string, regex: string): HeaderMatch { + return new HeaderMatchImpl(headerName, true, { regex }); } /** * The value sent by the client must be in a range of values + * @param headerName Test against the value of this HTTP request header * @param start Match on values starting at and including this value * @param end Match on values up to but not including this value */ - static valuesIsInRange(name: string, start: number, end: number): HeaderMatch { - return new HeaderMatchImpl(name, false, { + static valuesIsInRange(headerName: string, start: number, end: number): HeaderMatch { + return new HeaderMatchImpl(headerName, false, { range: { start, end, @@ -221,11 +238,12 @@ export abstract class HeaderMatch { /** * The value sent by the client must not be in a range of values + * @param headerName Test against the value of this HTTP request header * @param start Match on values starting at and including this value * @param end Match on values up to but not including this value */ - static valuesIsNotInRange(name: string, start: number, end: number): HeaderMatch { - return new HeaderMatchImpl(name, true, { + static valuesIsNotInRange(headerName: string, start: number, end: number): HeaderMatch { + return new HeaderMatchImpl(headerName, true, { range: { start, end, @@ -240,13 +258,17 @@ export abstract class HeaderMatch { } class HeaderMatchImpl extends HeaderMatch { - constructor(private readonly name: string, private readonly invert: boolean, private readonly matchProperty: CfnRoute.HeaderMatchMethodProperty) { + constructor( + private readonly headerName: string, + private readonly invert: boolean, + private readonly matchProperty: CfnRoute.HeaderMatchMethodProperty, + ) { super(); } bind(_scope: Construct): HeaderMatchConfig { return { - headerName: this.name, + headerName: this.headerName, invert: this.invert, matchProperty: this.matchProperty, }; From 8e6758bdec0d271e15c7e831225bb6868205f454 Mon Sep 17 00:00:00 2001 From: Josh Kellendonk Date: Mon, 8 Mar 2021 19:24:01 -0700 Subject: [PATCH 14/19] refactor: HttpRouteMatchScheme -> HttpRouteProtocol --- packages/@aws-cdk/aws-appmesh/lib/route-spec.ts | 4 ++-- packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts | 2 +- packages/@aws-cdk/aws-appmesh/test/test.route.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts b/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts index 58e2a1e3a89ef..12426e11b366e 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts @@ -55,7 +55,7 @@ export interface HttpRouteMatch { * * @default - do not match on HTTP2 request protocol */ - readonly protocol?: HttpRouteMatchScheme; + readonly protocol?: HttpRouteProtocol; } /** @@ -111,7 +111,7 @@ export enum HttpRouteMatchMethod { /** * Supported :scheme options for HTTP2 */ -export enum HttpRouteMatchScheme { +export enum HttpRouteProtocol { /** * Match HTTP requests */ diff --git a/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts b/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts index 6878e19fd9a0e..f0dc8b594dd39 100644 --- a/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts +++ b/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts @@ -146,7 +146,7 @@ router.addRoute('route-4', { match: { prefixPath: '/', method: appmesh.HttpRouteMatchMethod.POST, - protocol: appmesh.HttpRouteMatchScheme.HTTPS, + protocol: appmesh.HttpRouteProtocol.HTTPS, headers: [ appmesh.HeaderMatch.valueIs('Content-Type', 'application/json'), appmesh.HeaderMatch.valueStartsWith('Content-Type', 'application/json'), diff --git a/packages/@aws-cdk/aws-appmesh/test/test.route.ts b/packages/@aws-cdk/aws-appmesh/test/test.route.ts index c87c775d76f6b..48e4ededd0359 100644 --- a/packages/@aws-cdk/aws-appmesh/test/test.route.ts +++ b/packages/@aws-cdk/aws-appmesh/test/test.route.ts @@ -461,7 +461,7 @@ export = { weightedTargets: [{ virtualNode }], match: { prefixPath: '/', - protocol: appmesh.HttpRouteMatchScheme.HTTP, + protocol: appmesh.HttpRouteProtocol.HTTP, }, }), }); From 7441bb634d840645bac47c0c7995bdfa75687c1f Mon Sep 17 00:00:00 2001 From: Josh Kellendonk Date: Mon, 8 Mar 2021 19:30:09 -0700 Subject: [PATCH 15/19] docs: improve HeaderMatch comments and variable names --- .../@aws-cdk/aws-appmesh/lib/route-spec.ts | 52 +++++++++++++------ 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts b/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts index 12426e11b366e..3ff0763389399 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts @@ -150,25 +150,31 @@ export interface HeaderMatchConfig { */ export abstract class HeaderMatch { /** - * The value sent by the client must match the specified value exactly. + * The value of the header with the given name in the request must match the + * specified value exactly. + * * @param headerName Test against the value of this HTTP request header - * @param exact The exact value to test against + * @param headerValue The exact value to test against */ - static valueIs(headerName: string, exact: string): HeaderMatch { - return new HeaderMatchImpl(headerName, false, { exact }); + static valueIs(headerName: string, headerValue: string): HeaderMatch { + return new HeaderMatchImpl(headerName, false, { exact: headerValue }); } /** - * The value sent by the client must not match the specified value exactly. + * The value of the header with the given name in the request must not match + * the specified value exactly. + * * @param headerName Test against the value of this HTTP request header - * @param exact The exact value to test against + * @param headerValue The exact value to test against */ - static valueIsNot(headerName: string, exact: string): HeaderMatch { - return new HeaderMatchImpl(headerName, true, { exact }); + static valueIsNot(headerName: string, headerValue: string): HeaderMatch { + return new HeaderMatchImpl(headerName, true, { exact: headerValue }); } /** - * The value sent by the client must start with the specified characters. + * The value of the header with the given name in the request must start with + * the specified characters. + * * @param headerName Test against the value of this HTTP request header * @param prefix The prefix to test against */ @@ -177,7 +183,9 @@ export abstract class HeaderMatch { } /** - * The value sent by the client must not start with the specified characters. + * The value of the header with the given name in the request must not start + * with the specified characters. + * * @param headerName Test against the value of this HTTP request header * @param prefix The prefix to test against */ @@ -186,7 +194,9 @@ export abstract class HeaderMatch { } /** - * The value sent by the client must end with the specified characters. + * The value of the header with the given name in the request must end with + * the specified characters. + * * @param headerName Test against the value of this HTTP request header * @param suffix The suffix to test against */ @@ -195,7 +205,9 @@ export abstract class HeaderMatch { } /** - * The value sent by the client must not end with the specified characters. + * The value of the header with the given name in the request must not end + * with the specified characters. + * * @param headerName Test against the value of this HTTP request header * @param suffix The suffix to test against */ @@ -204,7 +216,9 @@ export abstract class HeaderMatch { } /** - * The value sent by the client must include the specified characters. + * The value of the header with the given name in the request must include + * the specified characters. + * * @param headerName Test against the value of this HTTP request header * @param regex The regex to test against */ @@ -213,7 +227,9 @@ export abstract class HeaderMatch { } /** - * The value sent by the client must not include the specified characters. + * The value of the header with the given name in the request must not + * include the specified characters. + * * @param headerName Test against the value of this HTTP request header * @param regex The regex to test against */ @@ -222,7 +238,9 @@ export abstract class HeaderMatch { } /** - * The value sent by the client must be in a range of values + * The value of the header with the given name in the request must be in a + * range of values. + * * @param headerName Test against the value of this HTTP request header * @param start Match on values starting at and including this value * @param end Match on values up to but not including this value @@ -237,7 +255,9 @@ export abstract class HeaderMatch { } /** - * The value sent by the client must not be in a range of values + * The value of the header with the given name in the request must not be in + * a range of values. + * * @param headerName Test against the value of this HTTP request header * @param start Match on values starting at and including this value * @param end Match on values up to but not including this value From 60e787c1828c14a23db0e07b7cfa3ece0221328b Mon Sep 17 00:00:00 2001 From: Josh Kellendonk Date: Mon, 8 Mar 2021 19:31:11 -0700 Subject: [PATCH 16/19] refactor: inline the headers variable --- .../@aws-cdk/aws-appmesh/lib/route-spec.ts | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts b/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts index 3ff0763389399..3acf04f46edd8 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts @@ -503,25 +503,20 @@ class HttpRouteSpec extends RouteSpec { throw new Error(`Prefix Path must start with \'/\', got: ${prefixPath}`); } - let headers: CfnRoute.HttpRouteHeaderProperty[] | undefined; - if (this.match?.headers) { - headers = this.match.headers.map(header => { - const config = header.bind(_scope); - return { - name: config.headerName, - invert: config.invert, - match: config.matchProperty, - }; - }); - } - const httpConfig: CfnRoute.HttpRouteProperty = { action: { weightedTargets: renderWeightedTargets(this.weightedTargets), }, match: { prefix: prefixPath, - headers: headers, + headers: this.match?.headers?.map(header => { + const config = header.bind(_scope); + return { + name: config.headerName, + invert: config.invert, + match: config.matchProperty, + }; + }), method: this.match?.method, scheme: this.match?.protocol, }, From 49401578d69a9f04065ec1b9ad10e2542fc55ab6 Mon Sep 17 00:00:00 2001 From: Josh Kellendonk Date: Mon, 8 Mar 2021 19:32:49 -0700 Subject: [PATCH 17/19] docs: remove the jsdocs on the internal HttpRouteSpec --- .../@aws-cdk/aws-appmesh/lib/route-spec.ts | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts b/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts index 3acf04f46edd8..417f6e188166a 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts @@ -463,29 +463,11 @@ export abstract class RouteSpec { } class HttpRouteSpec extends RouteSpec { - /** - * The priority for the route. - */ public readonly priority?: number; - - /** - * Type of route you are creating - */ public readonly protocol: Protocol; - - /** - * The criteria for determining a request match - */ public readonly match?: HttpRouteMatch; - - /** - * The criteria for determining a timeout configuration - */ public readonly timeout?: HttpTimeout; - /** - * List of targets that traffic is routed to when a request matches the route - */ public readonly weightedTargets: WeightedTarget[]; constructor(props: HttpRouteSpecOptions, protocol: Protocol) { From 88c24be5a6a0231f59ea8dd6f81d39ff27f602a4 Mon Sep 17 00:00:00 2001 From: Josh Kellendonk Date: Mon, 8 Mar 2021 19:40:37 -0700 Subject: [PATCH 18/19] refactor: use only httpRouteHeader in HeaderMatchConfig --- .../@aws-cdk/aws-appmesh/lib/route-spec.ts | 35 +++++-------------- 1 file changed, 9 insertions(+), 26 deletions(-) diff --git a/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts b/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts index 417f6e188166a..700f8a6bc0ba8 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts @@ -128,21 +128,9 @@ export enum HttpRouteProtocol { */ export interface HeaderMatchConfig { /** - * The Http header name + * The HTTP route header. */ - readonly headerName: string; - - /** - * Invert the matching condition. - * - * @default false - */ - readonly invert?: boolean; - - /** - * The match property. - */ - readonly matchProperty: CfnRoute.HeaderMatchMethodProperty; + readonly httpRouteHeader: CfnRoute.HttpRouteHeaderProperty; } /** @@ -288,9 +276,11 @@ class HeaderMatchImpl extends HeaderMatch { bind(_scope: Construct): HeaderMatchConfig { return { - headerName: this.headerName, - invert: this.invert, - matchProperty: this.matchProperty, + httpRouteHeader: { + name: this.headerName, + invert: this.invert, + match: this.matchProperty, + }, }; } } @@ -479,7 +469,7 @@ class HttpRouteSpec extends RouteSpec { this.priority = props.priority; } - public bind(_scope: Construct): RouteSpecConfig { + public bind(scope: Construct): RouteSpecConfig { const prefixPath = this.match ? this.match.prefixPath : '/'; if (prefixPath[0] != '/') { throw new Error(`Prefix Path must start with \'/\', got: ${prefixPath}`); @@ -491,14 +481,7 @@ class HttpRouteSpec extends RouteSpec { }, match: { prefix: prefixPath, - headers: this.match?.headers?.map(header => { - const config = header.bind(_scope); - return { - name: config.headerName, - invert: config.invert, - match: config.matchProperty, - }; - }), + headers: this.match?.headers?.map(header => header.bind(scope).httpRouteHeader), method: this.match?.method, scheme: this.match?.protocol, }, From e7b8ca40b2353714aa16f1daa931e810ecc38333 Mon Sep 17 00:00:00 2001 From: Josh Kellendonk Date: Thu, 11 Mar 2021 09:29:08 -0700 Subject: [PATCH 19/19] perform review changes --- packages/@aws-cdk/aws-appmesh/README.md | 8 +-- .../@aws-cdk/aws-appmesh/lib/route-spec.ts | 52 +++++++++---------- .../@aws-cdk/aws-appmesh/test/integ.mesh.ts | 20 +++---- .../@aws-cdk/aws-appmesh/test/test.route.ts | 20 +++---- 4 files changed, 50 insertions(+), 50 deletions(-) diff --git a/packages/@aws-cdk/aws-appmesh/README.md b/packages/@aws-cdk/aws-appmesh/README.md index 6b228021c900b..e68f0b46a0bad 100644 --- a/packages/@aws-cdk/aws-appmesh/README.md +++ b/packages/@aws-cdk/aws-appmesh/README.md @@ -309,13 +309,13 @@ router.addRoute('route-http2', { }, ], match: { - prefixPath: '/path-to-app', + prefixPath: '/', method: appmesh.HttpRouteMatchMethod.POST, - scheme: appmesh.HttpRouteMatchScheme.HTTPS, + protocol: appmesh.HttpRouteProtocol.HTTPS, headers: [ // All specified headers must match for the route to match. - appmesh.HeaderMatch.valueStartsWith('Content-Type', 'application/json'), - appmesh.HeaderMatch.valueIsNot('Content-Type', 'application/json+unsupported'), + appmesh.HttpHeaderMatch.valueIs('Content-Type', 'application/json'), + appmesh.HttpHeaderMatch.valueIsNot('Content-Type', 'application/json'), ] }, }), diff --git a/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts b/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts index 700f8a6bc0ba8..ccd615c63933e 100644 --- a/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts +++ b/packages/@aws-cdk/aws-appmesh/lib/route-spec.ts @@ -41,7 +41,7 @@ export interface HttpRouteMatch { * * @default - do not match on headers */ - readonly headers?: HeaderMatch[]; + readonly headers?: HttpHeaderMatch[]; /** * The HTTP client request method to match on. @@ -126,7 +126,7 @@ export enum HttpRouteProtocol { /** * Configuration for `HeaderMatch` */ -export interface HeaderMatchConfig { +export interface HttpHeaderMatchConfig { /** * The HTTP route header. */ @@ -136,15 +136,15 @@ export interface HeaderMatchConfig { /** * Used to generate header matching methods. */ -export abstract class HeaderMatch { +export abstract class HttpHeaderMatch { /** * The value of the header with the given name in the request must match the * specified value exactly. * - * @param headerName Test against the value of this HTTP request header + * @param headerName the name of the HTTP header to match against * @param headerValue The exact value to test against */ - static valueIs(headerName: string, headerValue: string): HeaderMatch { + static valueIs(headerName: string, headerValue: string): HttpHeaderMatch { return new HeaderMatchImpl(headerName, false, { exact: headerValue }); } @@ -152,10 +152,10 @@ export abstract class HeaderMatch { * The value of the header with the given name in the request must not match * the specified value exactly. * - * @param headerName Test against the value of this HTTP request header + * @param headerName the name of the HTTP header to match against * @param headerValue The exact value to test against */ - static valueIsNot(headerName: string, headerValue: string): HeaderMatch { + static valueIsNot(headerName: string, headerValue: string): HttpHeaderMatch { return new HeaderMatchImpl(headerName, true, { exact: headerValue }); } @@ -163,10 +163,10 @@ export abstract class HeaderMatch { * The value of the header with the given name in the request must start with * the specified characters. * - * @param headerName Test against the value of this HTTP request header + * @param headerName the name of the HTTP header to match against * @param prefix The prefix to test against */ - static valueStartsWith(headerName: string, prefix: string): HeaderMatch { + static valueStartsWith(headerName: string, prefix: string): HttpHeaderMatch { return new HeaderMatchImpl(headerName, false, { prefix }); } @@ -174,10 +174,10 @@ export abstract class HeaderMatch { * The value of the header with the given name in the request must not start * with the specified characters. * - * @param headerName Test against the value of this HTTP request header + * @param headerName the name of the HTTP header to match against * @param prefix The prefix to test against */ - static valueDoesNotStartWith(headerName: string, prefix: string): HeaderMatch { + static valueDoesNotStartWith(headerName: string, prefix: string): HttpHeaderMatch { return new HeaderMatchImpl(headerName, true, { prefix }); } @@ -185,10 +185,10 @@ export abstract class HeaderMatch { * The value of the header with the given name in the request must end with * the specified characters. * - * @param headerName Test against the value of this HTTP request header + * @param headerName the name of the HTTP header to match against * @param suffix The suffix to test against */ - static valueEndsWith(headerName: string, suffix: string): HeaderMatch { + static valueEndsWith(headerName: string, suffix: string): HttpHeaderMatch { return new HeaderMatchImpl(headerName, false, { suffix }); } @@ -196,10 +196,10 @@ export abstract class HeaderMatch { * The value of the header with the given name in the request must not end * with the specified characters. * - * @param headerName Test against the value of this HTTP request header + * @param headerName the name of the HTTP header to match against * @param suffix The suffix to test against */ - static valueDoesNotEndWith(headerName: string, suffix: string): HeaderMatch { + static valueDoesNotEndWith(headerName: string, suffix: string): HttpHeaderMatch { return new HeaderMatchImpl(headerName, true, { suffix }); } @@ -207,10 +207,10 @@ export abstract class HeaderMatch { * The value of the header with the given name in the request must include * the specified characters. * - * @param headerName Test against the value of this HTTP request header + * @param headerName the name of the HTTP header to match against * @param regex The regex to test against */ - static valueMatchesRegex(headerName: string, regex: string): HeaderMatch { + static valueMatchesRegex(headerName: string, regex: string): HttpHeaderMatch { return new HeaderMatchImpl(headerName, false, { regex }); } @@ -218,10 +218,10 @@ export abstract class HeaderMatch { * The value of the header with the given name in the request must not * include the specified characters. * - * @param headerName Test against the value of this HTTP request header + * @param headerName the name of the HTTP header to match against * @param regex The regex to test against */ - static valueDoesNotMatchRegex(headerName: string, regex: string): HeaderMatch { + static valueDoesNotMatchRegex(headerName: string, regex: string): HttpHeaderMatch { return new HeaderMatchImpl(headerName, true, { regex }); } @@ -229,11 +229,11 @@ export abstract class HeaderMatch { * The value of the header with the given name in the request must be in a * range of values. * - * @param headerName Test against the value of this HTTP request header + * @param headerName the name of the HTTP header to match against * @param start Match on values starting at and including this value * @param end Match on values up to but not including this value */ - static valuesIsInRange(headerName: string, start: number, end: number): HeaderMatch { + static valuesIsInRange(headerName: string, start: number, end: number): HttpHeaderMatch { return new HeaderMatchImpl(headerName, false, { range: { start, @@ -246,11 +246,11 @@ export abstract class HeaderMatch { * The value of the header with the given name in the request must not be in * a range of values. * - * @param headerName Test against the value of this HTTP request header + * @param headerName the name of the HTTP header to match against * @param start Match on values starting at and including this value * @param end Match on values up to but not including this value */ - static valuesIsNotInRange(headerName: string, start: number, end: number): HeaderMatch { + static valuesIsNotInRange(headerName: string, start: number, end: number): HttpHeaderMatch { return new HeaderMatchImpl(headerName, true, { range: { start, @@ -262,10 +262,10 @@ export abstract class HeaderMatch { /** * Returns the header match configuration. */ - abstract bind(scope: Construct): HeaderMatchConfig; + abstract bind(scope: Construct): HttpHeaderMatchConfig; } -class HeaderMatchImpl extends HeaderMatch { +class HeaderMatchImpl extends HttpHeaderMatch { constructor( private readonly headerName: string, private readonly invert: boolean, @@ -274,7 +274,7 @@ class HeaderMatchImpl extends HeaderMatch { super(); } - bind(_scope: Construct): HeaderMatchConfig { + bind(_scope: Construct): HttpHeaderMatchConfig { return { httpRouteHeader: { name: this.headerName, diff --git a/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts b/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts index f0dc8b594dd39..78357a6ee2777 100644 --- a/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts +++ b/packages/@aws-cdk/aws-appmesh/test/integ.mesh.ts @@ -148,16 +148,16 @@ router.addRoute('route-4', { method: appmesh.HttpRouteMatchMethod.POST, protocol: appmesh.HttpRouteProtocol.HTTPS, headers: [ - appmesh.HeaderMatch.valueIs('Content-Type', 'application/json'), - appmesh.HeaderMatch.valueStartsWith('Content-Type', 'application/json'), - appmesh.HeaderMatch.valueEndsWith('Content-Type', 'application/json'), - appmesh.HeaderMatch.valueMatchesRegex('Content-Type', 'application/.*'), - appmesh.HeaderMatch.valuesIsInRange('Content-Type', 1, 5), - appmesh.HeaderMatch.valueIsNot('Content-Type', 'application/json'), - appmesh.HeaderMatch.valueDoesNotStartWith('Content-Type', 'application/json'), - appmesh.HeaderMatch.valueDoesNotEndWith('Content-Type', 'application/json'), - appmesh.HeaderMatch.valueDoesNotMatchRegex('Content-Type', 'application/.*'), - appmesh.HeaderMatch.valuesIsNotInRange('Content-Type', 1, 5), + appmesh.HttpHeaderMatch.valueIs('Content-Type', 'application/json'), + appmesh.HttpHeaderMatch.valueStartsWith('Content-Type', 'application/json'), + appmesh.HttpHeaderMatch.valueEndsWith('Content-Type', 'application/json'), + appmesh.HttpHeaderMatch.valueMatchesRegex('Content-Type', 'application/.*'), + appmesh.HttpHeaderMatch.valuesIsInRange('Content-Type', 1, 5), + appmesh.HttpHeaderMatch.valueIsNot('Content-Type', 'application/json'), + appmesh.HttpHeaderMatch.valueDoesNotStartWith('Content-Type', 'application/json'), + appmesh.HttpHeaderMatch.valueDoesNotEndWith('Content-Type', 'application/json'), + appmesh.HttpHeaderMatch.valueDoesNotMatchRegex('Content-Type', 'application/.*'), + appmesh.HttpHeaderMatch.valuesIsNotInRange('Content-Type', 1, 5), ], }, }), diff --git a/packages/@aws-cdk/aws-appmesh/test/test.route.ts b/packages/@aws-cdk/aws-appmesh/test/test.route.ts index 48e4ededd0359..911c89ad17e72 100644 --- a/packages/@aws-cdk/aws-appmesh/test/test.route.ts +++ b/packages/@aws-cdk/aws-appmesh/test/test.route.ts @@ -306,16 +306,16 @@ export = { match: { prefixPath: '/', headers: [ - appmesh.HeaderMatch.valueIs('Content-Type', 'application/json'), - appmesh.HeaderMatch.valueIsNot('Content-Type', 'text/html'), - appmesh.HeaderMatch.valueStartsWith('Content-Type', 'application/'), - appmesh.HeaderMatch.valueDoesNotStartWith('Content-Type', 'text/'), - appmesh.HeaderMatch.valueEndsWith('Content-Type', '/json'), - appmesh.HeaderMatch.valueDoesNotEndWith('Content-Type', '/json+foobar'), - appmesh.HeaderMatch.valueMatchesRegex('Content-Type', 'application/.*'), - appmesh.HeaderMatch.valueDoesNotMatchRegex('Content-Type', 'text/.*'), - appmesh.HeaderMatch.valuesIsInRange('Max-Forward', 1, 5), - appmesh.HeaderMatch.valuesIsNotInRange('Max-Forward', 1, 5), + appmesh.HttpHeaderMatch.valueIs('Content-Type', 'application/json'), + appmesh.HttpHeaderMatch.valueIsNot('Content-Type', 'text/html'), + appmesh.HttpHeaderMatch.valueStartsWith('Content-Type', 'application/'), + appmesh.HttpHeaderMatch.valueDoesNotStartWith('Content-Type', 'text/'), + appmesh.HttpHeaderMatch.valueEndsWith('Content-Type', '/json'), + appmesh.HttpHeaderMatch.valueDoesNotEndWith('Content-Type', '/json+foobar'), + appmesh.HttpHeaderMatch.valueMatchesRegex('Content-Type', 'application/.*'), + appmesh.HttpHeaderMatch.valueDoesNotMatchRegex('Content-Type', 'text/.*'), + appmesh.HttpHeaderMatch.valuesIsInRange('Max-Forward', 1, 5), + appmesh.HttpHeaderMatch.valuesIsNotInRange('Max-Forward', 1, 5), ], }, }),