-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: First commit, replicating pactflow-example-consumer-js-sns
- Loading branch information
0 parents
commit 4720ac6
Showing
26 changed files
with
1,237 additions
and
0 deletions.
There are no files selected for viewing
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
name: Build | ||
|
||
on: | ||
push: | ||
workflow_dispatch: | ||
|
||
env: | ||
PACT_BROKER_BASE_URL: https://test.pactflow.io | ||
PACT_BROKER_TOKEN: ${{ secrets.PACTFLOW_TOKEN_FOR_CI_CD_WORKSHOP }} | ||
REACT_APP_API_BASE_URL: http://localhost:8080 | ||
GIT_COMMIT: ${{ github.sha }} | ||
GIT_REF: ${{ github.ref }} | ||
|
||
jobs: | ||
test: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v2 | ||
- uses: actions/setup-python@v4 | ||
with: | ||
python-version: '3.8' | ||
- name: Install | ||
run: make deps | ||
- name: Test | ||
run: make test | ||
- name: Publish pacts | ||
run: GIT_BRANCH=${GIT_REF:11} make publish_pacts | ||
|
||
# Runs on branches as well, so we know the status of our PRs | ||
can-i-deploy: | ||
runs-on: ubuntu-latest | ||
needs: test | ||
steps: | ||
- uses: actions/checkout@v2 | ||
- run: docker pull pactfoundation/pact-cli:latest | ||
- name: Can I deploy? | ||
run: GIT_BRANCH=${GIT_REF:11} make can_i_deploy | ||
|
||
# Only deploy from master | ||
deploy: | ||
runs-on: ubuntu-latest | ||
needs: can-i-deploy | ||
steps: | ||
- uses: actions/checkout@v2 | ||
- run: docker pull pactfoundation/pact-cli:latest | ||
- name: Deploy | ||
run: GIT_BRANCH=${GIT_REF:11} make deploy | ||
if: github.ref == 'refs/heads/master' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
name: Trigger update to partners.pactflow.io | ||
|
||
on: | ||
push: | ||
branches: | ||
- master | ||
paths: | ||
- '**.md' | ||
|
||
jobs: | ||
run: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Trigger partner docs update | ||
if: github.repository == 'pactflow/example-consumer-js-sns' | ||
uses: peter-evans/repository-dispatch@v1 | ||
with: | ||
token: ${{ secrets.GHTOKENFORTRIGGERINGPACTDOCSUPDATE }} | ||
repository: pactflow/partners.pactflow.io | ||
event-type: pactflow-example-consumer-js-sns-updated | ||
- name: Trigger docs update | ||
if: github.repository == 'pactflow/example-consumer-js-sns' | ||
uses: peter-evans/repository-dispatch@v1 | ||
with: | ||
token: ${{ secrets.GHTOKENFORTRIGGERINGPACTDOCSUPDATE }} | ||
repository: pactflow/docs.pactflow.io | ||
event-type: pactflow-example-consumer-js-sns-updated |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
.venv | ||
.aws-sam | ||
.idea | ||
.python-version | ||
__pycache__ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
MIT License | ||
|
||
Copyright (c) 2019-2021 Pactflow | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
# Default to the read only token - the read/write token will be present on Travis CI. | ||
# It's set as a secure environment variable in the .travis.yml file | ||
PACTICIPANT := "pactflow-example-consumer-python-sns" | ||
GITHUB_WEBHOOK_UUID := "c76b601e-d66a-4eb1-88a4-6ebc50c0df8b" | ||
PACT_CLI="docker run --rm -v ${PWD}:${PWD} -e PACT_BROKER_BASE_URL -e PACT_BROKER_TOKEN pactfoundation/pact-cli:latest" | ||
|
||
# Only deploy from master | ||
ifeq ($(GIT_BRANCH),master) | ||
DEPLOY_TARGET=deploy | ||
else | ||
DEPLOY_TARGET=no_deploy | ||
endif | ||
|
||
all: test | ||
|
||
## ==================== | ||
## CI tasks | ||
## ==================== | ||
|
||
ci: test publish_pacts can_i_deploy $(DEPLOY_TARGET) | ||
|
||
# Run the ci target from a developer machine with the environment variables | ||
# set as if it was on Travis CI. | ||
# Use this for quick feedback when playing around with your workflows. | ||
fake_ci: .env | ||
CI=true \ | ||
GIT_COMMIT=`git rev-parse --short HEAD`+`date +%s` \ | ||
GIT_BRANCH=`git rev-parse --abbrev-ref HEAD` \ | ||
make ci | ||
|
||
|
||
publish_pacts: .env | ||
@"${PACT_CLI}" publish ${PWD}/pacts --consumer-app-version ${GIT_COMMIT} --tag ${GIT_BRANCH} | ||
|
||
## ===================== | ||
## Build/test tasks | ||
## ===================== | ||
|
||
test: .env | ||
python3 -m pytest | ||
|
||
## ===================== | ||
## Deploy tasks | ||
## ===================== | ||
|
||
create_environment: | ||
@"${PACT_CLI}" broker create-environment --name production --production | ||
|
||
deploy: deploy_app record_deployment | ||
|
||
no_deploy: | ||
@echo "Not deploying as not on master branch" | ||
|
||
can_i_deploy: .env | ||
@"${PACT_CLI}" broker can-i-deploy \ | ||
--pacticipant ${PACTICIPANT} \ | ||
--version ${GIT_COMMIT} \ | ||
--to-environment production \ | ||
--retry-while-unknown 0 \ | ||
--retry-interval 10 | ||
|
||
deploy_app: | ||
@echo "Deploying to production" | ||
|
||
record_deployment: .env | ||
@"${PACT_CLI}" broker record-deployment --pacticipant ${PACTICIPANT} --version ${GIT_COMMIT} --environment production | ||
|
||
## ===================== | ||
## Pactflow set up tasks | ||
## ===================== | ||
|
||
# This should be called once before creating the webhook | ||
# with the environment variable GITHUB_TOKEN set | ||
create_github_token_secret: | ||
@curl -v -X POST ${PACT_BROKER_BASE_URL}/secrets \ | ||
-H "Authorization: Bearer ${PACT_BROKER_TOKEN}" \ | ||
-H "Content-Type: application/json" \ | ||
-H "Accept: application/hal+json" \ | ||
-d "{\"name\":\"githubCommitStatusToken\",\"description\":\"Github token for updating commit statuses\",\"value\":\"${GITHUB_TOKEN}\"}" | ||
|
||
# This webhook will update the Github commit status for this commit | ||
# so that any PRs will get a status that shows what the status of | ||
# the pact is. | ||
create_or_update_github_webhook: | ||
@"${PACT_CLI}" \ | ||
broker create-or-update-webhook \ | ||
'https://api.github.com/repos/pactflow/example-consumer-js-sns/statuses/$${pactbroker.consumerVersionNumber}' \ | ||
--header 'Content-Type: application/json' 'Accept: application/vnd.github.v3+json' 'Authorization: token $${user.githubCommitStatusToken}' \ | ||
--request POST \ | ||
--data @${PWD}/pactflow/github-commit-status-webhook.json \ | ||
--uuid ${GITHUB_WEBHOOK_UUID} \ | ||
--consumer ${PACTICIPANT} \ | ||
--contract-published \ | ||
--provider-verification-published \ | ||
--description "Github commit status webhook for ${PACTICIPANT}" | ||
|
||
test_github_webhook: | ||
@curl -v -X POST ${PACT_BROKER_BASE_URL}/webhooks/${GITHUB_WEBHOOK_UUID}/execute -H "Authorization: Bearer ${PACT_BROKER_TOKEN}" | ||
|
||
## ====================== | ||
## Misc | ||
## ====================== | ||
|
||
.env: | ||
touch .env | ||
|
||
.PHONY: test | ||
|
||
## ====================== | ||
## Python additions | ||
## ====================== | ||
PROJECT := example-consumer-python-sns | ||
PYTHON_MAJOR_VERSION := 3.8 | ||
|
||
sgr0 := $(shell tput sgr0) | ||
red := $(shell tput setaf 1) | ||
green := $(shell tput setaf 2) | ||
|
||
deps: | ||
poetry install | ||
|
||
integration: | ||
sam local invoke ProductEventHandler --event ./__tests__/events/update.json | ||
|
||
venv: | ||
@if [ -d "./.venv" ]; then echo "$(red).venv already exists, not continuing!$(sgr0)"; exit 1; fi | ||
@type pyenv >/dev/null 2>&1 || (echo "$(red)pyenv not found$(sgr0)"; exit 1) | ||
|
||
@echo "\n$(green)Try to find the most recent minor version of the major version specified$(sgr0)" | ||
$(eval PYENV_VERSION=$(shell pyenv install -l | grep "\s\s$(PYTHON_MAJOR_VERSION)\.*" | tail -1 | xargs)) | ||
@echo "$(PYTHON_MAJOR_VERSION) -> $(PYENV_VERSION)" | ||
|
||
@echo "\n$(green)Install the Python pyenv version if not already available$(sgr0)" | ||
pyenv install $(PYENV_VERSION) -s | ||
|
||
@echo "\n$(green)Make a .venv dir$(sgr0)" | ||
~/.pyenv/versions/${PYENV_VERSION}/bin/python3 -m venv ${CURDIR}/.venv | ||
|
||
@echo "\n$(green)Make it 'available' to pyenv$(sgr0)" | ||
ln -sf ${CURDIR}/.venv ~/.pyenv/versions/${PROJECT} | ||
|
||
@echo "\n$(green)Use it! (populate .python-version)$(sgr0)" | ||
pyenv local ${PROJECT} | ||
|
||
deploy: | ||
scripts/deploy.sh | ||
|
||
publish: | ||
scripts/publish.sh | ||
|
||
logs: | ||
sam logs -n ProductEventHandler --stack-name pactflow-example-consumer-python-sns -t |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
# Example Python AWS SNS Consumer | ||
|
||
[![Build Status](https://github.com/pactflow/example-consumer-python-sns/actions/workflows/build.yml/badge.svg)](https://github.com/pactflow/example-consumer-python-sns/actions) | ||
|
||
[![Can I deploy Status](https://test.pactflow.io/pacticipants/pactflow-example-consumer-python-sns/branches/master/latest-version/can-i-deploy/to-environment/production/badge.svg)](https://test.pactflow.io/pacticipants/pactflow-example-consumer-python-sns/branches/master/latest-version/can-i-deploy/to-environment/production/badge) | ||
|
||
[![Pact Status](https://test.pactflow.io/pacts/provider/pactflow-example-provider-python-sns/consumer/pactflow-example-consumer-python-sns/latest/badge.svg?label=consumer)](https://test.pactflow.io/pacts/provider/pactflow-example-provider-python-sns/consumer/pactflow-example-consumer-python-sns/latest) (latest pact) | ||
|
||
[![Pact Status](https://test.pactflow.io/matrix/provider/pactflow-example-provider-python-sns/latest/master/consumer/pactflow-example-consumer-python-sns/latest/master/badge.svg?label=consumer)](https://test.pactflow.io/pacts/provider/pactflow-example-provider-python-sns/consumer/pactflow-example-consumer-python-sns/latest/prod) (prod/prod pact) | ||
|
||
This is an example of a Python AWS SNS consumer that uses Pact, [Pactflow](https://pactflow.io) and GitHub Actions to ensure that it is compatible with the expectations its consumers have of it. | ||
|
||
All examples in the series `example-consumer-<language>-sns` provide the same functionality to be easily comparable across languages. | ||
As such, please refer to [https://docs.pactflow.io/docs/examples/aws/sns/consumer/](AWS SNS Consumer Examples) to avoid unnecessary duplication of details here. | ||
|
||
Language specific sections which differ from the canonical example only can be found below. | ||
|
||
### How to write tests? | ||
|
||
We recommend that you split the code that is responsible for handling the protocol specific things - in this case the lambda and SNS input - and the piece of code that actually handles the payload. | ||
|
||
You're probably familiar with layered architectures such as Ports and Adaptors (also referred to as a Hexagonal architecture). Following a modular architecture will allow you to do this much more easily: | ||
|
||
![Code Modularity](docs/ports-and-adapters.png "Code Modularity") | ||
|
||
This code base is setup with this modularity in mind: | ||
|
||
* [Lambda Handler](src/_lambda/product.py) | ||
* [Event Service](src/product/product_service.py) | ||
* Business Logic | ||
* [Product](src/product/product.py) | ||
* [Repository](src/product/product_repository.py) | ||
|
||
The target of our [consumer pact test](tests/unit/product_service_pact_test.py) is the [Event Service](src/product/product_service.js), which is responsible for consuming a Product update event, and persisting it to a database (the Repository). | ||
|
||
See also: | ||
|
||
* https://dius.com.au/2017/09/22/contract-testing-serverless-and-asynchronous-applications/ | ||
* https://dius.com.au/2018/10/01/contract-testing-serverless-and-asynchronous-applications---part-2/ | ||
|
||
## Usage | ||
### Testing | ||
|
||
* Run the unit tests: `make test` | ||
* Run a (local) lambda integration test: `make integration` | ||
|
||
### Running | ||
|
||
* Deploy the actual app: `make deploy` (see below for more background) | ||
* Publish a test event: `make publish` | ||
* View the lambda logs: `make logs` | ||
|
||
Here is some sample output publishing and viewing the logs: | ||
``` | ||
➜ example-consumer-js-sns git:(master) ✗ npm run publish <aws:pact-dev> | ||
> product-service@1.0.0 publish /Users/matthewfellows/development/public/example-consumer-js-sns | ||
> ./scripts/publish.sh | ||
finding topic | ||
have topic: arn:aws:sns:ap-southeast-2:838728264948:pactflow-example-consumer-js-sns-ProductEvent-144XVHN8QP2D3, publishing message | ||
{ | ||
"MessageId": "735a2daa-7eaa-53d7-b362-75b0d9227708" | ||
} | ||
> product-service@1.0.0 logs /Users/matthewfellows/development/public/example-consumer-js-sns | ||
> sam logs -n ProductEventHandler --stack-name pactflow-example-consumer-js-sns -t | ||
2020/11/03/[$LATEST]df9d6b71ef1e49789f4ebca64fc19270 2020-11-03T00:25:24.984000 START RequestId: 47e97e7d-52cf-4c83-9133-545749ed2750 Version: $LATEST | ||
2020/11/03/[$LATEST]df9d6b71ef1e49789f4ebca64fc19270 2020-11-03T00:25:25.012000 2020-11-03T00:25:24.988Z 47e97e7d-52cf-4c83-9133-545749ed2750 INFO { | ||
Records: [ | ||
{ | ||
EventSource: 'aws:sns', | ||
EventVersion: '1.0', | ||
EventSubscriptionArn: 'arn:aws:sns:ap-southeast-2:838728264948:pactflow-example-consumer-js-sns-ProductEvent-144XVHN8QP2D3:efaf0845-3847-4b5d-a4b1-68f33ef524e8', | ||
Sns: [Object] | ||
} | ||
] | ||
} | ||
2020/11/03/[$LATEST]df9d6b71ef1e49789f4ebca64fc19270 2020-11-03T00:25:25.032000 END RequestId: 47e97e7d-52cf-4c83-9133-545749ed2750 | ||
2020/11/03/[$LATEST]df9d6b71ef1e49789f4ebca64fc19270 2020-11-03T00:25:25.032000 REPORT RequestId: 47e97e7d-52cf-4c83-9133-545749ed2750 Duration: 48.28 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 64 MB Init Duration: 136.98 ms | ||
``` | ||
|
||
If you edit the file `./scripts/publish.sh` to remove a valid property, or upload invalid JSON you will get something like this: | ||
|
||
``` | ||
2020/11/03/[$LATEST]df9d6b71ef1e49789f4ebca64fc19270 2020-11-03T00:36:23.376000 2020-11-03T00:36:23.376Z 3eb496cd-c663-4ae2-a717-8f261b7ad48c ERROR Invoke Error {"errorType":"AssertionError","errorMessage":"id is a mandatory field","code":"ERR_ASSERTION","generatedMessage":false,"expected":true,"operator":"==","stack":["AssertionError [ERR_ASSERTION]: id is a mandatory field"," at new Product (/var/task/src/product/product.js:5:5)"," at handler (/var/task/src/product/product.handler.js:7:23)"," at /var/task/src/service/product.js:10:44"," at Array.map (<anonymous>)"," at Runtime.lambda [as handler] (/var/task/src/service/product.js:10:33)"," at Runtime.handleOnce (/var/runtime/Runtime.js:66:25)"]} | ||
2020/11/03/[$LATEST]df9d6b71ef1e49789f4ebca64fc19270 2020-11-03T00:36:23.416000 END RequestId: 3eb496cd-c663-4ae2-a717-8f261b7ad48c | ||
2020/11/03/[$LATEST]df9d6b71ef1e49789f4ebca64fc19270 2020-11-03T00:36:23.416000 REPORT RequestId: 3eb496cd-c663-4ae2-a717-8f261b7ad48c Duration: 75.82 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 65 MB | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"state": "${pactbroker.githubVerificationStatus}", | ||
"description": "Pact Verification Tests", | ||
"context": "${pactbroker.providerName} ${pactbroker.providerVersionTags}", | ||
"target_url": "${pactbroker.verificationResultUrl}" | ||
} |
Oops, something went wrong.