

# canary 스크립트 샘플 코드
<a name="CloudWatch_Synthetics_Canaries_Samples"></a>

이 단원에는 CloudWatch Synthetics canary 스크립트에 사용할 수 있는 몇 가지 함수를 보여 주는 코드 샘플이 포함되어 있습니다.

## Node.js 및 Playwright의 샘플
<a name="Synthetics_Canaries_Samples_nodejs_playwright"></a>

### 여러 단계로 된 Playwright 카나리
<a name="Synthetics_canary_example_nodejs_playwright_multistep"></a>

아래의 스크립트는 여러 단계로 된 Node.js Playwright 카나리의 예제입니다.

```
import { synthetics } from '@aws/synthetics-playwright';

export async function handler(event, context) {
  try {
    console.log('Running Synthetics Playwright canary');
    const browser = await synthetics.launch();
    const browserContext = await browser.newContext();
    const page = await synthetics.getPage(browserContext);
    

    // Add steps
    // Step 1
    await synthetics.executeStep("home-page", async () => {
      console.log("Verify home page loads")
      await page.goto('https://www.amazon.com', {waitUntil: "load"});
      await new Promise(r => setTimeout(r, 5000));
    });
    
    // Step 2
    await synthetics.executeStep("search", async () => {
      console.log("Searching for a product")
      const searchInput = page.getByPlaceholder("Search Amazon").first();
      await searchInput.click()
      await searchInput.fill('Amazon echo');
      const btn = page.getByRole('button', { name: 'Go' }).first()
      await btn.click({ timeout: 15000 })
      console.log("Clicked search button")
    });

    // Step 3
    await synthetics.executeStep("search-results", async () => {
      console.log("Verifying search results")
      const resultsHeading = page.getByText("Results", {exact: true}).first()
      await resultsHeading.highlight();
      await new Promise(r => setTimeout(r, 5000));
    });

  } finally {
    // Close all browser contexts and browser
    await synthetics.close();
  }
}
```

### Playwright 카나리 설정 쿠키
<a name="Synthetics_canaries_nodejs_playwright_cookies"></a>

아래의 스크립트는 여러 단계로 된 Node.js Playwright 카나리 설정인 3가지 쿠키의 예제입니다.

```
import { synthetics } from '@aws/synthetics-playwright';

export const handler = async (event, context) => {
  try {
    let url = "http://smile.amazon.com/";
    const browser = await synthetics.launch();
    const page = await synthetics.getPage(browser);
    const cookies = [{
        'name': 'cookie1',
        'value': 'val1',
        'url': url
    },
    {
        'name': 'cookie2',
        'value': 'val2',
        'url': url
    },
    {
        'name': 'cookie3',
        'value': 'val3',
        'url': url
    }
   ];
   await page.context().addCookies(cookies);
   await page.goto(url, {waitUntil: 'load', timeout: 30000});
   await page.screenshot({ path: '/tmp/smile.png' });
    
  } finally {
    await synthetics.close();
  }
};
```

## Node.js 및 Puppeteer 샘플
<a name="CloudWatch_Synthetics_Canaries_Samples_nodejspup"></a>

### 쿠키 설정
<a name="CloudWatch_Synthetics_Canaries_Samples_cookies"></a>

웹 사이트는 사용자 지정 기능을 제공하거나 사용자를 추적하기 위해 쿠키를 사용합니다. CloudWatch Synthetics 스크립트에서 쿠키를 설정함으로써 이 사용자 지정 동작을 모방하고 검증할 수 있습니다.

예를 들어 웹 사이트는 재방문 사용자에 대해 [**등록(Register)**] 링크 대신 [**로그인(Login)**] 링크를 표시할 수 있습니다.

```
var synthetics = require('@aws/synthetics-puppeteer');
const log = require('@aws/synthetics-logger');

const pageLoadBlueprint = async function () {

    let url = "http://smile.amazon.com/";

    let page = await synthetics.getPage();

    // Set cookies.  I found that name, value, and either url or domain are required fields.
    const cookies = [{
      'name': 'cookie1',
      'value': 'val1',
      'url': url
    },{
      'name': 'cookie2',
      'value': 'val2',
      'url': url
    },{
      'name': 'cookie3',
      'value': 'val3',
      'url': url
    }];
    
    await page.setCookie(...cookies);

    // Navigate to the url
    await synthetics.executeStep('pageLoaded_home', async function (timeoutInMillis = 30000) {
        
        var response = await page.goto(url, {waitUntil: ['load', 'networkidle0'], timeout: timeoutInMillis});

        // Log cookies for this page and this url
        const cookiesSet = await page.cookies(url);
        log.info("Cookies for url: " + url + " are set to: " + JSON.stringify(cookiesSet));
    });

};

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

### 디바이스 에뮬레이션
<a name="CloudWatch_Synthetics_Canaries_Samples_device"></a>

다양한 디바이스를 에뮬레이션하는 스크립트를 작성하여 해당 디바이스에서 페이지가 어떻게 보이고 동작하는지 대략적으로 알 수 있습니다.

다음 샘플은 iPhone 6 디바이스를 에뮬레이션합니다. 에뮬레이션에 대한 자세한 내용은 Puppeteer 설명서의 [page.emulate(options)](https://pptr.dev/#?product=Puppeteer&version=v5.3.1&show=api-pageemulateoptions)를 참조하세요.

```
var synthetics = require('@aws/synthetics-puppeteer');
const log = require('@aws/synthetics-logger');
const puppeteer = require('puppeteer-core');

const pageLoadBlueprint = async function () {
    
    const iPhone = puppeteer.devices['iPhone 6'];

    // INSERT URL here
    const URL = "https://amazon.com";

    let page = await synthetics.getPage();
    await page.emulate(iPhone);

    //You can customize the wait condition here. For instance,
    //using 'networkidle2' may be less restrictive.
    const response = await page.goto(URL, {waitUntil: 'domcontentloaded', timeout: 30000});
    if (!response) {
        throw "Failed to load page!";
    }
    
    await page.waitFor(15000);

    await synthetics.takeScreenshot('loaded', 'loaded');
    
    //If the response status code is not a 2xx success code
    if (response.status() < 200 || response.status() > 299) {
        throw "Failed to load page!";
    }
};

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

### 다단계 API canary
<a name="CloudWatch_Synthetics_Canaries_Samples_APIsteps"></a>

이 샘플 코드는 양성 및 음성 테스트 사례에 대해 동일한 API를 테스트하는 두 HTTP 단계로 API canary를 보여 줍니다. 단계 구성이 전달되어 요청 또는 응답 헤더의 보고를 사용 설정합니다. 또한 X-Amz-Security-Token 및 Authorization 헤더를 숨깁니다. 이러한 헤더에 사용자 자격 증명이 포함되어 있기 때문입니다.

이 스크립트를 canary로 사용하면 각 단계 및 관련 HTTP 요청에 관한 세부 정보(예: 단계 통과 또는 실패, 지속 시간 그리고 DNS 조회 시간 및 첫 번째 바이트 시간과 같은 성능 지표)를 볼 수 있습니다. canary 실행에 대한 2xx, 4xx, 5xx의 수를 볼 수 있습니다.

```
var synthetics = require('@aws/synthetics-puppeteer');
const log = require('@aws/synthetics-logger');

const apiCanaryBlueprint = async function () {
    
    // Handle validation for positive scenario
    const validatePositiveCase = 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. For ex, your status code is 200 but data might be empty
                resolve();
            });
        });
    };
    
    // Handle validation for negative scenario
    const validateNegativeCase = async function(res) {
        return new Promise((resolve, reject) => {
            if (res.statusCode < 400) {
                throw res.statusCode + ' ' + res.statusMessage;
            }
            
            resolve();
        });
    };
    
    let requestOptionsStep1 = {
        'hostname': 'myproductsEndpoint.com',
        'method': 'GET',
        'path': '/test/product/validProductName',
        'port': 443,
        'protocol': 'https:'
    };
    
    let headers = {};
    headers['User-Agent'] = [synthetics.getCanaryUserAgentString(), headers['User-Agent']].join(' ');
    
    requestOptionsStep1['headers'] = headers;

    // By default headers, post data and response body are not included in the report for security reasons. 
    // Change the configuration at global level or add as step configuration for individual steps
    let stepConfig = {
        includeRequestHeaders: true, 
        includeResponseHeaders: true,
        restrictedHeaders: ['X-Amz-Security-Token', 'Authorization'], // Restricted header values do not appear in report generated.
        includeRequestBody: true,
        includeResponseBody: true
    };
       

    await synthetics.executeHttpStep('Verify GET products API with valid name', requestOptionsStep1, validatePositiveCase, stepConfig);
    
    let requestOptionsStep2 = {
        'hostname': 'myproductsEndpoint.com',
        'method': 'GET',
        'path': '/test/canary/InvalidName(',
        'port': 443,
        'protocol': 'https:'
    };
    
    headers = {};
    headers['User-Agent'] = [synthetics.getCanaryUserAgentString(), headers['User-Agent']].join(' ');
    
    requestOptionsStep2['headers'] = headers;

    // By default headers, post data and response body are not included in the report for security reasons. 
    // Change the configuration at global level or add as step configuration for individual steps
    stepConfig = {
        includeRequestHeaders: true, 
        includeResponseHeaders: true,
        restrictedHeaders: ['X-Amz-Security-Token', 'Authorization'], // Restricted header values do not appear in report generated.
        includeRequestBody: true,
        includeResponseBody: true
    };
    
    await synthetics.executeHttpStep('Verify GET products API with invalid name', requestOptionsStep2, validateNegativeCase, stepConfig);
    
};

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

## Python 및 Selenium 샘플
<a name="CloudWatch_Synthetics_Canaries_Samples_pythonsel"></a>

다음 Selenium 샘플 코드는 대상 요소가 로드되지 않을 때 사용자 지정 오류 메시지를 표시하며 실패하는 canary입니다.

```
from aws_synthetics.selenium import synthetics_webdriver as webdriver
from aws_synthetics.common import synthetics_logger as logger
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By

def custom_selenium_script():
    # create a browser instance
    browser = webdriver.Chrome()
    browser.get('https://www.example.com/')
    logger.info('navigated to home page')
    # set cookie
    browser.add_cookie({'name': 'foo', 'value': 'bar'})
    browser.get('https://www.example.com/')
    # save screenshot
    browser.save_screenshot('signed.png')
    # expected status of an element
    button_condition = EC.element_to_be_clickable((By.CSS_SELECTOR, '.submit-button'))
    # add custom error message on failure
    WebDriverWait(browser, 5).until(button_condition, message='Submit button failed to load').click()
    logger.info('Submit button loaded successfully')
    # browser will be quit automatically at the end of canary run, 
    # quit action is not necessary in the canary script
    browser.quit()

# entry point for the canary
def handler(event, context):
    return custom_selenium_script()
```