1(self.webpackChunkpartners=self.webpackChunkpartners||[]).push([[26242],{15680:(e,t,a)=>{"use strict";a.d(t,{xA:()=>p,yg:()=>g});var n=a(96540);function i(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 n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,n)}return a}function r(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){i(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 s(e,t){if(null==e)return{};var a,n,i=function(e,t){if(null==e)return{};var a,n,i={},o=Object.keys(e);for(n=0;n<o.length;n++)a=o[n],t.indexOf(a)>=0||(i[a]=e[a]);return i}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(n=0;n<o.length;n++)a=o[n],t.indexOf(a)>=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(i[a]=e[a])}return i}var l=n.createContext({}),c=function(e){var t=n.useContext(l),a=t;return e&&(a="function"==typeof e?e(t):r(r({},t),e)),a},p=function(e){var t=c(e.components);return n.createElement(l.Provider,{value:t},e.children)},d="mdxType",u={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},m=n.forwardRef((function(e,t){var a=e.components,i=e.mdxType,o=e.originalType,l=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),d=c(a),m=i,g=d["".concat(l,".").concat(m)]||d[m]||u[m]||o;return a?n.createElement(g,r(r({ref:t},p),{},{components:a})):n.createElement(g,r({ref:t},p))}));function g(e,t){var a=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var o=a.length,r=new Array(o);r[0]=m;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[d]="string"==typeof e?e:i,r[1]=s;for(var c=2;c<o;c++)r[c]=a[c];return n.createElement.apply(null,r)}return n.createElement.apply(null,a)}m.displayName="MDXCreateElement"},19365:(e,t,a)=>{"use strict";a.d(t,{A:()=>r});var n=a(96540),i=a(40870);const o={tabItem:"tabItem_Ymn6"};function r(e){let{children:t,hidden:a,className:r}=e;return n.createElement("div",{role:"tabpanel",className:(0,i.A)(o.tabItem,r),hidden:a},t)}},11470:(e,t,a)=>{"use strict";a.d(t,{A:()=>k});var n=a(58168),i=a(96540),o=a(40870),r=a(23104),s=a(56347),l=a(57485),c=a(31682),p=a(89466);function d(e){return function(e){var t,a;return null!=(t=null==(a=i.Children.map(e,(e=>{if(!e||(0,i.isValidElement)(e)&&function(e){const{props:t}=e;return!!t&&"object"==typeof t&&"value"in t}(e))return e;throw new Error("Docusaurus error: Bad <Tabs> child <"+("string"==typeof e.type?e.type:e.type.name)+'>: all children of the <Tabs> component should be <TabItem>, and every <TabItem> should have a unique "value" prop.')})))?void 0:a.filter(Boolean))?t:[]}(e).map((e=>{let{props:{value:t,label:a,attributes:n,default:i}}=e;return{value:t,label:a,attributes:n,default:i}}))}function u(e){const{values:t,children:a}=e;return(0,i.useMemo)((()=>{const e=null!=t?t:d(a);return function(e){const t=(0,c.X)(e,((e,t)=>e.value===t.value));if(t.length>0)throw new Error('Docusaurus error: Duplicate values "'+t.map((e=>e.value)).join(", ")+'" found in <Tabs>. Every value needs to be unique.')}(e),e}),[t,a])}function m(e){let{value:t,tabValues:a}=e;return a.some((e=>e.value===t))}function g(e){let{queryString:t=!1,groupId:a}=e;const n=(0,s.W6)(),o=function(e){let{queryString:t=!1,groupId:a}=e;if("string"==typeof t)return t;if(!1===t)return null;if(!0===t&&!a)throw new Error('Docusaurus error: The <Tabs> component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return null!=a?a:null}({queryString:t,groupId:a});return[(0,l.aZ)(o),(0,i.useCallback)((e=>{if(!o)return;const t=new URLSearchParams(n.location.search);t.set(o,e),n.replace({...n.location,search:t.toString()})}),[o,n])]}function h(e){const{defaultValue:t,queryString:a=!1,groupId:n}=e,o=u(e),[r,s]=(0,i.useState)((()=>function(e){var t;let{defaultValue:a,tabValues:n}=e;if(0===n.length)throw new Error("Docusaurus error: the <Tabs> component requires at least one <TabItem> children component");if(a){if(!m({value:a,tabValues:n}))throw new Error('Docusaurus error: The <Tabs> has a defaultValue "'+a+'" but none of its children has the corresponding value. Available values are: '+n.map((e=>e.value)).join(", ")+". If you intend to show no default tab, use defaultValue={null} instead.");return a}const i=null!=(t=n.find((e=>e.default)))?t:n[0];if(!i)throw new Error("Unexpected error: 0 tabValues");return i.value}({defaultValue:t,tabValues:o}))),[l,c]=g({queryString:a,groupId:n}),[d,h]=function(e){let{groupId:t}=e;const a=function(e){return e?"docusaurus.tab."+e:null}(t),[n,o]=(0,p.Dv)(a);return[n,(0,i.useCallback)((e=>{a&&o.set(e)}),[a,o])]}({groupId:n}),y=(()=>{const e=null!=l?l:d;return m({value:e,tabValues:o})?e:null})();(0,i.useLayoutEffect)((()=>{y&&s(y)}),[y]);return{selectedValue:r,selectValue:(0,i.useCallback)((e=>{if(!m({value:e,tabValues:o}))throw new Error("Can't select invalid tab value="+e);s(e),c(e),h(e)}),[c,h,o]),tabValues:o}}var y=a(92303);const b={tabList:"tabList__CuJ",tabItem:"tabItem_LNqP"};function f(e){let{className:t,block:a,selectedValue:s,selectValue:l,tabValues:c}=e;const p=[],{blockElementScrollPositionUntilNextRender:d}=(0,r.a_)(),u=e=>{const t=e.currentTarget,a=p.indexOf(t),n=c[a].value;n!==s&&(d(t),l(n))},m=e=>{var t;let a=null;switch(e.key){case"Enter":u(e);break;case"ArrowRight":{var n;const t=p.indexOf(e.currentTarget)+1;a=null!=(n=p[t])?n:p[0];break}case"ArrowLeft":{var i;const t=p.indexOf(e.currentTarget)-1;a=null!=(i=p[t])?i:p[p.length-1];break}}
1null==(t=a)||t.focus()};return i.createElement("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,o.A)("tabs",{"tabs--block":a},t)},c.map((e=>{let{value:t,label:a,attributes:r}=e;return i.createElement("li",(0,n.A)({role:"tab",tabIndex:s===t?0:-1,"aria-selected":s===t,key:t,ref:e=>p.push(e),onKeyDown:m,onClick:u},r,{className:(0,o.A)("tabs__item",b.tabItem,null==r?void 0:r.className,{"tabs__item--active":s===t})}),null!=a?a:t)})))}function v(e){let{lazy:t,children:a,selectedValue:n}=e;const o=(Array.isArray(a)?a:[a]).filter(Boolean);if(t){const e=o.find((e=>e.props.value===n));return e?(0,i.cloneElement)(e,{className:"margin-top--md"}):null}return i.createElement("div",{className:"margin-top--md"},o.map(((e,t)=>(0,i.cloneElement)(e,{key:t,hidden:e.props.value!==n}))))}function w(e){const t=h(e);return i.createElement("div",{className:(0,o.A)("tabs-container",b.tabList)},i.createElement(f,(0,n.A)({},e,t)),i.createElement(v,(0,n.A)({},e,t)))}function k(e){const t=(0,y.A)();return i.createElement(w,(0,n.A)({key:String(t)},e))}},69628:(e,t,a)=>{"use strict";a.d(t,{A:()=>u});var n=a(58168),i=a(96540),o=a(37911),r=a(16065),s=(a(94878),a(28794)),l=a(99563),c=a(66588);function p(e){var t;const a=(0,c.kh)("docusaurus-plugin-redoc");return e?null==a?void 0:a[e]:null==(t=Object.values(null!=a?a:{}))?void 0:t[0]}const d=e=>{let{id:t,example:a,pointer:c,...d}=e;const u=p(t),{store:m}=(0,l.r)(u);return(0,i.useEffect)((()=>{m.menu.dispose()}),[m]),i.createElement(r.ThemeProvider,{theme:m.options.theme},i.createElement("div",{className:(0,o.A)(["redocusaurus","redocusaurus-schema",a?null:"hide-example"])},i.createElement(s.SchemaDefinition,(0,n.A)({parser:m.spec.parser,options:m.options,schemaRef:c},d))))};d.defaultProps={example:!1};const u=d},47522:(e,t,a)=>{"use strict";a.d(t,{R:()=>i,z:()=>o});var n=a(96540);function i(e){let{iconTitle:t,iconLocation:a,iconLink:i}=e;return n.createElement("div",{style:{textAlign:"center"}},n.createElement("a",{href:i||void 0},n.createElement("img",{src:a,height:"48",width:"48"}),n.createElement("br",null),t))}function o(e){let{iconLocation:t,iconLink:a}=e;return n.createElement("div",{style:{textAlign:"center"}},n.createElement("a",{href:a||void 0},n.createElement("img",{src:t,height:"48",width:"48"})))}},64974:(e,t,a)=>{"use strict";a.d(t,{E3:()=>p,L0:()=>c});var n=a(96540),i=a(11470),o=a(19365),r=a(47522),s=a(82667);function l(e){let{data:t,key:a,content:i,withLabel:s=!1,withLink:l=!1}=e;const c=l?t[a]:{...t[a],iconLink:void 0},p=s?(0,r.R)(c):(0,r.z)(c);return n.createElement(o.A,{value:a,label:p,key:a},p.content?p.content:i)}function c(e){let{data:t,withLabel:a,withLink:o,useCaseFilter:r}=e;return n.createElement(i.A,{groupId:"languages"},Object.keys(t).map((e=>l({data:t,key:e,content:n.createElement("div",null,n.createElement("a",{href:t[e].iconLink},"Pact Language implementation guide for ",t[e].iconTitle),n.createElement("p",null),"Demo applications:",n.createElement("p",null),s.x.filter((a=>a.language===t[e].iconTitle&&a.useCase!==r)).map((e=>n.createElement("li",{key:e.linkTitle+"language_link"+Math.random()},n.createElement("a",{href:e.linkUrl},e.linkTitle))))),withLabel:a,withLink:o}))))}function p(e){let{data:t,withLabel:a,withLink:o,side:r}=e;return n.createElement(i.A,{groupId:"testing_tools"},Object.keys(t).filter((e=>{var a;return null==(a=r&&t[e].side===r)||a})).map((e=>l({data:t,key:e,content:n.createElement("a",{href:t[e].iconLink,key:e},"PactFlow ",t[e].side," side demo with ",t[e].iconTitle),withLabel:a,withLink:o}))))}},82667:(e,t,a)=>{"use strict";a.d(t,{Y:()=>o,x:()=>i});var n=a(96540);const i=[{linkTitle:"React JavaScript Website tested using Pact to generate consumer pacts",linkUrl:"/docs/examples/js/consumer",badges:["https://testdemo.pactflow.io/pacts/provider/pactflow-example-provider/consumer/pactflow-example-consumer/latest/badge.svg"],language:"JavaScript",useCase:"Web",side:"Consumer",compatible_clients:[{name:"Example Provider",link:"/docs/examples/js/provider"}]},{linkTitle:"React JavaScript Website tested using Cypress to generate consumer pacts",linkUrl:"/docs/examples/cypress",badges:["https://testdemo.pactflow.io/pacts/provider/pactflow-example-provider/consumer/example-consumer-cypress/latest/badge.svg"],language:"JavaScript",useCase:"Web",side:"C
1onsumer",compatible_clients:[{name:"Example Provider",link:"/docs/examples/js/provider"}]},{linkTitle:"ExpressJS API provider tested with Pact Verifier",linkUrl:"/docs/examples/js/provider",badges:["https://testdemo.pactflow.io/pacts/provider/pactflow-example-provider/consumer/pactflow-example-consumer/latest/badge.svg","https://testdemo.pactflow.io/pacts/provider/pactflow-example-provider/consumer/example-consumer-cypress/latest/badge.svg"],language:"JavaScript",useCase:"API",side:"Provider",compatible_clients:[{name:"Example Consumer Cypress",link:"/docs/examples/cypress/"},{name:"Example Consumer",link:"/docs/examples/js/consumer"}]},{linkTitle:".NET API Provider tested wtih Swashbuckle and Schemathesis against OAS",linkUrl:"/docs/examples/bi-directional/provider/dotnet",badges:[],language:".NET",useCase:"OpenAPI",side:"Provider",compatible_clients:[{name:"Example Bi-Directional Consumer .NET",link:"/docs/examples/bi-directional/provider/dotnet/"}]},{linkTitle:"SpringBoot API Provider tested with RestAssured against OAS",linkUrl:"/docs/examples/bi-directional/provider/restassured",badges:[],language:"Java",useCase:"OpenAPI",side:"Provider",compatible_clients:[{name:"Example Bi-Directional Consumer Cypress",link:"/docs/examples/bi-directional/consumer/cypress/"},{name:"Example Bi-Directional Consumer Mountebank",link:"/docs/examples/bi-directional/consumer/mountebank/"},{name:"Example Bi-Directional Consumer Wiremock",link:"/docs/examples/bi-directional/consumer/wiremock/"},{name:"Example Bi-Directional Consumer Nock",link:"/docs/examples/bi-directional/consumer/nock/"},{name:"Example Bi-Directional Consumer MSW",link:"/docs/examples/bi-directional/consumer/msw/"}]},{linkTitle:"ExpressJS API Provider tested with Postman against OAS",linkUrl:"/docs/examples/bi-directional/provider/postman",badges:[],language:"JavaScript",useCase:"OpenAPI",side:"Provider",compatible_clients:[{name:"Example Bi-Directional Consumer Cypress",link:"/docs/examples/bi-directional/consumer/cypress/"},{name:"Example Bi-Directional Consumer Mountebank",link:"/docs/examples/bi-directional/consumer/mountebank/"},{name:"Example Bi-Directional Consumer Wiremock",link:"/docs/examples/bi-directional/consumer/wiremock/"},{name:"Example Bi-Directional Consumer Nock",link:"/docs/examples/bi-directional/consumer/nock/"},{name:"Example Bi-Directional Consumer MSW",link:"/docs/examples/bi-directional/consumer/msw/"}]},{linkTitle:"ExpressJS API Provider tested with ReadyAPI against OAS",linkUrl:"/docs/examples/bi-directional/provider/readyapi",badges:[],language:"JavaScript",useCase:"OpenAPI",side:"Provider",compatible_clients:[{name:"Example Bi-Directional Consumer Cypress",link:"/docs/examples/bi-directional/consumer/cypress/"},{name:"Example Bi-Directional Consumer Mountebank",link:"/docs/examples/bi-directional/consumer/mountebank/"},{name:"Example Bi-Directional Consumer Wiremock",link:"/docs/examples/bi-directional/consumer/wiremock/"},{name:"Example Bi-Directional Consumer Nock",link:"/docs/examples/bi-directional/consumer/nock/"},{name:"Example Bi-Directional Consumer MSW",link:"/docs/examples/bi-directional/consumer/msw/"}]},{linkTitle:"ExpressJS API Provider tested with SoapUI against OAS",linkUrl:"/docs/examples/bi-directional/provider/soapui",badges:[],language:"JavaScript",useCase:"OpenAPI",side:"Provider",compatible_clients:[{name:"Example Bi-Directional Consumer Cypress",link:"/docs/examples/bi-directional/consumer/cypress/"},{name:"Example Bi-Directional Consumer Mountebank",link:"/docs/examples/bi-directional/consumer/mountebank/"},{name:"Example Bi-Directional Consumer Wiremock",link:"/docs/examples/bi-directional/consumer/wiremock/"},{name:"Example Bi-Directional Consumer Nock",link:"/docs/examples/bi-directional/consumer/nock/"},{name:"Example Bi-Directional Consumer MSW",link:"/docs/examples/bi-directional/consumer/msw/"}]},{linkTitle:"ExpressJS API Provider tested with Dredd against OAS",linkUrl:"/docs/examples/bi-directional/provider/dredd",badges:[],language:"JavaScript",useCase:"OpenAPI",side:"Provider",compatible_clients:[{name:"Example Bi-Directional Consumer Cypress",link:"/docs/examples/bi-directional/consumer/cypress/"},{name:"Example Bi-Directional Consumer Mountebank",link:"/docs/examples/bi-directional/consumer/mountebank/"},{name:"Example Bi-Directional Consumer Wiremock",link:"/docs/examples/bi-directional/consumer/wiremock/"},{name:"Example Bi-Directional Consumer Nock",link:"/docs/examples/bi-directional/consumer/nock/"},{name:"Example Bi-Directional Consumer MSW",link:"/docs/examples/bi-directional/consumer/msw/"}]},{linkTitle:"Python API Provider tested with Pact Verifier",linkUrl:"/docs/examples/python/provider",badges:[],language:"Python",useCase:"API",side:"Provider",compatible_clients:[{name:"Example Python Consumer",link:"/docs/examples/python/consumer/"}
1]},{linkTitle:"Golang Gin API Provider tested with Pact Verifier",linkUrl:"/docs/examples/golang/provider",badges:[],language:"Golang",useCase:"API",side:"Provider",compatible_clients:[{name:"Example Golang Consumer",link:"/docs/examples/golang/consumer/"}]},{linkTitle:".NET API Provider tested with Pact Verifier",linkUrl:"/docs/examples/dotnet/provider",badges:[],language:".NET",useCase:"API",side:"Provider",compatible_clients:[{name:"Example .NET Consumer",link:"/docs/examples/dotnet/consumer/"}]},{linkTitle:"Java SOAP API provider tested with Pact Verifier",linkUrl:"/docs/examples/soap/java/provider",badges:[],language:"Java",useCase:"SOAP",side:"Provider",compatible_clients:[{name:"Example Java XML Consumer",link:"/docs/examples/soap/java/consumer/"}]},{linkTitle:"Java Kafka message provider tested with Pact Verifier",linkUrl:"/docs/examples/kafka/java/provider",badges:[],language:"Java",useCase:"Messages",side:"Provider",compatible_clients:[{name:"Example Java Kafka Consumer",link:"/docs/examples/kafka/java/consumer/"},{name:"Example NodeJS Kafka Consumer",link:"/docs/examples/kafka/js/consumer/"}]},{linkTitle:"Java Spring Boot API provider tested with Pact Verifier",linkUrl:"/docs/examples/java/provider-springboot",badges:[],language:"Java",useCase:"API",side:"Provider",compatible_clients:[{name:"Example Java Junit Consumer",link:"/docs/examples/java/consumer/junit/"}]},{linkTitle:"ExpressJS AWS Lambda SNS Provider tested with Pact Verifier",linkUrl:"/docs/examples/aws/sns/provider",badges:[],language:"JavaScript",useCase:"Messages",side:"Provider",compatible_clients:[{name:"Example Node AWS SNS Consumer",link:"/docs/examples/aws/sns/consumer"}]},{linkTitle:"Node API consumer using Mountebank stubs and Pact Bi-Directional BYO adapters to generate Pact contracts",linkUrl:"/docs/examples/bi-directional/consumer/mountebank",badges:[],language:"JavaScript",useCase:"OpenAPI",side:"Consumer",compatible_clients:[{name:"Example Bi-Directional Provider Dredd",link:"/docs/examples/bi-directional/provider/dredd/"},{name:"Example Bi-Directional Provider Postman",link:"/docs/examples/bi-directional/provider/postman/"},{name:"Example Bi-Directional Provider RestAssured",link:"/docs/examples/bi-directional/provider/restassured/"},{name:"Example Provider",link:"/docs/examples/js/provider"}]},{linkTitle:"Java API consumer using Wiremock stubs and Pact Bi-Directional BYO adapters to generate Pact contracts",linkUrl:"/docs/examples/bi-directional/consumer/wiremock",badges:[],language:"Java",useCase:"OpenAPI",side:"Consumer",compatible_clients:[{name:"Example Bi-Directional Provider Dredd",link:"/docs/examples/bi-directional/provider/dredd/"},{name:"Example Bi-Directional Provider Postman",link:"/docs/examples/bi-directional/provider/postman/"},{name:"Example Bi-Directional Provider RestAssured",link:"/docs/examples/bi-directional/provider/restassured/"},{name:"Example Provider",link:"/docs/examples/js/provider"}]},{linkTitle:"React JavaScript Website using MSW mocks and pact-msw-adapter to generate Pact contracts",linkUrl:"/docs/examples/bi-directional/consumer/msw",badges:[],language:"JavaScript",useCase:"OpenAPI",side:"Consumer",compatible_clients:[{name:"Example Bi-Directional Provider Dredd",link:"/docs/examples/bi-directional/provider/dredd/"},{name:"Example Bi-Directional Provider Postman",link:"/docs/examples/bi-directional/provider/postman/"},{name:"Example Bi-Directional Provider RestAssured",link:"/docs/examples/bi-directional/provider/restassured/"},{name:"Example Provider",link:"/docs/examples/js/provider"}]},{linkTitle:"React JavaScript Website using Playwright routes and custom pact adapter to generate Pact contracts",linkUrl:"/docs/examples/bi-directional/consumer/playwright_js",badges:[],language:"JavaScript",useCase:"OpenAPI",side:"Consumer",compatible_clients:[{name:"Example Bi-Directional Provider Dredd",link:"/docs/examples/bi-directional/provider/dredd/"},{name:"Example Bi-Directional Provider Postman",link:"/docs/examples/bi-directional/provider/postman/"},{name:"Example Bi-Directional Provider RestAssured",link:"/docs/examples/bi-directional/provider/restassured/"},{name:"Example Provider",link:"/docs/examples/js/provider"}]},{linkTitle:"React JavaScript Website using Cypress fixtures and pact-cypress-adapter to generate Pact contracts",linkUrl:"/docs/examples/bi-directional/consumer/cypress",badges:[],language:"JavaScript",useCase:"OpenAPI",side:"C
1onsumer",compatible_clients:[{name:"Example Bi-Directional Provider Dredd",link:"/docs/examples/bi-directional/provider/dredd/"},{name:"Example Bi-Directional Provider Postman",link:"/docs/examples/bi-directional/provider/postman/"},{name:"Example Bi-Directional Provider RestAssured",link:"/docs/examples/bi-directional/provider/restassured/"},{name:"Example Provider",link:"/docs/examples/js/provider"}]},{linkTitle:"React JavaScript Website using Nock Record & Replay feature and pact-nock-adapter to generate Pact contracts",linkUrl:"/docs/examples/bi-directional/consumer/recordreplay",badges:[],language:"JavaScript",useCase:"OpenAPI",side:"Consumer",compatible_clients:[{name:"Example Bi-Directional Provider Dredd",link:"/docs/examples/bi-directional/provider/dredd/"},{name:"Example Bi-Directional Provider Postman",link:"/docs/examples/bi-directional/provider/postman/"},{name:"Example Bi-Directional Provider RestAssured",link:"/docs/examples/bi-directional/provider/restassured/"},{name:"Example Provider",link:"/docs/examples/js/provider"}]},{linkTitle:"Python API Consumer tested using Pact to generate consumer pacts",linkUrl:"/docs/examples/python/consumer",badges:[],language:"Python",useCase:"API",side:"Consumer",compatible_clients:[{name:"Example Python Provider",link:"/docs/examples/python/provider/"}]},{linkTitle:"Golang API Consumer tested using Pact to generate consumer pacts",linkUrl:"/docs/examples/golang/consumer",badges:[],language:"Golang",useCase:"API",side:"Consumer",compatible_clients:[{name:"Example Golang Provider",link:"/docs/examples/golang/provider/"}]},{linkTitle:".NET (v3.x.x) API Consumer tested using Pact to generate consumer pacts",linkUrl:"/docs/examples/dotnet/consumer",badges:[],language:".NET",useCase:"API",side:"Consumer",compatible_clients:[{name:"Example Provider .NET",link:"/docs/examples/dotnet/provider/"}]},{linkTitle:".NET (v4.x.x) API Consumer tested using Pact to generate consumer pacts",linkUrl:"/docs/examples/bi-directional/consumer/dotnet",badges:[],language:".NET",useCase:"API",side:"Consumer",compatible_clients:[{name:"Example Bi-Directional Provider .NET",link:"/docs/examples/bi-directional/consumer/dotnet/"}]},{linkTitle:"Java Junit API Consumer tested using Pact to generate consumer pacts",linkUrl:"/docs/examples/java/consumer/junit",badges:[],language:"Java",useCase:"API",side:"Consumer",compatible_clients:[{name:"Example Java Spring Boot Provider",link:"/docs/examples/java/provider-springboot/"}]},{linkTitle:"Java SOAP API Consumer tested using Pact to generate consumer pacts",linkUrl:"/docs/examples/soap/java/consumer",badges:[],language:"Java",useCase:"SOAP",side:"Consumer",compatible_clients:[{name:"Example Java XML Provider",link:"/docs/examples/soap/java/provider"}]},{linkTitle:"Java Kafka Consumer tested using Pact to generate consumer pacts",linkUrl:"/docs/examples/kafka/java/consumer",badges:[],language:"Java",useCase:"Messages",side:"Consumer",compatible_clients:[{name:"Example Java Kafka Producer",link:"/docs/examples/kafka/java/provider"}]},{linkTitle:"NodeJS Kafka Consumer tested using Pact to generate consumer pacts",linkUrl:"/docs/examples/kafka/js/consumer",badges:[],language:"NodeJS",useCase:"Messages",side:"Consumer",compatible_clients:[{name:"Example Java Kafka Producer",link:"/docs/examples/kafka/java/provider"}]},{linkTitle:"NodeJS AWS Lambda SNS Consumer tested using Pact to generate consumer pacts",linkUrl:"/docs/examples/aws/sns/consumer",badges:[],language:"NodeJS",useCase:"Messages",side:"Consumer",compatible_clients:[{name:"Example NodeJS SNS Provider",link:"/docs/examples/aws/sns/provider/"}]}],o=[{Header:"Language",accessor:"language",className:"data-table"},{Header:"Type",accessor:"side",className:"data-table"},{Header:"Use Case",accessor:"useCase",className:"data-table"},{Header:"Demo",accessor:"linkTitle",className:"data-table left",Cell:e=>{let{cell:{value:t},row:{original:a}}=e;return n.createElement("a",{href:""+a.linkUrl,rel:"noreferrer noopener"},t)}},{Header:"Compatible With",accessor:"compatible_clients",className:"data-table",Cell:e=>{let{row:{original:t}}=e;const a=t.compatible_clients;return a?a.map((e=>n.createElement("li",{key:e.link+t.linkTitle+Math.random()},n.createElement("a",{href:e.link},e.name)))):""}}]},93255:(e,t,a)=>{"use strict";a.d(t,{m:()=>l});const n={readyapi:{iconTitle:"ReadyAPI",iconLocation:"/img/testing_tools/readyapi.svg",iconLink:"/docs/examples/bi-directional/provider/readyapi/",contentUrl:"",content:"",iconType:"svg",side:"provider"},soapui:{iconTitle:"SoapUI",iconLocation:"/img/testing_tools/soapui-icon.svg",iconLink:"/docs/examples/bi-directional/provider/soapui/",contentUrl:"",content:"",iconType:"svg",side:"provider"},postman:{iconTitle:"Postman",iconLocation:"/img/testing_tools/postman.svg",iconLink:"/docs/examples/bi-directional/provider/postman/",contentUrl:"",content:"",iconType:"svg",side:"provider"},restassured:{iconTitle:"REST-assured",iconLocation:"/img/testing_tools/rest_assured.png",iconLink:"/docs/examples/bi-directional/provider/restassured/",contentUrl:"",content:"",iconType:"png",side:"provider"},dredd:{iconTitle:"Dredd",iconLocation:"/img/testing_tools/dredd-logo.png",iconLink:"/docs/examples/bi-directional/provider/dredd/",contentUrl:"",content:"",iconType:"png",side:"provider"},schemathesis:{iconTitle:"Schemathesis",iconLocation:"/img/testing_tools/schemathesis.png",iconLink:"/docs/examples/bi-directional/provider/dotnet/",contentUrl:"",content:"",iconType:"png",side:"provider"},cypress:{iconTitle:"Cypress",iconLocation:"/img/testing_tools/cypress.svg",iconLink:"/docs/examples/bi-directional/consumer/cypress/",contentUrl:"",content:"",iconType:"svg",side:"consumer"},wiremock:{iconTitle:"Wiremock",iconLocation:"/img/testing_tools/wireMockLogo.png",iconLink:"/docs/examples/bi-directional/consumer/wiremock/",contentUrl:"",content:"",iconType:"png",side:"consumer"},nock:{iconTitle:"Nock",iconLocation:"/img/testing_tools/nock.svg",iconLink:"/docs/examples/bi-directional/consumer/recordreplay/",contentUrl:"",content:"",iconType:"svg",side:"consumer"},mountebank:{iconTitle:"Mountebank",iconLocation:"/img/testing_tools/mountebank.png",iconLink:"/docs/examples/bi-directional/consumer/mountebank/",contentUrl:"",content:"",iconType:"png",side:"consumer"}
1,pact:{iconTitle:"Pact",iconLocation:"/img/testing_tools/pact.svg",iconLink:"/docs/examples/bi-directional/consumer/dotnet/",contentUrl:"",content:"",iconType:"svg",side:"consumer"},msw:{iconTitle:"Mock Service Worker",iconLocation:"/img/testing_tools/msw.svg",iconLink:"/docs/examples/bi-directional/consumer/msw/",contentUrl:"",content:"",iconType:"svg",side:"consumer"},playwright_js:{iconTitle:"Playwright - JS",iconLocation:"/img/testing_tools/playwright.png",iconLink:"/docs/examples/bi-directional/consumer/playwright_js/",contentUrl:"",content:"",iconType:"png",side:"consumer"}},i=["readyapi","soapui","dredd","restassured","postman","schemathesis"],o=["cypress","wiremock","nock","mountebank","pact","msw","playwright_js"],r=Object.keys(n).filter((e=>i.includes(e))).reduce(((e,t)=>(e[t]=n[t],e)),{}),s=Object.keys(n).filter((e=>o.includes(e))).reduce(((e,t)=>(e[t]=n[t],e)),{}),l={languages:{js:{iconTitle:"JavaScript",iconLocation:"https://raw.githubusercontent.com/pact-foundation/pact.io/master/pages/assets/img/languages/javascript-original.svg",iconLink:"https://docs.pact.io/implementation_guides/javascript",contentUrl:"",content:""},java:{iconTitle:"Java",iconLocation:"https://raw.githubusercontent.com/pact-foundation/pact.io/master/pages/assets/img/languages/java-original.svg",iconLink:"https://docs.pact.io/implementation_guides/jvm",contentUrl:"",content:""},golang:{iconTitle:"Golang",iconLocation:"https://raw.githubusercontent.com/pact-foundation/pact.io/master/pages/assets/img/languages/go-original.svg",iconLink:"https://docs.pact.io/implementation_guides/go",contentUrl:"",content:""},ruby:{iconTitle:"Ruby",iconLocation:"https://raw.githubusercontent.com/pact-foundation/pact.io/master/pages/assets/img/languages/ruby-original.svg",iconLink:"https://docs.pact.io/implementation_guides/ruby/readme",contentUrl:"",content:""},dotnet:{iconTitle:".NET",iconLocation:"https://raw.githubusercontent.com/pact-foundation/pact.io/master/pages/assets/img/languages/csharp-original.svg",iconLink:"https://docs.pact.io/implementation_guides/net",contentUrl:"",content:""},docker:{iconTitle:"Docker",iconLocation:"https://www.svgrepo.com/show/331370/docker.svg",iconLink:"https://docs.pact.io/docker",contentUrl:"",content:""},kotlin:{iconTitle:"Kotlin",iconLocation:"https://raw.githubusercontent.com/pact-foundation/pact.io/master/pages/assets/img/languages/kotlin-original.svg",iconLink:"https://docs.pact.io/implementation_guides/jvm",contentUrl:"",content:""},scala:{iconTitle:"Scala",iconLocation:"https://raw.githubusercontent.com/pact-foundation/pact.io/master/pages/assets/img/languages/scala-original.svg",iconLink:"https://docs.pact.io/implementation_guides/jvm",contentUrl:"",content:""},clojure:{iconTitle:"Clojure",iconLocation:"https://raw.githubusercontent.com/pact-foundation/pact.io/master/pages/assets/img/languages/clojure-original.svg",iconLink:"https://docs.pact.io/implementation_guides/jvm",contentUrl:"",content:""},python:{iconTitle:"Python",iconLocation:"https://raw.githubusercontent.com/pact-foundation/pact.io/master/pages/assets/img/languages/python-original.svg",iconLink:"https://docs.pact.io/implementation_guides/python",contentUrl:"",content:""},swift:{iconTitle:"Swift and Objective-C",iconLocation:"https://raw.githubusercontent.com/pact-foundation/pact.io/master/pages/assets/img/languages/swift-original.svg",iconLink:"https://docs.pact.io/implementation_guides/swift",contentUrl:"",content:""},php:{iconTitle:"PHP",iconLocation:"https://raw.githubusercontent.com/pact-foundation/pact.io/master/pages/assets/img/languages/php-original.svg",iconLink:"https://docs.pact.io/implementation_guides/php",contentUrl:"",content:""},cplusplus:{iconTitle:"C++",iconLocation:"https://raw.githubusercontent.com/pact-foundation/pact.io/master/pages/assets/img/languages/csharp-original.svg",iconLink:"https://docs.pact.io/implementation_guides/cpp",contentUrl:"",content:""}},testing_tools:n,testing_tools_provider_side:r,testing_tools_consumer_side:s}},47724:(e,t,a)=>{"use strict";a.r(t),a.d(t,{assets:()=>c,contentTitle:()=>s,default:()=>m,frontMatter:()=>r,metadata:()=>l,toc:()=>p});var n=a(58168),i=(a(96540),a(15680)),o=a(69628);a(64974),a(93255);const r={},s="Quick Start Guide with Github Actions",l={unversionedId:"docs/workshops/quick_starts/design_first",id:"docs/workshops/quick_starts/design_first",title:"Quick Start Guide with Github Actions",description:"If you want to get hands-on and learn about Design First API Design with Contract testing, and see it come to life, then you are in the right place.",source:"@site/docs/docs/workshops/quick_starts/design_first.mdx",sourceDirName:"docs/workshops/quick_starts",slug:"/docs/workshops/quick_starts/design_first",permalink:"/docs/workshops/quick_starts/design_first",draft:!1,editUrl:"https://github.com/pactflow/docs.pactflow.io/edit/master/website/docs/docs/workshops/quick_starts/design_first.mdx",tags:[],version:"current",lastUpdatedBy:"Yousaf Nabi",lastUpdatedAt:1674493712,formattedLastUpdatedAt:"Jan 23, 2023",frontMatter:{},sidebar:"university",previous:{title:"Conclusion",permalink:"/docs/workshops/ci-cd-legacy/workshop/conclusion"},next:{title:"Overview",permalink:"/docs/workshops/bi-directional-contract-testing"}},c={},p=[{value:"Agenda",id:"agenda",level:2},{value:"Table of Contents",id:"table-of-contents",level:2},{value:"Pre-requisites",id:"pre-requisites",level:2},{value:"Get a SwaggerHub account",id:"get-a-swaggerhub-account",level:3},{value:"Get a GitHub account",id:"get-a-github-account",level:3},{value:"Get a PactFlow account",id:"get-a-pactflow-account",level:3},{value:"Provider side",id:"provider-side",level:2},{value:"Design the API",id:"design-the-api",level:3},{value:"Publish your provider design spec to PactFlow",id:"publish-your-prov
1ider-design-spec-to-pactflow",level:3},{value:"Check if we could deploy our design candidate safely to production",id:"check-if-we-could-deploy-our-design-candidate-safely-to-production",level:3},{value:"Check if it is safe to deploy the provider to production",id:"check-if-it-is-safe-to-deploy-the-provider-to-production",level:3},{value:"Expected state by the end of this step",id:"expected-state-by-the-end-of-this-step",level:3},{value:"Consumer side",id:"consumer-side",level:2},{value:"Setup and trigger the example consumer project",id:"setup-and-trigger-the-example-consumer-project",level:3},{value:"Build triggered",id:"build-triggered",level:3},{value:"Pact Contract Tests passed",id:"pact-contract-tests-passed",level:3},{value:"Pact Contract files uploaded",id:"pact-contract-files-uploaded",level:3},{value:"Consumer can-i-deploy check",id:"consumer-can-i-deploy-check",level:3},{value:"Consumer record deployment",id:"consumer-record-deployment",level:3},{value:"Expected state by the end of this step",id:"expected-state-by-the-end-of-this-step-1",level:3},{value:"See a breaking change in action",id:"see-a-breaking-change-in-action",level:2},{value:"Remove a field the consumer requires",id:"remove-a-field-the-consumer-requires",level:3},{value:"Breaking change prevention with PactFlow",id:"breaking-change-prevention-with-pactflow",level:3},{value:"Graceful change",id:"graceful-change",level:3},{value:"Design verified as compatible (expand & contract)",id:"design-verified-as-compatible-expand--contract",level:3}],d={toc:p},u="wrapper";
1function m(e){let{components:t,...r}=e;return(0,i.yg)(u,(0,n.A)({},d,r,{components:t,mdxType:"MDXLayout"}),(0,i.yg)("h1",{id:"quick-start-guide-with-github-actions"},"Quick Start Guide with Github Actions"),(0,i.yg)("admonition",{title:"Welcome to the Quick Start Guide",type:"success"},(0,i.yg)("p",{parentName:"admonition"},"If you want to get hands-on and learn about Design First API Design with Contract testing, and see it come to life, then you are in the right place. "),(0,i.yg)("p",{parentName:"admonition"},"This guide shows you how to integrate your development workflow."),(0,i.yg)("p",{parentName:"admonition"},"To setup an integration within the SwaggerHub UI see the ",(0,i.yg)("a",{parentName:"p",href:"/docs/integrations/swaggerhub"},"SwaggerHub integration guide"),".")),(0,i.yg)("admonition",{title:"Guides",type:"info"},(0,i.yg)("p",{parentName:"admonition"},"These testing guides will help provide describe each step in more detail and should serve as a handy reference point"),(0,i.yg)("p",{parentName:"admonition"},(0,i.yg)("a",{parentName:"p",href:"/docs/bi-directional-contract-testing/provider"},"Provider Testing Guide")),(0,i.yg)("p",{parentName:"admonition"},(0,i.yg)("a",{parentName:"p",href:"/docs/bi-directional-contract-testing/consumer"},"Consumer Testing Guide")," ")),(0,i.yg)("h2",{id:"agenda"},"Agenda"),(0,i.yg)("p",null,"In this workshop, you will get to see the full development workflow in action. Following the scenario below, we will simplify it for demonstration by using GitHub to fork the example repositories and use GitHub actions to run the CI pipelines"),(0,i.yg)("ol",null,(0,i.yg)("li",{parentName:"ol"},"Create and document an API using OpenAPI Specification"),(0,i.yg)("li",{parentName:"ol"},"Publish the provider contract (an OpenAPI document) to PactFlow"),(0,i.yg)("li",{parentName:"ol"},"Write the API consumer"),(0,i.yg)("li",{parentName:"ol"},"Write tests for an API client using tools such as Mountebank,Nock,Wiremock,Nock,Cypress and Mock-Service-Worker or traditional Pact .NET to mock the API, and convert those mocks into a consumer contract"),(0,i.yg)("li",{parentName:"ol"},"Publish the consumer contract to PactFlow"),(0,i.yg)("li",{parentName:"ol"},"Learn about PactFlow's breaking change detection system")),(0,i.yg)("h2",{id:"table-of-contents"},"Table of Contents"),(0,i.yg)("ul",null,(0,i.yg)("li",{parentName:"ul"},(0,i.yg)("a",{parentName:"li",href:"#quick-start-guide-with-github-actions"},"Quick Start Guide with Github Actions"),(0,i.yg)("ul",{parentName:"li"},(0,i.yg)("li",{parentName:"ul"},(0,i.yg)("a",{parentName:"li",href:"#agenda"},"Agenda")),(0,i.yg)("li",{parentName:"ul"},(0,i.yg)("a",{parentName:"li",href:"#table-of-contents"},"Table of Contents")),(0,i.yg)("li",{parentName:"ul"},(0,i.yg)("a",{parentName:"li",href:"#pre-requisites"},"Pre-requisites"),(0,i.yg)("ul",{parentName:"li"},(0,i.yg)("li",{parentName:"ul"},(0,i.yg)("a",{parentName:"li",href:"#get-a-swaggerhub-account"},"Get a SwaggerHub account")),(0,i.yg)("li",{parentName:"ul"},(0,i.yg)("a",{parentName:"li",href:"#get-a-github-account"},"Get a GitHub account")),(0,i.yg)("li",{parentName:"ul"},(0,i.yg)("a",{parentName:"li",href:"#get-a-pactflow-account"},"Get a PactFlow account")))),(0,i.yg)("li",{parentName:"ul"},(0,i.yg)("a",{parentName:"li",href:"#provider-side"},"Provider side"),(0,i.yg)("ul",{parentName:"li"},(0,i.yg)("li",{parentName:"ul"},(0,i.yg)("a",{parentName:"li",href:"#design-the-api"},"Design the API")),(0,i.yg)("li",{parentName:"ul"},(0,i.yg)("a",{parentName:"li",href:"#publish-your-provider-design-spec-to-pactflow"},"Publish your provider design spec to PactFlow")),(0,i.yg)("li",{parentName:"ul"},(0,i.yg)("a",{parentName:"li",href:"#check-if-we-could-deploy-our-design-candidate-safely-to-production"},"Check if we could deploy our design candidate safely to production")),(0,i.yg)("li",{parentName:"ul"},(0,i.yg)("a",{parentName:"li",href:"#check-if-it-is-safe-to-deploy-the-provider-to-production"},"Check if it is safe to deploy the provider to production")),(0,i.yg)("li",{parentName:"ul"},(0,i.yg)("a",{parentName:"li",href:"#expected-state-by-the-end-of-this-step"},"Expected state by the end of this step")))),(0,i.yg)("li",{parentName:"ul"},(0,i.yg)("a",{parentName:"li",href:"#consumer-side"},"Consumer side"),(0,i.yg)("ul",{parentName:"li"},(0,i.yg)("li",{parentName:"ul"},(0,i.yg)("a",{parentName:"li",href:"#setup-and-trigger-the-example-consumer-project"},"Setup and trigger the example consumer project")),(0,i.yg)("li",{parentName:"ul"},(0,i.yg)("a",{parentName:"li",href:"#build-triggered"},"Build triggered")),(0,i.yg)("li",{parentName:"ul"},(0,i.yg)("a",{parentName:"li",href:"#pact-contract-tests-passed"},"Pact Contract Tests passed")),(0,i.yg)("li",{parentName:"ul"},(0,i.yg)("a",{parentName:"li",href:"#pact-contract-files-uploaded"},"Pact Contract files uploaded")),(0,i.yg)("li",{parentName:"ul"},(0,i.yg)("a",{parentName:"li",href:"#consumer-can-i-deploy-check"},"Consumer can-i-deploy check")),(0,i.yg)("li",{parentName:"ul"},(0,i.yg)("a",{parentName:"li",href:"#consumer-record-deployment"},"Consumer record deployment")),(0,i.yg)("li",{parentName:"ul"},(0,i.yg)("a",{parentName:"li",href:"#expected-state-by-the-end-of-this-step-1"},"Expected state by the end of this step")))),(0,i.yg)("li",{parentName:"ul"},(0,i.yg)("a",{parentName:"li",href:"#see-a-breaking-change-in-action"},"See a breaking change in action"),(0,i.yg)("ul",{parentName:"li"},(0,i.yg)("li",{parentName:"ul"},(0,i.yg)("a",{parentName:"li",href:"#remove-a-field-the-consumer-requires"},"Remove a field the consumer requires")),(0,i.yg)("li",{parentName:"ul"},(0,i.yg)("a",{parentName:"li",href:"#breaking-change-prevention-with-pactflow"},"Breaking change prevention with PactFlow")),(0,i.yg)("li",{parentName:"ul"},(0,i.yg)("a",{parentName:"li",href:"#graceful-change"},"Graceful change")),(0,i.yg)("li",{parentName:"ul"},(0,i.yg)("a",{parentName:"li",href:"#design-verified-as-compatible-expand--contract"},"Design verified as compatible (expand & contract)"))))))),(0,i.yg)("h2",{id:"pre-requisites"},"Pre-requisites"),(0,i.yg)("h3",{id:"get-a-swaggerhub-account"},"Get a SwaggerHub account"),(0,i.yg)("admonition",{type:"info"},(0,i.yg)("p",{parentName:"admonition"},"You'll need an account, Don't worry, we will be using the ",(0,i.yg)("a",{parentName:"p",href:"https://swagger.io/tools/swaggerhub/pricing/"},"free tier"),".")),(0,i.yg)("ul",{className:"contains-task-list"},(0,i.yg)("li",{parentName:"ul",className:"task-list-item"},(0,i.yg)("input",{parentName:"li",type:"checkbox",checked:!1,disabled:!0})," ","Don't have one - sign up \ud83d\udc49 ",(0,i.yg)("a",{parentName:"li",href:"https://try.smartbear.com/"},"here")),(0,i.yg)("li",{parentName:"ul",className:"task-list-item"},(0,i.yg)("input",{parentName:"li",type:"checkbox",checked:!0,disabled:!0})," ","I've already got one!")),(0,i.yg)("admonition",{type:"success"},(0,i.yg)("p",{parentName:"admonition"},"You have got your SwaggerHub account, and are successfully logged in.")),(0,i.yg)("admonition",{title:"Where is my email?",type:"warning"},(0,i.yg)("p",{parentName:"admonition"},"Not got an email? Don't forget to check your spam folder")),(0,i.yg)("h3",{id:"get-a-github-account"},"Get a GitHub account"),(0,i.yg)("admonition",{type:"info"},(0,i.yg)("p",{parentName:"admonition"},"All our examples run on Github Actions CI pipelines. You'll need an account. Don't worry its free")),(0,i.yg)("ul",{className:"contains-task-list"},(0,i.yg)("li",{parentName:"ul",className:"task-list-item"},(0,i.yg)("input",{parentName:"li",type:"checkbox",checked:!1,disabled:!0})," ","Don't have one - sign up \ud83d\udc49 ",(0,i.yg)("a",{parentName:"li",href:"https://github.com/signup"},"here")),(0,i.yg)("li",{parentName:"ul",className:"task-list-item"},(0,i.yg)("input",{parentName:"li",type:"checkbox",checked:!0,disabled:!0})," ","I've already got one!")),(0,i.yg)("admonition",{type:"success"},(0,i.yg)("p",{parentName:"admonition"},"You are logged in to your GitHub account")),(0,i.yg)("h3",{id:"get-a-pactflow-account"},"Get a PactFlow account"),(0,i.yg)("admonition",{type:"info"},(0,i.yg)("p",{parentName:"admonition"},"The Bi-Directional Feature is only supported by PactFlow, so you'll need an account, Don't worry, the developer tier is free.")),(0,i.yg)("ul",{className:"contains-task-list"},(0,i.yg)("li",{parentName:"ul",className:"task-list-item"},(0,i.yg)("input",{parentName:"li",type:"checkbox",checked:!1,disabled:!0})," ","Don't have one - sign up \ud83d\udc49 ",(0,i.yg)("a",{parentName:"li",href:"https://pactflow.io/try-for-free"},"here")),(0,i.yg)("li",{parentName:"ul",className:"task-list-item"},(0,i.yg)("input",{parentName:"li",type:"checkbox",checked:!1,disabled:!0})," ","I've got a company account (see below)"),(0,i.yg)("li",{parentName:"ul",className:"task-list-item"},(0,i.yg)("input",{parentName:"li",type:"checkbox",checked:!0,disabled:!0})," ","I've already got one!")),(0,i.yg)("details",null,(0,i.yg)("summary",null,"Using a shared company PactFlow account?"),(0,i.yg)("p",null,"You can use a shared company PactFlow account, but it will make things a bit fiddly, as you'll need to change the identifiers of the various resources that get created so that they don't clash with those from other workshop participants. We've found from past experience running workshops that it's much simpler if everyone has their own account.")),(0,i.yg)("admonition",{type:"success"},(0,i.yg)("p",{parentName:"admonition"},"You have got your PactFlow account, and are successfully logged in.")),(0,i.yg)("admonition",{title:"Where is my email?",type:"warning"},(0,i.yg)("p",{parentName:"admonition"},"Not got an email? Don't forget to check your spam folder, otherwise reach out to the team at ",(0,i.yg)("a",{parentName:"p",href:"mailto:[email protected]"},"[email protected]"))),(0,i.yg)("h2",{id:"provider-side"},"Provider side"),(0,i.yg)("h3",{id:"design-the-api"},"Design the API"),(0,i.yg)("p",null,(0,i.yg)("em",{parentName:"p"},"Create and document an API using OpenAPI")),(0,i.yg)("p",null,"As we are following a specification or ",(0,i.yg)("a",{parentName:"p",href:"https://swagger.io/blog/api-design/design-first-or-code-first-api-development/"},"design first approach")," to API development, we start by creating an ",(0,i.yg)("a",{parentName:"p",href:"https://oai.github.io/Documentation/start-here.html"},"OpenAPI")," description document, that describes how our API should work."),(0,i.yg)("p",null,"Authoring an OAS document is beyond the scope of this tutorial, but you can find plenty of resources on the internet (such as at ",(0,i.yg)("a",{parentName:"p",href:"https://swagger.io"},"swagger.io"),")."),(0,i.yg)("p",null,"Our products reference openAPI spec is ",(0,i.yg)("a",{parentName:"p",href:"/oas/products"},"here")),(0,i.yg)("p",null,"Inside it, we have 3 main endpoints:"),(0,i.yg)("ol",null,(0,i.yg)("li",{parentName:"ol"},(0,i.yg)("inlineCode",{parentName:"li"},"POST /products")," - create a new product"),(0,i.yg)("li",{parentName:"ol"},(0,i.yg)("inlineCode",{parentName:"li"},"GET /products")," - gets all products"),(0,i.yg)("li",{parentName:"ol"},(0,i.yg)("inlineCode",{parentName:"li"},"GET /products/:id")," - gets a single product")),(0,i.yg)("p",null,"The product schema is as follows"),(0,i.yg)(o.A,{id:"oas-product",pointer:"#/components/schemas/Product",mdxType:"ApiSchema"}),(0,i.yg)("ol",null,(0,i.yg)("li",{parentName:"ol"},"Create OpenAPI Document in SwaggerHub",(0,i.yg)("ol",{parentName:"li"},(0,i.yg)("li",{parentName:"ol"},"Select ",(0,i.yg)("inlineCode",{parentName:"li"},"Create New")," -> ",(0,i.yg)("inlineCode",{parentName:"li"},"Create new API")),(0,i.yg)("li",{parentName:"ol"},"Select ",(0,i.yg)("inlineCode",{parentName:"li"},"Owner")),(0,i.yg)("li",{parentName:"ol"},"Select ",(0,i.yg)("inlineCode",{parentName:"li"},"Specification"),": ",(0,i.yg)("inlineCode",{parentName:"li"},"OpenAPI 3.0.x")),(0,i.yg)("li",{parentName:"ol"},"Select ",(0,i.yg)("inlineCode",{parentName:"li"},"Template"),": ",(0,i.yg)("inlineCode",{parentName:"li"},"--None--")),(0,i.yg)("li",{parentName:"ol"},"Select ",(0,i.yg)("inlineCode",{parentName:"li"},"Name"),": ",(0,i.yg)("inlineCode",{parentName:"li"},"Enter the name of your API here")),(0,i.yg)("li",{parentName:"ol"},"Select ",(0,i.yg)("inlineCode",{parentName:"li"},"Version"),": ",(0,i.yg)("inlineCode",{parentName:"li"},"1.0.0")),(0,i.yg)("li",{parentName:"ol"},"Select ",(0,i.yg)("inlineCode",{parentName:"li"},"Auto Mock API"),": ",(0,i.yg)("inlineCode",{parentName:"li"},"off")),(0,i.yg)("li",{parentName:"ol"},"Select ",(0,i.yg)("inlineCode",{parentName:"li"},"Create API")),(0,i.yg)("li",{parentName:"ol"},"Paste the contents of ",(0,i.yg)("a",{parentName:"li",href:"https://github.com/pactflow/docs.pactflow.io/blob/master/website/static/contractTypes/oas/products.yml"},"the reference OpenAPI specification"))))),(0,i.yg)("p",null,(0,i.yg)("img",{parentName:"p",src:"https://user-images.githubusercontent.com/19932401/195682898-c93cde1e-4725-4291-a9cb-cdc8eab43cd1.png",alt:"1_SwaggerHub_create_new_openapi"}),"\n",(0,i.yg)("img",{parentName:"p",src:"https://user-images.githubusercontent.com/19932401/195682906-aa29541e-7934-4886-9ba8-eb5a5e9fab6b.png",alt:"2_SwaggerHub_create_options"}),"\n",(0,i.yg)("img",{parentName:"p",src:"https://user-images.githubusercontent.com/19932401/195682909-83b5081c-0517-4581-ac8b-5daaefe33b6a.png",alt:"3_SwaggerHub_api_created"}),"\n",(0,i.yg)("img",{parentName:"p",src:"https://user-images.githubusercontent.com/19932401/195682915-39521b05-4917-47ea-b7bd-8cb7b5eba907.png",alt:"4_SwaggerHub_demo_a
1pi_pasted"})),(0,i.yg)("admonition",{title:"success",type:"success"},(0,i.yg)("p",{parentName:"admonition"},"Your should have created your first API definition in SwaggerHub. For fuller information on SwaggerHub Authoring - Follow this ",(0,i.yg)("a",{parentName:"p",href:"https://support.smartbear.com/swaggerhub/docs/tutorials/getting-started.html"},"guide")," to create your first API in SwaggerHub.")),(0,i.yg)("h3",{id:"publish-your-provider-design-spec-to-pactflow"},"Publish your provider design spec to PactFlow"),(0,i.yg)("admonition",{title:"Find out more",type:"info"},(0,i.yg)("p",{parentName:"admonition"},"See our Publishing contracts ",(0,i.yg)("a",{parentName:"p",href:"/docs/bi-directional-contract-testing/publishing"},"docs")," for more info")),(0,i.yg)("p",null,'Now that we have created our provider specification, we need to share it to our consumers. This is where PactFlow comes in to the picture. This step is referred to as "publishing" the provider contract.'),(0,i.yg)("p",null,"The publishing step takes two key components:"),(0,i.yg)("ul",null,(0,i.yg)("li",{parentName:"ul"},"The provider contract itself (in our case, the OAS document)"),(0,i.yg)("li",{parentName:"ul"},"The test results (in our case, we will use our OAS document, as we are just testing the design, we will use a seperate flow to test our implementation when it is created)")),(0,i.yg)("p",null,"This information will be helpful later on, when we need to check compatibility with its consumers."),(0,i.yg)("ol",null,(0,i.yg)("li",{parentName:"ol"},"Fork the ",(0,i.yg)("a",{parentName:"li",href:"https://github.com/pactflow/example-provider"},"example-provider")," projects in to your own Github account (click the 'Fork' button in the top right).\n",(0,i.yg)("img",{parentName:"li",src:"https://user-images.githubusercontent.com/19932401/195682925-595f91ef-9126-4623-8d2a-e0a767655a5c.png",alt:"5_GitHub_fork_example_provider"}),(0,i.yg)("ol",{parentName:"li"},(0,i.yg)("li",{parentName:"ol"},"Open your forked ",(0,i.yg)("inlineCode",{parentName:"li"},"example-provider")," project (",(0,i.yg)("inlineCode",{parentName:"li"},"https://github.com/<your-username>/example-provider"),")"),(0,i.yg)("li",{parentName:"ol"},"Open ",(0,i.yg)("inlineCode",{parentName:"li"},".github/workflows/ProviderDesignFeedback.yml")),(0,i.yg)("li",{parentName:"ol"},"In the upper right corner of the file view, click \ud83d\udd8a\ufe0f to open the file editor."),(0,i.yg)("li",{parentName:"ol"},"Update the value of ",(0,i.yg)("inlineCode",{parentName:"li"},"PACT_BROKER_BASE_URL")," to the base URL of your own PactFlow account. You can easily get this by clicking the COPY PACTFLOW BASE URL button on the API Tokens page in PactFlow.\n",(0,i.yg)("img",{parentName:"li",src:"https://user-images.githubusercontent.com/19932401/195682933-36d6ffd4-948c-448e-a16a-84cbeb55ee62.png",alt:"6_GitHub_update_build_script"})),(0,i.yg)("li",{parentName:"ol"},"Remove the line ",(0,i.yg)("inlineCode",{parentName:"li"},"if: ${{ github.repository_owner == 'pactflow' }}")),(0,i.yg)("li",{parentName:"ol"},"Press the green ",(0,i.yg)("inlineCode",{parentName:"li"},"Commit changes")," button"))),(0,i.yg)("li",{parentName:"ol"},"Create a Github Secret to store your PactFlow API token in.",(0,i.yg)("ol",{parentName:"li"},(0,i.yg)("li",{parentName:"ol"},"In PactFlow:",(0,i.yg)("ol",{parentName:"li"},(0,i.yg)("li",{parentName:"ol"},"Log in to your PactFlow account (",(0,i.yg)("inlineCode",{parentName:"li"},"https://<your-subdomain>.pactflow.io"),"), and go to Settings > API Tokens - See ",(0,i.yg)("a",{parentName:"li",href:"/#configuring-your-api-token"},"here")," for the docs."),(0,i.yg)("li",{parentName:"ol"},"Click the Copy button for the read/write CI token (make sure it's the read ",(0,i.yg)("em",{parentName:"li"},"write")," one, not the read only one)."))),(0,i.yg)("li",{parentName:"ol"},"In Github:",(0,i.yg)("ol",{parentName:"li"},(0,i.yg)("li",{parentName:"ol"},"In your forked ",(0,i.yg)("inlineCode",{parentName:"li"},"example-provider")," project (",(0,i.yg)("inlineCode",{parentName:"li"},"https://github.com/<your-username>/example-provider"),")"),(0,i.yg)("li",{parentName:"ol"},"Click on the ",(0,i.yg)("inlineCode",{parentName:"li"},"Settings")," tab."),(0,i.yg)("li",{parentName:"ol"},"Select ",(0,i.yg)("inlineCode",{parentName:"li"},"Secrets")," from the side menu."),(0,i.yg)("li",{parentName:"ol"},"Click ",(0,i.yg)("inlineCode",{parentName:"li"},"New repository secret"),' (the button is to the right of the "Actions secrets" heading)'),(0,i.yg)("li",{parentName:"ol"},"Set the name of the secret to ",(0,i.yg)("inlineCode",{parentName:"li"},"PACTFLOW_TOKEN_FOR_CI_CD_WORKSHOP")),(0,i.yg)("li",{parentName:"ol"},"Paste in the PactFlow API token value you copied in the previous step."),(0,i.yg)("li",{parentName:"ol"},"Click ",(0,i.yg)("inlineCode",{parentName:"li"},"Add Secret"),(0,i.yg)("img",{parentName:"li",src:"https://user-images.githubusercontent.com/19932401/195682938-25d60568-99ed-45d8-a416-2fdf1cc06056.png",alt:"7_GitHub_add_pactflow_token"})))))),(0,i.yg)("li",{parentName:"ol"},"Click on the ",(0,i.yg)("inlineCode",{parentName:"li"},"Actions")," tab."),(0,i.yg)("li",{parentName:"ol"},'Click the button with the text "I understand my workflows, go ahead and enable them"'),(0,i.yg)("li",{parentName:"ol"},"Sync OpenAPI -> SCM with Github Sync - ",(0,i.yg)("a",{parentName:"li",href:"https://support.smartbear.com/swaggerhub/docs/integrations/github-sync.html"},"https://support.smartbear.com/swaggerhub/docs/integrations/github-sync.html"),
1(0,i.yg)("ol",{parentName:"li"},(0,i.yg)("li",{parentName:"ol"},"Open the API page in SwaggerHub."),(0,i.yg)("li",{parentName:"ol"},"Click the API name, switch to the Integrations tab, and click Add New Integrations:"),(0,i.yg)("li",{parentName:"ol"},"Select GitHub Sync."),(0,i.yg)("li",{parentName:"ol"},"In the subsequent dialog, specify the integration parameters:",(0,i.yg)("ol",{parentName:"li"},(0,i.yg)("li",{parentName:"ol"},"Name \u2013 Required. A display name for the integration. ",(0,i.yg)("inlineCode",{parentName:"li"},"gh-design-to-pactflow")),(0,i.yg)("li",{parentName:"ol"},"GitHub Token \u2013 Required. The GitHub access token that SwaggerHub will use to access the target GitHub repository."),(0,i.yg)("li",{parentName:"ol"},"The easiest way to get the token is to click Connect to GitHub and allow SwaggerHub to retrieve information from your GitHub account:"),(0,i.yg)("li",{parentName:"ol"},"Click Next in the GitHub Token edit box to continue. SwaggerHub will validate the token and then display other parameters."),(0,i.yg)("li",{parentName:"ol"},"Repository Owner \u2013 Select you GitHub user or organization that owns the repository you created in the previous step"),(0,i.yg)("li",{parentName:"ol"},"Repository \u2013 Select the repository you setup earlier to push the code to"))),(0,i.yg)("li",{parentName:"ol"},"Sync Method \u2013 Select the synchronization type: ",(0,i.yg)("inlineCode",{parentName:"li"},"Basic Sync")),(0,i.yg)("li",{parentName:"ol"},"Branch \u2013 Required. The repository branch to push the code to. If this branch does not exist, it will be created based on the repository\u2019s default branch. Choose ",(0,i.yg)("inlineCode",{parentName:"li"},"swaggerhub")),(0,i.yg)("li",{parentName:"ol"},"Generated API Code \u2013 Required. Select what you want to generate: ",(0,i.yg)("inlineCode",{parentName:"li"},"YAML(Resolved)")),(0,i.yg)("li",{parentName:"ol"},"Output Folder - Select ",(0,i.yg)("inlineCode",{parentName:"li"},"oas")),(0,i.yg)("li",{parentName:"ol"},"Output File - Select ",(0,i.yg)("inlineCode",{parentName:"li"},"swagger.yaml")),(0,i.yg)("li",{parentName:"ol"},"Click ",(0,i.yg)("inlineCode",{parentName:"li"},"Create And Execute")," -> ",(0,i.yg)("inlineCode",{parentName:"li"},"Done"),".\n",(0,i.yg)("img",{parentName:"li",src:"https://user-images.githubusercontent.com/19932401/195682941-2278c5f8-42cb-4318-a0d6-f62d10e9e450.png",alt:"8_SwaggerHub_add_github_sync"}))))),(0,i.yg)("p",null,"After you run through the process your dashboard should look like this"),(0,i.yg)("p",null,(0,i.yg)("img",{parentName:"p",src:"https://user-images.githubusercontent.com/19932401/195682950-a2b1b903-006a-4574-9566-6c6026bc3e7f.png",alt:"10_Pactflow_provider_contract_uploaded"}),"\n",(0,i.yg)("img",{parentName:"p",src:"https://user-images.githubusercontent.com/19932401/195682952-3cde777c-5838-48fa-a6af-2f7a0d407f34.png",alt:"11_Pactflow_provider_contract_view"}),"\n",(0,i.yg)("img",{parentName:"p",src:"https://user-images.githubusercontent.com/19932401/195682956-94c08de7-57b6-4bfa-95df-3ae37a5b9da8.png",alt:"12_Pactflow_provider_self_verification_results"})),(0,i.yg)("admonition",{title:"success",type:"success"},(0,i.yg)("p",{parentName:"admonition"},"Your should have uploaded your first API definition to PactFlow.")),(0,i.yg)("h3",{id:"check-if-we-could-deploy-our-design-candidate-safely-to-production"},"Check if we could deploy our design candidate safely to production"),(0,i.yg)("p",null,"Now that we have designed our provider specification and published our provider contract, we can check if we deploy the application to production."),(0,i.yg)("p",null,"Whilst we don't currently have any consumers to worry about, we want to be prepared for when we do. PactFlow has a tool called ",(0,i.yg)("a",{parentName:"p",href:"https://docs.pact.io/pact_broker/can_i_deploy"},(0,i.yg)("inlineCode",{parentName:"a"},"can-i-deploy"))," to help us."),(0,i.yg)("p",null,"The ",(0,i.yg)("inlineCode",{parentName:"p"},"can-i-deploy")," command is an important part of a CI/CD workflow, adding stage gates to prevent deploying incompatible applications to environments such as production."),(0,i.yg)("p",null,"This diagram shows an illustrative CI/CD pipeline as it relates to our progress to date:"),(0,i.yg)("p",null,(0,i.yg)("img",{alt:"first provider pipeline run",src:a(97971).A,width:"960",height:"540"})),(0,i.yg)("h3",{id:"check-if-it-is-safe-to-deploy-the-provider-to-production"},"Check if it is safe to deploy the provider to production"),(0,i.yg)("p",null,"We run the command:"),(0,i.yg)("p",null,(0,i.yg)("inlineCode",{parentName:"p"},"can-i-deploy")),(0,i.yg)("p",null,"This should pass, because as we discussed above, there are no consumers:"),(0,i.yg)("p",null,(0,i.yg)("img",{parentName:"p",src:"https://user-images.githubusercontent.com/19932401/195682961-cf378073-4f39-43d4-b450-5b8623f2cc80.png",alt:"13_GitHub_provider_first_run"})),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre"},'\n$ pact-broker can-i-deploy --pacticipant pactflow-example-consumer --version="1.0.0-21b1f7fdf6428bfb0f583e151d9893c230a1c555-design" --to-environment production\n\nComputer says yes \\o/\n\nThere are no missing dependencies\n')),(0,i.yg)("p",null,"Later on, when consumers start to use our API, we will be prevented from releasing a change that results in a backwards incompatible change for our consumers. Consumers will also use this command to ensure they are compatible with the Provider API in the target environment (in this case, ",(0,i.yg)("inlineCode",{parentName:"p"},"production"),")."),(0,i.yg)("p",null,"If this was our actual implementation, this is where we would deploy our provider to production.\nOnce we have deployed, we would PactFlow know that the new version of the Provider has been promoted to that environment,\nallowing PactFlow to communicate to any future consumers of the provider, that the OAS associated with this version of the provider is supported in production.\nIf a consumer adds functionality that uses a subset of the OAS, they will be free to deploy safely!"),(0,i.yg)("p",null,"By using the can-i-deploy against our design, we will be able check that our future design candidate changes will be compatible with any of the future consumers that are deployed or released.\nGiving us visibility and feedback, at the earliest opportunity."),(0,i.yg)("h3",{id:"expected-state-by-the-end-of-this-step"},"Expected state by the end of this step"),(0,i.yg)("ul",null,(0,i.yg)("li",{parentName:"ul"},"The provider build is passing and can-i-deploy states it is free to be deployed to production \u2705")),(0,i.yg)("h2",{id:"consumer-side"},"Consumer side"),(0,i.yg)("h3",{id:"setup-and-trigger-the-example-consumer-project"},"Setup and trigger the example consumer project"),(0,i.yg)("ol",null,(0,i.yg)("li",{parentName:"ol"},"Fork the ","[example-consumer][https://github.com/pactflow/example-consumer]"," project in to your own Github account (click the 'Fork' button in the top right)."),(0,i.yg)("li",{parentName:"ol"},"Open your forked ",(0,i.yg)("inlineCode",{parentName:"li"},"example-consumer")," project (",(0,i.yg)("inlineCode",{parentName:"li"},"https://github.com/<your-username>/example-consumer"),")"),(0,i.yg)("li",{parentName:"ol"},"Create a Github Secret to store your PactFlow API token in.",(0,i.yg)("ol",{parentName:"li"},(0,i.yg)("li",{parentName:"ol"},"In PactFlow:",(0,i.yg)("ol",{parentName:"li"},(0,i.yg)("li",{parentName:"ol"},"Log in to your PactFlow account (",(0,i.yg)("inlineCode",{parentName:"li"},"https://<your-subdomain>.pactflow.io"),"), and go to Settings > API Tokens - See ",(0,i.yg)("a",{parentName:"li",href:"/#configuring-your-api-token"},"here")," for the docs."),(0,i.yg)("li",{parentName:"ol"},"Click the Copy button for the read/write CI token (make sure it's the read ",(0,i.yg)("em",{parentName:"li"},"write")," one, not the read only one)."))),(0,i.yg)("li",{parentName:"ol"},"In Github:",(0,i.yg)("ol",{parentName:"li"},(0,i.yg)("li",{parentName:"ol"},"In your forked ",(0,i.yg)("inlineCode",{parentName:"li"},"example-consumer")," project (",(0,i.yg)("inlineCode",{parentName:"li"},"https://github.com/<your-username>/example-consumer"),")"),(0,i.yg)("li",{parentName:"ol"},"Click on the ",(0,i.yg)("inlineCode",{parentName:"li"},"Settings")," tab."),(0,i.yg)("li",{parentName:"ol"},"Select ",(0,i.yg)("inlineCode",{parentName:"li"},"Secrets")," from the side menu."),(0,i.yg)("li",{parentName:"ol"},"Click ",(0,i.yg)("inlineCode",{parentName:"li"},"New repository secret"),' (the button is to the right of the "Actions secrets" heading)'),(0,i.yg)("li",{parentName:"ol"},"Set the name of the secret to ",(0,i.yg)("inlineCode",{parentName:"li"},"PACTFLOW_TOKEN_FOR_CI_CD_WORKSHOP")),(0,i.yg)("li",{parentName:"ol"},"Paste in the PactFlow API token value you copied in the previous step."),(0,i.yg)("li",{parentName:"ol"},"Click on the ",(0,i.yg)("inlineCode",{parentName:"li"},"Actions")," tab."),(0,i.yg)("li",{parentName:"ol"},'Click the button with the text "I understand my workflows, go ahead and enable them"'),(0,i.yg)("li",{parentName:"ol"},"Open ",(0,i.yg)("inlineCode",{parentName:"li"},".github/workflows/Build.yml")),(0,i.yg)("li",{parentName:"ol"},"In the upper right corner of the file view, click \ud83d\udd8a\ufe0f to open the file editor."),(0,i.yg)("li",{parentName:"ol"},"Update the value of ",(0,i.yg)("inlineCode",{parentName:"li"},"PACT_BROKER_BASE_URL")," to the base URL of your own PactFlow account. You can easily get this by clicking the COPY PACTFLOW BASE URL button on the API Tokens page in PactFlow."),(0,i.yg)("li",{parentName:"ol"},"Press the green ",(0,i.yg)("inlineCode",{parentName:"li"},"Commit changes")," button")))))),(0,i.yg)("h3",{id:"build-triggered"},"Build triggered"),(0,i.yg)("p",null,"The consumer build will trigger"),(0,i.yg)("p",null,(0,i.yg)("img",{parentName:"p",src:"https://user-images.githubusercontent.com/19932401/195682963
1-a1653ba9-8adf-42b3-8eae-1a5723cc5998.png",alt:"14_GitHub_consumer_first_run_triggered"})),(0,i.yg)("h3",{id:"pact-contract-tests-passed"},"Pact Contract Tests passed"),(0,i.yg)("p",null,"The consumers unit tests will pass"),(0,i.yg)("p",null,(0,i.yg)("img",{parentName:"p",src:"https://user-images.githubusercontent.com/19932401/195682967-2b6fab80-84b4-4b98-a3d7-0495313714c2.png",alt:"15_GitHub_consumer_first_run_test_complete"})),(0,i.yg)("p",null,"You can click into the build job to see the output"),(0,i.yg)("p",null,(0,i.yg)("img",{parentName:"p",src:"https://user-images.githubusercontent.com/19932401/195682970-1a114513-5738-40c5-9dc5-4b2bbd4b6366.png",alt:"16_GitHub_consumer_first_run_test_output"})),(0,i.yg)("h3",{id:"pact-contract-files-uploaded"},"Pact Contract files uploaded"),(0,i.yg)("p",null,"The pact files are uploaded to the PactFlow Broker"),(0,i.yg)("p",null,(0,i.yg)("img",{parentName:"p",src:"https://user-images.githubusercontent.com/19932401/195682976-00f43ab9-6b4d-46ad-a86f-4be6df131302.png",alt:"17_GitHub_consumer_first_pact_published"})),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre"},"Pact successfully published for pactflow-example-consumer version 253c165958d15624ce7245d5739689860439d24b and provider pactflow-example-provider.\n View the published pact at https://saflow.pactflow.io/pacts/provider/pactflow-example-provider/consumer/pactflow-example-consumer/version/253c165958d15624ce7245d5739689860439d24b\n Events detected: contract_published, contract_content_changed (first time untagged pact published)\n No enabled webhooks found for the detected events\n Next steps:\n * Add Pact verification tests to the pactflow-example-provider build. See https://docs.pact.io/go/provider_verification\n")),(0,i.yg)("h3",{id:"consumer-can-i-deploy-check"},"Consumer can-i-deploy check"),(0,i.yg)("p",null,"It will then call the ",(0,i.yg)("inlineCode",{parentName:"p"},"can-i-deploy")," command before it tries to deploy, it passes. This is because the consumers pact is a valid subset of the OpenAPI specificiation"),(0,i.yg)("p",null,(0,i.yg)("img",{parentName:"p",src:"https://user-images.githubusercontent.com/19932401/195682978-091f4976-ee27-4f77-9f06-e7b52082b862.png",alt:"18_GitHub_consumer_safe_to_deploy"})),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre"},"========== STAGE: can-i-deploy? ==========\n\nComputer says yes \\o/ \n\nCONSUMER | C.VERSION | PROVIDER | P.VERSION | SUCCESS? | RESULT#\n--------------------------|------------|---------------------------|-------------------------|----------|--------\npactflow-example-consumer | 253c165... | pactflow-example-provider | 1.0.0-21b1f7f...-design | true | 1 \n\nVERIFICATION RESULTS\n--------------------\n1. https://saflow.pactflow.io/contracts/bi-directional/provider/pactflow-example-provider/version/1.0.0-21b1f7fdf6428bfb0f583e151d9893c230a1c555-design/consumer/pactflow-example-consumer/version/253c165958d15624ce7245d5739689860439d24b/cross-contract-verification-results (success)\n\nAll required verification results are published and successful\n")),(0,i.yg)("h3",{id:"consumer-record-deployment"},"Consumer record deployment"),(0,i.yg)("p",null,(0,i.yg)("inlineCode",{parentName:"p"},"can-i-deploy")," is successful, so we can deploy our consumer, once complete, we record the consumers application version as deployed to the production environment"),(0,i.yg)("p",null,(0,i.yg)("img",{parentName:"p",src:"https://user-images.githubusercontent.com/19932401/195682988-dfcf3aba-ad55-4df5-a31f-874136a13e92.png",alt:"20_GitHub_record_deployment"})),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre"},"========== STAGE: deploy ==========\n\nDeploying to production\ntouch .env\nRecorded deployment of pactflow-example-consumer version 253c165958d15624ce7245d5739689860439d24b to production environment in PactFlow.\n")),(0,i.yg)("h3",{id:"expected-state-by-the-end-of-this-step-1"},"Expected state by the end of this step"),(0,i.yg)("ul",null,(0,i.yg)("li",{parentName:"ul"},"Both consumer and provider builds passing \u2705")),(0,i.yg)("p",null,(0,i.yg)("img",{parentName:"p",src:"https://user-images.githubusercontent.com/19932401/195682992-c878ba4b-dc81-4fcb-8056-cfdb1da96521.png",alt:"21_Pactflow_contract_verified"}),"\n",(0,i.yg)("img",{parentName:"p",src:"https://user-images.githubusercontent.com/19932401/195682996-fbcf4c0e-fad8-4001-82f6-b8cd0bd61ef5.png",alt:"22_Pactflow_contract_verified_comparison"}),"\n",(0,i.yg)("img",{parentName:"p",src:"https://user-images.githubusercontent.com/19932401/195683001-d670f34c-48f8-4763-90c1-1dd6301a83e1.png",alt:"23_Pactflow_contract_verified_consumer_contract"}),"\n",(0,i.yg)("img",{parentName:"p",src:"https://user-images.githubusercontent.com/19932401/195683003-d8bf37ad-e5f4-41a7-b346-7ea9b1700277.png",alt:"24_Pactflow_contract_verified_provider_contract"}),"\n",(0,i.yg)("img",{parentName:"p",src:"https://user-images.githubusercontent.com/19932401/195683005-1bd0c641-706c-4074-b2ce-7416b9e60386.png",alt:"25_Pactflow_contract_verified_provider_results"})),(0,i.yg)("h2",{id:"see-a-breaking-change-in-action"},"See a breaking change in action"),(0,i.yg)("p",null,"Let's go a bit deeper and introduce a breaking change into the system. Breaking changes come in two main ways:"),(0,i.yg)("ol",null,(0,i.yg)("li",{parentName:"ol"}
1,"A consumer can add a new expectation (e.g. a new field/endpoint) on a provider that doesn't exist"),(0,i.yg)("li",{parentName:"ol"},"A provider might make a change (e.g. remove or rename a field, or change an endpoint) that breaks an existing consumer")),(0,i.yg)("p",null,"PactFlow will detect such situations using the ",(0,i.yg)("inlineCode",{parentName:"p"},"can-i-deploy")," tool. When it runs, it performs a contract comparison that checks if the consumer contract is a valid subset of the provider contract in the target environment."),(0,i.yg)("h3",{id:"remove-a-field-the-consumer-requires"},"Remove a field the consumer requires"),(0,i.yg)("p",null,"As we are concentrating on our design specification, we will propose 2 changes in SwaggerHub. One will be an unsafe operation that renames a field the consumer requires"),(0,i.yg)("p",null,"Let's see it in action."),(0,i.yg)("ol",null,(0,i.yg)("li",{parentName:"ol"},"In SwaggerHub:",(0,i.yg)("ol",{parentName:"li"},(0,i.yg)("li",{parentName:"ol"},"Open your API definition we created earlier"),(0,i.yg)("li",{parentName:"ol"},"We are going to update the ",(0,i.yg)("inlineCode",{parentName:"li"},"name")," field in the ",(0,i.yg)("inlineCode",{parentName:"li"},"Product")," schema to ",(0,i.yg)("inlineCode",{parentName:"li"},"firstName"),(0,i.yg)("img",{parentName:"li",src:"https://user-images.githubusercontent.com/19932401/195683011-b9a1452b-548f-46f2-90d4-1cd560f1abd4.png",alt:"26_SwaggerHub_remove_name"})),(0,i.yg)("li",{parentName:"ol"},"Click ",(0,i.yg)("inlineCode",{parentName:"li"},"Save")),(0,i.yg)("li",{parentName:"ol"},"Click ",(0,i.yg)("inlineCode",{parentName:"li"},"Sync")),(0,i.yg)("li",{parentName:"ol"},"Update the commit message ",(0,i.yg)("inlineCode",{parentName:"li"},"feat: removing name field")),(0,i.yg)("li",{parentName:"ol"},"Press ",(0,i.yg)("inlineCode",{parentName:"li"},"Push"),(0,i.yg)("img",{parentName:"li",src:"https://user-images.githubusercontent.com/19932401/195683015-732b09c2-2a04-46ba-973f-14ff0cd8ad3c.png",alt:"27_SwaggerHub_remove_name_push_change"}))))),(0,i.yg)("p",null,(0,i.yg)("img",{parentName:"p",src:"https://user-images.githubusercontent.com/19932401/195683020-27f4e37d-ed43-4400-9f1c-d02b3c0c28d9.png",alt:"28_GitHub_provider_build_triggered"})),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre"},"Run pactflow/actions/[email protected]\nSuccessfully published provider contract for pactflow-example-provider version 1.0.0-3a694151f8837a8f38ed80124e9d81530d614dde-design to PactFlow\n")),(0,i.yg)("h3",{id:"breaking-change-prevention-with-pactflow"},"Breaking change prevention with PactFlow"),(0,i.yg)("p",null,(0,i.yg)("inlineCode",{parentName:"p"},"can-i-deploy")," fails"),(0,i.yg)("p",null,(0,i.yg)("img",{parentName:"p",src:"https://user-images.githubusercontent.com/19932401/195683027-8ea897d2-1377-43a7-9801-110ea2583602.png",alt:"29_GitHub_provider_can_i_deploy_failed"})),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre"},'pact-broker can-i-deploy --pacticipant pactflow-example-provider --version="1.0.0-3a694151f8837a8f38ed80124e9d81530d614dde-design" --to-environment production\n\nComputer says no \xaf_(\u30c4)_/\xaf\n\nCONSUMER | C.VERSION | PROVIDER | P.VERSION | SUCCESS? | RESULT#\n--------------------------|------------|---------------------------|-------------------------|----------|--------\npactflow-example-consumer | 253c165... | pactflow-example-provider | 1.0.0-3a69415...-design | false | 1 \n\nVERIFICATION RESULTS\n--------------------\n1. https://saflow.pactflow.io/contracts/bi-directional/provider/pactflow-example-provider/version/1.0.0-3a694151f8837a8f38ed80124e9d81530d614dde-design/consumer/pactflow-example-consumer/version/253c165958d15624ce7245d5739689860439d24b/cross-contract-verification-results (failure)\n\nThe cross contract verification between the pact for the version of pactflow-example-consumer currently deployed or released to production (253c165958d15624ce7245d5739689860439d24b) and the oas for version 1.0.0-3a694151f8837a8f38ed80124e9d81530d614dde-design of pactflow-example-provider failed\n')),(0,i.yg)("admonition",{type:"warning"},(0,i.yg)("p",{parentName:"admonition"},"This build will fail, PactFlow knows all of the consumers needs down to the field level."),(0,i.yg)("p",{parentName:"admonition"},'If you head into the PactFlow UI and drill down into the "contract comparison" tab, you\'ll see the output from comparing the consumer and provider contracts:'),(0,i.yg)("p",{parentName:"admonition"},(0,i.yg)("img",{parentName:"p",src:"https://user-images.githubusercontent.com/19932401/195683032-f4e4a506-9060-4859-a597-279bc9f24edc.png",alt:"30_Pactflow_provider_can_i_deploy_failed_overview"}),"\n",(0,i.yg)("img",{parentName:"p",src:"https://user-images.githubusercontent.com/19932401/195683035-83536269-5c94-4412-bb22-50fefafb9c40.png",alt:"31_Pactflow_provider_can_i_deploy_failed_consumer"}),"\n",(0,i.yg)("img",{parentName:"p",src:"https://user-images.githubusercontent.com/19932401/195683039-9b71c289-6cc0-4bb3-9867-79e5367a7e64.png",alt:"32_Pactflow_provider_can_i_deploy_failed_provider"})),(0,i.yg)("p",{parentName:"admonition"},"As you can see in this example, it's alerting us to the fact that the consumer needs a field but the provider doesn't support it."),(0,i.yg)("p",{parentName:"admonition"},"Read more about how to ",(0,i.yg)("a",{parentName:"p",href:"https://docs.pactflow.io/docs/bi-directional-contract-testing/compatibility-checks"},"interpret failures"),".")),(0,i.yg)("h3",{id:"graceful-change"},"Graceful change"),(0,i.yg)("p",null,"Let's see a non breaking change in action, we want to rename the ",(0,i.yg)("inlineCode",{parentName:"p"},"name")," field to ",(0,i.yg)("inlineCode",{parentName:"p"},"firstName"),". We are going to use the expand and contract pattern, where we\nsupport our consumers using the ",(0,i.yg)("inlineCode",{parentName:"p"},"name")," field, until they have changed their code to use ",(0,i.yg)("inlineCode",{parentName:"p"},"firstName"),". We will do this by adding both properties into the ",(0,i.yg)("inlineCode",{parentName:"p"},"Product")," schema"),(0,i.yg)("ol",null,(0,i.yg)("li",{parentName:"ol"},"In SwaggerHub:",(0,i.yg)("ol",{parentName:"li"},(0,i.yg)("li",{parentName:"ol"},"Open your API definition we created earlier"),(0,i.yg)("li",{parentName:"ol"},"We are going to add the ",(0,i.yg)("inlineCode",{parentName:"li"},"name")," field back in the ",(0,i.yg)("inlineCode",{parentName:"li"},"Product")," schema, so it should contain both ",(0,i.yg)("inlineCode",{parentName:"li"},"name")," and ",(0,i.yg)("inlineCode",{parentName:"li"},"firstName"),(0,i.yg)("img",{parentName:"li",src:"https://user-images.githubusercontent.com/19932401/195683045-ba0b7cb5-ccea-41db-8c53-02f1caa9f3fb.png",alt:"33_SwaggerHub_add_firstname"})),(0,i.yg)("li",{parentName:"ol"},"Click ",(0,i.yg)("inlineCode",{parentName:"li"},"Save")),(0,i.yg)("li",{parentName:"ol"},"Click ",(0,i.yg)("inlineCode",{parentName:"li"},"Sync")),(0,i.yg)("li",{parentName:"ol"},"Update the commit message ",(0,i.yg)("inlineCode",{parentName:"li"},"feat: adding firstName field")),(0,i.yg)("li",{parentName:"ol"},"Press ",(0,i.yg)("inlineCode",{parentName:"li"},"Push"),(0,i.yg)("img",{parentName:"li",src:"https://user-images.githubusercontent.com/19932401/195683050-57a8ad98-a127-4409-b80c-15438085f309.png",alt:"34_SwaggerHub_add_firstname_push_change"}))))),(0,i.yg)("p",null,(0,i.yg)("img",{parentName:"p",src:"https://user-images.githubusercontent.com/19932401/195683053-e3ccfd69-3761-417d-9617-8810a21f176e.png",alt:"35_GitHub_provider_build_triggered"})),(0,i.yg)("p",null,(0,i.yg)("img",{parentName:"p",src:"https://user-images.githubusercontent.com/19932401/195683059-7c21ee84-cd91-4e35-a901-65be7ff35731.png",alt:"36_GitHub_provider_contract_published"})),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre"},"Run pactflow/actions/[email protected]\nSuccessfully published provider contract for pactflow-example-provider version 1.0.0-4bff20fab7b711b66a508c999
1690547d9559d12b-design to PactFlow\n")),(0,i.yg)("h3",{id:"design-verified-as-compatible-expand--contract"},"Design verified as compatible (expand & contract)"),(0,i.yg)("p",null,(0,i.yg)("img",{parentName:"p",src:"https://user-images.githubusercontent.com/19932401/195683063-eade4854-1765-4e32-b3f9-c1cf3f86e6aa.png",alt:"37_GitHub_can_i_deploy_passed"})),(0,i.yg)("pre",null,(0,i.yg)("code",{parentName:"pre"},'pact-broker can-i-deploy --pacticipant pactflow-example-provider --version="1.0.0-4bff20fab7b711b66a508c999690547d9559d12b-design" --to-environment production\nComputer says yes \\o/ \n\nCONSUMER | C.VERSION | PROVIDER | P.VERSION | SUCCESS? | RESULT#\n--------------------------|------------|---------------------------|-------------------------|----------|--------\npactflow-example-consumer | 253c165... | pactflow-example-provider | 1.0.0-4bff20f...-design | true | 1 \n\nVERIFICATION RESULTS\n--------------------\n1. https://saflow.pactflow.io/contracts/bi-directional/provider/pactflow-example-provider/version/1.0.0-4bff20fab7b711b66a508c999690547d9559d12b-design/consumer/pactflow-example-consumer/version/253c165958d15624ce7245d5739689860439d24b/cross-contract-verification-results (success)\n\nAll required verification results are published and successful\n')),(0,i.yg)("admonition",{type:"success"},(0,i.yg)("p",{parentName:"admonition"},"This build should pass, PactFlow knows all of the consumers needs down to the field level. Because the consumers request is a subset of the product schema, this is a safe operation."),(0,i.yg)("p",{parentName:"admonition"},"We could now update our consumer code to use ",(0,i.yg)("inlineCode",{parentName:"p"},"firstName"),", allowing the consumer to deprecate the ",(0,i.yg)("inlineCode",{parentName:"p"},"name")," field."),(0,i.yg)("p",{parentName:"admonition"},(0,i.yg)("img",{parentName:"p",src:"https://user-images.githubusercontent.com/19932401/195683069-a32fa26f-6ab2-415f-80af-8f1251c179c1.png",alt:"38_GitHub_workflow_overview"}))),(0,i.yg)("admonition",{title:"what have we learned?",type:"info"},(0,i.yg)("ol",{parentName:"admonition"},(0,i.yg)("li",{parentName:"ol"},"It is always safe to remove a field from a provider, if no consumers are currently using it"),(0,i.yg)("li",{parentName:"ol"},"It is not safe to remove a field/endpoint from a provider, if an existing consumer ",(0,i.yg)("em",{parentName:"li"},"is")," using it, and PactFlow will detect this situation."),(0,i.yg)("li",{parentName:"ol"},"PactFlow will prevent a consumer from deploying a change that a Provider has yet to support")),(0,i.yg)("p",{parentName:"admonition"},"\ud83d\ude80 If you've made it this far, you should now have a good understanding of how SwaggerHub & PactFlow's bi-directional contract testing feature works to make it safe to release software into production quickly and reliably."),(0,i.yg)("p",{parentName:"admonition"},"You may be interested in one of"),(0,i.yg)("ul",{parentName:"admonition"},(0,i.yg)("li",{parentName:"ul"},(0,i.yg)("a",{parentName:"li",href:"/docs/bi-directional-contract-testing/provider"},"Provider Testing Guide")),(0,i.yg)("li",{parentName:"ul"},(0,i.yg)("a",{parentName:"li",href:"/docs/bi-directional-contract-testing/consumer"},"Consumer Testing Guide")," "),(0,i.yg)("li",{parentName:"ul"},"our quick ",(0,i.yg)("a",{parentName:"li",href:"/docs/tutorials"},"katacodas")),(0,i.yg)("li",{parentName:"ul"},"our other ",(0,i.yg)("a",{parentName:"li",href:"/docs/workshops"},"workshops"),", or"),(0,i.yg)("li",{parentName:"ul"},"getting deeper into ",(0,i.yg)("a",{parentName:"li",href:"/docs/bi-directional-contract-testing"},"BDCT")),(0,i.yg)("li",{parentName:"ul"},"a dive into our ",(0,i.yg)("a",{parentName:"li",href:"/docs/workshops/ci-cd/"},"CI/CD")," workshop with Pact and the consumer driven contract testing approach - this is use the codebase you've just exercised and will introduce the key concepts on Pact in more detail"))))}m.isMDXComponent=!0},97971:(e,t,a)=>{"use strict";a.d(t,{A:()=>n});
1const n=a.p+"assets/images/2-bi-directional-provider-pipeline-first-run-b1a5250e5e055636f26234582fd7f785.png"},67992:()=>{},3986:()=>{},49936:()=>{},2941:()=>{},28825:()=>{},7411:()=>{},92441:()=>{}}]);
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.