Skip to content

Commit

Permalink
fix: Warn if broker arguments are set without a broker URL and ignore…
Browse files Browse the repository at this point in the history
… the argument (fixes pact-foundation/pact-js#760)
  • Loading branch information
TimothyJones committed Oct 16, 2021
1 parent 9f73355 commit 17d3eb1
Show file tree
Hide file tree
Showing 8 changed files with 165 additions and 86 deletions.
49 changes: 0 additions & 49 deletions src/ffi/argumentMapper/index.ts

This file was deleted.

17 changes: 0 additions & 17 deletions src/ffi/argumentMapper/types.ts

This file was deleted.

89 changes: 89 additions & 0 deletions src/verifier/argumentMapper/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { ArgMapping, FunctionMapping, IgnoreOptionCombinations } from './types';
import logger from '../../logger';
import { InternalPactVerifierOptions } from '../types';

/**
* This function maps arguments from the Verifier to the Rust core's verifier arguments
*
* @internal
*
* @param argMapping The argument mapping for the internal verifier
* @param options The actual options passed by the user
* @param ignoredArguments An array of strings for options to ignore (this is strings, because pact-js might put options on the object that we don't need)
* @param ignoreOptionCombinations Describes some options that we might want to ignore if certain other options are not set
* @returns An array of strings to past to the Rust core verifier
*/
export const argumentMapper = (
argMapping: ArgMapping<InternalPactVerifierOptions>,
options: InternalPactVerifierOptions,
ignoredArguments: Array<string>,
ignoreOptionCombinations: IgnoreOptionCombinations<InternalPactVerifierOptions>
): string[] =>
(Object.keys(options) as Array<keyof InternalPactVerifierOptions>)
.filter((k) => {
const ignoreCondition = ignoreOptionCombinations[k];
if (ignoreCondition !== undefined) {
logger.trace(
`The argument mapper has an ignored combination for '${k}'`
);
// We might have multiple ways to ignore option combinations in the future
if (
ignoreCondition.ifNotSet &&
options[ignoreCondition.ifNotSet] === undefined
) {
logger.warn(
`Ignoring option '${k}' because it is invalid without '${ignoreCondition.ifNotSet}' also being set. This may indicate an error in your configuration`
);
return false;
}
logger.trace(`But it was not ignored: '${k}'`);
}
return true;
})
.map((key: keyof InternalPactVerifierOptions) => {
// We pull these out, because TypeScript doesn't like to
// reason that argMapping[key] is a constant value.
// So, we need to pull it out to be able to say
// `if('someKey' in thisMapping)` and retain type checking
const thisMapping = argMapping[key];
const thisValue = options[key];

if (!thisMapping) {
if (!ignoredArguments.includes(key)) {
logger.error(`Pact-core is ignoring unknown option '${key}'`);
}
return [];
}
if (thisValue === undefined) {
logger.warn(
`The Verifier option '${key}' was was explicitly set to undefined and will be ignored. This may indicate an error in your config. Remove the option entirely to prevent this warning`
);
return [];
}
if ('warningMessage' in thisMapping) {
logger.warn(thisMapping.warningMessage);
return [];
}
if ('arg' in thisMapping) {
switch (thisMapping.mapper) {
case 'string':
return [thisMapping.arg, `${thisValue}`];
case 'flag':
return thisValue ? [thisMapping.arg] : [];
default:
logger.pactCrash(
`Option mapper for '${key}' maps to '${thisMapping.arg}' with unknown mapper type '${thisMapping.mapper}'`
);
return [];
}
}
if (typeof thisMapping === 'function') {
return (thisMapping as FunctionMapping<unknown>)(thisValue);
}
logger.pactCrash(
`The option mapper completely failed to find a mapping for '${key}'.`
);
return [];
})
// This can be replaced with .flat() when node 10 is EOL
.reduce((acc: string[], current: string[]) => [...acc, ...current], []);
24 changes: 24 additions & 0 deletions src/verifier/argumentMapper/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
type BasicMapping = {
arg: string;
mapper: 'string' | 'flag';
};

type IgnoreAndWarn = {
warningMessage: string;
};

/** @internal */
export type FunctionMapping<T> = (arg: NonNullable<T>) => string[];

/** @internal */
export type ArgMapping<PactOptions> = {
[Key in keyof PactOptions]-?:
| BasicMapping
| IgnoreAndWarn
| FunctionMapping<PactOptions[Key]>;
};

/** @internal */
export type IgnoreOptionCombinations<T> = {
[Key in keyof Partial<T>]: { ifNotSet: keyof T };
};
31 changes: 16 additions & 15 deletions src/verifier/arguments.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,17 @@
import url = require('url');

import { ArgMapping } from '../ffi/argumentMapper/types';
import { ConsumerVersionSelector, VerifierOptions } from './types';
import { ArgMapping, IgnoreOptionCombinations } from './argumentMapper/types';
import {
ConsumerVersionSelector,
InternalPactVerifierOptions,
VerifierOptions,
} from './types';

import { getUriType } from './filesystem';
import { LogLevel } from '../logger/types';

type DeprecatedVerifierOptions = {
format?: 'json' | 'xml' | 'progress' | 'RspecJunitFormatter';
out?: string;
customProviderHeaders?: string[];
verbose?: boolean;
monkeypatch?: string;
logDir?: string;
};

// These are arguments that are on the PactJS object that we don't need to use
export const ignoredArguments = [
export const ignoredArguments: Array<string> = [
'requestFilter',
'stateHandlers',
'messageProviders',
Expand All @@ -26,9 +21,15 @@ export const ignoredArguments = [
'validateSSL',
];

export const argMapping: ArgMapping<
VerifierOptions & DeprecatedVerifierOptions
> = {
export const ignoreOptionCombinations: IgnoreOptionCombinations<VerifierOptions> =
{
enablePending: { ifNotSet: 'pactBrokerUrl' },
consumerVersionSelectors: { ifNotSet: 'pactBrokerUrl' },
consumerVersionTags: { ifNotSet: 'pactBrokerUrl' },
publishVerificationResult: { ifNotSet: 'pactBrokerUrl' },
};

export const argMapping: ArgMapping<InternalPactVerifierOptions> = {
providerBaseUrl: (providerBaseUrl: string) => {
const u = url.parse(providerBaseUrl);
return u && u.port && u.hostname
Expand Down
20 changes: 16 additions & 4 deletions src/verifier/nativeVerifier.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import { VerifierOptions } from './types';
import { getFfiLib } from '../ffi';
import logger from '../logger';
import { argMapping, ignoredArguments } from './arguments';
import { argumentMapper } from '../ffi/argumentMapper';
import logger, { setLogLevel } from '../logger';
import {
argMapping,
ignoredArguments,
ignoreOptionCombinations,
} from './arguments';
import { argumentMapper } from './argumentMapper';

const VERIFICATION_SUCCESSFUL = 0;
const VERIFICATION_FAILED = 1;
Expand All @@ -12,9 +16,17 @@ const INVALID_ARGUMENTS = 4;

export const verify = (opts: VerifierOptions): Promise<string> => {
const verifierLib = getFfiLib(opts.logLevel);
if (opts.logLevel) {
setLogLevel(opts.logLevel);
}
// Todo: probably separate out the sections of this logic into separate promises
return new Promise<string>((resolve, reject) => {
const request = argumentMapper(argMapping, opts, ignoredArguments)
const request = argumentMapper(
argMapping,
opts,
ignoredArguments,
ignoreOptionCombinations
)
.map((s) => s.replace('\n', ''))
.join('\n');

Expand Down
18 changes: 18 additions & 0 deletions src/verifier/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,21 @@ export interface VerifierOptions {
logLevel?: LogLevel;
disableSslVerification?: boolean;
}

/** These are the deprecated verifier options, removed prior to this verison,
* but it's useful to know what they were so we can potentially map or warn.
*/
type DeprecatedVerifierOptions = {
format?: 'json' | 'xml' | 'progress' | 'RspecJunitFormatter';
out?: string;
customProviderHeaders?: string[];
verbose?: boolean;
monkeypatch?: string;
logDir?: string;
};

/** Helper type for the mapper to reason about the options we want to be able to handle.
* Not exposed, because we only want to expose the current VerifierOptions to the user
* @internal */
export type InternalPactVerifierOptions = VerifierOptions &
DeprecatedVerifierOptions;
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
"skipLibCheck": true,
"strictNullChecks": true,
"strict": true,
"noUnusedLocals": true
"noUnusedLocals": true,
"stripInternal": true
},
"include": ["**/*.ts"],
"exclude": ["node_modules", "**/*.spec.ts", "**/__mocks__"],
Expand Down

0 comments on commit 17d3eb1

Please sign in to comment.