UNPKG

@contract-case/case-core

Version:

Core functionality for the ContractCase contract testing suite

163 lines 9.68 kB
"use strict"; // We need to allow underscores because they're part of the HAL response /* eslint-disable no-underscore-dangle */ Object.defineProperty(exports, "__esModule", { value: true }); exports.makeBrokerApi = void 0; const case_plugin_base_1 = require("@contract-case/case-plugin-base"); const axios_1 = require("./axios"); const versionString_1 = require("../../entities/versionString"); const logNotices_1 = require("./logNotices"); const trimSlash = (str) => { if (typeof str !== 'string') return ''; if (str.endsWith('/')) { return trimSlash(str.substring(0, str.length - 1)); } return str; }; const validatePrecondition = (baseUrl, authToken, basicAuth) => { if (baseUrl === undefined || baseUrl === '') { return new case_plugin_base_1.CaseConfigurationError("Can't access a broker without specifying the base URL. Set the environment variable CASE_BROKER_BASEURL or the config property brokerBaseUrl", 'DONT_ADD_LOCATION', 'INVALID_CONFIG'); } if (typeof baseUrl !== 'string') { return new case_plugin_base_1.CaseConfigurationError(`Expected the baseurl to be a string, but it was '${typeof authToken}'`, 'DONT_ADD_LOCATION', 'INVALID_CONFIG'); } if (authToken === undefined && basicAuth === undefined) { return new case_plugin_base_1.CaseConfigurationError("Can't access a broker without an authorization token or basic auth set. Set the environment variable CASE_BROKER_CI_TOKEN", 'DONT_ADD_LOCATION', 'INVALID_CONFIG'); } if (authToken !== undefined) { if (authToken === '') { return new case_plugin_base_1.CaseConfigurationError("Can't access a broker without an authorization token. Set the environment variable CASE_BROKER_CI_TOKEN", 'DONT_ADD_LOCATION', 'INVALID_CONFIG'); } if (typeof authToken !== 'string') { return new case_plugin_base_1.CaseConfigurationError(`Expected the authToken to be a string, but it was '${typeof authToken}'`, 'DONT_ADD_LOCATION', 'INVALID_CONFIG'); } } if (authToken === undefined && basicAuth === undefined) { return new case_plugin_base_1.CaseConfigurationError("Can't access a broker without an authorization token or basic auth set. Set the environment variable CASE_BROKER_CI_TOKEN", 'DONT_ADD_LOCATION', 'INVALID_CONFIG'); } return undefined; }; const makeBrokerApi = (configContext) => { const authToken = configContext['_case:currentRun:context:brokerCiAccessToken']; const baseUrl = configContext['_case:currentRun:context:brokerBaseUrl']; const basicAuth = configContext['_case:currentRun:context:brokerBasicAuth']; const auth = (authToken ?? basicAuth); const server = (0, axios_1.makeAxiosConnector)(trimSlash(baseUrl), auth); const err = validatePrecondition(baseUrl, authToken, basicAuth); return { publishContract: (contract, version, branch, logContext) => { if (err !== undefined) throw err; logContext.logger.debug(`Publishing contract for ${contract.description.consumerName}@${version} -> ${contract.description.providerName} to broker at ${baseUrl}`); return server .authedPost('/contracts/publish', { pacticipantName: contract.description.consumerName, pacticipantVersionNumber: version, ...(branch !== false ? { branch } : {}), tags: [], contracts: [ { consumerName: contract.description.consumerName, providerName: contract.description.providerName, // We claim that this is a pact spec, because otherwise the pact // broker won't accept it. However, this value has no effect on the // broker's behaviour. // TODO: Replace this with '_case::contract' when the broker supports it specification: 'pact', contentType: 'application/json', content: Buffer.from(JSON.stringify(contract)).toString('base64'), }, ], }, logContext) .then((data) => { (0, logNotices_1.logNotices)(data.notices, logContext); }); }, publishVerificationResults: (contract, success, providerVersion, branch, logContext) => Promise.resolve() .then(() => { if (err !== undefined) throw err; if (contract._links === undefined) { logContext.logger.maintainerDebug('No links section in the following contract:', contract); throw new case_plugin_base_1.CaseConfigurationError(`Trying to publish verification status for the contract between '${contract.description.consumerName}' and '${contract.description.providerName}', but it doesn't have a links section.\n\nThis usually means it wasn't downloaded from a broker. We can't publish verification status for non-brokered contracts.`, 'DONT_ADD_LOCATION', 'NON_BROKERED_CONTRACT'); } if (contract._links['pb:publish-verification-results'] === undefined) { logContext.logger.maintainerDebug('No pb:publish-verification-result section in the following contract:', contract); throw new case_plugin_base_1.CaseConfigurationError(`The contract between '${contract.description.consumerName}' and '${contract.description.providerName}' doesn't have a publish verification results URL, so we can't publish it.`, 'DONT_ADD_LOCATION', 'NON_BROKERED_CONTRACT'); } }) .then(() => (0, axios_1.makeAxiosConnector)(contract._links['pb:publish-verification-results'].href, auth) .authedPost('', { providerApplicationVersion: providerVersion, success, ...(branch !== false ? { branch } : {}), verifiedByImplementation: 'ContractCase', verifiedByVersion: versionString_1.caseVersion, executionDate: new Date(Date.now()).toISOString(), tags: [], }, logContext) .then((t) => t)), downloadContract: (url, logContext) => { if (err !== undefined) throw err; return (0, axios_1.makeAxiosConnector)(url, auth).authedGet('', {}, logContext); }, urlsForVerification: (serviceName, logContext) => { if (err !== undefined) throw err; logContext.logger.debug(`Finding contracts to verify for service '${serviceName}' on broker at ${baseUrl}`); const path = `/pacts/provider/${encodeURIComponent(serviceName)}/for-verification`; logContext.logger.maintainerDebug(`forVerification path is: ${path}`); return server .authedPost(path, { consumerVersionSelectors: [ { mainBranch: true, }, { deployedOrReleased: true, }, { latest: true }, ], providerVersionTags: ['main'], }, logContext) .then((d) => { logContext.logger.deepMaintainerDebug(`Pacts for verification responded with`, JSON.stringify(d, undefined, 2)); const numPacts = d._embedded.pacts.length; logContext.logger.debug(`Broker returned ${numPacts} URLs to possible contracts for verification`); return d._embedded.pacts.map((contract) => contract._links.self); }); }, canDeploy: (serviceName, serviceVersion, environment, logContext) => { if (err !== undefined) throw err; logContext.logger.debug(`Asking if it's safe to deploy '${serviceName}' at version '${serviceVersion}' to '${environment}', using broker at ${baseUrl}`); return server .authedGet('/can-i-deploy', { pacticipant: serviceName, version: serviceVersion, environment, }, logContext) .then((data) => { logContext.logger.maintainerDebug(`Can-I-Deploy returned with`, JSON.stringify(data, undefined, 2)); if (data.summary.deployable === true && (data.summary.failed !== 0 || data.summary.unknown !== 0)) { logContext.logger.warn(`!!!! The broker is not behaving as it is documented !!!!`); logContext.logger.warn(`The broker said it was safe to deploy but had a non zero count of services in state failed (${data.summary.failed}) or unknown (${data.summary.unknown})`); logContext.logger.warn(`Please raise this with the maintainers of the broker you are using`); } if (data.summary.deployable !== true) { logContext.logger.debug("The broker said it's not safe to deploy. Here's the matrix from the broker, which may be useful:", data.matrix); } (0, logNotices_1.logNotices)(data.notices, logContext); return { deployable: data.summary.deployable === true, reason: `${data.summary.reason}\nsuccess (${data.summary.success}), failed (${data.summary.failed}), unknown / never-verified (${data.summary.unknown}) services`, }; }); }, }; }; exports.makeBrokerApi = makeBrokerApi; //# sourceMappingURL=broker.js.map