jonny-rimek / wowmate

combatlog analysis for world of warcraft players
http://wowmate.io
MIT License
4 stars 0 forks source link

Add created at field to dynamodb entries #360

Closed jonny-rimek closed 3 years ago

jonny-rimek commented 3 years ago

fix #360 fix #351

github-actions[bot] commented 3 years ago
Stack wm
Resources
[~] AWS::Lambda::Function Api-/Lambda ApiLambdaB75D04FB 
 ├─ [~] Code
 │   └─ [~] .S3Key:
 │       ├─ [-] c070daac02489a748e9e0644a4457029e3a83e5040022b98ebd633daa7185557.zip
 │       └─ [+] 2a2dd2b026f1d171665207eb38bc3f350f5dd3ffa7de786ad8e4158b50154c8e.zip
 └─ [~] Metadata
     └─ [~] .aws:asset:path:
         ├─ [-] asset.c070daac02489a748e9e0644a4457029e3a83e5040022b98ebd633daa7185557
         └─ [+] asset.2a2dd2b026f1d171665207eb38bc3f350f5dd3ffa7de786ad8e4158b50154c8e
[~] Custom::CDKBucketDeployment Frontend-/DeployWebsite/CustomResource FrontendDeployWebsiteCustomResource1B4A6C7F 
 └─ [~] SourceObjectKeys
     └─ @@ -1,3 +1,3 @@
        [ ] [
        [-]   "243ebf0b364a71e47601ee0c80c9b50fe18a3e8ea11ddca13e7d2086a947c771.zip"
        [+]   "c1587a27d93e18f26a3020c355e683dc77e257bf06e89cafd5a4017bbebcbb64.zip"
        [ ] ]
[~] AWS::Synthetics::Canary Canary-/API CanaryAPI9F4B22EA 
 └─ [~] Code
     └─ [~] .Script:
         ├─ [-] 
            var synthetics = require('Synthetics');
            const log = require('SyntheticsLogger');

            const apiCanaryBlueprint = async function () {
                const hostname = process.env.API_URL

                // Handle validation for positive scenario
                const validateSuccessful = async function(res) {
                    return new Promise((resolve, reject) => {
                        if (res.statusCode < 200 || res.statusCode > 299) {
                            throw res.statusCode + ' ' + res.statusMessage;
                        }

                        let responseBody = '';
                        res.on('data', (d) => {
                            responseBody += d;
                        });

                        res.on('end', () => {
                            // Add validation on 'responseBody' here if required.
                            resolve();
                        });
                    });
                };

                // Set request option for Verify /combatlogs/keys
                let requestOptionsStep1 = {
                    hostname: hostname,
                    method: 'GET',
                    path: '/combatlogs/keys',
                    port: '443',
                    protocol: 'https:',
                    body: "",
                    headers: {}
                };
                requestOptionsStep1['headers']['User-Agent'] = [synthetics.getCanaryUserAgentString(), requestOptionsStep1['headers']['User-Agent']].join(' ');

                // Set step config option for Verify /combatlogs/keys
                let stepConfig1 = {
                    includeRequestHeaders: false,
                    includeResponseHeaders: false,
                    includeRequestBody: false,
                    includeResponseBody: false,
                    restrictedHeaders: [],
                    continueOnHttpStepFailure: true
                };

                await synthetics.executeHttpStep('Verify /combatlogs/keys', requestOptionsStep1, validateSuccessful, stepConfig1);

                // Set request option for Verify /presign/{filename}
                let requestOptionsStep2 = {
                    hostname: hostname,
                    method: 'POST',
                    path: '/presign/test.txt',
                    port: '443',
                    protocol: 'https:',
                    body: "",
                    headers: {}
                };
                requestOptionsStep2['headers']['User-Agent'] = [synthetics.getCanaryUserAgentString(), requestOptionsStep2['headers']['User-Agent']].join(' ');

                // Set step config option for Verify /presign/{filename}
                let stepConfig2 = {
                    includeRequestHeaders: false,
                    includeResponseHeaders: false,
                    includeRequestBody: false,
                    includeResponseBody: false,
                    restrictedHeaders: [],
                    continueOnHttpStepFailure: true
                };

                await synthetics.executeHttpStep('Verify /presign/{filename}', requestOptionsStep2, validateSuccessful, stepConfig2);

                // Set request option for Verify /combatlogs/keys/{dungeon_id}
                let requestOptionsStep3 = {
                    hostname: hostname,
                    method: 'GET',
                    path: '/combatlogs/keys/2291',
                    port: '443',
                    protocol: 'https:',
                    body: "",
                    headers: {}
                };
                requestOptionsStep3['headers']['User-Agent'] = [synthetics.getCanaryUserAgentString(), requestOptionsStep3['headers']['User-Agent']].join(' ');

                // Set step config option for Verify /combatlogs/keys/{dungeon_id}
                let stepConfig3 = {
                    includeRequestHeaders: false,
                    includeResponseHeaders: false,
                    includeRequestBody: false, 
                    includeResponseBody: false,
                    restrictedHeaders: [],
                    continueOnHttpStepFailure: true
                };

                // TODO: XMLHttpRequest
                // TODO: check response, must include body
                await synthetics.executeHttpStep('Verify /combatlogs/keys/{dungeon_id}', requestOptionsStep3, validateSuccessful, stepConfig3);

                const combatlogUUID = getCombatlogUUID(hostname)
                // Set request option for Verify /combatlogs/keys/{combatlog_uuid}/player-damage-done
                let requestOptionsStep4 = {
                    hostname: hostname,
                    method: 'GET',
                    path: '/combatlogs/keys/' + combatlogUUID,
                    port: '443',
                    protocol: 'https:',
                    body: "",
                    headers: {}
                };
                requestOptionsStep4['headers']['User-Agent'] = [synthetics.getCanaryUserAgentString(), requestOptionsStep4['headers']['User-Agent']].join(' ');

                // Set step config option for Verify /combatlogs/keys/{combatlog_uuid}/player-damage-done
                let stepConfig4 = {
                    includeRequestHeaders: false,
                    includeResponseHeaders: false,
                    includeRequestBody: false,
                    includeResponseBody: false,
                    restrictedHeaders: [],
                    continueOnHttpStepFailure: true
                };

                await synthetics.executeHttpStep('Verify /combatlogs/keys/{combatlog_uuid}/player-damage-done', requestOptionsStep4, validateSuccessful, stepConfig4);
            };

            // neither fetch or XMLHttpRequest works and I can't install packages with inline code
            function getCombatlogUUID(apiUrl) {
                if (process.env.STAGE === "prod") {
                    return "ac18f293-e573-4994-8745-2a291d12bf9a"
                }
                return "2ac44ef5-d053-4232-881e-c8c21300ec4f"
                // let url = "https://" + apiUrl + "/combatlogs/keys"
                // let xmlHttp = new XMLHttpRequest()
                // xmlHttp.open( "GET", url, false ) // false for synchronous request
                // xmlHttp.send( null )
                // const resp = JSON.parse(xmlHttp.responseText)
                // return resp.data[0].combatlog_uuid
            }

            exports.handler = async () => {
                return await apiCanaryBlueprint();
            };

         └─ [+] 
            var synthetics = require('Synthetics');
            const log = require('SyntheticsLogger');

            const apiCanaryBlueprint = async function () {
                const hostname = process.env.API_URL

                // Handle validation for positive scenario
                const validateSuccessful = async function(res) {
                    return new Promise((resolve, reject) => {
                        if (res.statusCode < 200 || res.statusCode > 299) {
                            throw res.statusCode + ' ' + res.statusMessage;
                        }

                        let responseBody = '';
                        res.on('data', (d) => {
                            responseBody += d;
                        });

                        res.on('end', () => {
                            // Add validation on 'responseBody' here if required.
                            resolve();
                        });
                    });
                };

                // Set request option for Verify /combatlogs/keys
                let requestOptionsStep1 = {
                    hostname: hostname,
                    method: 'GET',
                    path: '/combatlogs/keys',
                    port: '443',
                    protocol: 'https:',
                    body: "",
                    headers: {}
                };
                requestOptionsStep1['headers']['User-Agent'] = [synthetics.getCanaryUserAgentString(), requestOptionsStep1['headers']['User-Agent']].join(' ');

                // Set step config option for Verify /combatlogs/keys
                let stepConfig1 = {
                    includeRequestHeaders: false,
                    includeResponseHeaders: false,
                    includeRequestBody: false,
                    includeResponseBody: false,
                    restrictedHeaders: [],
                    continueOnHttpStepFailure: true
                };

                await synthetics.executeHttpStep('Verify /combatlogs/keys', requestOptionsStep1, validateSuccessful, stepConfig1);

                // Set request option for Verify /presign/{filename}
                let requestOptionsStep2 = {
                    hostname: hostname,
                    method: 'POST',
                    path: '/presign/test.txt',
                    port: '443',
                    protocol: 'https:',
                    body: "",
                    headers: {}
                };
                requestOptionsStep2['headers']['User-Agent'] = [synthetics.getCanaryUserAgentString(), requestOptionsStep2['headers']['User-Agent']].join(' ');

                // Set step config option for Verify /presign/{filename}
                let stepConfig2 = {
                    includeRequestHeaders: false,
                    includeResponseHeaders: false,
                    includeRequestBody: false,
                    includeResponseBody: false,
                    restrictedHeaders: [],
                    continueOnHttpStepFailure: true
                };

                await synthetics.executeHttpStep('Verify /presign/{filename}', requestOptionsStep2, validateSuccessful, stepConfig2);

                // Set request option for Verify /combatlogs/keys/{dungeon_id}
                let requestOptionsStep3 = {
                    hostname: hostname,
                    method: 'GET',
                    path: '/combatlogs/keys/2291',
                    port: '443',
                    protocol: 'https:',
                    body: "",
                    headers: {}
                };
                requestOptionsStep3['headers']['User-Agent'] = [synthetics.getCanaryUserAgentString(), requestOptionsStep3['headers']['User-Agent']].join(' ');

                // Set step config option for Verify /combatlogs/keys/{dungeon_id}
                let stepConfig3 = {
                    includeRequestHeaders: false,
                    includeResponseHeaders: false,
                    includeRequestBody: false, 
                    includeResponseBody: false,
                    restrictedHeaders: [],
                    continueOnHttpStepFailure: true
                };

                // TODO: XMLHttpRequest
                // TODO: check response, must include body
                await synthetics.executeHttpStep('Verify /combatlogs/keys/{dungeon_id}', requestOptionsStep3, validateSuccessful, stepConfig3);

                const combatlogUUID = getCombatlogUUID(hostname)
                // Set request option for Verify /combatlogs/keys/{combatlog_uuid}/player-damage-done
                let requestOptionsStep4 = {
                    hostname: hostname,
                    method: 'GET',
                    path: '/combatlogs/keys/' + combatlogUUID,
                    port: '443',
                    protocol: 'https:',
                    body: "",
                    headers: {}
                };
                requestOptionsStep4['headers']['User-Agent'] = [synthetics.getCanaryUserAgentString(), requestOptionsStep4['headers']['User-Agent']].join(' ');

                // Set step config option for Verify /combatlogs/keys/{combatlog_uuid}/player-damage-done
                let stepConfig4 = {
                    includeRequestHeaders: false,
                    includeResponseHeaders: false,
                    includeRequestBody: false,
                    includeResponseBody: false,
                    restrictedHeaders: [],
                    continueOnHttpStepFailure: true
                };

                await synthetics.executeHttpStep('Verify /combatlogs/keys/{combatlog_uuid}/player-damage-done', requestOptionsStep4, validateSuccessful, stepConfig4);

                // ----------------------------------------------------
                // Set request option for Verify /
                let requestOptionsStep5 = {
                    hostname: hostname,
                    method: 'GET',
                    path: '/',
                    port: '443',
                    protocol: 'https:',
                    body: "",
                    headers: {}
                };
                requestOptionsStep4['headers']['User-Agent'] = [synthetics.getCanaryUserAgentString(), requestOptionsStep4['headers']['User-Agent']].join(' ');

                // Set step config option for Verify /
                let stepConfig5 = {
                    includeRequestHeaders: false,
                    includeResponseHeaders: false,
                    includeRequestBody: false,
                    includeResponseBody: false,
                    restrictedHeaders: [],
                    continueOnHttpStepFailure: true
                };

                await synthetics.executeHttpStep('Verify /', requestOptionsStep5, validateSuccessful, stepConfig5);
            };

            // neither fetch or XMLHttpRequest works and I can't install packages with inline code
            function getCombatlogUUID(apiUrl) {
                if (process.env.STAGE === "prod") {
                    return "ac18f293-e573-4994-8745-2a291d12bf9a"
                }
                return "2ac44ef5-d053-4232-881e-c8c21300ec4f"
                // let url = "https://" + apiUrl + "/combatlogs/keys"
                // let xmlHttp = new XMLHttpRequest()
                // xmlHttp.open( "GET", url, false ) // false for synchronous request
                // xmlHttp.send( null )
                // const resp = JSON.parse(xmlHttp.responseText)
                // return resp.data[0].combatlog_uuid
            }

            exports.handler = async () => {
                return await apiCanaryBlueprint();
            };
github-actions[bot] commented 3 years ago
Stack wm
Resources
[~] AWS::Lambda::Function Api-/Lambda ApiLambdaB75D04FB 
 ├─ [~] Code
 │   └─ [~] .S3Key:
 │       ├─ [-] c070daac02489a748e9e0644a4457029e3a83e5040022b98ebd633daa7185557.zip
 │       └─ [+] 2a2dd2b026f1d171665207eb38bc3f350f5dd3ffa7de786ad8e4158b50154c8e.zip
 └─ [~] Metadata
     └─ [~] .aws:asset:path:
         ├─ [-] asset.c070daac02489a748e9e0644a4457029e3a83e5040022b98ebd633daa7185557
         └─ [+] asset.2a2dd2b026f1d171665207eb38bc3f350f5dd3ffa7de786ad8e4158b50154c8e
[~] Custom::CDKBucketDeployment Frontend-/DeployWebsite/CustomResource FrontendDeployWebsiteCustomResource1B4A6C7F 
 └─ [~] SourceObjectKeys
     └─ @@ -1,3 +1,3 @@
        [ ] [
        [-]   "243ebf0b364a71e47601ee0c80c9b50fe18a3e8ea11ddca13e7d2086a947c771.zip"
        [+]   "c1587a27d93e18f26a3020c355e683dc77e257bf06e89cafd5a4017bbebcbb64.zip"
        [ ] ]
[~] AWS::Synthetics::Canary Canary-/API CanaryAPI9F4B22EA 
 └─ [~] Code
     └─ [~] .Script:
         ├─ [-] 
            var synthetics = require('Synthetics');
            const log = require('SyntheticsLogger');

            const apiCanaryBlueprint = async function () {
                const hostname = process.env.API_URL

                // Handle validation for positive scenario
                const validateSuccessful = async function(res) {
                    return new Promise((resolve, reject) => {
                        if (res.statusCode < 200 || res.statusCode > 299) {
                            throw res.statusCode + ' ' + res.statusMessage;
                        }

                        let responseBody = '';
                        res.on('data', (d) => {
                            responseBody += d;
                        });

                        res.on('end', () => {
                            // Add validation on 'responseBody' here if required.
                            resolve();
                        });
                    });
                };

                // Set request option for Verify /combatlogs/keys
                let requestOptionsStep1 = {
                    hostname: hostname,
                    method: 'GET',
                    path: '/combatlogs/keys',
                    port: '443',
                    protocol: 'https:',
                    body: "",
                    headers: {}
                };
                requestOptionsStep1['headers']['User-Agent'] = [synthetics.getCanaryUserAgentString(), requestOptionsStep1['headers']['User-Agent']].join(' ');

                // Set step config option for Verify /combatlogs/keys
                let stepConfig1 = {
                    includeRequestHeaders: false,
                    includeResponseHeaders: false,
                    includeRequestBody: false,
                    includeResponseBody: false,
                    restrictedHeaders: [],
                    continueOnHttpStepFailure: true
                };

                await synthetics.executeHttpStep('Verify /combatlogs/keys', requestOptionsStep1, validateSuccessful, stepConfig1);

                // Set request option for Verify /presign/{filename}
                let requestOptionsStep2 = {
                    hostname: hostname,
                    method: 'POST',
                    path: '/presign/test.txt',
                    port: '443',
                    protocol: 'https:',
                    body: "",
                    headers: {}
                };
                requestOptionsStep2['headers']['User-Agent'] = [synthetics.getCanaryUserAgentString(), requestOptionsStep2['headers']['User-Agent']].join(' ');

                // Set step config option for Verify /presign/{filename}
                let stepConfig2 = {
                    includeRequestHeaders: false,
                    includeResponseHeaders: false,
                    includeRequestBody: false,
                    includeResponseBody: false,
                    restrictedHeaders: [],
                    continueOnHttpStepFailure: true
                };

                await synthetics.executeHttpStep('Verify /presign/{filename}', requestOptionsStep2, validateSuccessful, stepConfig2);

                // Set request option for Verify /combatlogs/keys/{dungeon_id}
                let requestOptionsStep3 = {
                    hostname: hostname,
                    method: 'GET',
                    path: '/combatlogs/keys/2291',
                    port: '443',
                    protocol: 'https:',
                    body: "",
                    headers: {}
                };
                requestOptionsStep3['headers']['User-Agent'] = [synthetics.getCanaryUserAgentString(), requestOptionsStep3['headers']['User-Agent']].join(' ');

                // Set step config option for Verify /combatlogs/keys/{dungeon_id}
                let stepConfig3 = {
                    includeRequestHeaders: false,
                    includeResponseHeaders: false,
                    includeRequestBody: false, 
                    includeResponseBody: false,
                    restrictedHeaders: [],
                    continueOnHttpStepFailure: true
                };

                // TODO: XMLHttpRequest
                // TODO: check response, must include body
                await synthetics.executeHttpStep('Verify /combatlogs/keys/{dungeon_id}', requestOptionsStep3, validateSuccessful, stepConfig3);

                const combatlogUUID = getCombatlogUUID(hostname)
                // Set request option for Verify /combatlogs/keys/{combatlog_uuid}/player-damage-done
                let requestOptionsStep4 = {
                    hostname: hostname,
                    method: 'GET',
                    path: '/combatlogs/keys/' + combatlogUUID,
                    port: '443',
                    protocol: 'https:',
                    body: "",
                    headers: {}
                };
                requestOptionsStep4['headers']['User-Agent'] = [synthetics.getCanaryUserAgentString(), requestOptionsStep4['headers']['User-Agent']].join(' ');

                // Set step config option for Verify /combatlogs/keys/{combatlog_uuid}/player-damage-done
                let stepConfig4 = {
                    includeRequestHeaders: false,
                    includeResponseHeaders: false,
                    includeRequestBody: false,
                    includeResponseBody: false,
                    restrictedHeaders: [],
                    continueOnHttpStepFailure: true
                };

                await synthetics.executeHttpStep('Verify /combatlogs/keys/{combatlog_uuid}/player-damage-done', requestOptionsStep4, validateSuccessful, stepConfig4);
            };

            // neither fetch or XMLHttpRequest works and I can't install packages with inline code
            function getCombatlogUUID(apiUrl) {
                if (process.env.STAGE === "prod") {
                    return "ac18f293-e573-4994-8745-2a291d12bf9a"
                }
                return "2ac44ef5-d053-4232-881e-c8c21300ec4f"
                // let url = "https://" + apiUrl + "/combatlogs/keys"
                // let xmlHttp = new XMLHttpRequest()
                // xmlHttp.open( "GET", url, false ) // false for synchronous request
                // xmlHttp.send( null )
                // const resp = JSON.parse(xmlHttp.responseText)
                // return resp.data[0].combatlog_uuid
            }

            exports.handler = async () => {
                return await apiCanaryBlueprint();
            };

         └─ [+] 
            var synthetics = require('Synthetics');
            const log = require('SyntheticsLogger');

            const apiCanaryBlueprint = async function () {
                const hostname = process.env.API_URL

                // Handle validation for positive scenario
                const validateSuccessful = async function(res) {
                    return new Promise((resolve, reject) => {
                        if (res.statusCode < 200 || res.statusCode > 299) {
                            throw res.statusCode + ' ' + res.statusMessage;
                        }

                        let responseBody = '';
                        res.on('data', (d) => {
                            responseBody += d;
                        });

                        res.on('end', () => {
                            // Add validation on 'responseBody' here if required.
                            resolve();
                        });
                    });
                };

                // Set request option for Verify /combatlogs/keys
                let requestOptionsStep1 = {
                    hostname: hostname,
                    method: 'GET',
                    path: '/combatlogs/keys',
                    port: '443',
                    protocol: 'https:',
                    body: "",
                    headers: {}
                };
                requestOptionsStep1['headers']['User-Agent'] = [synthetics.getCanaryUserAgentString(), requestOptionsStep1['headers']['User-Agent']].join(' ');

                // Set step config option for Verify /combatlogs/keys
                let stepConfig1 = {
                    includeRequestHeaders: false,
                    includeResponseHeaders: false,
                    includeRequestBody: false,
                    includeResponseBody: false,
                    restrictedHeaders: [],
                    continueOnHttpStepFailure: true
                };

                await synthetics.executeHttpStep('Verify /combatlogs/keys', requestOptionsStep1, validateSuccessful, stepConfig1);

                // Set request option for Verify /presign/{filename}
                let requestOptionsStep2 = {
                    hostname: hostname,
                    method: 'POST',
                    path: '/presign/test.txt',
                    port: '443',
                    protocol: 'https:',
                    body: "",
                    headers: {}
                };
                requestOptionsStep2['headers']['User-Agent'] = [synthetics.getCanaryUserAgentString(), requestOptionsStep2['headers']['User-Agent']].join(' ');

                // Set step config option for Verify /presign/{filename}
                let stepConfig2 = {
                    includeRequestHeaders: false,
                    includeResponseHeaders: false,
                    includeRequestBody: false,
                    includeResponseBody: false,
                    restrictedHeaders: [],
                    continueOnHttpStepFailure: true
                };

                await synthetics.executeHttpStep('Verify /presign/{filename}', requestOptionsStep2, validateSuccessful, stepConfig2);

                // Set request option for Verify /combatlogs/keys/{dungeon_id}
                let requestOptionsStep3 = {
                    hostname: hostname,
                    method: 'GET',
                    path: '/combatlogs/keys/2291',
                    port: '443',
                    protocol: 'https:',
                    body: "",
                    headers: {}
                };
                requestOptionsStep3['headers']['User-Agent'] = [synthetics.getCanaryUserAgentString(), requestOptionsStep3['headers']['User-Agent']].join(' ');

                // Set step config option for Verify /combatlogs/keys/{dungeon_id}
                let stepConfig3 = {
                    includeRequestHeaders: false,
                    includeResponseHeaders: false,
                    includeRequestBody: false, 
                    includeResponseBody: false,
                    restrictedHeaders: [],
                    continueOnHttpStepFailure: true
                };

                // TODO: XMLHttpRequest
                // TODO: check response, must include body
                await synthetics.executeHttpStep('Verify /combatlogs/keys/{dungeon_id}', requestOptionsStep3, validateSuccessful, stepConfig3);

                const combatlogUUID = getCombatlogUUID(hostname)
                // Set request option for Verify /combatlogs/keys/{combatlog_uuid}/player-damage-done
                let requestOptionsStep4 = {
                    hostname: hostname,
                    method: 'GET',
                    path: '/combatlogs/keys/' + combatlogUUID,
                    port: '443',
                    protocol: 'https:',
                    body: "",
                    headers: {}
                };
                requestOptionsStep4['headers']['User-Agent'] = [synthetics.getCanaryUserAgentString(), requestOptionsStep4['headers']['User-Agent']].join(' ');

                // Set step config option for Verify /combatlogs/keys/{combatlog_uuid}/player-damage-done
                let stepConfig4 = {
                    includeRequestHeaders: false,
                    includeResponseHeaders: false,
                    includeRequestBody: false,
                    includeResponseBody: false,
                    restrictedHeaders: [],
                    continueOnHttpStepFailure: true
                };

                await synthetics.executeHttpStep('Verify /combatlogs/keys/{combatlog_uuid}/player-damage-done', requestOptionsStep4, validateSuccessful, stepConfig4);

                // ----------------------------------------------------
                // Set request option for Verify /
                let requestOptionsStep5 = {
                    hostname: hostname,
                    method: 'GET',
                    path: '/',
                    port: '443',
                    protocol: 'https:',
                    body: "",
                    headers: {}
                };
                requestOptionsStep4['headers']['User-Agent'] = [synthetics.getCanaryUserAgentString(), requestOptionsStep4['headers']['User-Agent']].join(' ');

                // Set step config option for Verify /
                let stepConfig5 = {
                    includeRequestHeaders: false,
                    includeResponseHeaders: false,
                    includeRequestBody: false,
                    includeResponseBody: false,
                    restrictedHeaders: [],
                    continueOnHttpStepFailure: true
                };

                await synthetics.executeHttpStep('Verify /', requestOptionsStep5, validateSuccessful, stepConfig5);
            };

            // neither fetch or XMLHttpRequest works and I can't install packages with inline code
            function getCombatlogUUID(apiUrl) {
                if (process.env.STAGE === "prod") {
                    return "ac18f293-e573-4994-8745-2a291d12bf9a"
                }
                return "2ac44ef5-d053-4232-881e-c8c21300ec4f"
                // let url = "https://" + apiUrl + "/combatlogs/keys"
                // let xmlHttp = new XMLHttpRequest()
                // xmlHttp.open( "GET", url, false ) // false for synchronous request
                // xmlHttp.send( null )
                // const resp = JSON.parse(xmlHttp.responseText)
                // return resp.data[0].combatlog_uuid
            }

            exports.handler = async () => {
                return await apiCanaryBlueprint();
            };
github-actions[bot] commented 3 years ago
Stack wm
IAM Statement Changes
┌───┬────────────────────────┬────────┬──────────────────────┬────────────────────────────────────────┬─────────────────────────────────────────────┐
│   │ Resource               │ Effect │ Action               │ Principal                              │ Condition                                   │
├───┼────────────────────────┼────────┼──────────────────────┼────────────────────────────────────────┼─────────────────────────────────────────────┤
│ + │ ${Kms-/WowmateKey.Arn} │ Allow  │ kms:Decrypt          │ AWS:${Convert-/Lambda/ServiceRole.Arn} │                                             │
│ + │ ${Kms-/WowmateKey.Arn} │ Allow  │ kms:Decrypt          │ Service:s3.amazonaws.com               │ "ArnLike": {                                │
│   │                        │        │ kms:Encrypt          │                                        │   "aws:SourceArn": "${Buckets-/Upload.Arn}" │
│   │                        │        │ kms:GenerateDataKey* │                                        │ }                                           │
│   │                        │        │ kms:ReEncrypt*       │                                        │                                             │
│ + │ ${Kms-/WowmateKey.Arn} │ Allow  │ kms:Decrypt          │ Service:s3.amazonaws.com               │                                             │
│   │                        │        │ kms:GenerateDataKey* │                                        │                                             │
│ + │ ${Kms-/WowmateKey.Arn} │ Allow  │ kms:Decrypt          │ AWS:${Convert-/Lambda/ServiceRole}     │                                             │
└───┴────────────────────────┴────────┴──────────────────────┴────────────────────────────────────────┴─────────────────────────────────────────────┘
(NOTE: There may be security-related changes not in this list. See https://github.com/aws/aws-cdk/issues/1299)

Resources
[~] AWS::KMS::Key Kms-/WowmateKey KmsWowmateKeyD6DEF5B7 
 └─ [~] KeyPolicy
     └─ [~] .Statement:
         └─ @@ -71,6 +71,19 @@
            [ ]   "Resource": "*"
            [ ] },
            [ ] {
            [+]   "Action": "kms:Decrypt",
            [+]   "Effect": "Allow",
            [+]   "Principal": {
            [+]     "AWS": {
            [+]       "Fn::GetAtt": [
            [+]         "ConvertLambdaServiceRole179DFF01",
            [+]         "Arn"
            [+]       ]
            [+]     }
            [+]   },
            [+]   "Resource": "*"
            [+] },
            [+] {
            [ ]   "Action": [
            [ ]     "kms:Decrypt",
            [ ]     "kms:Encrypt",
            @@ -87,5 +100,39 @@
            [ ]       }
            [ ]     },
            [ ]     "Resource": "*"
            [+]   },
            [+]   {
            [+]     "Action": [
            [+]       "kms:Decrypt",
            [+]       "kms:Encrypt",
            [+]       "kms:ReEncrypt*",
            [+]       "kms:GenerateDataKey*"
            [+]     ],
            [+]     "Condition": {
            [+]       "ArnLike": {
            [+]         "aws:SourceArn": {
            [+]           "Fn::GetAtt": [
            [+]             "BucketsUpload0B7F8F15",
            [+]             "Arn"
            [+]           ]
            [+]         }
            [+]       }
            [+]     },
            [+]     "Effect": "Allow",
            [+]     "Principal": {
            [+]       "Service": "s3.amazonaws.com"
            [+]     },
            [+]     "Resource": "*"
            [+]   },
            [+]   {
            [+]     "Action": [
            [+]       "kms:GenerateDataKey*",
            [+]       "kms:Decrypt"
            [+]     ],
            [+]     "Effect": "Allow",
            [+]     "Principal": {
            [+]       "Service": "s3.amazonaws.com"
            [+]     },
            [+]     "Resource": "*"
            [ ]   }
            [ ] ]
[~] AWS::Lambda::Function Api-/Lambda ApiLambdaB75D04FB 
 ├─ [~] Code
 │   └─ [~] .S3Key:
 │       ├─ [-] c070daac02489a748e9e0644a4457029e3a83e5040022b98ebd633daa7185557.zip
 │       └─ [+] 2a2dd2b026f1d171665207eb38bc3f350f5dd3ffa7de786ad8e4158b50154c8e.zip
 └─ [~] Metadata
     └─ [~] .aws:asset:path:
         ├─ [-] asset.c070daac02489a748e9e0644a4457029e3a83e5040022b98ebd633daa7185557
         └─ [+] asset.2a2dd2b026f1d171665207eb38bc3f350f5dd3ffa7de786ad8e4158b50154c8e
[~] Custom::CDKBucketDeployment Frontend-/DeployWebsite/CustomResource FrontendDeployWebsiteCustomResource1B4A6C7F 
 └─ [~] SourceObjectKeys
     └─ @@ -1,3 +1,3 @@
        [ ] [
        [-]   "243ebf0b364a71e47601ee0c80c9b50fe18a3e8ea11ddca13e7d2086a947c771.zip"
        [+]   "c1587a27d93e18f26a3020c355e683dc77e257bf06e89cafd5a4017bbebcbb64.zip"
        [ ] ]
[~] AWS::SQS::Queue Convert-/ProcessingQueue ConvertProcessingQueueE8D6E023 
 └─ [+] KmsMasterKeyId
     └─ {"Fn::GetAtt":["KmsWowmateKeyD6DEF5B7","Arn"]}
[~] AWS::IAM::Policy Convert-/Lambda/ServiceRole/DefaultPolicy ConvertLambdaServiceRoleDefaultPolicyE631747C 
 └─ [~] PolicyDocument
     └─ [~] .Statement:
         └─ @@ -24,6 +24,16 @@
            [ ]   }
            [ ] },
            [ ] {
            [+]   "Action": "kms:Decrypt",
            [+]   "Effect": "Allow",
            [+]   "Resource": {
            [+]     "Fn::GetAtt": [
            [+]       "KmsWowmateKeyD6DEF5B7",
            [+]       "Arn"
            [+]     ]
            [+]   }
            [+] },
            [+] {
            [ ]   "Action": [
            [ ]     "dynamodb:BatchGetItem",
            [ ]     "dynamodb:GetRecords",
[~] AWS::Lambda::Function Convert-/Lambda ConvertLambda3540DCCB 
 ├─ [~] Code
 │   └─ [~] .S3Key:
 │       ├─ [-] e51c6295ef9f6201157f07444da91a2ea4be0e0b8ba63c4d66c39fb603740853.zip
 │       └─ [+] 0f89e757d338d2f46e1539a2ff01e9ee25cfe5b05471f4dbf750075d2670870a.zip
 └─ [~] Metadata
     └─ [~] .aws:asset:path:
         ├─ [-] asset.e51c6295ef9f6201157f07444da91a2ea4be0e0b8ba63c4d66c39fb603740853
         └─ [+] asset.0f89e757d338d2f46e1539a2ff01e9ee25cfe5b05471f4dbf750075d2670870a
[~] AWS::Synthetics::Canary Canary-/API CanaryAPI9F4B22EA 
 └─ [~] Code
     └─ [~] .Script:
         ├─ [-] 
            var synthetics = require('Synthetics');
            const log = require('SyntheticsLogger');

            const apiCanaryBlueprint = async function () {
                const hostname = process.env.API_URL

                // Handle validation for positive scenario
                const validateSuccessful = async function(res) {
                    return new Promise((resolve, reject) => {
                        if (res.statusCode < 200 || res.statusCode > 299) {
                            throw res.statusCode + ' ' + res.statusMessage;
                        }

                        let responseBody = '';
                        res.on('data', (d) => {
                            responseBody += d;
                        });

                        res.on('end', () => {
                            // Add validation on 'responseBody' here if required.
                            resolve();
                        });
                    });
                };

                // Set request option for Verify /combatlogs/keys
                let requestOptionsStep1 = {
                    hostname: hostname,
                    method: 'GET',
                    path: '/combatlogs/keys',
                    port: '443',
                    protocol: 'https:',
                    body: "",
                    headers: {}
                };
                requestOptionsStep1['headers']['User-Agent'] = [synthetics.getCanaryUserAgentString(), requestOptionsStep1['headers']['User-Agent']].join(' ');

                // Set step config option for Verify /combatlogs/keys
                let stepConfig1 = {
                    includeRequestHeaders: false,
                    includeResponseHeaders: false,
                    includeRequestBody: false,
                    includeResponseBody: false,
                    restrictedHeaders: [],
                    continueOnHttpStepFailure: true
                };

                await synthetics.executeHttpStep('Verify /combatlogs/keys', requestOptionsStep1, validateSuccessful, stepConfig1);

                // Set request option for Verify /presign/{filename}
                let requestOptionsStep2 = {
                    hostname: hostname,
                    method: 'POST',
                    path: '/presign/test.txt',
                    port: '443',
                    protocol: 'https:',
                    body: "",
                    headers: {}
                };
                requestOptionsStep2['headers']['User-Agent'] = [synthetics.getCanaryUserAgentString(), requestOptionsStep2['headers']['User-Agent']].join(' ');

                // Set step config option for Verify /presign/{filename}
                let stepConfig2 = {
                    includeRequestHeaders: false,
                    includeResponseHeaders: false,
                    includeRequestBody: false,
                    includeResponseBody: false,
                    restrictedHeaders: [],
                    continueOnHttpStepFailure: true
                };

                await synthetics.executeHttpStep('Verify /presign/{filename}', requestOptionsStep2, validateSuccessful, stepConfig2);

                // Set request option for Verify /combatlogs/keys/{dungeon_id}
                let requestOptionsStep3 = {
                    hostname: hostname,
                    method: 'GET',
                    path: '/combatlogs/keys/2291',
                    port: '443',
                    protocol: 'https:',
                    body: "",
                    headers: {}
                };
                requestOptionsStep3['headers']['User-Agent'] = [synthetics.getCanaryUserAgentString(), requestOptionsStep3['headers']['User-Agent']].join(' ');

                // Set step config option for Verify /combatlogs/keys/{dungeon_id}
                let stepConfig3 = {
                    includeRequestHeaders: false,
                    includeResponseHeaders: false,
                    includeRequestBody: false, 
                    includeResponseBody: false,
                    restrictedHeaders: [],
                    continueOnHttpStepFailure: true
                };

                // TODO: XMLHttpRequest
                // TODO: check response, must include body
                await synthetics.executeHttpStep('Verify /combatlogs/keys/{dungeon_id}', requestOptionsStep3, validateSuccessful, stepConfig3);

                const combatlogUUID = getCombatlogUUID(hostname)
                // Set request option for Verify /combatlogs/keys/{combatlog_uuid}/player-damage-done
                let requestOptionsStep4 = {
                    hostname: hostname,
                    method: 'GET',
                    path: '/combatlogs/keys/' + combatlogUUID,
                    port: '443',
                    protocol: 'https:',
                    body: "",
                    headers: {}
                };
                requestOptionsStep4['headers']['User-Agent'] = [synthetics.getCanaryUserAgentString(), requestOptionsStep4['headers']['User-Agent']].join(' ');

                // Set step config option for Verify /combatlogs/keys/{combatlog_uuid}/player-damage-done
                let stepConfig4 = {
                    includeRequestHeaders: false,
                    includeResponseHeaders: false,
                    includeRequestBody: false,
                    includeResponseBody: false,
                    restrictedHeaders: [],
                    continueOnHttpStepFailure: true
                };

                await synthetics.executeHttpStep('Verify /combatlogs/keys/{combatlog_uuid}/player-damage-done', requestOptionsStep4, validateSuccessful, stepConfig4);
            };

            // neither fetch or XMLHttpRequest works and I can't install packages with inline code
            function getCombatlogUUID(apiUrl) {
                if (process.env.STAGE === "prod") {
                    return "ac18f293-e573-4994-8745-2a291d12bf9a"
                }
                return "2ac44ef5-d053-4232-881e-c8c21300ec4f"
                // let url = "https://" + apiUrl + "/combatlogs/keys"
                // let xmlHttp = new XMLHttpRequest()
                // xmlHttp.open( "GET", url, false ) // false for synchronous request
                // xmlHttp.send( null )
                // const resp = JSON.parse(xmlHttp.responseText)
                // return resp.data[0].combatlog_uuid
            }

            exports.handler = async () => {
                return await apiCanaryBlueprint();
            };

         └─ [+] 
            var synthetics = require('Synthetics');
            const log = require('SyntheticsLogger');

            const apiCanaryBlueprint = async function () {
                const hostname = process.env.API_URL

                // Handle validation for positive scenario
                const validateSuccessful = async function(res) {
                    return new Promise((resolve, reject) => {
                        if (res.statusCode < 200 || res.statusCode > 299) {
                            throw res.statusCode + ' ' + res.statusMessage;
                        }

                        let responseBody = '';
                        res.on('data', (d) => {
                            responseBody += d;
                        });

                        res.on('end', () => {
                            // Add validation on 'responseBody' here if required.
                            resolve();
                        });
                    });
                };

                // Set request option for Verify /combatlogs/keys
                let requestOptionsStep1 = {
                    hostname: hostname,
                    method: 'GET',
                    path: '/combatlogs/keys',
                    port: '443',
                    protocol: 'https:',
                    body: "",
                    headers: {}
                };
                requestOptionsStep1['headers']['User-Agent'] = [synthetics.getCanaryUserAgentString(), requestOptionsStep1['headers']['User-Agent']].join(' ');

                // Set step config option for Verify /combatlogs/keys
                let stepConfig1 = {
                    includeRequestHeaders: false,
                    includeResponseHeaders: false,
                    includeRequestBody: false,
                    includeResponseBody: false,
                    restrictedHeaders: [],
                    continueOnHttpStepFailure: true
                };

                await synthetics.executeHttpStep('Verify /combatlogs/keys', requestOptionsStep1, validateSuccessful, stepConfig1);

                // Set request option for Verify /presign/{filename}
                let requestOptionsStep2 = {
                    hostname: hostname,
                    method: 'POST',
                    path: '/presign/test.txt',
                    port: '443',
                    protocol: 'https:',
                    body: "",
                    headers: {}
                };
                requestOptionsStep2['headers']['User-Agent'] = [synthetics.getCanaryUserAgentString(), requestOptionsStep2['headers']['User-Agent']].join(' ');

                // Set step config option for Verify /presign/{filename}
                let stepConfig2 = {
                    includeRequestHeaders: false,
                    includeResponseHeaders: false,
                    includeRequestBody: false,
                    includeResponseBody: false,
                    restrictedHeaders: [],
                    continueOnHttpStepFailure: true
                };

                await synthetics.executeHttpStep('Verify /presign/{filename}', requestOptionsStep2, validateSuccessful, stepConfig2);

                // Set request option for Verify /combatlogs/keys/{dungeon_id}
                let requestOptionsStep3 = {
                    hostname: hostname,
                    method: 'GET',
                    path: '/combatlogs/keys/2291',
                    port: '443',
                    protocol: 'https:',
                    body: "",
                    headers: {}
                };
                requestOptionsStep3['headers']['User-Agent'] = [synthetics.getCanaryUserAgentString(), requestOptionsStep3['headers']['User-Agent']].join(' ');

                // Set step config option for Verify /combatlogs/keys/{dungeon_id}
                let stepConfig3 = {
                    includeRequestHeaders: false,
                    includeResponseHeaders: false,
                    includeRequestBody: false, 
                    includeResponseBody: false,
                    restrictedHeaders: [],
                    continueOnHttpStepFailure: true
                };

                // TODO: XMLHttpRequest
                // TODO: check response, must include body
                await synthetics.executeHttpStep('Verify /combatlogs/keys/{dungeon_id}', requestOptionsStep3, validateSuccessful, stepConfig3);

                const combatlogUUID = getCombatlogUUID(hostname)
                // Set request option for Verify /combatlogs/keys/{combatlog_uuid}/player-damage-done
                let requestOptionsStep4 = {
                    hostname: hostname,
                    method: 'GET',
                    path: '/combatlogs/keys/' + combatlogUUID,
                    port: '443',
                    protocol: 'https:',
                    body: "",
                    headers: {}
                };
                requestOptionsStep4['headers']['User-Agent'] = [synthetics.getCanaryUserAgentString(), requestOptionsStep4['headers']['User-Agent']].join(' ');

                // Set step config option for Verify /combatlogs/keys/{combatlog_uuid}/player-damage-done
                let stepConfig4 = {
                    includeRequestHeaders: false,
                    includeResponseHeaders: false,
                    includeRequestBody: false,
                    includeResponseBody: false,
                    restrictedHeaders: [],
                    continueOnHttpStepFailure: true
                };

                await synthetics.executeHttpStep('Verify /combatlogs/keys/{combatlog_uuid}/player-damage-done', requestOptionsStep4, validateSuccessful, stepConfig4);

                // ----------------------------------------------------
                // Set request option for Verify /
                let requestOptionsStep5 = {
                    hostname: hostname,
                    method: 'GET',
                    path: '/',
                    port: '443',
                    protocol: 'https:',
                    body: "",
                    headers: {}
                };
                requestOptionsStep4['headers']['User-Agent'] = [synthetics.getCanaryUserAgentString(), requestOptionsStep4['headers']['User-Agent']].join(' ');

                // Set step config option for Verify /
                let stepConfig5 = {
                    includeRequestHeaders: false,
                    includeResponseHeaders: false,
                    includeRequestBody: false,
                    includeResponseBody: false,
                    restrictedHeaders: [],
                    continueOnHttpStepFailure: true
                };

                await synthetics.executeHttpStep('Verify /', requestOptionsStep5, validateSuccessful, stepConfig5);
            };

            // neither fetch or XMLHttpRequest works and I can't install packages with inline code
            function getCombatlogUUID(apiUrl) {
                if (process.env.STAGE === "prod") {
                    return "ac18f293-e573-4994-8745-2a291d12bf9a"
                }
                return "2ac44ef5-d053-4232-881e-c8c21300ec4f"
                // let url = "https://" + apiUrl + "/combatlogs/keys"
                // let xmlHttp = new XMLHttpRequest()
                // xmlHttp.open( "GET", url, false ) // false for synchronous request
                // xmlHttp.send( null )
                // const resp = JSON.parse(xmlHttp.responseText)
                // return resp.data[0].combatlog_uuid
            }

            exports.handler = async () => {
                return await apiCanaryBlueprint();
            };
github-actions[bot] commented 3 years ago
Stack wm
IAM Statement Changes
┌───┬────────────────────────┬────────┬──────────────────────┬────────────────────────────────────────┬─────────────────────────────────────────────┐
│   │ Resource               │ Effect │ Action               │ Principal                              │ Condition                                   │
├───┼────────────────────────┼────────┼──────────────────────┼────────────────────────────────────────┼─────────────────────────────────────────────┤
│ + │ ${Kms-/WowmateKey.Arn} │ Allow  │ kms:Decrypt          │ AWS:${Convert-/Lambda/ServiceRole.Arn} │                                             │
│ + │ ${Kms-/WowmateKey.Arn} │ Allow  │ kms:Decrypt          │ Service:s3.amazonaws.com               │ "ArnLike": {                                │
│   │                        │        │ kms:Encrypt          │                                        │   "aws:SourceArn": "${Buckets-/Upload.Arn}" │
│   │                        │        │ kms:GenerateDataKey* │                                        │ }                                           │
│   │                        │        │ kms:ReEncrypt*       │                                        │                                             │
│ + │ ${Kms-/WowmateKey.Arn} │ Allow  │ kms:Decrypt          │ Service:s3.amazonaws.com               │                                             │
│   │                        │        │ kms:GenerateDataKey* │                                        │                                             │
│ + │ ${Kms-/WowmateKey.Arn} │ Allow  │ kms:Decrypt          │ AWS:${Convert-/Lambda/ServiceRole}     │                                             │
└───┴────────────────────────┴────────┴──────────────────────┴────────────────────────────────────────┴─────────────────────────────────────────────┘
(NOTE: There may be security-related changes not in this list. See https://github.com/aws/aws-cdk/issues/1299)

Resources
[~] AWS::KMS::Key Kms-/WowmateKey KmsWowmateKeyD6DEF5B7 
 └─ [~] KeyPolicy
     └─ [~] .Statement:
         └─ @@ -71,6 +71,19 @@
            [ ]   "Resource": "*"
            [ ] },
            [ ] {
            [+]   "Action": "kms:Decrypt",
            [+]   "Effect": "Allow",
            [+]   "Principal": {
            [+]     "AWS": {
            [+]       "Fn::GetAtt": [
            [+]         "ConvertLambdaServiceRole179DFF01",
            [+]         "Arn"
            [+]       ]
            [+]     }
            [+]   },
            [+]   "Resource": "*"
            [+] },
            [+] {
            [ ]   "Action": [
            [ ]     "kms:Decrypt",
            [ ]     "kms:Encrypt",
            @@ -87,5 +100,39 @@
            [ ]       }
            [ ]     },
            [ ]     "Resource": "*"
            [+]   },
            [+]   {
            [+]     "Action": [
            [+]       "kms:Decrypt",
            [+]       "kms:Encrypt",
            [+]       "kms:ReEncrypt*",
            [+]       "kms:GenerateDataKey*"
            [+]     ],
            [+]     "Condition": {
            [+]       "ArnLike": {
            [+]         "aws:SourceArn": {
            [+]           "Fn::GetAtt": [
            [+]             "BucketsUpload0B7F8F15",
            [+]             "Arn"
            [+]           ]
            [+]         }
            [+]       }
            [+]     },
            [+]     "Effect": "Allow",
            [+]     "Principal": {
            [+]       "Service": "s3.amazonaws.com"
            [+]     },
            [+]     "Resource": "*"
            [+]   },
            [+]   {
            [+]     "Action": [
            [+]       "kms:GenerateDataKey*",
            [+]       "kms:Decrypt"
            [+]     ],
            [+]     "Effect": "Allow",
            [+]     "Principal": {
            [+]       "Service": "s3.amazonaws.com"
            [+]     },
            [+]     "Resource": "*"
            [ ]   }
            [ ] ]
[~] AWS::Lambda::Function Api-/Lambda ApiLambdaB75D04FB 
 ├─ [~] Code
 │   └─ [~] .S3Key:
 │       ├─ [-] c070daac02489a748e9e0644a4457029e3a83e5040022b98ebd633daa7185557.zip
 │       └─ [+] 2a2dd2b026f1d171665207eb38bc3f350f5dd3ffa7de786ad8e4158b50154c8e.zip
 └─ [~] Metadata
     └─ [~] .aws:asset:path:
         ├─ [-] asset.c070daac02489a748e9e0644a4457029e3a83e5040022b98ebd633daa7185557
         └─ [+] asset.2a2dd2b026f1d171665207eb38bc3f350f5dd3ffa7de786ad8e4158b50154c8e
[~] Custom::CDKBucketDeployment Frontend-/DeployWebsite/CustomResource FrontendDeployWebsiteCustomResource1B4A6C7F 
 └─ [~] SourceObjectKeys
     └─ @@ -1,3 +1,3 @@
        [ ] [
        [-]   "243ebf0b364a71e47601ee0c80c9b50fe18a3e8ea11ddca13e7d2086a947c771.zip"
        [+]   "c1587a27d93e18f26a3020c355e683dc77e257bf06e89cafd5a4017bbebcbb64.zip"
        [ ] ]
[~] AWS::SQS::Queue Convert-/ProcessingQueue ConvertProcessingQueueE8D6E023 
 ├─ [+] KmsMasterKeyId
 │   └─ {"Fn::GetAtt":["KmsWowmateKeyD6DEF5B7","Arn"]}
 └─ [~] Metadata
     ├─ [+] Added: .aws:cdk:path
     └─ [-] Removed: .cfn_nag
[~] AWS::IAM::Policy Convert-/Lambda/ServiceRole/DefaultPolicy ConvertLambdaServiceRoleDefaultPolicyE631747C 
 └─ [~] PolicyDocument
     └─ [~] .Statement:
         └─ @@ -24,6 +24,16 @@
            [ ]   }
            [ ] },
            [ ] {
            [+]   "Action": "kms:Decrypt",
            [+]   "Effect": "Allow",
            [+]   "Resource": {
            [+]     "Fn::GetAtt": [
            [+]       "KmsWowmateKeyD6DEF5B7",
            [+]       "Arn"
            [+]     ]
            [+]   }
            [+] },
            [+] {
            [ ]   "Action": [
            [ ]     "dynamodb:BatchGetItem",
            [ ]     "dynamodb:GetRecords",
[~] AWS::Lambda::Function Convert-/Lambda ConvertLambda3540DCCB 
 ├─ [~] Code
 │   └─ [~] .S3Key:
 │       ├─ [-] e51c6295ef9f6201157f07444da91a2ea4be0e0b8ba63c4d66c39fb603740853.zip
 │       └─ [+] 0f89e757d338d2f46e1539a2ff01e9ee25cfe5b05471f4dbf750075d2670870a.zip
 └─ [~] Metadata
     └─ [~] .aws:asset:path:
         ├─ [-] asset.e51c6295ef9f6201157f07444da91a2ea4be0e0b8ba63c4d66c39fb603740853
         └─ [+] asset.0f89e757d338d2f46e1539a2ff01e9ee25cfe5b05471f4dbf750075d2670870a
[~] AWS::Synthetics::Canary Canary-/API CanaryAPI9F4B22EA 
 └─ [~] Code
     └─ [~] .Script:
         ├─ [-] 
            var synthetics = require('Synthetics');
            const log = require('SyntheticsLogger');

            const apiCanaryBlueprint = async function () {
                const hostname = process.env.API_URL

                // Handle validation for positive scenario
                const validateSuccessful = async function(res) {
                    return new Promise((resolve, reject) => {
                        if (res.statusCode < 200 || res.statusCode > 299) {
                            throw res.statusCode + ' ' + res.statusMessage;
                        }

                        let responseBody = '';
                        res.on('data', (d) => {
                            responseBody += d;
                        });

                        res.on('end', () => {
                            // Add validation on 'responseBody' here if required.
                            resolve();
                        });
                    });
                };

                // Set request option for Verify /combatlogs/keys
                let requestOptionsStep1 = {
                    hostname: hostname,
                    method: 'GET',
                    path: '/combatlogs/keys',
                    port: '443',
                    protocol: 'https:',
                    body: "",
                    headers: {}
                };
                requestOptionsStep1['headers']['User-Agent'] = [synthetics.getCanaryUserAgentString(), requestOptionsStep1['headers']['User-Agent']].join(' ');

                // Set step config option for Verify /combatlogs/keys
                let stepConfig1 = {
                    includeRequestHeaders: false,
                    includeResponseHeaders: false,
                    includeRequestBody: false,
                    includeResponseBody: false,
                    restrictedHeaders: [],
                    continueOnHttpStepFailure: true
                };

                await synthetics.executeHttpStep('Verify /combatlogs/keys', requestOptionsStep1, validateSuccessful, stepConfig1);

                // Set request option for Verify /presign/{filename}
                let requestOptionsStep2 = {
                    hostname: hostname,
                    method: 'POST',
                    path: '/presign/test.txt',
                    port: '443',
                    protocol: 'https:',
                    body: "",
                    headers: {}
                };
                requestOptionsStep2['headers']['User-Agent'] = [synthetics.getCanaryUserAgentString(), requestOptionsStep2['headers']['User-Agent']].join(' ');

                // Set step config option for Verify /presign/{filename}
                let stepConfig2 = {
                    includeRequestHeaders: false,
                    includeResponseHeaders: false,
                    includeRequestBody: false,
                    includeResponseBody: false,
                    restrictedHeaders: [],
                    continueOnHttpStepFailure: true
                };

                await synthetics.executeHttpStep('Verify /presign/{filename}', requestOptionsStep2, validateSuccessful, stepConfig2);

                // Set request option for Verify /combatlogs/keys/{dungeon_id}
                let requestOptionsStep3 = {
                    hostname: hostname,
                    method: 'GET',
                    path: '/combatlogs/keys/2291',
                    port: '443',
                    protocol: 'https:',
                    body: "",
                    headers: {}
                };
                requestOptionsStep3['headers']['User-Agent'] = [synthetics.getCanaryUserAgentString(), requestOptionsStep3['headers']['User-Agent']].join(' ');

                // Set step config option for Verify /combatlogs/keys/{dungeon_id}
                let stepConfig3 = {
                    includeRequestHeaders: false,
                    includeResponseHeaders: false,
                    includeRequestBody: false, 
                    includeResponseBody: false,
                    restrictedHeaders: [],
                    continueOnHttpStepFailure: true
                };

                // TODO: XMLHttpRequest
                // TODO: check response, must include body
                await synthetics.executeHttpStep('Verify /combatlogs/keys/{dungeon_id}', requestOptionsStep3, validateSuccessful, stepConfig3);

                const combatlogUUID = getCombatlogUUID(hostname)
                // Set request option for Verify /combatlogs/keys/{combatlog_uuid}/player-damage-done
                let requestOptionsStep4 = {
                    hostname: hostname,
                    method: 'GET',
                    path: '/combatlogs/keys/' + combatlogUUID,
                    port: '443',
                    protocol: 'https:',
                    body: "",
                    headers: {}
                };
                requestOptionsStep4['headers']['User-Agent'] = [synthetics.getCanaryUserAgentString(), requestOptionsStep4['headers']['User-Agent']].join(' ');

                // Set step config option for Verify /combatlogs/keys/{combatlog_uuid}/player-damage-done
                let stepConfig4 = {
                    includeRequestHeaders: false,
                    includeResponseHeaders: false,
                    includeRequestBody: false,
                    includeResponseBody: false,
                    restrictedHeaders: [],
                    continueOnHttpStepFailure: true
                };

                await synthetics.executeHttpStep('Verify /combatlogs/keys/{combatlog_uuid}/player-damage-done', requestOptionsStep4, validateSuccessful, stepConfig4);
            };

            // neither fetch or XMLHttpRequest works and I can't install packages with inline code
            function getCombatlogUUID(apiUrl) {
                if (process.env.STAGE === "prod") {
                    return "ac18f293-e573-4994-8745-2a291d12bf9a"
                }
                return "2ac44ef5-d053-4232-881e-c8c21300ec4f"
                // let url = "https://" + apiUrl + "/combatlogs/keys"
                // let xmlHttp = new XMLHttpRequest()
                // xmlHttp.open( "GET", url, false ) // false for synchronous request
                // xmlHttp.send( null )
                // const resp = JSON.parse(xmlHttp.responseText)
                // return resp.data[0].combatlog_uuid
            }

            exports.handler = async () => {
                return await apiCanaryBlueprint();
            };

         └─ [+] 
            var synthetics = require('Synthetics');
            const log = require('SyntheticsLogger');

            const apiCanaryBlueprint = async function () {
                const hostname = process.env.API_URL

                // Handle validation for positive scenario
                const validateSuccessful = async function(res) {
                    return new Promise((resolve, reject) => {
                        if (res.statusCode < 200 || res.statusCode > 299) {
                            throw res.statusCode + ' ' + res.statusMessage;
                        }

                        let responseBody = '';
                        res.on('data', (d) => {
                            responseBody += d;
                        });

                        res.on('end', () => {
                            // Add validation on 'responseBody' here if required.
                            resolve();
                        });
                    });
                };

                // Set request option for Verify /combatlogs/keys
                let requestOptionsStep1 = {
                    hostname: hostname,
                    method: 'GET',
                    path: '/combatlogs/keys',
                    port: '443',
                    protocol: 'https:',
                    body: "",
                    headers: {}
                };
                requestOptionsStep1['headers']['User-Agent'] = [synthetics.getCanaryUserAgentString(), requestOptionsStep1['headers']['User-Agent']].join(' ');

                // Set step config option for Verify /combatlogs/keys
                let stepConfig1 = {
                    includeRequestHeaders: false,
                    includeResponseHeaders: false,
                    includeRequestBody: false,
                    includeResponseBody: false,
                    restrictedHeaders: [],
                    continueOnHttpStepFailure: true
                };

                await synthetics.executeHttpStep('Verify /combatlogs/keys', requestOptionsStep1, validateSuccessful, stepConfig1);

                // Set request option for Verify /presign/{filename}
                let requestOptionsStep2 = {
                    hostname: hostname,
                    method: 'POST',
                    path: '/presign/test.txt',
                    port: '443',
                    protocol: 'https:',
                    body: "",
                    headers: {}
                };
                requestOptionsStep2['headers']['User-Agent'] = [synthetics.getCanaryUserAgentString(), requestOptionsStep2['headers']['User-Agent']].join(' ');

                // Set step config option for Verify /presign/{filename}
                let stepConfig2 = {
                    includeRequestHeaders: false,
                    includeResponseHeaders: false,
                    includeRequestBody: false,
                    includeResponseBody: false,
                    restrictedHeaders: [],
                    continueOnHttpStepFailure: true
                };

                await synthetics.executeHttpStep('Verify /presign/{filename}', requestOptionsStep2, validateSuccessful, stepConfig2);

                // Set request option for Verify /combatlogs/keys/{dungeon_id}
                let requestOptionsStep3 = {
                    hostname: hostname,
                    method: 'GET',
                    path: '/combatlogs/keys/2291',
                    port: '443',
                    protocol: 'https:',
                    body: "",
                    headers: {}
                };
                requestOptionsStep3['headers']['User-Agent'] = [synthetics.getCanaryUserAgentString(), requestOptionsStep3['headers']['User-Agent']].join(' ');

                // Set step config option for Verify /combatlogs/keys/{dungeon_id}
                let stepConfig3 = {
                    includeRequestHeaders: false,
                    includeResponseHeaders: false,
                    includeRequestBody: false, 
                    includeResponseBody: false,
                    restrictedHeaders: [],
                    continueOnHttpStepFailure: true
                };

                // TODO: XMLHttpRequest
                // TODO: check response, must include body
                await synthetics.executeHttpStep('Verify /combatlogs/keys/{dungeon_id}', requestOptionsStep3, validateSuccessful, stepConfig3);

                const combatlogUUID = getCombatlogUUID(hostname)
                // Set request option for Verify /combatlogs/keys/{combatlog_uuid}/player-damage-done
                let requestOptionsStep4 = {
                    hostname: hostname,
                    method: 'GET',
                    path: '/combatlogs/keys/' + combatlogUUID,
                    port: '443',
                    protocol: 'https:',
                    body: "",
                    headers: {}
                };
                requestOptionsStep4['headers']['User-Agent'] = [synthetics.getCanaryUserAgentString(), requestOptionsStep4['headers']['User-Agent']].join(' ');

                // Set step config option for Verify /combatlogs/keys/{combatlog_uuid}/player-damage-done
                let stepConfig4 = {
                    includeRequestHeaders: false,
                    includeResponseHeaders: false,
                    includeRequestBody: false,
                    includeResponseBody: false,
                    restrictedHeaders: [],
                    continueOnHttpStepFailure: true
                };

                await synthetics.executeHttpStep('Verify /combatlogs/keys/{combatlog_uuid}/player-damage-done', requestOptionsStep4, validateSuccessful, stepConfig4);

                // ----------------------------------------------------
                // Set request option for Verify /
                let requestOptionsStep5 = {
                    hostname: hostname,
                    method: 'GET',
                    path: '/',
                    port: '443',
                    protocol: 'https:',
                    body: "",
                    headers: {}
                };
                requestOptionsStep4['headers']['User-Agent'] = [synthetics.getCanaryUserAgentString(), requestOptionsStep4['headers']['User-Agent']].join(' ');

                // Set step config option for Verify /
                let stepConfig5 = {
                    includeRequestHeaders: false,
                    includeResponseHeaders: false,
                    includeRequestBody: false,
                    includeResponseBody: false,
                    restrictedHeaders: [],
                    continueOnHttpStepFailure: true
                };

                await synthetics.executeHttpStep('Verify /', requestOptionsStep5, validateSuccessful, stepConfig5);
            };

            // neither fetch or XMLHttpRequest works and I can't install packages with inline code
            function getCombatlogUUID(apiUrl) {
                if (process.env.STAGE === "prod") {
                    return "ac18f293-e573-4994-8745-2a291d12bf9a"
                }
                return "2ac44ef5-d053-4232-881e-c8c21300ec4f"
                // let url = "https://" + apiUrl + "/combatlogs/keys"
                // let xmlHttp = new XMLHttpRequest()
                // xmlHttp.open( "GET", url, false ) // false for synchronous request
                // xmlHttp.send( null )
                // const resp = JSON.parse(xmlHttp.responseText)
                // return resp.data[0].combatlog_uuid
            }

            exports.handler = async () => {
                return await apiCanaryBlueprint();
            };
github-actions[bot] commented 3 years ago
Stack wm
IAM Statement Changes
┌───┬────────────────────────┬────────┬──────────────────────┬────────────────────────────────────────┬─────────────────────────────────────────────┐
│   │ Resource               │ Effect │ Action               │ Principal                              │ Condition                                   │
├───┼────────────────────────┼────────┼──────────────────────┼────────────────────────────────────────┼─────────────────────────────────────────────┤
│ + │ ${Kms-/WowmateKey.Arn} │ Allow  │ kms:Decrypt          │ AWS:${Convert-/Lambda/ServiceRole.Arn} │                                             │
│ + │ ${Kms-/WowmateKey.Arn} │ Allow  │ kms:Decrypt          │ Service:s3.amazonaws.com               │ "ArnLike": {                                │
│   │                        │        │ kms:Encrypt          │                                        │   "aws:SourceArn": "${Buckets-/Upload.Arn}" │
│   │                        │        │ kms:GenerateDataKey* │                                        │ }                                           │
│   │                        │        │ kms:ReEncrypt*       │                                        │                                             │
│ + │ ${Kms-/WowmateKey.Arn} │ Allow  │ kms:Decrypt          │ Service:s3.amazonaws.com               │                                             │
│   │                        │        │ kms:GenerateDataKey* │                                        │                                             │
│ + │ ${Kms-/WowmateKey.Arn} │ Allow  │ kms:Decrypt          │ AWS:${Convert-/Lambda/ServiceRole}     │                                             │
└───┴────────────────────────┴────────┴──────────────────────┴────────────────────────────────────────┴─────────────────────────────────────────────┘
(NOTE: There may be security-related changes not in this list. See https://github.com/aws/aws-cdk/issues/1299)

Resources
[~] AWS::KMS::Key Kms-/WowmateKey KmsWowmateKeyD6DEF5B7 
 └─ [~] KeyPolicy
     └─ [~] .Statement:
         └─ @@ -71,6 +71,19 @@
            [ ]   "Resource": "*"
            [ ] },
            [ ] {
            [+]   "Action": "kms:Decrypt",
            [+]   "Effect": "Allow",
            [+]   "Principal": {
            [+]     "AWS": {
            [+]       "Fn::GetAtt": [
            [+]         "ConvertLambdaServiceRole179DFF01",
            [+]         "Arn"
            [+]       ]
            [+]     }
            [+]   },
            [+]   "Resource": "*"
            [+] },
            [+] {
            [ ]   "Action": [
            [ ]     "kms:Decrypt",
            [ ]     "kms:Encrypt",
            @@ -87,5 +100,39 @@
            [ ]       }
            [ ]     },
            [ ]     "Resource": "*"
            [+]   },
            [+]   {
            [+]     "Action": [
            [+]       "kms:Decrypt",
            [+]       "kms:Encrypt",
            [+]       "kms:ReEncrypt*",
            [+]       "kms:GenerateDataKey*"
            [+]     ],
            [+]     "Condition": {
            [+]       "ArnLike": {
            [+]         "aws:SourceArn": {
            [+]           "Fn::GetAtt": [
            [+]             "BucketsUpload0B7F8F15",
            [+]             "Arn"
            [+]           ]
            [+]         }
            [+]       }
            [+]     },
            [+]     "Effect": "Allow",
            [+]     "Principal": {
            [+]       "Service": "s3.amazonaws.com"
            [+]     },
            [+]     "Resource": "*"
            [+]   },
            [+]   {
            [+]     "Action": [
            [+]       "kms:GenerateDataKey*",
            [+]       "kms:Decrypt"
            [+]     ],
            [+]     "Effect": "Allow",
            [+]     "Principal": {
            [+]       "Service": "s3.amazonaws.com"
            [+]     },
            [+]     "Resource": "*"
            [ ]   }
            [ ] ]
[~] AWS::Lambda::Function Api-/GetKeysLambda ApiGetKeysLambdaFDF1A526 
 ├─ [~] Code
 │   └─ [~] .S3Key:
 │       ├─ [-] 52b2cdc5160aa77f567c6fae26bdc71f06cc3311c43d6b505cdc583c415ffe2a.zip
 │       └─ [+] e879be6824f28368b30e679fce10c1e824b375566201922aab2c3896cfcc89bc.zip
 └─ [~] Metadata
     └─ [~] .aws:asset:path:
         ├─ [-] asset.52b2cdc5160aa77f567c6fae26bdc71f06cc3311c43d6b505cdc583c415ffe2a
         └─ [+] asset.e879be6824f28368b30e679fce10c1e824b375566201922aab2c3896cfcc89bc
[~] AWS::Lambda::Function Api-/GetKeysPerDungeonLambda ApiGetKeysPerDungeonLambda073DE524 
 ├─ [~] Code
 │   └─ [~] .S3Key:
 │       ├─ [-] d76e0aaf4f0fb33a99009ec9a7efcc3d5f35b5c29292b343df352c5c3ebd9db4.zip
 │       └─ [+] 6857a3821c4de6757d06019ba9dd75507df092fc82bb95c480d6885f4c60b456.zip
 └─ [~] Metadata
     └─ [~] .aws:asset:path:
         ├─ [-] asset.d76e0aaf4f0fb33a99009ec9a7efcc3d5f35b5c29292b343df352c5c3ebd9db4
         └─ [+] asset.6857a3821c4de6757d06019ba9dd75507df092fc82bb95c480d6885f4c60b456
[~] AWS::Lambda::Function Api-/GetPlayerDamageDoneLambda ApiGetPlayerDamageDoneLambda7F052E98 
 ├─ [~] Code
 │   └─ [~] .S3Key:
 │       ├─ [-] 9bd73d8ae6459a6ec226a160db099bfd3322d25a59c6c504979af0eca69f3b2f.zip
 │       └─ [+] 1dead25d404a1e85d975d399d23f0bee9c45c07b89e5a6b74d974e340155f287.zip
 └─ [~] Metadata
     └─ [~] .aws:asset:path:
         ├─ [-] asset.9bd73d8ae6459a6ec226a160db099bfd3322d25a59c6c504979af0eca69f3b2f
         └─ [+] asset.1dead25d404a1e85d975d399d23f0bee9c45c07b89e5a6b74d974e340155f287
[~] AWS::Lambda::Function Api-/Lambda ApiLambdaB75D04FB 
 ├─ [~] Code
 │   └─ [~] .S3Key:
 │       ├─ [-] c070daac02489a748e9e0644a4457029e3a83e5040022b98ebd633daa7185557.zip
 │       └─ [+] 2a2dd2b026f1d171665207eb38bc3f350f5dd3ffa7de786ad8e4158b50154c8e.zip
 └─ [~] Metadata
     └─ [~] .aws:asset:path:
         ├─ [-] asset.c070daac02489a748e9e0644a4457029e3a83e5040022b98ebd633daa7185557
         └─ [+] asset.2a2dd2b026f1d171665207eb38bc3f350f5dd3ffa7de786ad8e4158b50154c8e
[~] Custom::CDKBucketDeployment Frontend-/DeployWebsite/CustomResource FrontendDeployWebsiteCustomResource1B4A6C7F 
 └─ [~] SourceObjectKeys
     └─ @@ -1,3 +1,3 @@
        [ ] [
        [-]   "243ebf0b364a71e47601ee0c80c9b50fe18a3e8ea11ddca13e7d2086a947c771.zip"
        [+]   "c1587a27d93e18f26a3020c355e683dc77e257bf06e89cafd5a4017bbebcbb64.zip"
        [ ] ]
[~] AWS::Lambda::Function QueryKeys-/Lambda QueryKeysLambda58DE9A2E 
 ├─ [~] Code
 │   └─ [~] .S3Key:
 │       ├─ [-] 53a4b3e83b7536b8ab16f7d3426f8c6cee289f7b737fa44ecc0058276478d16a.zip
 │       └─ [+] 16b0a1161b37d26c070c219bfc5b8e277ee5e6626eb69f9b7f89e26b419a5014.zip
 └─ [~] Metadata
     └─ [~] .aws:asset:path:
         ├─ [-] asset.53a4b3e83b7536b8ab16f7d3426f8c6cee289f7b737fa44ecc0058276478d16a
         └─ [+] asset.16b0a1161b37d26c070c219bfc5b8e277ee5e6626eb69f9b7f89e26b419a5014
[~] AWS::Lambda::Function InsertKeysToDynamodb-/Lambda InsertKeysToDynamodbLambda15825024 
 ├─ [~] Code
 │   └─ [~] .S3Key:
 │       ├─ [-] 55a4e64e5751514b0589c43ec289c1e619902a98d08467434c5370f60cd9bbe7.zip
 │       └─ [+] e81d233834310b1b15e9aac581e61ca55e1f54966dd7b93f625cd854d33bf683.zip
 └─ [~] Metadata
     └─ [~] .aws:asset:path:
         ├─ [-] asset.55a4e64e5751514b0589c43ec289c1e619902a98d08467434c5370f60cd9bbe7
         └─ [+] asset.e81d233834310b1b15e9aac581e61ca55e1f54966dd7b93f625cd854d33bf683
[~] AWS::Lambda::Function InsertKeysToTimestream-/Lambda InsertKeysToTimestreamLambdaA07E952E 
 ├─ [~] Code
 │   └─ [~] .S3Key:
 │       ├─ [-] 6622fa2e9c5f72446ae2360b27a54a08ab4dd2b33aadb8ab6e2e9992b52d3699.zip
 │       └─ [+] 25c818bfe4f31ba515482352d749b791168d885ae675fa8fe6353a8b8281d2b4.zip
 └─ [~] Metadata
     └─ [~] .aws:asset:path:
         ├─ [-] asset.6622fa2e9c5f72446ae2360b27a54a08ab4dd2b33aadb8ab6e2e9992b52d3699
         └─ [+] asset.25c818bfe4f31ba515482352d749b791168d885ae675fa8fe6353a8b8281d2b4
[~] AWS::Lambda::Function QueryPlayerDamageDone-/Lambda QueryPlayerDamageDoneLambda98AFC037 
 ├─ [~] Code
 │   └─ [~] .S3Key:
 │       ├─ [-] 9668bf9008bcf9308a7c77689d6ac4ddddf007e659f5a7aab941e5cc86b348b5.zip
 │       └─ [+] 427ec4d755977222255bbe84005f6dba679e30bb54e992274beea3085e71d82a.zip
 └─ [~] Metadata
     └─ [~] .aws:asset:path:
         ├─ [-] asset.9668bf9008bcf9308a7c77689d6ac4ddddf007e659f5a7aab941e5cc86b348b5
         └─ [+] asset.427ec4d755977222255bbe84005f6dba679e30bb54e992274beea3085e71d82a
[~] AWS::Lambda::Function InsertPlayerDamageDoneToDynamodb-/Lambda InsertPlayerDamageDoneToDynamodbLambda659E0DA7 
 ├─ [~] Code
 │   └─ [~] .S3Key:
 │       ├─ [-] 60efd2874e341ec1ebb0eecfca16cb77af4e72092fcc610d5fd67d7dd4d2de33.zip
 │       └─ [+] ca648d974272bfc1f79917ff9af9335697880157f35fa7a1382e7b014a774177.zip
 └─ [~] Metadata
     └─ [~] .aws:asset:path:
         ├─ [-] asset.60efd2874e341ec1ebb0eecfca16cb77af4e72092fcc610d5fd67d7dd4d2de33
         └─ [+] asset.ca648d974272bfc1f79917ff9af9335697880157f35fa7a1382e7b014a774177
[~] AWS::SQS::Queue Convert-/ProcessingQueue ConvertProcessingQueueE8D6E023 
 ├─ [+] KmsMasterKeyId
 │   └─ {"Fn::GetAtt":["KmsWowmateKeyD6DEF5B7","Arn"]}
 └─ [~] Metadata
     ├─ [+] Added: .aws:cdk:path
     └─ [-] Removed: .cfn_nag
[~] AWS::IAM::Policy Convert-/Lambda/ServiceRole/DefaultPolicy ConvertLambdaServiceRoleDefaultPolicyE631747C 
 └─ [~] PolicyDocument
     └─ [~] .Statement:
         └─ @@ -24,6 +24,16 @@
            [ ]   }
            [ ] },
            [ ] {
            [+]   "Action": "kms:Decrypt",
            [+]   "Effect": "Allow",
            [+]   "Resource": {
            [+]     "Fn::GetAtt": [
            [+]       "KmsWowmateKeyD6DEF5B7",
            [+]       "Arn"
            [+]     ]
            [+]   }
            [+] },
            [+] {
            [ ]   "Action": [
            [ ]     "dynamodb:BatchGetItem",
            [ ]     "dynamodb:GetRecords",
[~] AWS::Lambda::Function Convert-/Lambda ConvertLambda3540DCCB 
 ├─ [~] Code
 │   └─ [~] .S3Key:
 │       ├─ [-] e51c6295ef9f6201157f07444da91a2ea4be0e0b8ba63c4d66c39fb603740853.zip
 │       └─ [+] f08a80660ecc5650c47ecdd4a96d1892d9680678d381d999fdd77982a782fbb3.zip
 └─ [~] Metadata
     └─ [~] .aws:asset:path:
         ├─ [-] asset.e51c6295ef9f6201157f07444da91a2ea4be0e0b8ba63c4d66c39fb603740853
         └─ [+] asset.f08a80660ecc5650c47ecdd4a96d1892d9680678d381d999fdd77982a782fbb3
[~] AWS::Synthetics::Canary Canary-/API CanaryAPI9F4B22EA 
 └─ [~] Code
     └─ [~] .Script:
         ├─ [-] 
            var synthetics = require('Synthetics');
            const log = require('SyntheticsLogger');

            const apiCanaryBlueprint = async function () {
                const hostname = process.env.API_URL

                // Handle validation for positive scenario
                const validateSuccessful = async function(res) {
                    return new Promise((resolve, reject) => {
                        if (res.statusCode < 200 || res.statusCode > 299) {
                            throw res.statusCode + ' ' + res.statusMessage;
                        }

                        let responseBody = '';
                        res.on('data', (d) => {
                            responseBody += d;
                        });

                        res.on('end', () => {
                            // Add validation on 'responseBody' here if required.
                            resolve();
                        });
                    });
                };

                // Set request option for Verify /combatlogs/keys
                let requestOptionsStep1 = {
                    hostname: hostname,
                    method: 'GET',
                    path: '/combatlogs/keys',
                    port: '443',
                    protocol: 'https:',
                    body: "",
                    headers: {}
                };
                requestOptionsStep1['headers']['User-Agent'] = [synthetics.getCanaryUserAgentString(), requestOptionsStep1['headers']['User-Agent']].join(' ');

                // Set step config option for Verify /combatlogs/keys
                let stepConfig1 = {
                    includeRequestHeaders: false,
                    includeResponseHeaders: false,
                    includeRequestBody: false,
                    includeResponseBody: false,
                    restrictedHeaders: [],
                    continueOnHttpStepFailure: true
                };

                await synthetics.executeHttpStep('Verify /combatlogs/keys', requestOptionsStep1, validateSuccessful, stepConfig1);

                // Set request option for Verify /presign/{filename}
                let requestOptionsStep2 = {
                    hostname: hostname,
                    method: 'POST',
                    path: '/presign/test.txt',
                    port: '443',
                    protocol: 'https:',
                    body: "",
                    headers: {}
                };
                requestOptionsStep2['headers']['User-Agent'] = [synthetics.getCanaryUserAgentString(), requestOptionsStep2['headers']['User-Agent']].join(' ');

                // Set step config option for Verify /presign/{filename}
                let stepConfig2 = {
                    includeRequestHeaders: false,
                    includeResponseHeaders: false,
                    includeRequestBody: false,
                    includeResponseBody: false,
                    restrictedHeaders: [],
                    continueOnHttpStepFailure: true
                };

                await synthetics.executeHttpStep('Verify /presign/{filename}', requestOptionsStep2, validateSuccessful, stepConfig2);

                // Set request option for Verify /combatlogs/keys/{dungeon_id}
                let requestOptionsStep3 = {
                    hostname: hostname,
                    method: 'GET',
                    path: '/combatlogs/keys/2291',
                    port: '443',
                    protocol: 'https:',
                    body: "",
                    headers: {}
                };
                requestOptionsStep3['headers']['User-Agent'] = [synthetics.getCanaryUserAgentString(), requestOptionsStep3['headers']['User-Agent']].join(' ');

                // Set step config option for Verify /combatlogs/keys/{dungeon_id}
                let stepConfig3 = {
                    includeRequestHeaders: false,
                    includeResponseHeaders: false,
                    includeRequestBody: false, 
                    includeResponseBody: false,
                    restrictedHeaders: [],
                    continueOnHttpStepFailure: true
                };

                // TODO: XMLHttpRequest
                // TODO: check response, must include body
                await synthetics.executeHttpStep('Verify /combatlogs/keys/{dungeon_id}', requestOptionsStep3, validateSuccessful, stepConfig3);

                const combatlogUUID = getCombatlogUUID(hostname)
                // Set request option for Verify /combatlogs/keys/{combatlog_uuid}/player-damage-done
                let requestOptionsStep4 = {
                    hostname: hostname,
                    method: 'GET',
                    path: '/combatlogs/keys/' + combatlogUUID,
                    port: '443',
                    protocol: 'https:',
                    body: "",
                    headers: {}
                };
                requestOptionsStep4['headers']['User-Agent'] = [synthetics.getCanaryUserAgentString(), requestOptionsStep4['headers']['User-Agent']].join(' ');

                // Set step config option for Verify /combatlogs/keys/{combatlog_uuid}/player-damage-done
                let stepConfig4 = {
                    includeRequestHeaders: false,
                    includeResponseHeaders: false,
                    includeRequestBody: false,
                    includeResponseBody: false,
                    restrictedHeaders: [],
                    continueOnHttpStepFailure: true
                };

                await synthetics.executeHttpStep('Verify /combatlogs/keys/{combatlog_uuid}/player-damage-done', requestOptionsStep4, validateSuccessful, stepConfig4);
            };

            // neither fetch or XMLHttpRequest works and I can't install packages with inline code
            function getCombatlogUUID(apiUrl) {
                if (process.env.STAGE === "prod") {
                    return "ac18f293-e573-4994-8745-2a291d12bf9a"
                }
                return "2ac44ef5-d053-4232-881e-c8c21300ec4f"
                // let url = "https://" + apiUrl + "/combatlogs/keys"
                // let xmlHttp = new XMLHttpRequest()
                // xmlHttp.open( "GET", url, false ) // false for synchronous request
                // xmlHttp.send( null )
                // const resp = JSON.parse(xmlHttp.responseText)
                // return resp.data[0].combatlog_uuid
            }

            exports.handler = async () => {
                return await apiCanaryBlueprint();
            };

         └─ [+] 
            var synthetics = require('Synthetics');
            const log = require('SyntheticsLogger');

            const apiCanaryBlueprint = async function () {
                const hostname = process.env.API_URL

                // Handle validation for positive scenario
                const validateSuccessful = async function(res) {
                    return new Promise((resolve, reject) => {
                        if (res.statusCode < 200 || res.statusCode > 299) {
                            throw res.statusCode + ' ' + res.statusMessage;
                        }

                        let responseBody = '';
                        res.on('data', (d) => {
                            responseBody += d;
                        });

                        res.on('end', () => {
                            // Add validation on 'responseBody' here if required.
                            resolve();
                        });
                    });
                };

                // Set request option for Verify /combatlogs/keys
                let requestOptionsStep1 = {
                    hostname: hostname,
                    method: 'GET',
                    path: '/combatlogs/keys',
                    port: '443',
                    protocol: 'https:',
                    body: "",
                    headers: {}
                };
                requestOptionsStep1['headers']['User-Agent'] = [synthetics.getCanaryUserAgentString(), requestOptionsStep1['headers']['User-Agent']].join(' ');

                // Set step config option for Verify /combatlogs/keys
                let stepConfig1 = {
                    includeRequestHeaders: false,
                    includeResponseHeaders: false,
                    includeRequestBody: false,
                    includeResponseBody: false,
                    restrictedHeaders: [],
                    continueOnHttpStepFailure: true
                };

                await synthetics.executeHttpStep('Verify /combatlogs/keys', requestOptionsStep1, validateSuccessful, stepConfig1);

                // Set request option for Verify /presign/{filename}
                let requestOptionsStep2 = {
                    hostname: hostname,
                    method: 'POST',
                    path: '/presign/test.txt',
                    port: '443',
                    protocol: 'https:',
                    body: "",
                    headers: {}
                };
                requestOptionsStep2['headers']['User-Agent'] = [synthetics.getCanaryUserAgentString(), requestOptionsStep2['headers']['User-Agent']].join(' ');

                // Set step config option for Verify /presign/{filename}
                let stepConfig2 = {
                    includeRequestHeaders: false,
                    includeResponseHeaders: false,
                    includeRequestBody: false,
                    includeResponseBody: false,
                    restrictedHeaders: [],
                    continueOnHttpStepFailure: true
                };

                await synthetics.executeHttpStep('Verify /presign/{filename}', requestOptionsStep2, validateSuccessful, stepConfig2);

                // Set request option for Verify /combatlogs/keys/{dungeon_id}
                let requestOptionsStep3 = {
                    hostname: hostname,
                    method: 'GET',
                    path: '/combatlogs/keys/2291',
                    port: '443',
                    protocol: 'https:',
                    body: "",
                    headers: {}
                };
                requestOptionsStep3['headers']['User-Agent'] = [synthetics.getCanaryUserAgentString(), requestOptionsStep3['headers']['User-Agent']].join(' ');

                // Set step config option for Verify /combatlogs/keys/{dungeon_id}
                let stepConfig3 = {
                    includeRequestHeaders: false,
                    includeResponseHeaders: false,
                    includeRequestBody: false, 
                    includeResponseBody: false,
                    restrictedHeaders: [],
                    continueOnHttpStepFailure: true
                };

                // TODO: XMLHttpRequest
                // TODO: check response, must include body
                await synthetics.executeHttpStep('Verify /combatlogs/keys/{dungeon_id}', requestOptionsStep3, validateSuccessful, stepConfig3);

                const combatlogUUID = getCombatlogUUID(hostname)
                // Set request option for Verify /combatlogs/keys/{combatlog_uuid}/player-damage-done
                let requestOptionsStep4 = {
                    hostname: hostname,
                    method: 'GET',
                    path: '/combatlogs/keys/' + combatlogUUID,
                    port: '443',
                    protocol: 'https:',
                    body: "",
                    headers: {}
                };
                requestOptionsStep4['headers']['User-Agent'] = [synthetics.getCanaryUserAgentString(), requestOptionsStep4['headers']['User-Agent']].join(' ');

                // Set step config option for Verify /combatlogs/keys/{combatlog_uuid}/player-damage-done
                let stepConfig4 = {
                    includeRequestHeaders: false,
                    includeResponseHeaders: false,
                    includeRequestBody: false,
                    includeResponseBody: false,
                    restrictedHeaders: [],
                    continueOnHttpStepFailure: true
                };

                await synthetics.executeHttpStep('Verify /combatlogs/keys/{combatlog_uuid}/player-damage-done', requestOptionsStep4, validateSuccessful, stepConfig4);

                // ----------------------------------------------------
                // Set request option for Verify /
                let requestOptionsStep5 = {
                    hostname: hostname,
                    method: 'GET',
                    path: '/',
                    port: '443',
                    protocol: 'https:',
                    body: "",
                    headers: {}
                };
                requestOptionsStep4['headers']['User-Agent'] = [synthetics.getCanaryUserAgentString(), requestOptionsStep4['headers']['User-Agent']].join(' ');

                // Set step config option for Verify /
                let stepConfig5 = {
                    includeRequestHeaders: false,
                    includeResponseHeaders: false,
                    includeRequestBody: false,
                    includeResponseBody: false,
                    restrictedHeaders: [],
                    continueOnHttpStepFailure: true
                };

                await synthetics.executeHttpStep('Verify /', requestOptionsStep5, validateSuccessful, stepConfig5);
            };

            // neither fetch or XMLHttpRequest works and I can't install packages with inline code
            function getCombatlogUUID(apiUrl) {
                if (process.env.STAGE === "prod") {
                    return "ac18f293-e573-4994-8745-2a291d12bf9a"
                }
                return "2ac44ef5-d053-4232-881e-c8c21300ec4f"
                // let url = "https://" + apiUrl + "/combatlogs/keys"
                // let xmlHttp = new XMLHttpRequest()
                // xmlHttp.open( "GET", url, false ) // false for synchronous request
                // xmlHttp.send( null )
                // const resp = JSON.parse(xmlHttp.responseText)
                // return resp.data[0].combatlog_uuid
            }

            exports.handler = async () => {
                return await apiCanaryBlueprint();
            };