-
Notifications
You must be signed in to change notification settings - Fork 80
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: update the argument validation and function mapping for ffi…
… verification
- Loading branch information
Showing
11 changed files
with
536 additions
and
625 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,120 +1,225 @@ | ||
import url = require('url'); | ||
|
||
import { ArgMapping, IgnoreOptionCombinations } from './types'; | ||
import { | ||
ConsumerVersionSelector, | ||
InternalPactVerifierOptions, | ||
VerifierOptions, | ||
} from '../types'; | ||
|
||
import { getUriType } from '../filesystem'; | ||
import { LogLevel } from '../../logger/types'; | ||
|
||
/** | ||
* These are arguments that are on the PactJS object that we don't need to use | ||
* An array of strings for options to ignore (typed as strings and not `keyof VerifierOptions`, | ||
* because pact-js puts extra options on the object that aren't in the core VerifierOptions) | ||
*/ | ||
export const ignoredArguments: Array<string> = [ | ||
'requestFilter', | ||
'stateHandlers', | ||
'messageProviders', | ||
'changeOrigin', | ||
'beforeEach', | ||
'afterEach', | ||
'validateSSL', | ||
]; | ||
|
||
export const ignoreOptionCombinations: IgnoreOptionCombinations<VerifierOptions> = | ||
{ | ||
enablePending: { ifNotSet: 'pactBrokerUrl' }, | ||
consumerVersionSelectors: { ifNotSet: 'pactBrokerUrl' }, | ||
consumerVersionTags: { ifNotSet: 'pactBrokerUrl' }, | ||
publishVerificationResult: { ifNotSet: 'pactBrokerUrl' }, | ||
}; | ||
|
||
export const argMapping: ArgMapping<InternalPactVerifierOptions> = { | ||
buildUrl: { arg: '--build-url', mapper: 'string' }, | ||
providerBranch: { arg: '--provider-branch', mapper: 'string' }, | ||
providerBaseUrl: (providerBaseUrl: string) => { | ||
const u = url.parse(providerBaseUrl); | ||
return u && u.port && u.hostname | ||
? ['--port', u.port, '--hostname', u.hostname] | ||
: []; | ||
import logger from '../../logger'; | ||
import fs = require('fs'); | ||
|
||
import { FnMapping, FnValidationStatus } from './types'; | ||
import { InternalPactVerifierOptions } from '../types'; | ||
|
||
import { FfiVerificationFunctions } from '../../ffi/types'; | ||
import { URL } from 'url'; | ||
|
||
const DEFAULT_TIMEOUT = 30000; | ||
|
||
const objArrayToStringArray = (obj: unknown[]) => { | ||
return obj.map((o) => JSON.stringify(o)); | ||
}; | ||
|
||
export type IgnoredFfiFunctions = { | ||
pactffiVerifierNewForApplication: 1; | ||
pactffiVerifierExecute: 1; | ||
pactffiVerifierShutdown: 1; | ||
}; | ||
|
||
export type MergedFfiSourceFunctions = { | ||
pactffiVerifierAddFileSource: 1; | ||
pactffiVerifierUrlSource: 1; | ||
}; | ||
|
||
export type RequiredFfiVerificationFunctions = Omit< | ||
FfiVerificationFunctions, | ||
keyof (IgnoredFfiFunctions & MergedFfiSourceFunctions) | ||
>; | ||
|
||
export const ffiFnMapping: FnMapping< | ||
RequiredFfiVerificationFunctions, | ||
InternalPactVerifierOptions | ||
> = { | ||
pactffiVerifierAddCustomHeader: { | ||
validateAndExecute(ffi, handle, options) { | ||
if (options.customProviderHeaders) { | ||
if (options.customProviderHeaders) { | ||
Object.entries(options.customProviderHeaders).forEach( | ||
([key, value]) => { | ||
ffi.pactffiVerifierAddCustomHeader(handle, key, value); | ||
} | ||
); | ||
} | ||
return { status: FnValidationStatus.SUCCESS }; | ||
} | ||
|
||
return { status: FnValidationStatus.IGNORE }; | ||
}, | ||
}, | ||
providerStatesSetupBody: { | ||
warningMessage: 'providerStatesSetupBody is not valid for the CLI', | ||
pactffiVerifierAddDirectorySource: { | ||
validateAndExecute(ffi, handle, options) { | ||
const messages: string[] = []; | ||
|
||
if (options.pactUrls) { | ||
options.pactUrls.forEach((file) => { | ||
logger.debug(`checking source type of given pactUrl: ${file}`); | ||
try { | ||
const u = new URL(file); | ||
|
||
if (u.hostname) { | ||
logger.debug(`adding ${file} as a Url source`); | ||
ffi.pactffiVerifierUrlSource( | ||
handle, | ||
file, | ||
options.pactBrokerUsername || | ||
process.env.PACT_BROKER_USERNAME || | ||
'', | ||
options.pactBrokerPassword || | ||
process.env.PACT_BROKER_PASSWORD || | ||
'', | ||
options.pactBrokerToken || process.env.PACT_BROKER_TOKEN || '' | ||
); | ||
} | ||
} catch { | ||
messages.push(`${file} is not a valid URL`); | ||
} | ||
|
||
try { | ||
const f = fs.lstatSync(file); | ||
|
||
if (f.isDirectory()) { | ||
logger.debug(`adding ${file} as Directory source`); | ||
ffi.pactffiVerifierAddDirectorySource(handle, file); | ||
} else if (f.isFile() || f.isSymbolicLink()) { | ||
logger.debug(`adding ${file} as File source`); | ||
ffi.pactffiVerifierAddFileSource(handle, file); | ||
} | ||
} catch { | ||
messages.push( | ||
`'${file}' does not exist, or is not a file or directory` | ||
); | ||
} | ||
}); | ||
|
||
return { status: FnValidationStatus.SUCCESS }; | ||
} | ||
|
||
if (messages.length > 0) { | ||
return { status: FnValidationStatus.FAIL, messages }; | ||
} | ||
|
||
return { status: FnValidationStatus.IGNORE }; | ||
}, | ||
}, | ||
providerStatesSetupTeardown: { | ||
warningMessage: 'providerStatesSetupTeardown is not valid for the CLI', | ||
pactffiVerifierBrokerSourceWithSelectors: { | ||
validateAndExecute(ffi, handle, opts) { | ||
const brokerUrl = opts.pactBrokerUrl || process.env.PACT_BROKER_BASE_URL; | ||
|
||
if (brokerUrl && opts.provider) { | ||
ffi.pactffiVerifierBrokerSourceWithSelectors( | ||
handle, | ||
brokerUrl, | ||
opts.pactBrokerUsername || process.env.PACT_BROKER_USERNAME || '', | ||
opts.pactBrokerPassword || process.env.PACT_BROKER_PASSWORD || '', | ||
opts.pactBrokerToken || process.env.PACT_BROKER_TOKEN || '', | ||
opts.enablePending || false, | ||
opts.includeWipPactsSince || '', | ||
opts.providerVersionTags || [], | ||
opts.providerBranch || '', | ||
opts.consumerVersionSelectors | ||
? objArrayToStringArray(opts.consumerVersionSelectors) | ||
: [], | ||
opts.consumerVersionTags || [] | ||
); | ||
return { status: FnValidationStatus.SUCCESS }; | ||
} | ||
return { status: FnValidationStatus.IGNORE }; | ||
}, | ||
}, | ||
logLevel: (logLevel: LogLevel) => ['--loglevel', logLevel], | ||
provider: { arg: '--provider-name', mapper: 'string' }, | ||
pactUrls: (pactUrls: string[]) => | ||
pactUrls.reduce<Array<string>>((acc: Array<string>, uri: string) => { | ||
switch (getUriType(uri)) { | ||
case 'URL': | ||
return [...acc, '--url', uri]; | ||
case 'DIRECTORY': | ||
return [...acc, '--dir', uri]; | ||
case 'FILE': | ||
return [...acc, '--file', uri]; | ||
default: | ||
return acc; | ||
pactffiVerifierSetConsumerFilters: { | ||
validateAndExecute(ffi, handle, options) { | ||
if (options.consumerFilters && options.consumerFilters.length > 0) { | ||
ffi.pactffiVerifierSetConsumerFilters(handle, options.consumerFilters); | ||
return { status: FnValidationStatus.SUCCESS }; | ||
} | ||
}, []), | ||
pactBrokerUrl: { arg: '--broker-url', mapper: 'string' }, | ||
pactBrokerUsername: { arg: '--user', mapper: 'string' }, | ||
pactBrokerPassword: { arg: '--password', mapper: 'string' }, | ||
pactBrokerToken: { arg: '--token', mapper: 'string' }, | ||
consumerVersionTags: (tags: string | string[]) => [ | ||
'--consumer-version-tags', | ||
Array.isArray(tags) ? tags.join(',') : tags, | ||
], | ||
providerVersionTags: (tags: string | string[]) => [ | ||
'--provider-tags', | ||
Array.isArray(tags) ? tags.join(',') : tags, | ||
], | ||
providerStatesSetupUrl: { arg: '--state-change-url', mapper: 'string' }, | ||
|
||
providerVersion: { arg: '--provider-version', mapper: 'string' }, | ||
|
||
includeWipPactsSince: { arg: '--include-wip-pacts-since', mapper: 'string' }, | ||
consumerVersionSelectors: (selectors: ConsumerVersionSelector[]) => | ||
selectors | ||
.map((s: ConsumerVersionSelector) => [ | ||
'--consumer-version-selectors', | ||
JSON.stringify(s), | ||
]) // This reduce can be replaced simply with .flat() when node 10 is EOL | ||
.reduce((acc: string[], current: string[]) => [...acc, ...current], []), | ||
publishVerificationResult: { arg: '--publish', mapper: 'flag' }, | ||
enablePending: { arg: '--enable-pending', mapper: 'flag' }, | ||
timeout: { arg: '--request-timeout', mapper: 'string' }, | ||
disableSslVerification: { arg: '--disable-ssl-verification', mapper: 'flag' }, | ||
// We should support these in the future, I think | ||
format: { | ||
warningMessage: | ||
"All output is now on standard out, setting 'format' has no effect", | ||
return { status: FnValidationStatus.IGNORE }; | ||
}, | ||
}, | ||
out: { | ||
warningMessage: | ||
"All output is now on standard out, setting 'out' has no effect", | ||
pactffiVerifierSetFilterInfo: { | ||
validateAndExecute(ffi, handle, options) { | ||
if ( | ||
process.env.PACT_DESCRIPTION || | ||
process.env.PACT_PROVIDER_STATE || | ||
process.env.PACT_PROVIDER_NO_STATE | ||
) { | ||
const filterDescription = process.env.PACT_DESCRIPTION || ''; | ||
const filterState = process.env.PACT_PROVIDER_STATE || ''; | ||
const filterNoState = process.env.PACT_PROVIDER_NO_STATE ? true : false; | ||
|
||
ffi.pactffiVerifierSetFilterInfo( | ||
handle, | ||
filterDescription, | ||
filterState, | ||
filterNoState | ||
); | ||
|
||
return { status: FnValidationStatus.SUCCESS }; | ||
} | ||
|
||
return { status: FnValidationStatus.IGNORE }; | ||
}, | ||
}, | ||
// Deprecate | ||
logDir: { | ||
warningMessage: | ||
'Setting logDir is deprecated as all logs are now on standard out', | ||
pactffiVerifierSetProviderInfo: { | ||
validateAndExecute(ffi, handle, options) { | ||
const uri = new URL(options.providerBaseUrl); | ||
|
||
ffi.pactffiVerifierSetProviderInfo( | ||
handle, | ||
options.provider || '', | ||
uri.protocol.split(':')[0], | ||
uri.hostname, | ||
parseInt(uri.port, 10), | ||
uri.pathname | ||
); | ||
|
||
return { status: FnValidationStatus.SUCCESS }; | ||
}, | ||
}, | ||
verbose: { | ||
warningMessage: | ||
"Verbose mode is deprecated and has no effect, please use logLevel: 'DEBUG' instead", | ||
pactffiVerifierSetProviderState: { | ||
validateAndExecute(ffi, handle, options) { | ||
if (options.providerStatesSetupUrl) { | ||
ffi.pactffiVerifierSetProviderState( | ||
handle, | ||
options.providerStatesSetupUrl, | ||
true, | ||
true | ||
); | ||
return { status: FnValidationStatus.SUCCESS }; | ||
} | ||
|
||
return { status: FnValidationStatus.IGNORE }; | ||
}, | ||
}, | ||
monkeypatch: { | ||
warningMessage: | ||
'The undocumented feature monkeypatch is no more, please file an issue if you were using it and need this functionality', | ||
pactffiVerifierSetPublishOptions: { | ||
validateAndExecute(ffi, handle, options) { | ||
if (options.publishVerificationResult && options.providerVersion) { | ||
ffi.pactffiVerifierSetPublishOptions( | ||
handle, | ||
options.providerVersion, | ||
options.buildUrl || '', | ||
options.providerVersionTags || [], | ||
options.providerBranch || '' | ||
); | ||
return { status: FnValidationStatus.SUCCESS }; | ||
} | ||
return { status: FnValidationStatus.IGNORE }; | ||
}, | ||
}, | ||
customProviderHeaders: { | ||
warningMessage: | ||
'customProviderHeaders have been removed. This functionality is provided by request filters in a much more flexible way', | ||
pactffiVerifierSetVerificationOptions: { | ||
validateAndExecute(ffi, handle, opts) { | ||
if (opts.disableSslVerification || opts.timeout) { | ||
ffi.pactffiVerifierSetVerificationOptions( | ||
handle, | ||
opts.disableSslVerification || false, | ||
opts.timeout || DEFAULT_TIMEOUT | ||
); | ||
return { status: FnValidationStatus.SUCCESS }; | ||
} | ||
|
||
return { status: FnValidationStatus.IGNORE }; | ||
}, | ||
}, | ||
}; |
Oops, something went wrong.