diff --git a/.github/scripts/review-apps.sh b/.github/scripts/review-apps.sh index 64fff197..fde890ba 100755 --- a/.github/scripts/review-apps.sh +++ b/.github/scripts/review-apps.sh @@ -2,7 +2,7 @@ set -e echo "Review App Script" # create "unique" name for fly review app -app="mvp-pr-$PR_NUMBER" +app=$APP_NAME secrets="AUTH_API_KEY=$AUTH_API_KEY ENCRYPTION_KEYS=$ENCRYPTION_KEYS" if [ "$EVENT_ACTION" = "closed" ]; then diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f8cdd33f..064187fd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,8 +3,6 @@ name: Elixir CI on: push: branches: [ main ] - pull_request: - branches: [ main ] jobs: @@ -56,64 +54,13 @@ jobs: - name: Upload coverage to Codecov uses: codecov/codecov-action@v1 - # API Definition testing - # https://docs.hoppscotch.io/cli - api_definition: - name: API Definition Tests - runs-on: ubuntu-latest - services: - postgres: - image: postgres:12 - ports: ['5432:5432'] - env: - POSTGRES_PASSWORD: postgres - options: >- - --health-cmd pg_isready - --health-interval 10s - --health-timeout 5s - --health-retries 5 - strategy: - matrix: - otp: ['25.1.2'] - elixir: ['1.14.2'] - steps: - - uses: actions/checkout@v2 - - name: Set up Elixir - uses: erlef/setup-beam@v1 - with: - otp-version: ${{ matrix.otp }} - elixir-version: ${{ matrix.elixir }} - - name: Restore deps and _build cache - uses: actions/cache@v3 - with: - path: | - deps - _build - key: deps-${{ runner.os }}-${{ matrix.otp }}-${{ matrix.elixir }}-${{ hashFiles('**/mix.lock') }} - restore-keys: | - deps-${{ runner.os }}-${{ matrix.otp }}-${{ matrix.elixir }} - - name: Install dependencies - run: mix deps.get - - - name: Install Hoppscotch CLI - run: npm i -g @hoppscotch/cli - - # Setups database and adds seed data for API definition tests - - name: Run mix setup - run: mix ecto.setup - env: - MIX_ENV: dev - AUTH_API_KEY: ${{ secrets.AUTH_API_KEY }} - - - name: Running server and Hoppscotch Tests - run: mix phx.server & sleep 5 && hopp test -e ./lib/api/localhost.json ./lib/api/MVP.json # Continuous Deployment to Fly.io # https://fly.io/docs/app-guides/continuous-deployment-with-github-actions/ deploy: name: Deploy app runs-on: ubuntu-latest - needs: [build, api_definition] + needs: [build] # https://stackoverflow.com/questions/58139406/only-run-job-on-specific-branch-with-github-actions if: github.ref == 'refs/heads/main' env: diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml new file mode 100644 index 00000000..5b4ffa78 --- /dev/null +++ b/.github/workflows/pr.yml @@ -0,0 +1,127 @@ +name: PR CI + +on: + pull_request: + branches: [ main ] + types: [opened, reopened, synchronize, closed] + +jobs: + + # BUILD AND UNIT TESTING + build: + + # Only run when not closed + if: github.event.pull_request.action != 'closed' + + name: Build and Test + runs-on: ubuntu-latest + services: + postgres: + image: postgres:12 + ports: ['5432:5432'] + env: + POSTGRES_PASSWORD: postgres + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + strategy: + matrix: + otp: ['25.1.2'] + elixir: ['1.14.2'] + steps: + - uses: actions/checkout@v2 + - name: Set up Elixir + uses: erlef/setup-beam@v1 + with: + otp-version: ${{ matrix.otp }} + elixir-version: ${{ matrix.elixir }} + - name: Restore deps and _build cache + uses: actions/cache@v3 + with: + path: | + deps + _build + key: deps-${{ runner.os }}-${{ matrix.otp }}-${{ matrix.elixir }}-${{ hashFiles('**/mix.lock') }} + restore-keys: | + deps-${{ runner.os }}-${{ matrix.otp }}-${{ matrix.elixir }} + - name: Install dependencies + run: mix deps.get + - name: Check code is formatted + run: mix format --check-formatted + - name: Run Tests + run: mix coveralls.json + env: + MIX_ENV: test + AUTH_API_KEY: ${{ secrets.AUTH_API_KEY }} + ENCRYPTION_KEYS: ${{ secrets.ENCRYPTION_KEYS }} + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v1 + + + + # DEPLOY THE REVIEW APP + # This will deploy an app to fly.io with the name 'mvp-pr-$PR_NUMBER' (check `review-apps.sh` script). + review_app: + + # Only run when it's not a dependabot PR + if: github.event.pull_request.user.login != 'dependabot[bot]' + + name: Review App Job + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Install flyctl + run: curl -L https://fly.io/install.sh | FLYCTL_INSTALL=/usr/local sh + + - name: Set up Elixir + uses: erlef/setup-beam@v1 + with: + otp-version: 24.3.4 + elixir-version: 1.14.1 + + - name: Run Review App Script + run: ./.github/scripts/review-apps.sh + env: + ENCRYPTION_KEYS: ${{ secrets. ENCRYPTION_KEYS }} + AUTH_API_KEY: ${{ secrets.FLY_AUTH_API_KEY }} + APP_NAME: ${{ format('mvp-pr-{0}', github.event.number) }} + EVENT_ACTION: ${{ github.event.action }} + FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }} + FLY_ORG: dwyl-mvp + FLY_REGION: lhr + FLY_POSTGRES_NAME: mvp-db + + + # API DEFINITION TESTING - https://docs.hoppscotch.io/cli + # NOTE: The tests are executed sequentially in each folder + # but all the sequence of folders are run in reverse order of what's displayed in the GUI. + # We've made use of env variables to make it work. Take this into account if you want to add more requests. + api_definition: + + # Only run when not closed + if: github.event.pull_request.action != 'closed' + + name: API Definition Tests + runs-on: ubuntu-latest + needs: [review_app] + steps: + - uses: actions/checkout@v2 + + # After the app is deployed, we need to change the host + # in the `lib/api/fly_dev.json` so the API tests work and target the deployed app. + - name: Update lib/api/fly_dev.json description + uses: jossef/action-set-json-field@v2.1 + with: + file: ./lib/api/fly_dev.json + field: host + value: ${{ format('https://mvp-pr-{0}.fly.dev', github.event.number) }} + + - name: Install Hoppscotch CLI + run: npm i -g @hoppscotch/cli@0.3.0 + + - name: Running server and Hoppscotch Tests + run: hopp test -e ./lib/api/fly_dev.json ./lib/api/MVP.json \ No newline at end of file diff --git a/.github/workflows/review-apps.yml b/.github/workflows/review-apps.yml deleted file mode 100644 index 5b646f73..00000000 --- a/.github/workflows/review-apps.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: Review App -on: - pull_request: - types: [opened, reopened, synchronize, closed] -env: - FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }} -jobs: - review_app: - if: github.event.pull_request.user.login != 'dependabot[bot]' - name: Review App Job - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v3 - - name: Install flyctl - run: curl -L https://fly.io/install.sh | FLYCTL_INSTALL=/usr/local sh - - name: Set up Elixir - uses: erlef/setup-beam@v1 - with: - otp-version: 24.3.4 - elixir-version: 1.14.1 - - name: Run Review App Script - run: ./.github/scripts/review-apps.sh - env: - ENCRYPTION_KEYS: ${{ secrets. ENCRYPTION_KEYS }} - AUTH_API_KEY: ${{ secrets.FLY_AUTH_API_KEY }} - PR_NUMBER: ${{ github.event.number}} - EVENT_ACTION: ${{ github.event.action }} - FLY_ORG: dwyl-mvp - FLY_REGION: lhr - FLY_POSTGRES_NAME: mvp-db - diff --git a/build-review-apps.md b/build-review-apps.md index 714900f4..39378e12 100644 --- a/build-review-apps.md +++ b/build-review-apps.md @@ -1,60 +1,130 @@ # Fly Review Apps with Github Actions -## Setup +In this small guide, +we are going to be setting up +[CI/CD](https://aws.amazon.com/devops/continuous-integration/) +so code tests are automatically executed in every PR made to the main branch +and to maintain code quality +throughout the development cycle. + +We are going to be focusing +on the job that **deploys the app to `fly.io`**, +which constitutes the **"Review App"**. -Firstly read [Understanding Github Actions](https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions). +- [Fly Review Apps with Github Actions](#fly-review-apps-with-github-actions) + - [Setup](#setup) + - [Workflow](#workflow) + - [Create script](#create-script) + - [You're done! 🎉](#youre-done-) -Now we can start to build our own Github actions to create "review apps" -when a PR is created. + +## Setup + +Firstly, +read [Understanding Github Actions](https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions) +to have a better understanding of what Github Actions are +and how to properly have them executed. + +Now we can start building our own Github actions +that create **"Review Apps"** when a PR is created. +**"Review Apps"** pertain to the code of the PR +that is deployed automatically to +[`fly.io`](https://fly.io/). +With this app deployed, +it is easy to see how different the app is +with the changes made in the PR +*and* is extremely useful to also run API definition tests against it. Because we are going to use `flyctl` to create and deploy the applications -based on the PRs, we need first to create a `fly auth token`. +based on the PRs, +we first need to create a `fly auth token`. -Run `flyctl auth token`. This will create and show the token in your terminal. -see: https://fly.io/docs/flyctl/auth-token/ +Run `flyctl auth token`. +This will create and show the token in your terminal. +see: https://fly.io/docs/flyctl/auth-token/. In your Github repository, create a new secret named `FLY_API_TOKEN` -and associate to it the token: go to `Settings` then `Secrets` and finally `Actions`. +and associate to it the token: +go to `Settings` then `Secrets` and finally `Actions`. At the top right corner you should see the `New repository secret` button. -`flyctl` will check if the `FLY_API_TOKEN` environment variable is defined, and -use it to manage the Fly applications. +secret + + +`flyctl` will check if the `FLY_API_TOKEN` environment variable is defined, +and use it to manage the Fly applications. -## Create Workflow +## Workflow -Create a new [workflow](https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions#workflows) -file, for example `.github/workflows/review-apps.yml`: +If you open the file +[`.github/workflows/pr.yml`](./.github/workflows/pr.yml), +you will see how the "Review App" is deployed. +This file is executed on all the PRs +that target the `main` branch. ```yml -name: Review App +name: PR CI + on: pull_request: + branches: [ main ] types: [opened, reopened, synchronize, closed] -env: - FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }} + jobs: + + ... + + # DEPLOY THE REVIEW APP + # This will deploy an app to fly.io with the name 'mvp-pr-$PR_NUMBER' (check `review-apps.sh` script). review_app: + + # Only run when it's not a dependabot PR + if: github.event.pull_request.user.login != 'dependabot[bot]' + name: Review App Job runs-on: ubuntu-latest + needs: [build] steps: - name: Checkout repository uses: actions/checkout@v3 + - name: Install flyctl run: curl -L https://fly.io/install.sh | FLYCTL_INSTALL=/usr/local sh + + - name: Set up Elixir + uses: erlef/setup-beam@v1 + with: + otp-version: 24.3.4 + elixir-version: 1.14.1 + - name: Run Review App Script - run: ./.github/scripts/review-app.sh + run: ./.github/scripts/review-apps.sh + env: + ENCRYPTION_KEYS: ${{ secrets. ENCRYPTION_KEYS }} + AUTH_API_KEY: ${{ secrets.FLY_AUTH_API_KEY }} + APP_NAME: ${{ format('mvp-pr-{0}', github.event.number) }} + EVENT_ACTION: ${{ github.event.action }} + FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }} + FLY_ORG: dwyl-mvp + FLY_REGION: lhr + FLY_POSTGRES_NAME: mvp-db + + ... ``` -- Trigger this Github action when a PR is created, updated, reopened or closed: +- The Github Action is triggered when a PR is created, updated, reopened or closed: ```yml on: pull_request: + branches: [ main ] types: [opened, reopened, synchronize, closed] ``` -- Add the environment variable to the workflow. The `FLY_API_TOKEN` is set -from the Github repository secrets. This environment is used by `flyctl` to +- The environment variable is added to the workflow. +The `FLY_API_TOKEN` is set +from the Github repository secrets. +This environment is used by `flyctl` to authenticate the requests to Fly.io. Learn more about Github action environment variables: @@ -65,34 +135,48 @@ env: FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }} ``` -- Start a new `job` with a runner based on the latest ubuntu: +- Start a new `job` with a runner based on the latest ubuntu. +The job only runs after the `build` job +and not when the [dependabot](https://github.com/dependabot) creates a PR. ```yml jobs: + + ... review_app: + + # Only run when it's not a dependabot PR + if: github.event.pull_request.user.login != 'dependabot[bot]' + name: Review App Job runs-on: ubuntu-latest + needs: [build] + steps: ``` -- Finally define the steps of the job. +- Defining the rest of the steps of the job -First we checkout the repository to the -runner using the `actions/checkout`. This allow the runner to access the files -in the repository. `flyctl` needs to be able to read the `Dockerfile` and the `fly.toml` -to build the application but we also need our runner to be able to access the shell -script defines in the last steps. +First, +we checkout the repository to the +runner using the `actions/checkout`. +This allows the runner to access the files in the repository. +`flyctl` needs to be able to read the `Dockerfile` and the `fly.toml` +to build the application, +but we also need our runner to be able to access +the shell script defined in the last step. -The second steps is to install `flyctl`: `curl -L https://fly.io/install.sh | FLYCTL_INSTALL=/usr/local sh` -This command download `flyctl` and install it in `/usr/local`. The `FLYCTL_INSTALL` -environment variable is used in the `install.sh` script to define where to install -`flyctl`, see: -https://github.com/superfly/flyctl/blob/74443a0696e3c3d7d8dbbec6feded3dc928e251f/installers/install.sh#L17 +The second step is to install `flyctl`: `curl -L https://fly.io/install.sh | FLYCTL_INSTALL=/usr/local sh` +This command downloads `flyctl` and installs it in `/usr/local`. +The `FLYCTL_INSTALL` environment variable +is used in the `install.sh` script to define where to install `flyctl`, +see: https://github.com/superfly/flyctl/blob/74443a0696e3c3d7d8dbbec6feded3dc928e251f/installers/install.sh#L17 -> /usr/local is used by the system administrator when installing software locally +> `/usr/local` is used by the system administrator when installing software locally. -The third and final step is to run the shell script that we're going to define next. -This script will contain all the required `flyctl` command calls to create and deploy -our review applications. +The third and final step is to run the shell script +that we're going to define next. +This script will contain all the required `flyctl` command calls +to create and deploy our "review apps". ```yml - name: Run Review App Script @@ -110,12 +194,11 @@ steps: run: ./.github/scripts/review-app.sh ``` -refs: -- [Using jobs in a workflow](https://docs.github.com/en/actions/using-jobs/using-jobs-in-a-workflow) +ref: [Using jobs in a workflow](https://docs.github.com/en/actions/using-jobs/using-jobs-in-a-workflow) -# Create script +## Create script -This script take inspiration from: https://github.com/superfly/fly-pr-review-apps/blob/main/entrypoint.sh +This script takes inspiration from: https://github.com/superfly/fly-pr-review-apps/blob/main/entrypoint.sh The first step is to create the script file `.github/scripts/review-app.sh` and make it executable by running `chmod +x .github/scripts/review-app.sh` @@ -127,7 +210,7 @@ set -e echo "Review App Script" # create "unique" name for fly review app -app="mvp-pr-$PR_NUMBER" +app=$APP_NAME secrets="AUTH_API_KEY=$AUTH_API_KEY ENCRYPTION_KEYS=$ENCRYPTION_KEYS" if [ "$EVENT_ACTION" = "closed" ]; then @@ -166,7 +249,7 @@ You can see the documentation with `help set`: variables we're going to add to the Fly application: ```sh -app="mvp-pr-$PR_NUMBER" +app=$APP_NAME secrets="AUTH_API_KEY=$AUTH_API_KEY ENCRYPTION_KEYS=$ENCRYPTION_KEYS" ``` @@ -187,12 +270,12 @@ If it doesn't, we first create the application with: flyctl launch --no-deploy --copy-config --name "$app" --region "$FLY_REGION" --org "$FLY_ORG" --remote-only ``` ---no-deploy to not deploy the application yet as we need to set environment variables +- `--no-deploy `to not deploy the application yet as we need to set environment variables and attach the database to it. ---copy-config, use the existing configuration file. +- `--copy-config`, use the existing configuration file. -See more documentation for launch: https://fly.io/docs/flyctl/launch/ +See more documentation for launch: https://fly.io/docs/flyctl/launch/. The next step is to attach the existing postgres cluster to the new application with: @@ -210,50 +293,63 @@ The `tr` command replaces the spaces by new lines. The values are then piped to the `flyctl secrets inport` command. -Finally we deploy the application: +Finally we deploy the application! ```sh flyctl deploy --app "$app" --region "$FLY_REGION" --strategy immediate ``` -see deploy doc: https://fly.io/docs/flyctl/deploy/ +see deploy doc: https://fly.io/docs/flyctl/deploy/. -> --strategy string -The strategy for replacing running instances. Options are canary, rolling, bluegreen, -or immediate. Default is canary, or rolling when max-per-region is set. +> `--strategy string` +The strategy for replacing running instances. +Options are `canary`, `rolling`, bl`uegreen, +or `immediate`. +Default is `canary`, or `rolling` when max-per-region is set. If the application already exists we only use the `deploy` command as we don't -need to `launch`, set environment variables or attach the postgres cluster. +need to `launch`, set environment variables or attach the Postgres cluster. -For new created applications a new empty database and user for the database will -be created. +For newly created applications, +a new empty database and user for the database will be created. -When PRs are merged/closed the Fly applications will be `destroyed`, however -the database and user will still be present in the cluster. +When PRs are merged/closed, the Fly apps will be **destroyed**. +However, the database and user will still be present in the cluster. -To remove them run: +To remove them, run: ```sh flyctl postgres connect -a ``` -Once connected in psql run: +Once connected, in `psql`, run: ```sh drop database with (force); drop user ; ``` -To see existing databases and users (roles): +## You're done! 🎉 -```sh -# \l -# \du -``` +Now you know how to create a job +that uses a *script* to deploy +a "Review App"! + +If you take a look at the +rest of +[`.github/workflows/pr.yml`](./.github/workflows/pr.yml), +you will notice there are two other jobs: + +- **`build`**: builds and runs the unit tests +of the Elixir app. +It also checks if code is formatted. +If executed successfully, +the code coverage is pushed to `Codecov`. +- **`api_definition`**: runs API definition tests. +It *must be executed **after*** the `review_app` job, +because it makes requests to the deployed "Review App". -\l list the databases -\du list the users diff --git a/lib/api/MVP.json b/lib/api/MVP.json index 83f721ed..74969983 100644 --- a/lib/api/MVP.json +++ b/lib/api/MVP.json @@ -1,97 +1,65 @@ [ { + "v": 1, + "requests": [], "folders": [ { - "folders": [], - "name": "Items", "v": 1, + "name": "Items", "requests": [ { - "params": [], - "endpoint": "<>/api/items/<>", - "headers": [ - { - "active": true, - "key": "accept", - "value": "application/json" - } - ], - "preRequestScript": "", "body": { - "body": null, - "contentType": null + "contentType": "application/json", + "body": "{\n \"text\": \"aaa text\"\n}" }, - "v": "1", - "testScript": "// Check status code is 2xx\npw.test(\"Status code is 2xx\", ()=> {\n pw.expect(pw.response.status).toBeLevel2xx();\n});\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.person_id).toBeType(\"number\");\n pw.expect(pw.response.body.status).toBeType(\"number\");\n});", - "name": "Get item", + "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.id).toBeType(\"number\");\n\n // Setting `item_id` to the response value \n \tpw.env.set(\"item_id\", pw.response.body.id.toString());\n});", + "preRequestScript": "", "auth": { "authActive": true, - "value": "", - "addTo": "Headers", - "key": "", "authType": "none" }, - "method": "GET" - }, - { + "endpoint": "<>/api/items", + "name": "Create item", "headers": [ { + "active": true, "value": "application/json", - "key": "accept", - "active": true + "key": "accept" } ], - "method": "GET", - "preRequestScript": "", + "v": "1", + "params": [], + "method": "POST" + }, + { "auth": { "authType": "none", "authActive": true }, - "params": [], - "testScript": "// Check status code is 404\npw.test(\"Status code is 404\", ()=> {\n pw.expect(pw.response.status).toBe(404);\n});", - "name": "Get item (404 - Item not found)", - "endpoint": "<>/api/items/<>", - "v": "1", - "body": { - "contentType": null, - "body": null - } - }, - { + "endpoint": "<>/api/items", "body": { - "body": null, - "contentType": null + "contentType": "application/json", + "body": "{\n \"invalid\": \"something\"\n}" }, - "params": [], - "preRequestScript": "", - "method": "GET", - "name": "Get item (400 - Invalid ID)", - "endpoint": "<>/api/items/<>", "testScript": "// Check status code is 400\npw.test(\"Status code is 400\", ()=> {\n pw.expect(pw.response.status).toBe(400);\n});", + "preRequestScript": "", + "method": "POST", + "params": [], "headers": [ { + "active": true, "value": "application/json", - "key": "accept", - "active": true + "key": "accept" } ], - "v": "1", - "auth": { - "authType": "none", - "authActive": true - } + "name": "Create item (400 - Invalid attributes)", + "v": "1" }, { + "preRequestScript": "const currentTime = Date.now();\npw.env.set(\"tag_text_to_be_repeated\", currentTime.toString() + \"_tagtext\");", + "params": [], + "endpoint": "<>/api/items", "v": "1", - "endpoint": "<>/api/items/<>", - "name": "Get item with tag embed", - "params": [ - { - "key": "embed", - "value": "tag", - "active": true - } - ], "headers": [ { "value": "application/json", @@ -99,52 +67,79 @@ "key": "accept" } ], - "method": "GET", "auth": { - "value": "", "authType": "none", - "authActive": true, - "key": "", - "addTo": "Headers" + "authActive": true }, - "preRequestScript": "", - "testScript": "// Check status code is 2xx\npw.test(\"Status code is 2xx\", ()=> {\n pw.expect(pw.response.status).toBeLevel2xx();\n});\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.person_id).toBeType(\"number\");\n pw.expect(pw.response.body.status).toBeType(\"number\");\n pw.expect(pw.response.body.tags).not.toBeType(\"undefined\");\n});", + "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.id).toBeType(\"number\");\n});", + "name": "Create item with tags", + "method": "POST", "body": { - "contentType": null, - "body": null + "body": "{\n \"text\": \"some text\",\n \"tags\": [\n {\n \"text\": \"<>\",\n \"color\": \"#FFFFFF\"\n }\n ]\n}", + "contentType": "application/json" } }, { + "name": "Create item with tags (400 - Tag already exists)", "headers": [ { - "key": "accept", "value": "application/json", - "active": true + "active": true, + "key": "accept" } ], - "method": "POST", + "v": "1", + "preRequestScript": "", "body": { "contentType": "application/json", - "body": "{\n \"text\": \"aaa text\"\n}" + "body": "{\n \"text\": \"some text\",\n \"tags\": [\n {\n \"text\": \"<>\",\n \"color\": \"#FFFFFF\"\n }\n ]\n}" }, - "v": "1", - "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.id).toBeType(\"number\");\n});", + "method": "POST", + "auth": { + "authActive": true, + "authType": "none" + }, + "testScript": "// Check status code is 400\npw.test(\"Status code is 400\", ()=> {\n pw.expect(pw.response.status).toBe(400);\n});", + "endpoint": "<>/api/items", + "params": [] + }, + { + "body": { + "body": "{\n \"text\": \"some text\",\n \"tags\": [\n {\n \"text\": \"yet another tag\",\n \"color\": \"#FFFFFF\"\n }\n ]\n}", + "contentType": "application/json" + }, + "method": "POST", + "testScript": "// Check status code is 400\npw.test(\"Status code is 400\", ()=> {\n pw.expect(pw.response.status).toBe(400);\n});", "endpoint": "<>/api/items", - "params": [], "preRequestScript": "", "auth": { "authType": "none", "authActive": true }, - "name": "Create item" + "v": "1", + "headers": [ + { + "key": "accept", + "value": "application/json", + "active": true + } + ], + "params": [], + "name": "Create item with tags (400 - Tag is malformed)" }, { - "body": { - "contentType": "application/json", - "body": "{\n \"invalid\": \"something\"\n}" + "name": "Create item with tags (400 - Tag is malformed)", + "auth": { + "authType": "none", + "authActive": true }, - "name": "Create item (400 - Invalid attributes)", + "v": "1", + "params": [], "endpoint": "<>/api/items", + "body": { + "body": "{\n \"text\": \"some text\",\n \"tags\": [\n {\n \"invalid\": \"yet another tag\"\n }\n ]\n}", + "contentType": "application/json" + }, "headers": [ { "active": true, @@ -154,192 +149,206 @@ ], "method": "POST", "preRequestScript": "", - "auth": { - "authActive": true, - "authType": "none" - }, - "testScript": "// Check status code is 400\npw.test(\"Status code is 400\", ()=> {\n pw.expect(pw.response.status).toBe(400);\n});", - "params": [], - "v": "1" + "testScript": "// Check status code is 400\npw.test(\"Status code is 400\", ()=> {\n pw.expect(pw.response.status).toBe(400);\n});" }, { - "v": "1", + "body": { + "contentType": null, + "body": null + }, + "testScript": "// Check status code is 2xx\npw.test(\"Status code is 2xx\", ()=> {\n pw.expect(pw.response.status).toBeLevel2xx();\n});\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n\n pw.expect(pw.response.body.person_id).toBeType(\"number\");\n pw.expect(pw.response.body.status).toBeType(\"number\");\n});", + "params": [], + "auth": { + "value": "", + "addTo": "Headers", + "key": "", + "authType": "none", + "authActive": true + }, "preRequestScript": "", + "endpoint": "<>/api/items/<>", "headers": [ { - "value": "application/json", "key": "accept", + "value": "application/json", "active": true } ], - "endpoint": "<>/api/items", + "method": "GET", + "name": "Get item", + "v": "1" + }, + { "body": { - "contentType": "application/json", - "body": "{\n \"text\": \"some text\",\n \"tags\": [\n {\n \"text\": \"yet another tag\",\n \"color\": \"#FFFFFF\"\n }\n ]\n}" + "body": null, + "contentType": null }, - "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.id).toBeType(\"number\");\n});", - "method": "POST", - "name": "Create item with tags", - "params": [], + "method": "GET", + "testScript": "// Check status code is 404\npw.test(\"Status code is 404\", ()=> {\n pw.expect(pw.response.status).toBe(404);\n});", "auth": { "authActive": true, "authType": "none" - } - }, - { - "name": "Create item with tags (400 - Tag already exists)", + }, + "name": "Get item (404 - Item not found)", "headers": [ { - "value": "application/json", "key": "accept", - "active": true + "active": true, + "value": "application/json" } ], - "endpoint": "<>/api/items", - "testScript": "// Check status code is 400\npw.test(\"Status code is 400\", ()=> {\n pw.expect(pw.response.status).toBe(400);\n});", - "params": [], - "method": "POST", - "auth": { - "authType": "none", - "authActive": true - }, - "preRequestScript": "", + "endpoint": "<>/api/items/<>", "v": "1", - "body": { - "body": "{\n \"text\": \"some text\",\n \"tags\": [\n {\n \"text\": \"yet another tag\",\n \"color\": \"#FFFFFF\"\n }\n ]\n}", - "contentType": "application/json" - } + "preRequestScript": "", + "params": [] }, { - "endpoint": "<>/api/items", - "name": "Create item with tags (400 - Tag is malformed)", "auth": { - "authType": "none", - "authActive": true + "authActive": true, + "authType": "none" }, + "v": "1", + "name": "Get item (400 - Invalid ID)", + "params": [], "headers": [ { "value": "application/json", - "key": "accept", - "active": true + "active": true, + "key": "accept" } ], + "preRequestScript": "", "testScript": "// Check status code is 400\npw.test(\"Status code is 400\", ()=> {\n pw.expect(pw.response.status).toBe(400);\n});", - "params": [], - "method": "POST", - "v": "1", + "method": "GET", + "endpoint": "<>/api/items/<>", "body": { - "contentType": "application/json", - "body": "{\n \"text\": \"some text\",\n \"tags\": [\n {\n \"text\": \"yet another tag\",\n \"color\": \"#FFFFFF\"\n }\n ]\n}" - }, - "preRequestScript": "" + "body": null, + "contentType": null + } }, { - "name": "Create item with tags (400 - Tag is malformed)", + "endpoint": "<>/api/items/<>", + "body": { + "contentType": null, + "body": null + }, + "testScript": "// Check status code is 2xx\npw.test(\"Status code is 2xx\", ()=> {\n pw.expect(pw.response.status).toBeLevel2xx();\n});\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.person_id).toBeType(\"number\");\n pw.expect(pw.response.body.status).toBeType(\"number\");\n pw.expect(pw.response.body.tags).not.toBeType(\"undefined\");\n});", + "auth": { + "authType": "none", + "authActive": true, + "addTo": "Headers", + "key": "", + "value": "" + }, + "method": "GET", + "v": "1", + "name": "Get item with tag embed", "preRequestScript": "", - "method": "POST", - "params": [], - "testScript": "// Check status code is 400\npw.test(\"Status code is 400\", ()=> {\n pw.expect(pw.response.status).toBe(400);\n});", - "headers": [ + "params": [ { - "value": "application/json", - "key": "accept", + "key": "embed", + "value": "tag", "active": true } ], - "body": { - "contentType": "application/json", - "body": "{\n \"text\": \"some text\",\n \"tags\": [\n {\n \"invalid\": \"yet another tag\"\n }\n ]\n}" - }, - "endpoint": "<>/api/items", - "v": "1", - "auth": { - "authType": "none", - "authActive": true - } + "headers": [ + { + "key": "accept", + "active": true, + "value": "application/json" + } + ] }, { - "params": [], + "method": "PUT", + "body": { + "body": "{\n \"text\": \"new updated text\"\n}", + "contentType": "application/json" + }, "auth": { "authType": "none", "authActive": true }, - "name": "Update item", "endpoint": "<>/api/items/<>", "headers": [ { - "value": "application/json", + "key": "accept", "active": true, - "key": "accept" + "value": "application/json" } ], - "body": { - "body": "{\n \"text\": \"new updated text\"\n}", - "contentType": "application/json" - }, + "preRequestScript": "", "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.person_id).toBeType(\"number\");\n pw.expect(pw.response.body.status).toBeType(\"number\");\n});", - "method": "PUT", "v": "1", - "preRequestScript": "" + "params": [], + "name": "Update item" }, { + "endpoint": "<>/api/items/<>", + "preRequestScript": "", + "testScript": "// Check status code is 404\npw.test(\"Status code is 404\", ()=> {\n pw.expect(pw.response.status).toBe(404);\n});", "method": "PUT", - "headers": [ - { - "active": true, - "key": "accept", - "value": "application/json" - } - ], "auth": { "authActive": true, "authType": "none" }, - "v": "1", "body": { "body": "{\n \"text\": \"new updated text\"\n}", "contentType": "application/json" }, - "preRequestScript": "", "params": [], + "v": "1", "name": "Update item (404 - Item not found)", - "testScript": "// Check status code is 404\npw.test(\"Status code is 404\", ()=> {\n pw.expect(pw.response.status).toBe(404);\n});", - "endpoint": "<>/api/items/<>" + "headers": [ + { + "value": "application/json", + "key": "accept", + "active": true + } + ] } - ] + ], + "folders": [] }, { + "v": 1, + "folders": [], + "name": "Timers", "requests": [ { - "endpoint": "<>/api/items/<>/timers", - "v": "1", - "params": [], - "name": "Get timers", - "preRequestScript": "", - "method": "GET", + "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.id).toBeType(\"number\");\n\n \t// Setting `item_id` to the response value \n \tpw.env.set(\"item_id\", pw.response.body.id.toString());\n});\n", "headers": [ { + "value": "application/json", "key": "accept", - "active": true, - "value": "application/json" + "active": true } ], + "preRequestScript": "", "body": { - "contentType": null, - "body": null + "body": "{\n \"text\": \"aaa text\"\n}", + "contentType": "application/json" }, - "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});", + "name": "Create item", + "v": "1", + "method": "POST", "auth": { - "authActive": true, - "authType": "none" - } + "authType": "none", + "authActive": true + }, + "endpoint": "<>/api/items", + "params": [] }, { - "preRequestScript": "", + "body": { + "contentType": "application/json", + "body": "{\n \"start\": \"2023-01-11T17:40:44\"\n}" + }, "auth": { "authActive": true, "authType": "none" }, + "name": "Create timer (custom start)", "headers": [ { "value": "application/json", @@ -347,78 +356,71 @@ "active": true } ], + "preRequestScript": "", + "v": "1", + "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.id).toBeType(\"number\");\n \t\n \t// Setting `timer_id` to the response value \n \tpw.env.set(\"timer_id\", pw.response.body.id.toString());\n});", + "endpoint": "<>/api/items/<>/timers", + "method": "POST", + "params": [] + }, + { "params": [], + "preRequestScript": "", + "name": "Create timer (no start)", "v": "1", + "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.id).toBeType(\"number\");\n});\n\n\n// Set an \"timer_to_stop\" env variable\npw.env.set(\"timer_id_to_stop\", pw.response.body.id.toString());\n", "body": { - "body": null, - "contentType": null + "contentType": null, + "body": null }, - "name": "Get timer", - "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});", - "endpoint": "<>/api/items/<>/timers/<>", - "method": "GET" - }, - { "headers": [ { - "active": true, "value": "application/json", + "active": true, "key": "accept" } ], - "name": "Get timer (404 - Timer not found)", - "preRequestScript": "", "auth": { "authType": "none", "authActive": true }, - "v": "1", - "testScript": "// Check status code is 404\npw.test(\"Status code is 404\", ()=> {\n pw.expect(pw.response.status).toBe(404);\n});", - "body": { - "body": null, - "contentType": null - }, - "method": "GET", - "endpoint": "<>/api/items/<>/timers/<>", - "params": [] + "method": "POST", + "endpoint": "<>/api/items/<>/timers" }, { + "params": [], "preRequestScript": "", "body": { - "contentType": null, - "body": null + "body": "{\n \"start\": \"2023-01-11T17:40:44\",\n \"stop\": \"2023-01-11T17:40:40\"\n}", + "contentType": "application/json" }, - "endpoint": "<>/api/items/<>/timers/<>", - "method": "GET", - "params": [], - "name": "Get timer (400 - Invalid ID)", "testScript": "// Check status code is 400\npw.test(\"Status code is 400\", ()=> {\n pw.expect(pw.response.status).toBe(400);\n});", - "v": "1", + "method": "POST", "auth": { - "authActive": true, - "authType": "none" + "authType": "none", + "authActive": true }, + "v": "1", "headers": [ { - "key": "accept", "active": true, + "key": "accept", "value": "application/json" } - ] + ], + "name": "Create timer (400 - Stop is after start) ", + "endpoint": "<>/api/items/<>/timers" }, { - "auth": { - "authActive": true, - "authType": "none" - }, - "method": "POST", - "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.id).toBeType(\"number\");\n});", "body": { "contentType": "application/json", - "body": "{\n \"start\": \"2023-01-11T17:40:44\"\n}" + "body": "{\n \"start\": \"2023-invalid-01\"\n}" }, - "name": "Create timer (custom start)", - "preRequestScript": "", + "params": [], + "testScript": "// Check status code is 400\npw.test(\"Status code is 400\", ()=> {\n pw.expect(pw.response.status).toBe(400);\n});", + "name": "Create timer (400 - Invalid date format) ", + "v": "1", + "endpoint": "<>/api/items/<>/timers", "headers": [ { "key": "accept", @@ -426,88 +428,121 @@ "active": true } ], - "endpoint": "<>/api/items/<>/timers", - "params": [], - "v": "1" + "method": "POST", + "auth": { + "authType": "none", + "authActive": true + }, + "preRequestScript": "" }, { - "params": [], - "v": "1", + "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});", + "preRequestScript": "", "headers": [ { + "value": "application/json", "active": true, - "key": "accept", - "value": "application/json" + "key": "accept" } ], - "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.id).toBeType(\"number\");\n});", + "endpoint": "<>/api/items/<>/timers", + "body": { + "body": null, + "contentType": null + }, + "name": "Get timers", + "params": [], + "method": "GET", + "v": "1", "auth": { "authType": "none", "authActive": true - }, - "name": "Create timer (no start)", - "preRequestScript": "", - "endpoint": "<>/api/items/<>/timers", - "body": { - "contentType": null, - "body": null - }, - "method": "POST" + } }, { - "body": { - "contentType": "application/json", - "body": "{\n \"start\": \"2023-01-11T17:40:44\",\n \"stop\": \"2023-01-11T17:40:40\"\n}" - }, "preRequestScript": "", + "method": "GET", + "params": [], + "endpoint": "<>/api/items/<>/timers/<>", "auth": { "authType": "none", "authActive": true }, - "v": "1", - "params": [], - "method": "POST", - "endpoint": "<>/api/items/<>/timers", + "name": "Get timer", + "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});", "headers": [ { - "key": "accept", "active": true, + "key": "accept", "value": "application/json" } ], - "name": "Create timer (400 - Stop is after start) ", - "testScript": "// Check status code is 400\npw.test(\"Status code is 400\", ()=> {\n pw.expect(pw.response.status).toBe(400);\n});" + "body": { + "contentType": null, + "body": null + }, + "v": "1" }, { + "testScript": "// Check status code is 404\npw.test(\"Status code is 404\", ()=> {\n pw.expect(pw.response.status).toBe(404);\n});", "params": [], + "method": "GET", + "endpoint": "<>/api/items/<>/timers/<>", + "preRequestScript": "", "body": { - "contentType": "application/json", - "body": "{\n \"start\": \"2023-invalid-01\"\n}" + "contentType": null, + "body": null }, "auth": { "authType": "none", "authActive": true }, - "endpoint": "<>/api/items/<>/timers", - "name": "Create timer (400 - Invalid date format) ", - "testScript": "// Check status code is 400\npw.test(\"Status code is 400\", ()=> {\n pw.expect(pw.response.status).toBe(400);\n});", - "method": "POST", "headers": [ { - "key": "accept", "active": true, + "key": "accept", "value": "application/json" } ], - "preRequestScript": "", + "name": "Get timer (404 - Timer not found)", "v": "1" }, { - "endpoint": "<>/api/items/<>/timers/<>", + "preRequestScript": "", + "v": "1", + "headers": [ + { + "key": "accept", + "active": true, + "value": "application/json" + } + ], + "testScript": "// Check status code is 400\npw.test(\"Status code is 400\", ()=> {\n pw.expect(pw.response.status).toBe(400);\n});", + "endpoint": "<>/api/items/<>/timers/<>", "body": { - "body": "{\n \"start\": \"2023-01-11T17:40:44\",\n \"stop\": \"2023-01-11T17:40:48\"\n}", - "contentType": "application/json" + "contentType": null, + "body": null + }, + "method": "GET", + "name": "Get timer (400 - Invalid ID)", + "auth": { + "authType": "none", + "authActive": true + }, + "params": [] + }, + { + "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});", + "endpoint": "<>/api/timers/<>", + "v": "1", + "preRequestScript": "", + "params": [], + "body": { + "body": null, + "contentType": null }, + "method": "PUT", + "name": "Stop timer", "headers": [ { "value": "application/json", @@ -515,31 +550,22 @@ "key": "accept" } ], - "name": "Update timer", - "params": [], - "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.start).toBeType(\"string\");\n pw.expect(pw.response.body.stop).toBeType(\"string\");\n});", - "v": "1", "auth": { "authActive": true, "authType": "none" - }, - "preRequestScript": "", - "method": "PUT" + } }, { "v": "1", - "endpoint": "<>/api/items/<>/timers/<>", - "params": [], + "endpoint": "<>/api/timers/<>", "body": { - "body": "{\n \"start\": \"2023-invalid-01\"\n}", - "contentType": "application/json" + "contentType": null, + "body": null }, "auth": { - "authActive": true, - "authType": "none" + "authType": "none", + "authActive": true }, - "preRequestScript": "", - "name": "Update timer (400 - Invalid date format)", "headers": [ { "active": true, @@ -547,150 +573,129 @@ "value": "application/json" } ], - "method": "PUT", - "testScript": "// Check status code is 400\npw.test(\"Status code is 400\", ()=> {\n pw.expect(pw.response.status).toBe(400);\n});" + "name": "Stop timer (403 - Timer has already been stopped)", + "params": [], + "testScript": "// Check status code is 403\npw.test(\"Status code is 403\", ()=> {\n pw.expect(pw.response.status).toBe(403);\n});", + "preRequestScript": "", + "method": "PUT" }, { - "endpoint": "<>/api/items/<>/timers/<>", - "testScript": "// Check status code is 400\npw.test(\"Status code is 400\", ()=> {\n pw.expect(pw.response.status).toBe(400);\n});", + "name": "Stop timer (404 - Timer not found)", "v": "1", - "body": { - "body": "{\n \"start\": \"2023-01-11T17:40:44\",\n \"stop\": \"2023-01-11T17:40:40\"\n}", - "contentType": "application/json" - }, "headers": [ { "key": "accept", - "value": "application/json", - "active": true + "active": true, + "value": "application/json" } ], - "params": [], + "method": "PUT", "auth": { "authType": "none", "authActive": true }, - "name": "Update timer (400 - Stop is after start)", - "method": "PUT", - "preRequestScript": "" + "preRequestScript": "", + "params": [], + "testScript": "// Check status code is 404\npw.test(\"Status code is 404\", ()=> {\n pw.expect(pw.response.status).toBe(404);\n});", + "endpoint": "<>/api/timers/<>", + "body": { + "contentType": null, + "body": null + } }, { - "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});", + "method": "PUT", + "preRequestScript": "", + "auth": { + "authActive": true, + "authType": "none" + }, "body": { - "body": null, - "contentType": null + "contentType": "application/json", + "body": "{\n \"start\": \"2023-01-11T17:40:44\",\n \"stop\": \"2023-01-11T17:40:48\"\n}" }, - "preRequestScript": "", - "endpoint": "<>/api/timers/<>", - "v": "1", - "method": "PUT", "params": [], - "name": "Stop timer", + "endpoint": "<>/api/items/<>/timers/<>", + "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.start).toBeType(\"string\");\n pw.expect(pw.response.body.stop).toBeType(\"string\");\n});", "headers": [ { + "value": "application/json", "active": true, - "key": "accept", - "value": "application/json" + "key": "accept" } ], - "auth": { - "authActive": true, - "authType": "none" - } + "name": "Update timer", + "v": "1" }, { - "name": "Stop timer (403 - Timer has already been stopped)", - "params": [], - "endpoint": "<>/api/timers/<>", - "preRequestScript": "", - "method": "PUT", - "body": { - "contentType": null, - "body": null - }, "headers": [ { - "active": true, "key": "accept", + "active": true, "value": "application/json" } ], - "testScript": "\n\n// Check status code is 403\npw.test(\"Status code is 403\", ()=> {\n pw.expect(pw.response.status).toBe(403);\n});", + "name": "Update timer (400 - Invalid date format)", + "params": [], + "preRequestScript": "", + "body": { + "body": "{\n \"start\": \"2023-invalid-01\"\n}", + "contentType": "application/json" + }, "v": "1", + "endpoint": "<>/api/items/<>/timers/<>", + "method": "PUT", + "testScript": "// Check status code is 400\npw.test(\"Status code is 400\", ()=> {\n pw.expect(pw.response.status).toBe(400);\n});", "auth": { "authType": "none", "authActive": true } }, { - "method": "PUT", "v": "1", - "testScript": "// Check status code is 404\npw.test(\"Status code is 404\", ()=> {\n pw.expect(pw.response.status).toBe(404);\n});", - "name": "Stop timer (404 - Timer not found)", "body": { - "contentType": null, - "body": null + "contentType": "application/json", + "body": "{\n \"start\": \"2023-01-11T17:40:44\",\n \"stop\": \"2023-01-11T17:40:40\"\n}" }, + "name": "Update timer (400 - Stop is after start)", + "preRequestScript": "", + "testScript": "// Check status code is 400\npw.test(\"Status code is 400\", ()=> {\n pw.expect(pw.response.status).toBe(400);\n});", + "params": [], + "method": "PUT", + "endpoint": "<>/api/items/<>/timers/<>", "headers": [ { - "key": "accept", "value": "application/json", - "active": true + "active": true, + "key": "accept" } ], - "endpoint": "<>/api/timers/<>", - "params": [], - "preRequestScript": "", "auth": { "authActive": true, "authType": "none" } } - ], - "folders": [], - "v": 1, - "name": "Timers" + ] }, { "v": 1, - "folders": [], + "name": "Tags", "requests": [ { - "testScript": "// Check status code is 2xx\npw.test(\"Status code is 2xx\", ()=> {\n pw.expect(pw.response.status).toBeLevel2xx();\n});\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.person_id).toBeType(\"number\");\n pw.expect(pw.response.body.text).toBeType(\"string\");\n\n \t// Color must be an hex color\n pw.expect(pw.response.body.color).toBeType(\"string\");\n pw.expect(pw.response.body.color).toHaveLength(7);\n pw.expect(pw.response.body.color).toInclude(\"#\");\n \n});", "auth": { - "addTo": "Headers", - "value": "", "authActive": true, + "value": "", "key": "", + "addTo": "Headers", "authType": "none" }, - "params": [], - "endpoint": "<>/api/tags/<>", - "name": "Get tag", + "name": "Create tag", + "method": "POST", "v": "1", - "method": "GET", - "headers": [ - { - "value": "application/json", - "active": true, - "key": "accept" - } - ], - "body": { - "body": null, - "contentType": null - }, - "preRequestScript": "" - }, - { + "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.id).toBeType(\"number\");\n \n // Setting `tag_id` to the response value \n \tpw.env.set(\"tag_id\", pw.response.body.id.toString());\n});", "body": { - "body": null, - "contentType": null - }, - "endpoint": "<>/api/tags/<>", - "auth": { - "authType": "none", - "authActive": true + "contentType": "application/json", + "body": "{\n \"text\": \"<>\",\n \"person_id\": 0,\n \"color\": \"#FFFFFF\"\n}" }, "headers": [ { @@ -699,187 +704,209 @@ "active": true } ], - "method": "GET", - "testScript": "// Check status code is 404\npw.test(\"Status code is 404\", ()=> {\n pw.expect(pw.response.status).toBe(404);\n});", - "preRequestScript": "", - "name": "Get tag (404 - Tag not found)", - "params": [], - "v": "1" + "preRequestScript": "const currentTime = Date.now();\npw.env.set(\"random_string\", currentTime.toString());", + "endpoint": "<>/api/tags", + "params": [] }, { + "params": [], + "endpoint": "<>/api/tags", "body": { - "body": null, - "contentType": null + "body": "{\n \"text\": \"<>\",\n \"person_id\": 0\n}", + "contentType": "application/json" }, + "preRequestScript": "const currentTime = Date.now();\npw.env.set(\"random_string\", currentTime.toString() + \"_nocolor\");", + "method": "POST", "v": "1", "headers": [ { - "key": "accept", "value": "application/json", - "active": true + "active": true, + "key": "accept" } ], - "method": "GET", + "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.id).toBeType(\"number\");\n});", "auth": { "authActive": true, "authType": "none" }, - "preRequestScript": "", - "params": [], - "endpoint": "<>/api/tags/<>", - "testScript": "// Check status code is 400\npw.test(\"Status code is 400\", ()=> {\n pw.expect(pw.response.status).toBe(400);\n});", - "name": "Get tag (400 - Invalid ID)" + "name": "Create tag (no color provided)" }, { "endpoint": "<>/api/tags", + "testScript": "// Check status code is 400\npw.test(\"Status code is 400\", ()=> {\n pw.expect(pw.response.status).toBe(400);\n});", + "name": "Create tag (400 - Invalid attributes)", + "body": { + "body": "{\n \"text\": \"tag text\",\n \"color\": \"invalid color\"\n}", + "contentType": "application/json" + }, + "params": [], "auth": { "authType": "none", "authActive": true }, + "v": "1", "headers": [ { - "active": true, + "value": "application/json", "key": "accept", - "value": "application/json" + "active": true } ], - "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.id).toBeType(\"number\");\n});", - "body": { - "contentType": "application/json", - "body": "{\n \"text\": \"tag text\",\n \"person_id\": 0,\n \"color\": \"#FFFFFF\"\n}" - }, - "preRequestScript": "", - "v": "1", - "params": [], - "name": "Create tag", - "method": "POST" + "method": "POST", + "preRequestScript": "" }, { - "endpoint": "<>/api/tags", + "params": [], + "preRequestScript": "", "headers": [ { - "value": "application/json", "active": true, + "value": "application/json", "key": "accept" } ], + "testScript": "// Check status code is 2xx\npw.test(\"Status code is 2xx\", ()=> {\n pw.expect(pw.response.status).toBeLevel2xx();\n});\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.person_id).toBeType(\"number\");\n pw.expect(pw.response.body.text).toBeType(\"string\");\n\n \t// Color must be an hex color\n pw.expect(pw.response.body.color).toBeType(\"string\");\n pw.expect(pw.response.body.color).toHaveLength(7);\n pw.expect(pw.response.body.color).toInclude(\"#\");\n \n});", + "method": "GET", + "body": { + "body": null, + "contentType": null + }, + "name": "Get tag", + "endpoint": "<>/api/tags/<>", "v": "1", - "method": "POST", "auth": { + "addTo": "Headers", + "authActive": true, + "key": "", "authType": "none", - "authActive": true + "value": "" + } + }, + { + "endpoint": "<>/api/tags/<>", + "v": "1", + "testScript": "// Check status code is 404\npw.test(\"Status code is 404\", ()=> {\n pw.expect(pw.response.status).toBe(404);\n});", + "body": { + "contentType": null, + "body": null }, + "name": "Get tag (404 - Tag not found)", "params": [], - "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.id).toBeType(\"number\");\n});", - "name": "Create tag (no color provided)", + "method": "GET", "preRequestScript": "", - "body": { - "contentType": "application/json", - "body": "{\n \"text\": \"another tag text\",\n \"person_id\": 0\n}" - } + "auth": { + "authType": "none", + "authActive": true + }, + "headers": [ + { + "value": "application/json", + "key": "accept", + "active": true + } + ] }, { + "auth": { + "authActive": true, + "authType": "none" + }, + "endpoint": "<>/api/tags/<>", + "name": "Get tag (400 - Invalid ID)", "testScript": "// Check status code is 400\npw.test(\"Status code is 400\", ()=> {\n pw.expect(pw.response.status).toBe(400);\n});", + "params": [], "headers": [ { + "active": true, "key": "accept", - "value": "application/json", - "active": true + "value": "application/json" } ], + "preRequestScript": "", "body": { - "contentType": "application/json", - "body": "{\n \"text\": \"tag text\",\n \"color\": \"invalid color\"\n}" - }, - "params": [], - "auth": { - "authType": "none", - "authActive": true + "contentType": null, + "body": null }, - "preRequestScript": "", - "name": "Create tag (400 - Invalid attributes)", - "endpoint": "<>/api/tags", - "method": "POST", + "method": "GET", "v": "1" }, { - "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.person_id).toBeType(\"number\");\n pw.expect(pw.response.body.text).toBeType(\"string\");\n\n \t\n \t// Color must be an hex color\n pw.expect(pw.response.body.color).toBeType(\"string\");\n pw.expect(pw.response.body.color).toHaveLength(7);\n pw.expect(pw.response.body.color).toInclude(\"#\");\n});", - "name": "Update tag", + "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.person_id).toBeType(\"number\");\n pw.expect(pw.response.body.text).toBeType(\"string\");\n\n \t\n// Color must be an hex color\n pw.expect(pw.response.body.color).toBeType(\"string\");\n pw.expect(pw.response.body.color).toHaveLength(7);\n pw.expect(pw.response.body.color).toInclude(\"#\");\n});", + "preRequestScript": "const currentTime = Date.now();\npw.env.set(\"random_string\", currentTime.toString() + \"_new_updated_string\");", "body": { - "body": "{\n \"text\": \"new updated tag text\"\n}", + "body": "{\n \"text\": \"<>\"\n}", "contentType": "application/json" }, - "v": "1", "headers": [ { "value": "application/json", - "key": "accept", - "active": true + "active": true, + "key": "accept" } ], + "params": [], + "method": "PUT", "auth": { "authActive": true, "authType": "none" }, + "v": "1", "endpoint": "<>/api/tags/<>", - "method": "PUT", - "params": [], - "preRequestScript": "" + "name": "Update tag" }, { - "preRequestScript": "", - "testScript": "// Check status code is 404\npw.test(\"Status code is 404\", ()=> {\n pw.expect(pw.response.status).toBe(404);\n});", "name": "Update tag (404 - Tag not found)", + "params": [], + "method": "PUT", + "testScript": "// Check status code is 404\npw.test(\"Status code is 404\", ()=> {\n pw.expect(pw.response.status).toBe(404);\n});", + "v": "1", "headers": [ { + "key": "accept", "value": "application/json", - "active": true, - "key": "accept" + "active": true } ], + "preRequestScript": "", + "endpoint": "<>/api/tags/<>", + "auth": { + "authType": "none", + "authActive": true + }, "body": { "contentType": "application/json", "body": "{\n \"text\": \"new updated text\"\n}" - }, + } + }, + { + "v": "1", + "params": [], + "method": "PUT", + "preRequestScript": "", + "endpoint": "<>/api/tags/<>", "auth": { "authType": "none", "authActive": true }, - "params": [], - "method": "PUT", - "endpoint": "<>/api/tags/<>", - "v": "1" - }, - { + "body": { + "body": "{\n \"text\": \"tag text\",\n \"color\": \"invalid color\"\n}", + "contentType": "application/json" + }, "name": "Update tag (400 - Invalid attributes)", - "endpoint": "<>/api/tags/<>", "testScript": "// Check status code is 400\npw.test(\"Status code is 400\", ()=> {\n pw.expect(pw.response.status).toBe(400);\n});", - "preRequestScript": "", - "v": "1", "headers": [ { "key": "accept", - "active": true, - "value": "application/json" + "value": "application/json", + "active": true } - ], - "method": "PUT", - "body": { - "contentType": "application/json", - "body": "{\n \"text\": \"tag text\",\n \"color\": \"invalid color\"\n}" - }, - "auth": { - "authType": "none", - "authActive": true - }, - "params": [] + ] } ], - "name": "Tags" + "folders": [] } ], - "name": "MVP", - "v": 1, - "requests": [] + "name": "MVP" } ] \ No newline at end of file diff --git a/lib/api/envs.json b/lib/api/envs.json index d77ddc66..ac06be73 100644 --- a/lib/api/envs.json +++ b/lib/api/envs.json @@ -11,20 +11,20 @@ "key": "item_id" }, { - "value": "-1", - "key": "notfound_item_id" + "key": "notfound_item_id", + "value": "-1" }, { - "key": "invalid_id", - "value": "invalid_id" + "value": "invalid_id", + "key": "invalid_id" }, { - "key": "timer_id", - "value": "1" + "value": "1", + "key": "timer_id" }, { - "value": "-1", - "key": "notfound_timer_id" + "key": "notfound_timer_id", + "value": "-1" }, { "key": "timer_id_to_stop", @@ -32,15 +32,23 @@ }, { "key": "tag_id", - "value": "1" + "value": "9" }, { "value": "-1", "key": "notfound_tag_id" }, { - "value": "1", - "key": "item_id_with_tag" + "key": "item_id_with_tag", + "value": "1" + }, + { + "value": "1677500147391_nocolor", + "key": "random_string" + }, + { + "key": "tag_text_to_be_repeated", + "value": "yet another tag" } ] } diff --git a/lib/api/fly_dev.json b/lib/api/fly_dev.json new file mode 100644 index 00000000..0f6c8bdb --- /dev/null +++ b/lib/api/fly_dev.json @@ -0,0 +1,14 @@ +{ + "host": "deployed_app_host", + "item_id": "1", + "notfound_item_id": "-1", + "invalid_id": "invalid_id", + "timer_id": "1", + "notfound_timer_id": "-1", + "timer_id_to_stop": "2", + "tag_id": "1", + "notfound_tag_id": "-1", + "item_id_with_tag": "1", + "random_string": "random_string", + "tag_text_to_be_repeated": "yet another tag" +} \ No newline at end of file diff --git a/lib/api/localhost.json b/lib/api/localhost.json index 76819a4d..db89b783 100644 --- a/lib/api/localhost.json +++ b/lib/api/localhost.json @@ -8,5 +8,7 @@ "timer_id_to_stop": "2", "tag_id": "1", "notfound_tag_id": "-1", - "item_id_with_tag": "1" + "item_id_with_tag": "1", + "random_string": "random_string", + "tag_text_to_be_repeated": "yet another tag" } \ No newline at end of file diff --git a/priv/repo/seeds.exs b/priv/repo/seeds.exs index 09796df7..619e26f9 100644 --- a/priv/repo/seeds.exs +++ b/priv/repo/seeds.exs @@ -8,28 +8,3 @@ if not Envar.is_set?("AUTH_API_KEY") do Envar.load(".env") end - -if Mix.env() == :dev do - # Create item - App.Item.create_item_with_tags(%{ - text: "random text", - person_id: 0, - status: 2, - tags: [%{text: "first_tag", person_id: 0}] - }) - - # Create timers - {:ok, _timer} = - App.Timer.start(%{ - item_id: 1, - start: "2023-01-19 15:52:00", - stop: "2023-01-19 15:52:03" - }) - - {:ok, _timer2} = - App.Timer.start(%{item_id: 1, start: "2023-01-19 15:55:00", stop: nil}) - - # Create tags - {:ok, _tag} = - App.Tag.create_tag(%{text: "random test", person_id: 0, color: "#FFFFFF"}) -end