1"use strict";(self.webpackChunkpartners=self.webpackChunkpartners||[]).push([[9755],{15680:(e,t,a)=>{a.d(t,{xA:()=>c,yg:()=>g});var r=a(96540);function s(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function o(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,r)}return a}function n(e){for(var t=1;t<arguments.length;t++){var a=null!=arguments[t]?arguments[t]:{};t%2?o(Object(a),!0).forEach((function(t){s(e,t,a[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(a)):o(Object(a)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(a,t))}))}return e}function p(e,t){if(null==e)return{};var a,r,s=function(e,t){if(null==e)return{};var a,r,s={},o=Object.keys(e);for(r=0;r<o.length;r++)a=o[r],t.indexOf(a)>=0||(s[a]=e[a]);return s}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r<o.length;r++)a=o[r],t.indexOf(a)>=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(s[a]=e[a])}return s}var l=r.createContext({}),i=function(e){var t=r.useContext(l),a=t;return e&&(a="function"==typeof e?e(t):n(n({},t),e)),a},c=function(e){var t=i(e.components);return r.createElement(l.Provider,{value:t},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},m=r.forwardRef((function(e,t){var a=e.components,s=e.mdxType,o=e.originalType,l=e.parentName,c=p(e,["components","mdxType","originalType","parentName"]),u=i(a),m=s,g=u["".concat(l,".").concat(m)]||u[m]||d[m]||o;return a?r.createElement(g,n(n({ref:t},c),{},{components:a})):r.createElement(g,n({ref:t},c))}));function g(e,t){var a=arguments,s=t&&t.mdxType;if("string"==typeof e||s){var o=a.length,n=new Array(o);n[0]=m;var p={};for(var l in t)hasOwnProperty.call(t,l)&&(p[l]=t[l]);p.originalType=e,p[u]="string"==typeof e?e:s,n[1]=p;for(var i=2;i<o;i++)n[i]=a[i];return r.createElement.apply(null,n)}return r.createElement.apply(null,a)}m.displayName="MDXCreateElement"},11229:(e,t,a)=>{a.r(t),a.d(t,{assets:()=>l,contentTitle:()=>n,default:()=>d,frontMatter:()=>o,metadata:()=>p,toc:()=>i});var r=a(58168),s=(a(96540),a(15680));const o={custom_edit_url:"https://github.com/pactflow/example-provider-js-sns/edit/master/README.md",title:"Example NodeJS SNS Provider",sidebar_label:"Example NodeJS SNS Provider"},n=void 0,p={unversionedId:"docs/examples/aws/sns/provider/readme",id:"docs/examples/aws/sns/provider/readme",title:"Example NodeJS SNS Provider",description:"Source Code",source:"@site/docs/docs/examples/aws/sns/provider/readme.md",sourceDirName:"docs/examples/aws/sns/provider",slug:"/docs/examples/aws/sns/provider/",permalink:"/docs/examples/aws/sns/provider/",draft:!1,editUrl:"https://github.com/pactflow/example-provider-js-sns/edit/master/README.md",tags:[],version:"current",lastUpdatedBy:"Github Action",lastUpdatedAt:1674584471,formattedLastUpdatedAt:"Jan 24, 2023",frontMatter:{custom_edit_url:"https://github.com/pactflow/example-provider-js-sns/edit/master/README.md",title:"Example NodeJS SNS Provider",sidebar_label:"Example NodeJS SNS Provider"},sidebar:"examples",previous:{title:"Example Python AWS SNS Consumer",permalink:"/docs/examples/aws/sns/consumer/python/"},next:{title:"Cypress Pact Example",permalink:"/docs/examples/cypress/"}},l={},i=[{value:"Source Code",id:"source-code",level:2},{value:"Theory",id:"theory",level:2},{value:"How to write tests?",id:"how-to-write-tests",level:3},{value:"Pre-requisites",id:"pre-requisites",level:2},{value:"Usage",id:"usage",level:2},{value:"Testing",id:"testing",level:3},{value:"Running locally",id:"running--locally",level:3}],c={toc:i},u="wrapper";function d(e){let{components:t,...a}=e;return(0,s.yg)(u,(0,r.A)({},c,a,{components:t,mdxType:"MDXLayout"}),(0,s.yg)("h2",{id:"source-code"},"Source Code"),(0,s.yg)("p",null,(0,s.yg)("a",{parentName:"p",href:"https://github.com/pactflow/example-provider-js-sns"},"https://github.com/pactflow/example-provider-js-sns")),(0,s.yg)("p",null,(0,s.yg)("a",{parentName:"p",href:"https://github.com/pactflow/example-provider-js-sns/actions"},(0,s.yg)("img",{parentName:"a",src:"https://github.com/pactflow/example-provider-js-sns/actions/workflows/build.yml/badge.svg",alt:"Build Status"}))),(0,s.yg)("p",null,(0,s.yg)("a",{parentName:"p",href:"https://test.pactflow.io/pacticipants/pactflow-example-provider-js-sns/branches/master/latest-version/can-i-deploy/to-environment/production/badge"},(0,s.yg)("img",{parentName:"a",src:"https://test.pactflow.io/pacticipants/pactflow-example-provider-js-sns/branches/master/latest-version/can-i-deploy/to-environment/production/badge.svg",alt:"Can I deploy Status"}))),(0,s.yg)("p",null,(0,s.yg)("a",{parentName:"p",href:"https://test.pactflow.io/pacts/provider/pactflow-example-provider-js-sns/consumer/pactflow-example-consumer-js-sns/latest"},(0,s.yg)("img",{parentName:"a",src:"https://test.pactflow.io/pacts/provider/pactflow-example-provider-js-sns/consumer/pactflow-example-consumer-js-sns/latest/badge.svg?label=provider",alt:"Pact Status"}))," (latest pact)"),(0,s.yg)("p",null,(0,s.yg)("a",{parentName:"p",href:"https://test.pactflow.io/pacts/provider/pactflow-example-provider-js-sns/consumer/pactflow-example-consumer-js-sns/latest/master"},(0,s.yg)("img",{parentName:"a",src:"https://test.pactflow.io/matrix/prov
1ider/pactflow-example-provider-js-sns/latest/master/consumer/pactflow-example-consumer-js-sns/latest/master/badge.svg?label=provider",alt:"Pact Status"}))," (prod/prod pact)"),(0,s.yg)("p",null,"This is an example of a NodeJS provider that uses Pact, ",(0,s.yg)("a",{parentName:"p",href:"https://pactflow.io"},"PactFlow")," and GitHub Actions to ensure that it is compatible with the expectations its consumers have of it."),(0,s.yg)("p",null,"It is using a public tenant on PactFlow, which you can access ",(0,s.yg)("a",{parentName:"p",href:"https://test.pactflow.io"},"here")," using the credentials ",(0,s.yg)("inlineCode",{parentName:"p"},"dXfltyFMgNOFZAxr8io9wJ37iUpY42M"),"/",(0,s.yg)("inlineCode",{parentName:"p"},"O5AIZWxelWbLvqMd8PkAVycBJh2Psyg1"),". The latest version of the Example Consumer/Example Provider pact is published ",(0,s.yg)("a",{parentName:"p",href:"https://test.pactflow.io/pacts/provider/pactflow-example-provider-js-sns/consumer/pactflow-example-consumer-js-sns/latest"},"here"),"."),(0,s.yg)("p",null,'In the following diagram, we\'ll be testing the "Product Update API", a simple HTTP service that receives product updates via a REST API and publishes product events on the ',(0,s.yg)("inlineCode",{parentName:"p"},"product")," topic."),(0,s.yg)("p",null,"We need to be able to test that we are able to produce valid events to the SNS topic that matches what the consumer(s) can handle:"),(0,s.yg)("p",null,(0,s.yg)("img",{parentName:"p",src:"https://raw.githubusercontent.com/pactflow/example-provider-js-sns/master/docs/js-sns.png",alt:"SNS Architecture"})),(0,s.yg)("h2",{id:"theory"},"Theory"),(0,s.yg)("p",null,"Modern distributed architectures are increasingly integrated in a decoupled, asynchronous fashion. Message queues such as ActiveMQ, RabbitMQ, SNS, SQS, Kafka and Kinesis are common, often integrated via small and frequent numbers of microservices (e.g. lambda)."),(0,s.yg)("p",null,"Pact supports these use cases, by abstracting away the ",(0,s.yg)("em",{parentName:"p"},"protocol")," and focussing on the messages passing between them."),(0,s.yg)("p",null,"To reiterate: Pact does not know about the various message queueing technologies - there are simply too many! And more importantly, Pact is really about testing the ",(0,s.yg)("em",{parentName:"p"},"messages")," that pass between them, you can still write your standard ",(0,s.yg)("em",{parentName:"p"},"functional")," tests using other frameworks designed for such things."),(0,s.yg)("p",null,"When writing tests, Pact takes the place of the intermediary (MQ/broker etc.) and confirms whether or not the consumer is able to ",(0,s.yg)("em",{parentName:"p"},"handle")," a given event, or that the provider will be able to ",(0,s.yg)("em",{parentName:"p"},"produce")," the correct message."),(0,s.yg)("h3",{id:"how-to-write-tests"},"How to write tests?"),(0,s.yg)("p",null,"We recommend that you split the code that is responsible for handling the protocol specific things - in this case the SNS publishing code - and the piece of code that actually ",(0,s.yg)("em",{parentName:"p"},"produces")," payload."),(0,s.yg)("p",null,"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:"),(0,s.yg)("p",null,(0,s.yg)("img",{parentName:"p",src:"https://raw.githubusercontent.com/pactflow/example-provider-js-sns/master/docs/ports-and-adapters.png",alt:"Code Modularity"})),(0,s.yg)("p",null,"This code base is setup with this modularity in mind (key files):"),(0,s.yg)("ul",null,(0,s.yg)("li",{parentName:"ul"},(0,s.yg)("a",{parentName:"li",href:"https://raw.githubusercontent.com/pactflow/example-provider-js-sns/master/server.js"},"REST API")),(0,s.yg)("li",{parentName:"ul"},(0,s.yg)("a",{parentName:"li",href:"https://raw.githubusercontent.com/pactflow/example-provider-js-sns/master/src/product/product.event.service.js"},"Event Service (SNS Producer)")),(0,s.yg)("li",{parentName:"ul"},"Business Logic",(0,s.yg)("ul",{parentName:"li"},(0,s.yg)("li",{parentName:"ul"},(0,s.yg)("a",{parentName:"li",href:"https://raw.githubusercontent.com/pactflow/example-provider-js-sns/master/src/product/product.js"},"Product")),(0,s.yg)("li",{parentName:"ul"},(0,s.yg)("a",{parentName:"li",href:"https://raw.githubusercontent.com/pactflow/example-provider-js-sns/master/src/product/product.event.js"},"Event Producer"))))),(0,s.yg)("p",null,"The target of our ",(0,s.yg)("a",{parentName:"p",href:"https://raw.githubusercontent.com/pactflow/example-provider-js-sns/master/src/product/product.pact.test.js"},"provider pact test")," is the ",(0,s.yg)("a",{parentName:"p",href:"https://raw.githubusercontent.com/pactflow/example-provider-js-sns/master/src/product/product.event.js"},"Event Producer"),", which is responsible for producing a Product update event, that the Event Service will publish to SNS."),(0,s.yg)("p",null,"See also:"),(0,s.yg)("ul",null,(0,s.yg)("li",{parentName:"ul"},(0,s.yg)("a",{parentName:"li",href:"https://dius.com.au/2017/09/22/contract-testing-serverless-and-asynchronous-applications/"},"https://dius.com.au/2017/09/22/contract-testing-serverless-and-asynchronous-applications/")),(0,s.yg)("li",{parentName:"ul"},(0,s.yg)("a",{parentName:"li",href:"https://dius.com.au/2018/10/01/contract-testing-serverless-and-asynchronous-applications---part-2/"},"https://dius.com.au/2018/10/01/contract-testing-serverless-and-asynchronous-applications---part-2/"))),(0,s.yg)("h2",{id:"pre-requisites"},"Pre-requisites"),(0,s.yg)("p",null,(0,s.yg)("strong",{parentName:"p"},"Software"),":"),(0,s.yg)("ul",null,(0,s.yg)("li",{parentName:"ul"},(0,s.yg)("a",{parentName:"li",href:"https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install-mac.html"},"AWS SAM")),(0,s.yg)("li",{parentName:"ul"},(0,s.yg)("a",{parentName:"li",href:"https://docs.pactflow.io/docs/workshops/ci-cd/set-up-ci/prerequisites/"},"https://docs.pactflow.io/docs/workshops/ci-cd/set-up-ci/prerequisites/"))),(0,s.yg)("h2",{id:"usage"},"Usage"),(0,s.yg)("p",null,"See also the ",(0,s.yg)("a",{parentName:"p",href:"https://github.com/pactflow/ci-cd-workshop"},"PactFlow CI/CD Workshop")," for more background."),(0,s.yg)("h3",{id:"testing"},"Testing"),(0,s.yg)("ul",null,(0,s.yg)("li",{parentName:"ul"},"Run the Pact tests: ",(0,s.yg)("inlineCode",{parentName:"li"},"make test"))),(0,s.yg)("h3",{id:"running--locally"},"Running locally"),(0,s.yg)("ul",null,(0,s.yg)("li",{parentName:"ul"},"Start the Provider API (with a local SNS setup with localstack): ",(0,s.yg)("inlineCode",{parentName:"li"},"make start")),(0,s.yg)("li",{parentName:"ul"},"Create a product: ",(0,s.yg)("inlineCode",{parentName:"li"},"make create-product")),(0,s.yg)("li",{parentName:"ul"},"Update a product: ",(0,s.yg)("inlineCode",{parentName:"li"},"make update-product")),(0,s.yg)("li",{parentName:"ul"},"Delete a product: ",(0,s.yg)("inlineCode",{parentName:"li"},"make delete-product"))))}d.isMDXComponent=!0}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.