From ad8a704cce7c09bf51f6ee4d688d00fcb2c86472 Mon Sep 17 00:00:00 2001 From: Steve HOUEL Date: Tue, 6 Dec 2022 09:28:01 +0100 Subject: [PATCH] feat(gamelift): add MatchmakingRuleSet L2 Construct for GameLift (#23091) Following https://github.com/aws/aws-cdk-rfcs/issues/436 I have written the Gamelift MatchmakingRuleSet L2 resource which create an MatchmakingRuleSet resource. ---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [x] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [x] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-gamelift/README.md | 71 +++++- packages/@aws-cdk/aws-gamelift/lib/index.ts | 2 + .../lib/matchmaking-ruleset-body.ts | 119 +++++++++ .../aws-gamelift/lib/matchmaking-ruleset.ts | 228 ++++++++++++++++++ ...efaultTestDeployAssert0688841C.assets.json | 19 ++ ...aultTestDeployAssert0688841C.template.json | 36 +++ .../aws-gamelift-build.assets.json | 19 ++ .../aws-gamelift-build.template.json | 60 +++++ .../cdk.out | 1 + .../integ.json | 12 + .../manifest.json | 123 ++++++++++ .../tree.json | 142 +++++++++++ .../test/integ.matchmaking-ruleset.ts | 29 +++ .../test/matchmaking-ruleset-body.test.ts | 56 +++++ .../test/matchmaking-ruleset.test.ts | 137 +++++++++++ .../aws-gamelift/test/my-ruleset/ruleset.json | 57 +++++ 16 files changed, 1109 insertions(+), 2 deletions(-) create mode 100644 packages/@aws-cdk/aws-gamelift/lib/matchmaking-ruleset-body.ts create mode 100644 packages/@aws-cdk/aws-gamelift/lib/matchmaking-ruleset.ts create mode 100644 packages/@aws-cdk/aws-gamelift/test/integ.matchmaking-ruleset.js.snapshot/BuildDefaultTestDeployAssert0688841C.assets.json create mode 100644 packages/@aws-cdk/aws-gamelift/test/integ.matchmaking-ruleset.js.snapshot/BuildDefaultTestDeployAssert0688841C.template.json create mode 100644 packages/@aws-cdk/aws-gamelift/test/integ.matchmaking-ruleset.js.snapshot/aws-gamelift-build.assets.json create mode 100644 packages/@aws-cdk/aws-gamelift/test/integ.matchmaking-ruleset.js.snapshot/aws-gamelift-build.template.json create mode 100644 packages/@aws-cdk/aws-gamelift/test/integ.matchmaking-ruleset.js.snapshot/cdk.out create mode 100644 packages/@aws-cdk/aws-gamelift/test/integ.matchmaking-ruleset.js.snapshot/integ.json create mode 100644 packages/@aws-cdk/aws-gamelift/test/integ.matchmaking-ruleset.js.snapshot/manifest.json create mode 100644 packages/@aws-cdk/aws-gamelift/test/integ.matchmaking-ruleset.js.snapshot/tree.json create mode 100644 packages/@aws-cdk/aws-gamelift/test/integ.matchmaking-ruleset.ts create mode 100644 packages/@aws-cdk/aws-gamelift/test/matchmaking-ruleset-body.test.ts create mode 100644 packages/@aws-cdk/aws-gamelift/test/matchmaking-ruleset.test.ts create mode 100644 packages/@aws-cdk/aws-gamelift/test/my-ruleset/ruleset.json diff --git a/packages/@aws-cdk/aws-gamelift/README.md b/packages/@aws-cdk/aws-gamelift/README.md index 2528789c4fbad..5631d73d2722d 100644 --- a/packages/@aws-cdk/aws-gamelift/README.md +++ b/packages/@aws-cdk/aws-gamelift/README.md @@ -50,6 +50,73 @@ deliver inexpensive, resilient game hosting for your players This module is part of the [AWS Cloud Development Kit](https://github.com/aws/aws-cdk) project. It allows you to define components for your matchmaking configuration or game server fleet management system. +## GameLift FlexMatch + +### Matchmaking RuleSet + +Every FlexMatch matchmaker must have a rule set. The rule set determines the +two key elements of a match: your game's team structure and size, and how to +group players together for the best possible match. + +For example, a rule set might describe a match like this: Create a match with +two teams of four to eight players each, one team is the cowboy and the other +team the aliens. A team can have novice and experienced players, but the +average skill of the two teams must be within 10 points of each other. If no +match is made after 30 seconds, gradually relax the skill requirements. + +```ts +new gamelift.MatchmakingRuleSet(this, 'RuleSet', { + matchmakingRuleSetName: 'my-test-ruleset', + content: gamelift.RuleSetContent.fromJsonFile(path.join(__dirname, 'my-ruleset/ruleset.json')), +}); +``` + +### FlexMatch Monitoring + +You can monitor GameLift FlexMatch activity for matchmaking configurations and +matchmaking rules using Amazon CloudWatch. These statistics are used to provide +a historical perspective on how your Gamelift FlexMatch solution is performing. + +#### FlexMatch Metrics + +GameLift FlexMatch sends metrics to CloudWatch so that you can collect and +analyze the activity of your matchmaking solution, including match acceptance +workflow, ticket consumtion. + +You can then use CloudWatch alarms to alert you, for example, when matches has +been rejected (potential matches that were rejected by at least one player +since the last report) exceed a certain thresold which could means that you may +have an issue in your matchmaking rules. + +CDK provides methods for accessing GameLift FlexMatch metrics with default configuration, +such as `metricRuleEvaluationsPassed`, or `metricRuleEvaluationsFailed` (see +[`IMatchmakingRuleSet`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-gamelift.IMatchmakingRuleSet.html) +for a full list). CDK also provides a generic `metric` method that can be used +to produce metric configurations for any metric provided by GameLift FlexMatch; +the configurations are pre-populated with the correct dimensions for the +matchmaking configuration. + +```ts +declare const matchmakingRuleSet: gamelift.MatchmakingRuleSet; +// Alarm that triggers when the per-second average of not placed matches exceed 10% +const ruleEvaluationRatio = new cloudwatch.MathExpression({ + expression: '1 - (ruleEvaluationsPassed / ruleEvaluationsFailed)', + usingMetrics: { + ruleEvaluationsPassed: matchmakingRuleSet.metricRuleEvaluationsPassed({ statistic: cloudwatch.Statistic.SUM }), + ruleEvaluationsFailed: matchmakingRuleSet.metric('ruleEvaluationsFailed'), + }, +}); +new cloudwatch.Alarm(this, 'Alarm', { + metric: ruleEvaluationRatio, + threshold: 0.1, + evaluationPeriods: 3, +}); +``` + +See: [Monitoring Using CloudWatch Metrics](https://docs.aws.amazon.com/gamelift/latest/developerguide/monitoring-cloudwatch.html) +in the *Amazon GameLift Developer Guide*. + + ## GameLift Hosting ### Uploading builds and scripts to GameLift @@ -344,7 +411,7 @@ in the *Amazon GameLift Developer Guide*. GameLift is integrated with CloudWatch, so you can monitor the performance of your game servers via logs and metrics. -#### Metrics +#### Fleet Metrics GameLift Fleet sends metrics to CloudWatch so that you can collect and analyze the activity of your Fleet, including game and player sessions and server @@ -517,7 +584,7 @@ new gamelift.GameServerGroup(this, 'GameServerGroup', { }); ``` -### Monitoring +### FleetIQ Monitoring GameLift FleetIQ sends metrics to CloudWatch so that you can collect and analyze the activity of your Game server fleet, including the number of diff --git a/packages/@aws-cdk/aws-gamelift/lib/index.ts b/packages/@aws-cdk/aws-gamelift/lib/index.ts index cf3f58b835042..6a74d01861c05 100644 --- a/packages/@aws-cdk/aws-gamelift/lib/index.ts +++ b/packages/@aws-cdk/aws-gamelift/lib/index.ts @@ -6,6 +6,8 @@ export * from './game-server-group'; export * from './ingress-rule'; export * from './fleet-base'; export * from './build-fleet'; +export * from './matchmaking-ruleset'; +export * from './matchmaking-ruleset-body'; // AWS::GameLift CloudFormation Resources: export * from './gamelift.generated'; diff --git a/packages/@aws-cdk/aws-gamelift/lib/matchmaking-ruleset-body.ts b/packages/@aws-cdk/aws-gamelift/lib/matchmaking-ruleset-body.ts new file mode 100644 index 0000000000000..4c7a086cec18e --- /dev/null +++ b/packages/@aws-cdk/aws-gamelift/lib/matchmaking-ruleset-body.ts @@ -0,0 +1,119 @@ +import * as fs from 'fs'; +import { Construct } from 'constructs'; + +/** + * Interface to represent Matchmaking RuleSet schema + */ +export interface IRuleSetBody {} + +/** + * Interface to represent output result of a RuleSetContent binding + */ +export interface RuleSetBodyConfig { + /** + * Inline ruleSet body. + */ + readonly ruleSetBody: string; +} + +/** + * Interface to represent a Matchmaking RuleSet content + */ +export interface IRuleSetContent { + + /** + * RuleSet body content + * + * @attribute + */ + readonly content: IRuleSetBody; + + /** + * Called when the matchmaking ruleSet is initialized to allow this object to bind + * to the stack and add resources. + * + * @param _scope The binding scope. + */ + bind(_scope: Construct): RuleSetBodyConfig; +} + +/** + * Properties for a new matchmaking ruleSet content + */ +export interface RuleSetContentProps { + + /** + * RuleSet body content + * + * @default use a default empty RuleSet body + */ + readonly content?: IRuleSetBody; +} + +/** + * The rule set determines the two key elements of a match: your game's team structure and size, and how to group players together for the best possible match. + * + * For example, a rule set might describe a match like this: + * - Create a match with two teams of five players each, one team is the defenders and the other team the invaders. + * - A team can have novice and experienced players, but the average skill of the two teams must be within 10 points of each other. + * - If no match is made after 30 seconds, gradually relax the skill requirements. + */ +export class RuleSetContent implements IRuleSetContent { + + /** + * Matchmaking ruleSet body from a file + * @returns `RuleSetContentBase` based on JSON file content. + * @param path The path to the ruleSet body file + */ + public static fromJsonFile(path: string): IRuleSetContent { + if (!fs.existsSync(path)) { + throw new Error(`RuleSet path does not exist, please verify it, actual ${path}`); + } + + if (!fs.lstatSync(path).isFile()) { + throw new Error(`RuleSet path is not link to a single file, please verify your path, actual ${path}`); + } + const file = fs.readFileSync(path); + + return this.fromInline(file.toString()); + } + + /** + * Inline body for Matchmaking ruleSet + * @returns `RuleSetContent` with inline code. + * @param body The actual ruleSet body (maximum 65535 characters) + */ + public static fromInline(body: string): IRuleSetContent { + if (body && body.length > 65535) { + throw new Error(`RuleSet body cannot exceed 65535 characters, actual ${body.length}`); + } + try { + return new RuleSetContent({ + content: JSON.parse(body), + }); + } catch (err) { + throw new Error('RuleSet body has an invalid Json format'); + } + } + + /** + * RuleSet body content + */ + public readonly content: IRuleSetBody; + + constructor(props: RuleSetContentProps) { + this.content = props.content || {}; + } + + /** + * Called when the matchmaking ruleSet is initialized to allow this object to bind + * to the stack and add resources. + * + * @param _scope The binding scope. + */ + public bind(_scope: Construct): RuleSetBodyConfig { + return { + ruleSetBody: JSON.stringify(this.content), + }; + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-gamelift/lib/matchmaking-ruleset.ts b/packages/@aws-cdk/aws-gamelift/lib/matchmaking-ruleset.ts new file mode 100644 index 0000000000000..eaa500f0f3979 --- /dev/null +++ b/packages/@aws-cdk/aws-gamelift/lib/matchmaking-ruleset.ts @@ -0,0 +1,228 @@ +import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; +import * as cdk from '@aws-cdk/core'; +import { Construct } from 'constructs'; +import { CfnMatchmakingRuleSet } from './gamelift.generated'; +import { RuleSetContent } from './matchmaking-ruleset-body'; + + +/** + * Represents a Gamelift matchmaking ruleset + */ +export interface IMatchmakingRuleSet extends cdk.IResource { + /** + * The unique name of the ruleSet. + * + * @attribute + */ + readonly matchmakingRuleSetName: string; + + /** + * The ARN of the ruleSet. + * + * @attribute + */ + readonly matchmakingRuleSetArn: string; + + /** + * Return the given named metric for this matchmaking ruleSet. + */ + metric(metricName: string, props?: cloudwatch.MetricOptions): cloudwatch.Metric; + + /** + * Rule evaluations during the matchmaking process that passed since the last report. + * + * This metric is limited to the top 50 rules. + */ + metricRuleEvaluationsPassed(props?: cloudwatch.MetricOptions): cloudwatch.Metric; + + + /** + * Rule evaluations during matchmaking that failed since the last report. + * + * This metric is limited to the top 50 rules. + */ + metricRuleEvaluationsFailed(props?: cloudwatch.MetricOptions): cloudwatch.Metric; +} + +/** + * Properties for a new matchmaking ruleSet + */ +export interface MatchmakingRuleSetProps { + /** + * A unique identifier for the matchmaking rule set. + * A matchmaking configuration identifies the rule set it uses by this name value. + * + * Note: the rule set name is different from the optional name field in the rule set body + */ + readonly matchmakingRuleSetName: string; + + /** + * A collection of matchmaking rules. + */ + readonly content: RuleSetContent; +} + +/** + * A full specification of a matchmaking ruleSet that can be used to import it fluently into the CDK application. + */ +export interface MatchmakingRuleSetAttributes { + /** + * The ARN of the matchmaking ruleSet + * + * At least one of `matchmakingRuleSetArn` and `matchmakingRuleSetName` must be provided. + * + * @default derived from `matchmakingRuleSetName`. + */ + readonly matchmakingRuleSetArn?: string; + + /** + * The unique name of the matchmaking ruleSet + * + * At least one of `ruleSetName` and `matchmakingRuleSetArn` must be provided. + * + * @default derived from `matchmakingRuleSetArn`. + */ + readonly matchmakingRuleSetName?: string; + +} + +/** + * Base class for new and imported GameLift matchmaking ruleSet. + */ +export abstract class MatchmakingRuleSetBase extends cdk.Resource implements IMatchmakingRuleSet { + + /** + * The unique name of the ruleSet. + */ + public abstract readonly matchmakingRuleSetName: string; + /** + * The ARN of the ruleSet. + */ + public abstract readonly matchmakingRuleSetArn: string; + + public metric(metricName: string, props?: cloudwatch.MetricOptions): cloudwatch.Metric { + return new cloudwatch.Metric({ + namespace: 'AWS/GameLift', + metricName: metricName, + dimensionsMap: { + FleetId: this.matchmakingRuleSetName, + }, + ...props, + }).attachTo(this); + } + + public metricRuleEvaluationsPassed(props?: cloudwatch.MetricOptions): cloudwatch.Metric { + return this.metric('RuleEvaluationsPassed', props); + } + public metricRuleEvaluationsFailed(props?: cloudwatch.MetricOptions): cloudwatch.Metric { + return this.metric('RuleEvaluationsFailed', props); + } +} + +/** + * Creates a new rule set for FlexMatch matchmaking. + * + * The rule set determines the two key elements of a match: your game's team structure and size, and how to group players together for the best possible match. + * + * For example, a rule set might describe a match like this: + * - Create a match with two teams of five players each, one team is the defenders and the other team the invaders. + * - A team can have novice and experienced players, but the average skill of the two teams must be within 10 points of each other. + * - If no match is made after 30 seconds, gradually relax the skill requirements. + * + * Rule sets must be defined in the same Region as the matchmaking configuration they are used with. + * + * @see https://docs.aws.amazon.com/gamelift/latest/flexmatchguide/match-rulesets.html + * + * @resource AWS::GameLift::MatchmakingRuleSet + */ +export class MatchmakingRuleSet extends MatchmakingRuleSetBase { + + /** + * Import a ruleSet into CDK using its name + */ + static fromMatchmakingRuleSetName(scope: Construct, id: string, matchmakingRuleSetName: string): IMatchmakingRuleSet { + return this.fromMatchmakingRuleSetAttributes(scope, id, { matchmakingRuleSetName }); + } + + /** + * Import a ruleSet into CDK using its ARN + */ + static fromMatchmakingRuleSetArn(scope: Construct, id: string, matchmakingRuleSetArn: string): IMatchmakingRuleSet { + return this.fromMatchmakingRuleSetAttributes(scope, id, { matchmakingRuleSetArn }); + } + + /** + * Import an existing matchmaking ruleSet from its attributes. + */ + static fromMatchmakingRuleSetAttributes(scope: Construct, id: string, attrs: MatchmakingRuleSetAttributes): IMatchmakingRuleSet { + if (!attrs.matchmakingRuleSetName && !attrs.matchmakingRuleSetArn) { + throw new Error('Either matchmakingRuleSetName or matchmakingRuleSetArn must be provided in MatchmakingRuleSetAttributes'); + } + const matchmakingRuleSetName = attrs.matchmakingRuleSetName ?? + cdk.Stack.of(scope).splitArn(attrs.matchmakingRuleSetArn!, cdk.ArnFormat.SLASH_RESOURCE_NAME).resourceName; + + if (!matchmakingRuleSetName) { + throw new Error(`No matchmaking ruleSet identifier found in ARN: '${attrs.matchmakingRuleSetArn}'`); + } + + const matchmakingRuleSetArn = attrs.matchmakingRuleSetArn ?? cdk.Stack.of(scope).formatArn({ + service: 'gamelift', + resource: 'matchmakingruleset', + resourceName: attrs.matchmakingRuleSetName, + arnFormat: cdk.ArnFormat.SLASH_RESOURCE_NAME, + }); + class Import extends MatchmakingRuleSetBase { + public readonly matchmakingRuleSetName = matchmakingRuleSetName!; + public readonly matchmakingRuleSetArn = matchmakingRuleSetArn; + + constructor(s: Construct, i: string) { + super(s, i, { + environmentFromArn: matchmakingRuleSetArn, + }); + } + } + return new Import(scope, id); + } + + /** + * The unique name of the ruleSet. + */ + public readonly matchmakingRuleSetName: string; + + /** + * The ARN of the ruleSet. + */ + public readonly matchmakingRuleSetArn: string; + + constructor(scope: Construct, id: string, props: MatchmakingRuleSetProps) { + super(scope, id, { + physicalName: props.matchmakingRuleSetName, + }); + + if (!cdk.Token.isUnresolved(props.matchmakingRuleSetName)) { + if (props.matchmakingRuleSetName.length > 128) { + throw new Error(`RuleSet name can not be longer than 128 characters but has ${props.matchmakingRuleSetName.length} characters.`); + } + + if (!/^[a-zA-Z0-9-\.]+$/.test(props.matchmakingRuleSetName)) { + throw new Error(`RuleSet name ${props.matchmakingRuleSetName} can contain only letters, numbers, hyphens, back slash or dot with no spaces.`); + } + } + const content = props.content.bind(this); + + const resource = new CfnMatchmakingRuleSet(this, 'Resource', { + name: props.matchmakingRuleSetName, + ruleSetBody: content.ruleSetBody, + }); + + this.matchmakingRuleSetName = this.getResourceNameAttribute(resource.ref); + this.matchmakingRuleSetArn = this.getResourceArnAttribute(resource.attrArn, { + service: 'gamelift', + resource: 'matchmakingruleset', + resourceName: this.physicalName, + arnFormat: cdk.ArnFormat.SLASH_RESOURCE_NAME, + }); + } + + +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-gamelift/test/integ.matchmaking-ruleset.js.snapshot/BuildDefaultTestDeployAssert0688841C.assets.json b/packages/@aws-cdk/aws-gamelift/test/integ.matchmaking-ruleset.js.snapshot/BuildDefaultTestDeployAssert0688841C.assets.json new file mode 100644 index 0000000000000..b35a218c4d89e --- /dev/null +++ b/packages/@aws-cdk/aws-gamelift/test/integ.matchmaking-ruleset.js.snapshot/BuildDefaultTestDeployAssert0688841C.assets.json @@ -0,0 +1,19 @@ +{ + "version": "22.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "BuildDefaultTestDeployAssert0688841C.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-gamelift/test/integ.matchmaking-ruleset.js.snapshot/BuildDefaultTestDeployAssert0688841C.template.json b/packages/@aws-cdk/aws-gamelift/test/integ.matchmaking-ruleset.js.snapshot/BuildDefaultTestDeployAssert0688841C.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk/aws-gamelift/test/integ.matchmaking-ruleset.js.snapshot/BuildDefaultTestDeployAssert0688841C.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-gamelift/test/integ.matchmaking-ruleset.js.snapshot/aws-gamelift-build.assets.json b/packages/@aws-cdk/aws-gamelift/test/integ.matchmaking-ruleset.js.snapshot/aws-gamelift-build.assets.json new file mode 100644 index 0000000000000..de6ee49a07f2e --- /dev/null +++ b/packages/@aws-cdk/aws-gamelift/test/integ.matchmaking-ruleset.js.snapshot/aws-gamelift-build.assets.json @@ -0,0 +1,19 @@ +{ + "version": "22.0.0", + "files": { + "3136917a7c2150678ed63057dcd3ea4e3218f8bca520725e637878a51ebc06ee": { + "source": { + "path": "aws-gamelift-build.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "3136917a7c2150678ed63057dcd3ea4e3218f8bca520725e637878a51ebc06ee.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-gamelift/test/integ.matchmaking-ruleset.js.snapshot/aws-gamelift-build.template.json b/packages/@aws-cdk/aws-gamelift/test/integ.matchmaking-ruleset.js.snapshot/aws-gamelift-build.template.json new file mode 100644 index 0000000000000..f34da5610324e --- /dev/null +++ b/packages/@aws-cdk/aws-gamelift/test/integ.matchmaking-ruleset.js.snapshot/aws-gamelift-build.template.json @@ -0,0 +1,60 @@ +{ + "Resources": { + "MatchmakingRuleSet360A3710": { + "Type": "AWS::GameLift::MatchmakingRuleSet", + "Properties": { + "Name": "my-test-ruleset", + "RuleSetBody": "{\"name\":\"three_team_game\",\"ruleLanguageVersion\":\"1.0\",\"playerAttributes\":[{\"name\":\"skill\",\"type\":\"number\",\"default\":10},{\"name\":\"character\",\"type\":\"string_list\",\"default\":[\"peasant\"]}],\"teams\":[{\"name\":\"trio\",\"minPlayers\":3,\"maxPlayers\":5,\"quantity\":3}],\"rules\":[{\"name\":\"FairTeamSkill\",\"description\":\"The average skill of players in each team is within 10 points from the average skill of players in the match\",\"type\":\"distance\",\"measurements\":[\"avg(teams[*].players.attributes[skill])\"],\"referenceValue\":\"avg(flatten(teams[*].players.attributes[skill]))\",\"maxDistance\":10},{\"name\":\"CloseTeamSizes\",\"description\":\"Only launch a game when the team sizes are within 1 of each other. e.g. 3 v 3 v 4 is okay, but not 3 v 5 v 5\",\"type\":\"distance\",\"measurements\":[\"max(count(teams[*].players))\"],\"referenceValue\":\"min(count(teams[*].players))\",\"maxDistance\":1},{\"name\":\"OverallMedicLimit\",\"description\":\"Don't allow more than 5 medics in the game\",\"type\":\"collection\",\"measurements\":[\"flatten(teams[*].players.attributes[character])\"],\"operation\":\"contains\",\"referenceValue\":\"medic\",\"maxCount\":5},{\"name\":\"FastConnection\",\"description\":\"Prefer matches with fast player connections first\",\"type\":\"latency\",\"maxLatency\":50}],\"expansions\":[{\"target\":\"rules[FastConnection].maxLatency\",\"steps\":[{\"waitTimeSeconds\":10,\"value\":100},{\"waitTimeSeconds\":20,\"value\":150}]}]}" + } + } + }, + "Outputs": { + "MatchmakingRuleSetArn": { + "Value": { + "Fn::GetAtt": [ + "MatchmakingRuleSet360A3710", + "Arn" + ] + } + }, + "MatchmakingRuleSetName": { + "Value": { + "Ref": "MatchmakingRuleSet360A3710" + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-gamelift/test/integ.matchmaking-ruleset.js.snapshot/cdk.out b/packages/@aws-cdk/aws-gamelift/test/integ.matchmaking-ruleset.js.snapshot/cdk.out new file mode 100644 index 0000000000000..145739f539580 --- /dev/null +++ b/packages/@aws-cdk/aws-gamelift/test/integ.matchmaking-ruleset.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"22.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-gamelift/test/integ.matchmaking-ruleset.js.snapshot/integ.json b/packages/@aws-cdk/aws-gamelift/test/integ.matchmaking-ruleset.js.snapshot/integ.json new file mode 100644 index 0000000000000..afc2a36ad8185 --- /dev/null +++ b/packages/@aws-cdk/aws-gamelift/test/integ.matchmaking-ruleset.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "22.0.0", + "testCases": { + "Build/DefaultTest": { + "stacks": [ + "aws-gamelift-build" + ], + "assertionStack": "Build/DefaultTest/DeployAssert", + "assertionStackName": "BuildDefaultTestDeployAssert0688841C" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-gamelift/test/integ.matchmaking-ruleset.js.snapshot/manifest.json b/packages/@aws-cdk/aws-gamelift/test/integ.matchmaking-ruleset.js.snapshot/manifest.json new file mode 100644 index 0000000000000..77ef071d85ea7 --- /dev/null +++ b/packages/@aws-cdk/aws-gamelift/test/integ.matchmaking-ruleset.js.snapshot/manifest.json @@ -0,0 +1,123 @@ +{ + "version": "22.0.0", + "artifacts": { + "aws-gamelift-build.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "aws-gamelift-build.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "aws-gamelift-build": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "aws-gamelift-build.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/3136917a7c2150678ed63057dcd3ea4e3218f8bca520725e637878a51ebc06ee.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "aws-gamelift-build.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "aws-gamelift-build.assets" + ], + "metadata": { + "/aws-gamelift-build/MatchmakingRuleSet/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MatchmakingRuleSet360A3710" + } + ], + "/aws-gamelift-build/MatchmakingRuleSetArn": [ + { + "type": "aws:cdk:logicalId", + "data": "MatchmakingRuleSetArn" + } + ], + "/aws-gamelift-build/MatchmakingRuleSetName": [ + { + "type": "aws:cdk:logicalId", + "data": "MatchmakingRuleSetName" + } + ], + "/aws-gamelift-build/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/aws-gamelift-build/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "aws-gamelift-build" + }, + "BuildDefaultTestDeployAssert0688841C.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "BuildDefaultTestDeployAssert0688841C.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "BuildDefaultTestDeployAssert0688841C": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "BuildDefaultTestDeployAssert0688841C.template.json", + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "BuildDefaultTestDeployAssert0688841C.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "BuildDefaultTestDeployAssert0688841C.assets" + ], + "metadata": { + "/Build/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/Build/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "Build/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-gamelift/test/integ.matchmaking-ruleset.js.snapshot/tree.json b/packages/@aws-cdk/aws-gamelift/test/integ.matchmaking-ruleset.js.snapshot/tree.json new file mode 100644 index 0000000000000..7d1746af43f3c --- /dev/null +++ b/packages/@aws-cdk/aws-gamelift/test/integ.matchmaking-ruleset.js.snapshot/tree.json @@ -0,0 +1,142 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "aws-gamelift-build": { + "id": "aws-gamelift-build", + "path": "aws-gamelift-build", + "children": { + "MatchmakingRuleSet": { + "id": "MatchmakingRuleSet", + "path": "aws-gamelift-build/MatchmakingRuleSet", + "children": { + "Resource": { + "id": "Resource", + "path": "aws-gamelift-build/MatchmakingRuleSet/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::GameLift::MatchmakingRuleSet", + "aws:cdk:cloudformation:props": { + "name": "my-test-ruleset", + "ruleSetBody": "{\"name\":\"three_team_game\",\"ruleLanguageVersion\":\"1.0\",\"playerAttributes\":[{\"name\":\"skill\",\"type\":\"number\",\"default\":10},{\"name\":\"character\",\"type\":\"string_list\",\"default\":[\"peasant\"]}],\"teams\":[{\"name\":\"trio\",\"minPlayers\":3,\"maxPlayers\":5,\"quantity\":3}],\"rules\":[{\"name\":\"FairTeamSkill\",\"description\":\"The average skill of players in each team is within 10 points from the average skill of players in the match\",\"type\":\"distance\",\"measurements\":[\"avg(teams[*].players.attributes[skill])\"],\"referenceValue\":\"avg(flatten(teams[*].players.attributes[skill]))\",\"maxDistance\":10},{\"name\":\"CloseTeamSizes\",\"description\":\"Only launch a game when the team sizes are within 1 of each other. e.g. 3 v 3 v 4 is okay, but not 3 v 5 v 5\",\"type\":\"distance\",\"measurements\":[\"max(count(teams[*].players))\"],\"referenceValue\":\"min(count(teams[*].players))\",\"maxDistance\":1},{\"name\":\"OverallMedicLimit\",\"description\":\"Don't allow more than 5 medics in the game\",\"type\":\"collection\",\"measurements\":[\"flatten(teams[*].players.attributes[character])\"],\"operation\":\"contains\",\"referenceValue\":\"medic\",\"maxCount\":5},{\"name\":\"FastConnection\",\"description\":\"Prefer matches with fast player connections first\",\"type\":\"latency\",\"maxLatency\":50}],\"expansions\":[{\"target\":\"rules[FastConnection].maxLatency\",\"steps\":[{\"waitTimeSeconds\":10,\"value\":100},{\"waitTimeSeconds\":20,\"value\":150}]}]}" + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-gamelift.CfnMatchmakingRuleSet", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-gamelift.MatchmakingRuleSet", + "version": "0.0.0" + } + }, + "MatchmakingRuleSetArn": { + "id": "MatchmakingRuleSetArn", + "path": "aws-gamelift-build/MatchmakingRuleSetArn", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnOutput", + "version": "0.0.0" + } + }, + "MatchmakingRuleSetName": { + "id": "MatchmakingRuleSetName", + "path": "aws-gamelift-build/MatchmakingRuleSetName", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnOutput", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "aws-gamelift-build/BootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "aws-gamelift-build/CheckBootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + }, + "Build": { + "id": "Build", + "path": "Build", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "Build/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "Build/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.168" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "Build/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "Build/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "Build/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "@aws-cdk/core.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTest", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.168" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-gamelift/test/integ.matchmaking-ruleset.ts b/packages/@aws-cdk/aws-gamelift/test/integ.matchmaking-ruleset.ts new file mode 100644 index 0000000000000..93273d6da1195 --- /dev/null +++ b/packages/@aws-cdk/aws-gamelift/test/integ.matchmaking-ruleset.ts @@ -0,0 +1,29 @@ +import * as path from 'path'; +import * as cdk from '@aws-cdk/core'; +import { CfnOutput } from '@aws-cdk/core'; +import { IntegTest } from '@aws-cdk/integ-tests'; +import { Construct } from 'constructs'; +import * as gamelift from '../lib'; + +class TestStack extends cdk.Stack { + constructor(scope: Construct, id: string, props?: cdk.StackProps) { + super(scope, id, props); + + const ruleSet = new gamelift.MatchmakingRuleSet(this, 'MatchmakingRuleSet', { + matchmakingRuleSetName: 'my-test-ruleset', + content: gamelift.RuleSetContent.fromJsonFile(path.join(__dirname, 'my-ruleset/ruleset.json')), + }); + + new CfnOutput(this, 'MatchmakingRuleSetArn', { value: ruleSet.matchmakingRuleSetArn }); + new CfnOutput(this, 'MatchmakingRuleSetName', { value: ruleSet.matchmakingRuleSetName }); + } +} + +// Beginning of the test suite +const app = new cdk.App(); +const stack = new TestStack(app, 'aws-gamelift-build'); +new IntegTest(app, 'Build', { + testCases: [stack], +}); + +app.synth(); diff --git a/packages/@aws-cdk/aws-gamelift/test/matchmaking-ruleset-body.test.ts b/packages/@aws-cdk/aws-gamelift/test/matchmaking-ruleset-body.test.ts new file mode 100644 index 0000000000000..ac725b0418077 --- /dev/null +++ b/packages/@aws-cdk/aws-gamelift/test/matchmaking-ruleset-body.test.ts @@ -0,0 +1,56 @@ +import * as fs from 'fs'; +import * as path from 'path'; +import * as cdk from '@aws-cdk/core'; +import * as gamelift from '../lib'; + +describe('MatchmakingRuleSetBody', () => { + let stack: cdk.Stack; + + beforeEach(() => { + stack = new cdk.Stack(); + }); + + describe('gamelift.MatchmakingRuleSetBody.fromInline', () => { + test('new RuleSetBody from Inline content', () => { + const ruleSet = gamelift.RuleSetContent.fromInline('{}'); + const content = ruleSet.bind(stack); + expect(content.ruleSetBody).toEqual('{}'); + }); + + test('fails if invlaid JSON format', () => { + expect(() => gamelift.RuleSetContent.fromInline('{ name }')) + .toThrow(/RuleSet body has an invalid Json format/); + }); + + test('fails if content too large', () => { + let incorrectContent = ''; + for (let i = 0; i < 65536; i++) { + incorrectContent += 'A'; + } + + expect(() => gamelift.RuleSetContent.fromInline(JSON.stringify({ name: incorrectContent }))) + .toThrow(/RuleSet body cannot exceed 65535 characters, actual 65547/); + }); + }); + + describe('gamelift.MatchmakingRuleSetBody.fromJsonFile', () => { + test('new RuleSetBody from Json file', () => { + const ruleSet = gamelift.RuleSetContent.fromJsonFile(path.join(__dirname, 'my-ruleset/ruleset.json')); + const content = ruleSet.bind(stack); + const result = JSON.parse(fs.readFileSync(path.join(__dirname, 'my-ruleset/ruleset.json')).toString()); + expect(content.ruleSetBody).toEqual(JSON.stringify(result)); + }); + + test('fails if file not exist', () => { + const content = path.join(__dirname, 'my-ruleset/file-not-exist.json'); + expect(() => gamelift.RuleSetContent.fromJsonFile(content)) + .toThrow(`RuleSet path does not exist, please verify it, actual ${content}`); + }); + + test('fails if file is a directory', () => { + const contentPath = path.join(__dirname, 'my-ruleset'); + expect(() => gamelift.RuleSetContent.fromJsonFile(contentPath)) + .toThrow(`RuleSet path is not link to a single file, please verify your path, actual ${contentPath}`); + }); + }); +}); \ No newline at end of file diff --git a/packages/@aws-cdk/aws-gamelift/test/matchmaking-ruleset.test.ts b/packages/@aws-cdk/aws-gamelift/test/matchmaking-ruleset.test.ts new file mode 100644 index 0000000000000..f04acc0467403 --- /dev/null +++ b/packages/@aws-cdk/aws-gamelift/test/matchmaking-ruleset.test.ts @@ -0,0 +1,137 @@ +import { Template } from '@aws-cdk/assertions'; +import * as cdk from '@aws-cdk/core'; +import * as gamelift from '../lib'; + +describe('MatchmakingRuleSet', () => { + + describe('new', () => { + let stack: cdk.Stack; + const ruleSetBody = JSON.stringify('{}'); + + beforeEach(() => { + stack = new cdk.Stack(); + }); + + test('default new ruleSet', () => { + new gamelift.MatchmakingRuleSet(stack, 'MyMatchmakingRuleSet', { + matchmakingRuleSetName: 'test-ruleSet', + content: gamelift.RuleSetContent.fromInline(ruleSetBody), + }); + + Template.fromStack(stack).hasResource('AWS::GameLift::MatchmakingRuleSet', { + Properties: + { + Name: 'test-ruleSet', + RuleSetBody: ruleSetBody, + }, + }); + }); + + test('with an incorrect name - too long', () => { + let incorrectName = ''; + for (let i = 0; i < 129; i++) { + incorrectName += 'A'; + } + + expect(() => new gamelift.MatchmakingRuleSet(stack, 'MyMatchmakingRuleSet', { + matchmakingRuleSetName: incorrectName, + content: gamelift.RuleSetContent.fromInline(ruleSetBody), + })).toThrow(/RuleSet name can not be longer than 128 characters but has 129 characters./); + }); + + test('with an incorrect name - bad format', () => { + let incorrectName = 'test with space'; + + expect(() => new gamelift.MatchmakingRuleSet(stack, 'MyMatchmakingRuleSet', { + matchmakingRuleSetName: incorrectName, + content: gamelift.RuleSetContent.fromInline(ruleSetBody), + })).toThrow(/RuleSet name test with space can contain only letters, numbers, hyphens, back slash or dot with no spaces./); + }); + }); + + describe('test import methods', () => { + test('MatchmakingRuleSet.fromMatchmakingRuleSetArn', () => { + // GIVEN + const stack2 = new cdk.Stack(); + + // WHEN + const imported = gamelift.MatchmakingRuleSet.fromMatchmakingRuleSetArn(stack2, 'Imported', 'arn:aws:gamelift:us-east-1:123456789012:matchmakingruleset/sample-ruleSet-name'); + + // THEN + expect(imported.matchmakingRuleSetArn).toEqual('arn:aws:gamelift:us-east-1:123456789012:matchmakingruleset/sample-ruleSet-name'); + expect(imported.matchmakingRuleSetName).toEqual('sample-ruleSet-name'); + }); + + test('MatchmakingRuleSet.fromMatchmakingRuleSetName', () => { + // GIVEN + const stack = new cdk.Stack(); + + // WHEN + const imported = gamelift.MatchmakingRuleSet.fromMatchmakingRuleSetName(stack, 'Imported', 'sample-ruleSet-name'); + + // THEN + expect(stack.resolve(imported.matchmakingRuleSetArn)).toStrictEqual({ + 'Fn::Join': ['', [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':gamelift:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':matchmakingruleset/sample-ruleSet-name', + ]], + }); + expect(stack.resolve(imported.matchmakingRuleSetName)).toStrictEqual('sample-ruleSet-name'); + }); + }); + + describe('MatchmakingRuleSet.fromMatchmakingRuleSetAttributes()', () => { + let stack: cdk.Stack; + const matchmakingRuleSetName = 'ruleSet-test-identifier'; + const matchmakingRuleSetArn = `arn:aws:gamelift:ruleSet-region:123456789012:matchmakingruleset/${matchmakingRuleSetName}`; + + beforeEach(() => { + const app = new cdk.App(); + stack = new cdk.Stack(app, 'Base', { + env: { account: '111111111111', region: 'stack-region' }, + }); + }); + + describe('', () => { + test('with required attrs only', () => { + const importedFleet = gamelift.MatchmakingRuleSet.fromMatchmakingRuleSetAttributes(stack, 'ImportedMatchmakingRuleSet', { matchmakingRuleSetArn }); + + expect(importedFleet.matchmakingRuleSetName).toEqual(matchmakingRuleSetName); + expect(importedFleet.matchmakingRuleSetArn).toEqual(matchmakingRuleSetArn); + expect(importedFleet.env.account).toEqual('123456789012'); + expect(importedFleet.env.region).toEqual('ruleSet-region'); + }); + + test('with missing attrs', () => { + expect(() => gamelift.MatchmakingRuleSet.fromMatchmakingRuleSetAttributes(stack, 'ImportedMatchmakingRuleSet', { })) + .toThrow(/Either matchmakingRuleSetName or matchmakingRuleSetArn must be provided in MatchmakingRuleSetAttributes/); + }); + + test('with invalid ARN', () => { + expect(() => gamelift.MatchmakingRuleSet.fromMatchmakingRuleSetAttributes(stack, 'ImportedMatchmakingRuleSet', { matchmakingRuleSetArn: 'arn:aws:gamelift:ruleSet-region:123456789012:matchmakingruleset' })) + .toThrow(/No matchmaking ruleSet identifier found in ARN: 'arn:aws:gamelift:ruleSet-region:123456789012:matchmakingruleset'/); + }); + }); + + describe('for an ruleSet in a different account and region', () => { + let ruleSet: gamelift.IMatchmakingRuleSet; + + beforeEach(() => { + ruleSet = gamelift.MatchmakingRuleSet.fromMatchmakingRuleSetAttributes(stack, 'ImportedMatchmakingRuleSet', { matchmakingRuleSetArn }); + }); + + test("the ruleSet's region is taken from the ARN", () => { + expect(ruleSet.env.region).toBe('ruleSet-region'); + }); + + test("the ruleSet's account is taken from the ARN", () => { + expect(ruleSet.env.account).toBe('123456789012'); + }); + }); + }); +}); \ No newline at end of file diff --git a/packages/@aws-cdk/aws-gamelift/test/my-ruleset/ruleset.json b/packages/@aws-cdk/aws-gamelift/test/my-ruleset/ruleset.json new file mode 100644 index 0000000000000..a490527207ba1 --- /dev/null +++ b/packages/@aws-cdk/aws-gamelift/test/my-ruleset/ruleset.json @@ -0,0 +1,57 @@ +{ + "name": "three_team_game", + "ruleLanguageVersion": "1.0", + "playerAttributes": [{ + "name": "skill", + "type": "number", + "default": 10 + },{ + "name": "character", + "type": "string_list", + "default": [ "peasant" ] + }], + "teams": [{ + "name": "trio", + "minPlayers": 3, + "maxPlayers": 5, + "quantity": 3 + }], + "rules": [{ + "name": "FairTeamSkill", + "description": "The average skill of players in each team is within 10 points from the average skill of players in the match", + "type": "distance", + "measurements": [ "avg(teams[*].players.attributes[skill])" ], + "referenceValue": "avg(flatten(teams[*].players.attributes[skill]))", + "maxDistance": 10 + }, { + "name": "CloseTeamSizes", + "description": "Only launch a game when the team sizes are within 1 of each other. e.g. 3 v 3 v 4 is okay, but not 3 v 5 v 5", + "type": "distance", + "measurements": [ "max(count(teams[*].players))"], + "referenceValue": "min(count(teams[*].players))", + "maxDistance": 1 + }, { + "name": "OverallMedicLimit", + "description": "Don't allow more than 5 medics in the game", + "type": "collection", + "measurements": [ "flatten(teams[*].players.attributes[character])"], + "operation": "contains", + "referenceValue": "medic", + "maxCount": 5 + }, { + "name": "FastConnection", + "description": "Prefer matches with fast player connections first", + "type": "latency", + "maxLatency": 50 + }], + "expansions": [{ + "target": "rules[FastConnection].maxLatency", + "steps": [{ + "waitTimeSeconds": 10, + "value": 100 + }, { + "waitTimeSeconds": 20, + "value": 150 + }] + }] +} \ No newline at end of file