Skip to content

Commit 593f028

Browse files
authored
Merge pull request #339 from pact-foundation/fix/ignore-some-args
Fix/ignore some args
2 parents e77a821 + dc5217d commit 593f028

File tree

9 files changed

+181
-83
lines changed

9 files changed

+181
-83
lines changed

src/ffi/argumentMapper/index.ts

Lines changed: 0 additions & 49 deletions
This file was deleted.

src/ffi/argumentMapper/types.ts

Lines changed: 0 additions & 11 deletions
This file was deleted.

src/verifier/arguments.ts renamed to src/verifier/argumentMapper/arguments.ts

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,21 @@
11
import url = require('url');
22

3-
import { ArgMapping } from '../ffi/argumentMapper/types';
4-
import { ConsumerVersionSelector, VerifierOptions } from './types';
3+
import { ArgMapping, IgnoreOptionCombinations } from './types';
4+
import {
5+
ConsumerVersionSelector,
6+
InternalPactVerifierOptions,
7+
VerifierOptions,
8+
} from '../types';
59

6-
import { getUriType } from './filesystem';
7-
import { LogLevel } from '../logger/types';
10+
import { getUriType } from '../filesystem';
11+
import { LogLevel } from '../../logger/types';
812

9-
type DeprecatedVerifierOptions = {
10-
format?: 'json' | 'xml' | 'progress' | 'RspecJunitFormatter';
11-
out?: string;
12-
customProviderHeaders?: string[];
13-
verbose?: boolean;
14-
monkeypatch?: string;
15-
logDir?: string;
16-
};
17-
18-
// These are arguments that are on the PactJS object that we don't need to use
19-
export const ignoredArguments = [
13+
/**
14+
* These are arguments that are on the PactJS object that we don't need to use
15+
* An array of strings for options to ignore (typed as strings and not `keyof VerifierOptions`,
16+
* because pact-js puts extra options on the object that aren't in the core VerifierOptions)
17+
*/
18+
export const ignoredArguments: Array<string> = [
2019
'requestFilter',
2120
'stateHandlers',
2221
'messageProviders',
@@ -26,9 +25,15 @@ export const ignoredArguments = [
2625
'validateSSL',
2726
];
2827

29-
export const argMapping: ArgMapping<
30-
VerifierOptions & DeprecatedVerifierOptions
31-
> = {
28+
export const ignoreOptionCombinations: IgnoreOptionCombinations<VerifierOptions> =
29+
{
30+
enablePending: { ifNotSet: 'pactBrokerUrl' },
31+
consumerVersionSelectors: { ifNotSet: 'pactBrokerUrl' },
32+
consumerVersionTags: { ifNotSet: 'pactBrokerUrl' },
33+
publishVerificationResult: { ifNotSet: 'pactBrokerUrl' },
34+
};
35+
36+
export const argMapping: ArgMapping<InternalPactVerifierOptions> = {
3237
providerBaseUrl: (providerBaseUrl: string) => {
3338
const u = url.parse(providerBaseUrl);
3439
return u && u.port && u.hostname
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import { FunctionMapping } from './types';
2+
import logger from '../../logger';
3+
import { InternalPactVerifierOptions } from '../types';
4+
import {
5+
argMapping,
6+
ignoredArguments,
7+
ignoreOptionCombinations,
8+
} from './arguments';
9+
10+
/**
11+
* This function maps arguments from the Verifier to the Rust core's verifier arguments
12+
*
13+
* @internal
14+
*
15+
* @param options The actual options passed by the user
16+
* @returns An array of strings to past to the Rust core verifier
17+
*/
18+
export const argumentMapper = (
19+
options: InternalPactVerifierOptions
20+
): string[] =>
21+
(Object.keys(options) as Array<keyof InternalPactVerifierOptions>)
22+
.filter((k) => {
23+
const ignoreCondition = ignoreOptionCombinations[k];
24+
if (ignoreCondition !== undefined) {
25+
logger.trace(
26+
`The argument mapper has an ignored combination for '${k}'`
27+
);
28+
// We might have multiple ways to ignore option combinations in the future
29+
if (
30+
ignoreCondition.ifNotSet &&
31+
options[ignoreCondition.ifNotSet] === undefined
32+
) {
33+
logger.warn(
34+
`Ignoring option '${k}' because it is invalid without '${ignoreCondition.ifNotSet}' also being set. This may indicate an error in your configuration`
35+
);
36+
return false;
37+
}
38+
logger.trace(`But it was not ignored: '${k}'`);
39+
}
40+
return true;
41+
})
42+
.map((key: keyof InternalPactVerifierOptions) => {
43+
// We pull these out, because TypeScript doesn't like to
44+
// reason that argMapping[key] is a constant value.
45+
// So, we need to pull it out to be able to say
46+
// `if('someKey' in thisMapping)` and retain type checking
47+
const thisMapping = argMapping[key];
48+
const thisValue = options[key];
49+
50+
if (!thisMapping) {
51+
if (!ignoredArguments.includes(key)) {
52+
logger.error(`Pact-core is ignoring unknown option '${key}'`);
53+
}
54+
return [];
55+
}
56+
if (thisValue === undefined) {
57+
logger.warn(
58+
`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`
59+
);
60+
return [];
61+
}
62+
if ('warningMessage' in thisMapping) {
63+
logger.warn(thisMapping.warningMessage);
64+
return [];
65+
}
66+
if ('arg' in thisMapping) {
67+
switch (thisMapping.mapper) {
68+
case 'string':
69+
return [thisMapping.arg, `${thisValue}`];
70+
case 'flag':
71+
return thisValue ? [thisMapping.arg] : [];
72+
default:
73+
logger.pactCrash(
74+
`Option mapper for '${key}' maps to '${thisMapping.arg}' with unknown mapper type '${thisMapping.mapper}'`
75+
);
76+
return [];
77+
}
78+
}
79+
if (typeof thisMapping === 'function') {
80+
return (thisMapping as FunctionMapping<unknown>)(thisValue);
81+
}
82+
logger.pactCrash(
83+
`The option mapper completely failed to find a mapping for '${key}'.`
84+
);
85+
return [];
86+
})
87+
// This can be replaced with .flat() when node 10 is EOL
88+
.reduce((acc: string[], current: string[]) => [...acc, ...current], []);
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
type BasicMapping = {
2+
arg: string;
3+
mapper: 'string' | 'flag';
4+
};
5+
6+
type IgnoreAndWarn = {
7+
warningMessage: string;
8+
};
9+
10+
/** @internal */
11+
export type FunctionMapping<T> = (arg: NonNullable<T>) => string[];
12+
13+
/** @internal */
14+
export type ArgMapping<PactOptions> = {
15+
[Key in keyof PactOptions]-?:
16+
| BasicMapping
17+
| IgnoreAndWarn
18+
| FunctionMapping<PactOptions[Key]>;
19+
};
20+
21+
/** @internal */
22+
export type IgnoreOptionCombinations<T> = {
23+
[Key in keyof Partial<T>]: { ifNotSet: keyof T };
24+
};

src/verifier/nativeVerifier.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import { VerifierOptions } from './types';
22
import { getFfiLib } from '../ffi';
3-
import logger from '../logger';
4-
import { argMapping, ignoredArguments } from './arguments';
5-
import { argumentMapper } from '../ffi/argumentMapper';
3+
import logger, { setLogLevel } from '../logger';
4+
import { argumentMapper } from './argumentMapper';
65

76
const VERIFICATION_SUCCESSFUL = 0;
87
const VERIFICATION_FAILED = 1;
@@ -12,9 +11,12 @@ const INVALID_ARGUMENTS = 4;
1211

1312
export const verify = (opts: VerifierOptions): Promise<string> => {
1413
const verifierLib = getFfiLib(opts.logLevel);
14+
if (opts.logLevel) {
15+
setLogLevel(opts.logLevel);
16+
}
1517
// Todo: probably separate out the sections of this logic into separate promises
1618
return new Promise<string>((resolve, reject) => {
17-
const request = argumentMapper(argMapping, opts, ignoredArguments)
19+
const request = argumentMapper(opts)
1820
.map((s) => s.replace('\n', ''))
1921
.join('\n');
2022

src/verifier/types.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,21 @@ export interface VerifierOptions {
3131
logLevel?: LogLevel;
3232
disableSslVerification?: boolean;
3333
}
34+
35+
/** These are the deprecated verifier options, removed prior to this verison,
36+
* but it's useful to know what they were so we can potentially map or warn.
37+
*/
38+
type DeprecatedVerifierOptions = {
39+
format?: 'json' | 'xml' | 'progress' | 'RspecJunitFormatter';
40+
out?: string;
41+
customProviderHeaders?: string[];
42+
verbose?: boolean;
43+
monkeypatch?: string;
44+
logDir?: string;
45+
};
46+
47+
/** Helper type for the mapper to reason about the options we want to be able to handle.
48+
* Not exposed, because we only want to expose the current VerifierOptions to the user
49+
* @internal */
50+
export type InternalPactVerifierOptions = VerifierOptions &
51+
DeprecatedVerifierOptions;

test/verifier.integration.spec.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,26 @@ describe('Verifier Integration Spec', () => {
4646
],
4747
}).verify()
4848
).to.eventually.be.fulfilled);
49+
context('with some broker args but no broker URL', () => {
50+
it('should return a successful promise', () =>
51+
expect(
52+
verifierFactory({
53+
...DEFAULT_ARGS,
54+
providerBaseUrl: providerBaseUrl,
55+
pactUrls: [
56+
path.resolve(
57+
__dirname,
58+
'integration/me-they-weird path-success.json'
59+
),
60+
],
61+
// These don't mean anything without a broker URL, but should not fail the verification
62+
enablePending: true,
63+
consumerVersionSelectors: [{ latest: true }],
64+
consumerVersionTags: ['main'],
65+
publishVerificationResult: true,
66+
}).verify()
67+
).to.eventually.be.fulfilled);
68+
});
4969
});
5070

5171
context('without provider states', () => {

tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
"skipLibCheck": true,
1717
"strictNullChecks": true,
1818
"strict": true,
19-
"noUnusedLocals": true
19+
"noUnusedLocals": true,
20+
"stripInternal": true
2021
},
2122
"include": ["**/*.ts"],
2223
"exclude": ["node_modules", "**/*.spec.ts", "**/__mocks__"],

0 commit comments

Comments
 (0)