Skip to content

Commit

Permalink
feat: Added publishing swagger files
Browse files Browse the repository at this point in the history
  • Loading branch information
pawfa committed Mar 18, 2022
1 parent cc8b641 commit 6b4d650
Show file tree
Hide file tree
Showing 16 changed files with 340 additions and 79 deletions.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,13 @@ Publish contracts
Options:
--url Url to judge-d instance [string] [required]
--pactsDir Path to directory with pacts [string] [required]
--serviceName Service name [string] [required]
--serviceVersion Service version [string] [required]
Required one of:
--pactsDir Path to directory with pacts [string]
--swaggerFile Path to swagger json file [string]
```

```sh
Expand Down
2 changes: 1 addition & 1 deletion src/api/post-service-contracts-form.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ServiceContractsForm } from '../utils/generate-contracts-form';
import { ServiceContractsForm } from '../types';
import axios from 'axios';

export function postServiceContractsForm(
Expand Down
31 changes: 25 additions & 6 deletions src/commands/publish.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,37 @@
import { generateContractsForm } from '../utils/generate-contracts-form';
import { generateExpectations } from '../utils/generate-expectations';
import { CliPublishArguments } from '../utils/define-args';
import { readPacts } from '../utils/read-pacts';
import { postServiceContractsForm } from '../api/post-service-contracts-form';
import { URL } from 'url';
import { readSwagger } from '../utils/read-swagger';
import { generateRestContract } from '../utils/generate-rest-contract';
import { ServiceContractsForm } from '../types';

export async function publish(argv: CliPublishArguments) {
const { pactsDir, serviceName, url, serviceVersion } = argv;
const { pactsDir, serviceName, url, serviceVersion, swaggerFile } = argv;

const pacts = readPacts(pactsDir);
const contractsForm = generateContractsForm(pacts);
const judgeDUrl = new URL(
const serviceContractsForm: ServiceContractsForm = {
capabilities: {},
expectations: {},
};

if (pactsDir) {
const pacts = readPacts(pactsDir);
serviceContractsForm.expectations = generateExpectations(pacts);
}

if (swaggerFile) {
const swagger = readSwagger(swaggerFile);
serviceContractsForm.capabilities = generateRestContract(swagger);
}

const postJudgeDServiceContractsFormUrl = new URL(
`./contracts/services/${serviceName}/versions/${serviceVersion}`,
url
).toString();

await postServiceContractsForm(judgeDUrl, contractsForm);
await postServiceContractsForm(
postJudgeDServiceContractsFormUrl,
serviceContractsForm
);
}
19 changes: 19 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export interface ServiceContractsForm {
capabilities: Partial<RestContract>;
expectations: Record<string, RestContract>;
}

export interface RestContract {
rest: {
value: string;
mimeType: 'application/json';
};
}

export type SwaggerDefinition = object;

export interface Pact {
provider?: { name?: string };
interactions: Array<unknown>;
[x: string]: any;
}
33 changes: 24 additions & 9 deletions src/utils/define-args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ export interface CliArguments {
}

export interface CliPublishArguments extends CliArguments {
pactsDir: string;
pactsDir?: string;
swaggerFile?: string;
}

export interface CliVerifyArguments extends CliArguments {
Expand Down Expand Up @@ -41,14 +42,27 @@ export function defineArgs(
'publish',
'Publish contracts',
(yargs) => {
return yargs.options({
...commonArguments,
pactsDir: {
type: 'string',
demandOption: true,
describe: 'Path to directory with pacts',
},
});
return yargs
.options({
...commonArguments,
pactsDir: {
type: 'string',
describe: 'Path to directory with pacts',
},
swaggerFile: {
type: 'string',
describe: 'Path to swagger json file',
},
})
.check(({ pactsDir, swaggerFile }) => {
if (!pactsDir && !swaggerFile) {
throw new Error(
'Either pactsDir or swaggerFile argument must be passed.'
);
}

return true;
});
}
)
.command('verify', 'Verify contracts', (yargs) => {
Expand All @@ -67,5 +81,6 @@ export function defineArgs(
})
.strict()
.demandCommand(1, 'You need at least one command before moving on')
.group(['pactsDir', 'swaggerFile'], 'Required one of:')
.help().argv;
}
39 changes: 0 additions & 39 deletions src/utils/generate-contracts-form.ts

This file was deleted.

18 changes: 18 additions & 0 deletions src/utils/generate-expectations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Pact, ServiceContractsForm } from '../types';
import { generateRestContract } from './generate-rest-contract';

export function generateExpectations(
pactFiles: Pact[]
): ServiceContractsForm['expectations'] {
const expectations: ServiceContractsForm['expectations'] = {};

for (const pactFile of pactFiles) {
if (pactFile.provider?.name) {
expectations[pactFile.provider.name] = generateRestContract(
pactFile
);
}
}

return expectations;
}
10 changes: 10 additions & 0 deletions src/utils/generate-rest-contract.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { RestContract } from '../types';

export function generateRestContract(value: object): RestContract {
return {
rest: {
value: JSON.stringify(value),
mimeType: 'application/json',
},
};
}
7 changes: 1 addition & 6 deletions src/utils/read-pacts.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as fs from 'fs';
import * as path from 'path';
import { Pact } from '../types';

export function readPacts(dir: string): Pact[] {
const pactFiles = fs.readdirSync(dir).map((fileName) => {
Expand Down Expand Up @@ -30,9 +31,3 @@ export function readPacts(dir: string): Pact[] {

return pactFiles.map(({ pact }) => pact);
}

export interface Pact {
provider?: { name?: string };
interactions: Array<unknown>;
[x: string]: any;
}
10 changes: 10 additions & 0 deletions src/utils/read-swagger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import * as fs from 'fs';
import { SwaggerDefinition } from '../types';

export function readSwagger(swaggerFilePath: string): SwaggerDefinition {
if (!swaggerFilePath.endsWith('.json')) {
throw new Error(`Only json swagger files are supported.`);
}

return JSON.parse(fs.readFileSync(swaggerFilePath, 'utf8'));
}
34 changes: 27 additions & 7 deletions test/define-args.test.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,45 @@
import { defineArgs } from '../src/utils/define-args';

describe('defineArgs', () => {
const commonRequiredArgs = [
'publish',
'--url',
'judge-d.instance.com',
'--serviceName',
'example-service',
'--serviceVersion',
'1.0.0',
];

test('returns correctly parsed process arguments', () => {
const argv = defineArgs([
'publish',
'--url',
'judge-d.instance.com',
...commonRequiredArgs,
'--pactsDir',
'/pacts',
'--serviceName',
'example-service',
'--serviceVersion',
'1.0.0',
'--swaggerFile',
'/swagger/swagger.json',
]);

expect(argv).toMatchObject({
_: ['publish'],
url: 'judge-d.instance.com',
pactsDir: '/pacts',
swaggerFile: '/swagger/swagger.json',
serviceName: 'example-service',
serviceVersion: '1.0.0',
});
});

test('throws error and exit when both swaggerFile and pactsDir arguments are not passed', () => {
jest.spyOn(console, 'error').mockImplementation(jest.fn());
jest.spyOn(process, 'exit').mockImplementation();

defineArgs(commonRequiredArgs);

expect(console.error).toHaveBeenNthCalledWith(
3,
'Either pactsDir or swaggerFile argument must be passed.'
);
expect(process.exit).toHaveBeenCalledWith(1);
});
});
12 changes: 2 additions & 10 deletions test/mocks/serviceContractsForm.mock.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,9 @@
import { makeFactory } from 'factory.ts';
import { ServiceContractsForm } from '../../src/utils/generate-contracts-form';
import { pactMockFactory } from './pact.mock';
import { ServiceContractsForm } from '../../src/types';

export const serviceContractsFormMockFactory = makeFactory<ServiceContractsForm>(
{
capabilities: {},
expectations: {
'provider-service-1': {
rest: {
value: JSON.stringify(pactMockFactory.build()),
mimeType: 'application/json',
},
},
},
expectations: {},
}
);
72 changes: 72 additions & 0 deletions test/mocks/swagger.mock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
export const swaggerFileMock = {
swagger: '2.0',
info: {
description: 'Api Documentation',
version: '1.0',
title: 'Api Documentation',
termsOfService: 'urn:tos',
contact: {},
license: {
name: 'Apache 2.0',
url: 'http://www.apache.org/licenses/LICENSE-2.0',
},
},
host: 'localhost',
basePath: '/',
tags: [
{
name: 'environment-controller',
description: 'Environment Controller',
},
],
paths: {
'/environments/{name}': {
put: {
tags: ['environment-controller'],
summary: 'Update the environment',
operationId: 'update environment',
consumes: ['application/json'],
produces: ['*/*'],
parameters: [
{
name: 'name',
in: 'path',
description: 'name',
required: true,
type: 'string',
},
{
in: 'body',
name: 'services',
description: 'services',
required: true,
schema: {
type: 'array',
items: {
$ref: '#/definitions/ServiceForm',
},
},
},
],
responses: {
'200': {
description: 'Success',
},
},
},
},
},
definitions: {
ServiceForm: {
type: 'object',
properties: {
name: {
type: 'string',
},
version: {
type: 'string',
},
},
},
},
};
Loading

0 comments on commit 6b4d650

Please sign in to comment.