diff --git a/README.md b/README.md index 27419a1e..f596da8c 100644 --- a/README.md +++ b/README.md @@ -13,34 +13,36 @@ An idiomatic Node interface for the [Pact](http://pact.io) mock service (Consume -- [Pact Node](#pact-node) - - [Installation](#installation) - - [Do Not Track](#do-not-track) - - [Pact Download Location](#pact-download-location) - - [Usage](#usage) - - [Documentation](#documentation) - - [Set Log Level](#set-log-level) - - [Mock Servers](#mock-servers) - - [Create Mock Server](#create-mock-server) - - [List Mock Servers](#list-mock-servers) - - [Remove All Mock Servers](#remove-all-mock-servers) - - [Start a Mock Server](#start-a-mock-server) - - [Stop a Mock server](#stop-a-mock-server) - - [Delete a Mock server](#delete-a-mock-server) - - [Check if a Mock server is running](#check-if-a-mock-server-is-running) - - [Mock Server Events](#mock-server-events) - - [Provider Verification](#provider-verification) - - [Pact Broker Publishing](#pact-broker-publishing) - - [Stub Servers](#stub-servers) - - [Create Stub Server](#create-stub-server) - - [Message Pacts](#message-pacts) - - [Create Message Pacts](#create-message-pacts) - - [Example](#example) - - [Example CLI invocation:](#example-cli-invocation) - - [Windows Issues](#windows-issues) - - [Contributing](#contributing) - - [Testing](#testing) - - [Questions?](#questions) +- [Pact Node](#pact-node) + - [Installation](#installation) + - [Do Not Track](#do-not-track) + - [Pact Download Location](#pact-download-location) + - [Usage](#usage) + - [Documentation](#documentation) + - [Set Log Level](#set-log-level) + - [Mock Servers](#mock-servers) + - [Create Mock Server](#create-mock-server) + - [List Mock Servers](#list-mock-servers) + - [Remove All Mock Servers](#remove-all-mock-servers) + - [Start a Mock Server](#start-a-mock-server) + - [Stop a Mock server](#stop-a-mock-server) + - [Delete a Mock server](#delete-a-mock-server) + - [Check if a Mock server is running](#check-if-a-mock-server-is-running) + - [Mock Server Events](#mock-server-events) + - [Provider Verification](#provider-verification) + - [Pact Broker Publishing](#pact-broker-publishing) + - [Pact Broker Deployment Check](#pact-broker-deployment-check) + - [Stub Servers](#stub-servers) + - [Create Stub Server](#create-stub-server) + - [Message Pacts](#message-pacts) + - [Create Message Pacts](#create-message-pacts) + - [Example](#example) + - [Example CLI invocation:](#example-cli-invocation) + - [Windows Issues](#windows-issues) + - [Enable Long Paths](#enable-long-paths) + - [Contributing](#contributing) + - [Testing](#testing) + - [Questions?](#questions) @@ -251,8 +253,7 @@ pact.verifyPacts({ | Parameter | Required? | Type | Description | | --------------------------- | --------- | ------- | ---------------------------------------------------------------------------------------------------------- | | `providerBaseUrl` | true | string | Running API provider host endpoint. | -| `pactBrokerBaseUrl` | false | string | Base URL of the Pact Broker from which to retrieve the pacts. | -| `pactBrokerUrl` | false | string | URL of your Pact Broker to dynamically discover relevent pacts to verify. Required if `pactUrls` not given | +| `pactBrokerUrl` | false | string | Base URL of the Pact Broker from which to retrieve the pacts. Required if `pactUrls` not given. | | `provider` | false | string | Name of the provider if fetching from a Broker | | `tags` | false | array | Array of tags, used to filter pacts from the Broker | | `consumerVersionTag` | false | string | Retrieve the latest pacts with this consumer version tag | diff --git a/src/broker.spec.ts b/src/broker.spec.ts deleted file mode 100644 index 0b23191d..00000000 --- a/src/broker.spec.ts +++ /dev/null @@ -1,93 +0,0 @@ -import chai = require("chai"); -import chaiAsPromised = require("chai-as-promised"); -import logger from "./logger"; -import brokerMock from "../test/integration/broker-mock"; -import brokerFactory, {BrokerOptions} from "./broker"; -import * as http from "http"; - -const expect = chai.expect; -chai.use(chaiAsPromised); - -describe("Broker Spec", () => { - let server: http.Server; - const PORT = Math.floor(Math.random() * 999) + 9000; - const pactBrokerBaseUrl = `http://localhost:${PORT}`; - - before(() => brokerMock(PORT).then((s) => { - logger.debug(`Pact Broker Mock listening on port: ${PORT}`); - server = s; - })); - - after(() => server.close()); - - describe("Broker", () => { - context("when not given a Pact Broker URL", () => { - it("should fail with an error", () => { - expect(() => brokerFactory({ - provider: "foobar" - } as BrokerOptions)).to.throw(Error); - }); - }); - context("when not given a Provider name", () => { - it("should fail with an error", () => { - expect(() => { - brokerFactory({ - brokerUrl: "http://test.pact.dius.com.au" - } as BrokerOptions); - }).to.throw(Error); - }); - }); - context("when given a valid Pact Broker URL", () => { - it("should return a Broker object", () => { - expect(() => brokerFactory({ - brokerUrl: "http://test.pact.dius.com.au", - provider: "foobar" - })).to.not.throw(Error); - }); - }); - }); - - describe("Find Consumers", () => { - context("when provider 'notfound' does not exist", () => { - context("and given the provider name 'notfound'", () => { - it("should fail with an Error", () => { - return expect(brokerFactory({ - brokerUrl: pactBrokerBaseUrl, - provider: "notfound" - }).findConsumers()).to.eventually.be.rejected; - }); - }); - }); - context("when no pacts exist for provider 'nolinks'", () => { - context("and given the provider name", () => { - it("should return an empty array of pact links", () => { - return expect(brokerFactory({ - brokerUrl: pactBrokerBaseUrl, - provider: "nolinks" - }).findConsumers()).to.eventually.eql([]); - }); - }); - }); - - context("When pacts exist for provider 'they'", () => { - context("and given the provider name and tags", () => { - it("should find pacts from all known consumers of the provider given any of the tags", () => { - return expect(brokerFactory({ - brokerUrl: pactBrokerBaseUrl, - provider: "they", - tags: ["prod"] - }).findConsumers()).to.eventually.have.lengthOf(2); - }); - }); - - context("and given the provider name without tags", () => { - it("should find pacts from all known consumers of the provider", () => { - return expect(brokerFactory({ - brokerUrl: pactBrokerBaseUrl, - provider: "they" - }).findConsumers()).to.eventually.have.lengthOf(2); - }); - }); - }); - }); -}); diff --git a/src/broker.ts b/src/broker.ts deleted file mode 100644 index 9e71f4c6..00000000 --- a/src/broker.ts +++ /dev/null @@ -1,94 +0,0 @@ -import q = require("q"); -import logger from "./logger"; -import {IWhenable} from "q"; -import {deprecate} from "util"; - -const _ = require("underscore"); -const checkTypes = require("check-types"); -const request = q.denodeify(require("request")); - -export class Broker { - public static create = deprecate( - (options: BrokerOptions) => new Broker(options), - "Create function will be removed in future release, please use the default export function or use `new Broker()`"); - - public readonly options: BrokerOptions; - - constructor(options: BrokerOptions) { - options = options || {}; - options.tags = options.tags || []; - - checkTypes.assert.nonEmptyString(options.brokerUrl); - checkTypes.assert.nonEmptyString(options.provider); - - if (options.tags) { - checkTypes.assert.array.of.string(options.tags); - } - - if (options.username) { - checkTypes.assert.string(options.username); - } - - if (options.password) { - checkTypes.assert.string(options.password); - } - - this.options = options; - } - - // Find Pacts returns the raw response from the HAL resource - public findPacts(tag?: string): q.Promise { - logger.debug(`finding pacts for Provider: ${this.options.provider} Tag: ${tag}`); - const requestOptions = { - uri: this.options.brokerUrl, - method: "GET", - headers: { - "Content-Type": "application/json" - }, - "auth": this.options.username && this.options.password ? { - "user": this.options.username, - "password": this.options.password - } : null - }; - return request(requestOptions) - .then((data: any) => data[0]) - .then((response) => { - if (response.statusCode < 200 && response.statusCode >= 300) { - return q.reject(response); - } - const body = JSON.parse(response.body); - return request(_.extend({}, requestOptions, { - uri: body._links[`pb:latest-provider-pacts${tag ? "-with-tag" : ""}`].href.replace("{tag}", tag).replace("{provider}", this.options.provider) - })); - }) - .then((data: any) => data[0]) - .then((response) => response.statusCode < 200 && response.statusCode >= 300 ? q.reject(response) : JSON.parse(response.body)); - } - - // Find all consumers collates all of the pacts for a given provider (with optional tags) - // and removes duplicates (e.g. where multiple tags on the same pact) - public findConsumers(): q.Promise { - logger.debug("Finding consumers"); - const promises = _.isEmpty(this.options.tags) ? [this.findPacts()] : _.map(this.options.tags, (t: string) => this.findPacts(t)); - - return q.all(promises) - .then((values) => _.reduce(values, (array: string[], v: any) => { - if (v && v._links && v._links.pacts) { - array.push(..._.pluck(v._links.pacts, "href")); - } - return array; - }, [])) - .catch(() => q.reject>(`Unable to find pacts for given provider '${this.options.provider}' and tags '${this.options.tags}'`)); - } -} - -// Creates a new instance of the Pact Broker HAL client with the specified option -export default (options: BrokerOptions) => new Broker(options); - -export interface BrokerOptions { - brokerUrl: string; - provider: string; - username?: string; - password?: string; - tags?: string[]; -} diff --git a/src/index.ts b/src/index.ts index 45ce7813..4b60fee8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,6 +8,4 @@ export * from "./server"; export * from "./publisher"; -export * from "./broker"; - export * from "./stub"; diff --git a/src/verifier.ts b/src/verifier.ts index 226274d3..0d27cc0a 100644 --- a/src/verifier.ts +++ b/src/verifier.ts @@ -1,6 +1,5 @@ import path = require("path"); import url = require("url"); -import brokerFactory, {BrokerOptions} from "./broker"; import logger from "./logger"; import pactUtil, {DEFAULT_ARG, SpawnArguments} from "./pact-util"; import q = require("q"); @@ -21,13 +20,15 @@ export class Verifier { private readonly __argMapping = { "pactUrls": DEFAULT_ARG, "providerBaseUrl": "--provider-base-url", - "pactBrokerBaseUrl": "--pact-broker-base-url", + "pactBrokerUrl": "--pact-broker-base-url", "providerStatesSetupUrl": "--provider-states-setup-url", "pactBrokerUsername": "--broker-username", "pactBrokerPassword": "--broker-password", + "pactBrokerToken": "--broker-token", "consumerVersionTag": "--consumer-version-tag", "publishVerificationResult": "--publish-verification-results", "providerVersion": "--provider-app-version", + "provider": "--provider", "customProviderHeaders": "--custom-provider-header", "format": "--format", "out": "--out", @@ -36,7 +37,6 @@ export class Verifier { constructor(options: VerifierOptions) { options = options || {}; options.pactBrokerUrl = options.pactBrokerUrl || ""; - options.pactBrokerBaseUrl = options.pactBrokerBaseUrl || ""; options.consumerVersionTag = options.consumerVersionTag || ""; options.tags = options.tags || []; options.pactUrls = options.pactUrls || []; @@ -69,11 +69,11 @@ export class Verifier { checkTypes.assert.nonEmptyString(options.providerBaseUrl, "Must provide the providerBaseUrl argument"); if (checkTypes.emptyArray(options.pactUrls) && !options.pactBrokerUrl) { - throw new Error("Must provide the pactUrls argument if no brokerUrl provided"); + throw new Error("Must provide the pactUrls argument if no pactBrokerUrl provided"); } - if ((!options.pactBrokerUrl || _.isEmpty(options.provider)) && checkTypes.emptyArray(options.pactUrls)) { - throw new Error("Must provide both provider and brokerUrl or if pactUrls not provided."); + if (( !options.pactBrokerUrl || _.isEmpty(options.provider)) && checkTypes.emptyArray(options.pactUrls)) { + throw new Error("Must provide both provider and pactBrokerUrl if pactUrls not provided."); } if (options.providerStatesSetupUrl) { @@ -88,8 +88,8 @@ export class Verifier { checkTypes.assert.string(options.pactBrokerPassword); } - if (options.pactBrokerBaseUrl) { - checkTypes.assert.string(options.pactBrokerBaseUrl); + if (options.pactBrokerUrl) { + checkTypes.assert.string(options.pactBrokerUrl); } if (options.consumerVersionTag) { @@ -146,36 +146,19 @@ export class Verifier { public verify(): q.Promise { logger.info("Verifying Pact Files"); - return q(this.options.pactUrls) - // TODO: fix this promise type issue by using regular old es6 promises, remove Q - .then((uris): any => { - if (!uris || uris.length === 0) { - return brokerFactory({ - brokerUrl: this.options.pactBrokerUrl, - provider: this.options.provider, - username: this.options.pactBrokerUsername, - password: this.options.pactBrokerPassword, - tags: this.options.tags - } as BrokerOptions).findConsumers(); - } - return uris; - }) - .then((data: string[]): PromiseLike => { - const deferred = q.defer(); - this.options.pactUrls = data; - const instance = pactUtil.spawnBinary(pactStandalone.verifierPath, this.options, this.__argMapping); - const output: any[] = []; - instance.stdout.on("data", (l) => output.push(l)); - instance.stderr.on("data", (l) => output.push(l)); - instance.once("close", (code) => { - const o = output.join("\n"); - code === 0 ? deferred.resolve(o) : deferred.reject(new Error(o)); - }); - - return deferred.promise - .timeout(this.options.timeout as number, `Timeout waiting for verification process to complete (PID: ${instance.pid})`) - .tap(() => logger.info("Pact Verification succeeded.")) as PromiseLike; - }); + const deferred = q.defer(); + const instance = pactUtil.spawnBinary(pactStandalone.verifierPath, this.options, this.__argMapping); + const output: any[] = []; + instance.stdout.on("data", (l) => output.push(l)); + instance.stderr.on("data", (l) => output.push(l)); + instance.once("close", (code) => { + const o = output.join("\n"); + code === 0 ? deferred.resolve(o) : deferred.reject(new Error(o)); + }); + + return deferred.promise + .timeout(this.options.timeout as number, `Timeout waiting for verification process to complete (PID: ${instance.pid})`) + .tap(() => logger.info("Pact Verification succeeded.")); } } @@ -186,15 +169,15 @@ export interface VerifierOptions extends SpawnArguments { providerBaseUrl: string; provider?: string; pactUrls?: string[]; - pactBrokerBaseUrl?: string; + pactBrokerUrl?: string; providerStatesSetupUrl?: string; pactBrokerUsername?: string; pactBrokerPassword?: string; + pactBrokerToken?: string; consumerVersionTag?: string; customProviderHeaders?: string[]; publishVerificationResult?: boolean; providerVersion?: string; - pactBrokerUrl?: string; tags?: string[]; timeout?: number; monkeypatch?: string; diff --git a/standalone/install.ts b/standalone/install.ts index 43077d3f..fcc5331b 100644 --- a/standalone/install.ts +++ b/standalone/install.ts @@ -13,7 +13,7 @@ const sumchecker = require("sumchecker"); const request = Request.defaults({proxy: process.env.npm_config_https_proxy || process.env.npm_config_proxy || undefined}); // Get latest version from https://github.com/pact-foundation/pact-ruby-standalone/releases -export const PACT_STANDALONE_VERSION = "1.63.0"; +export const PACT_STANDALONE_VERSION = "1.64.0"; const PACT_DEFAULT_LOCATION = `https://github.com/pact-foundation/pact-ruby-standalone/releases/download/v${PACT_STANDALONE_VERSION}/`; const HTTP_REGEX = /^http(s?):\/\//; const CONFIG = createConfig();