Skip to content

Commit

Permalink
validate payload version
Browse files Browse the repository at this point in the history
  • Loading branch information
teunmooij committed Jun 26, 2023
1 parent d83c420 commit 939e291
Show file tree
Hide file tree
Showing 14 changed files with 201 additions and 44 deletions.
2 changes: 1 addition & 1 deletion packages/openapi/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "payload-openapi",
"version": "1.2.0",
"version": "1.2.1",
"description": "Create openapi documentation for your payload cms",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
6 changes: 5 additions & 1 deletion packages/openapi/src/open-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { SanitizedConfig } from 'payload/config';
import { analyzePayload } from './payload-config';

import createBaseConfig from './base-config';
import { merge } from './utils';
import { getUnsupportedSchema, isSupported, merge } from './utils';
import { parseOptions, RawOptions as Options } from './options';

interface PackageInfo {
Expand Down Expand Up @@ -36,6 +36,10 @@ const readJsonFile = async <T = any>(relativePath: string): Promise<Partial<T>>
export const createDocument = async (payloadConfig: SanitizedConfig, options: Options = {}): Promise<OpenAPIV3.Document> => {
const parsedOptions = await parseOptions(options, payloadConfig);

if (!isSupported()) {
return getUnsupportedSchema(parsedOptions);
}

const { name, version, description, license, openapi = {} } = await readJsonFile<PackageInfo>('package.json');
const hasLicenseFile = license && fs.existsSync(path.join(process.cwd(), 'LICENSE'));
const licenseInfo: OpenAPIV3.LicenseObject | undefined = license
Expand Down
43 changes: 3 additions & 40 deletions packages/openapi/src/options.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { SanitizedConfig } from 'payload/config';
import path from 'path';
import { Version, getPayloadVersion, supports, toVersion } from './utils';

/**
* Payload openapi options
Expand Down Expand Up @@ -28,12 +28,6 @@ export interface RawOptions {
payloadVersion?: string;
}

interface Version {
major: number;
minor: number;
patch: number;
}

export interface Options {
payloadVersion?: Version;
access: {
Expand All @@ -53,40 +47,9 @@ export interface Options {
};
}

const toVersion = (versionString: string | undefined): Version | undefined => {
if (!versionString) return undefined;

const parts = versionString.split('.');

return {
major: Number(parts[0]),
minor: Number(parts[1]),
patch: Number(parts[2]),
};
};

const supports = (initialVersion: Version | undefined, currentVersion: Version | undefined) => {
if (!initialVersion || !currentVersion) return true;

return (
currentVersion.major > initialVersion.major ||
(currentVersion.major === initialVersion.major &&
(currentVersion.minor > initialVersion.minor ||
(currentVersion.minor === initialVersion.minor && currentVersion.patch >= initialVersion.patch)))
);
};

const getPayloadVersion = async (): Promise<string | undefined> => {
try {
const pkg = await import(path.join(process.cwd(), 'node_modules/payload/package.json'));
return pkg.version;
} catch (e) {
return undefined;
}
};

export const parseOptions = async (options: RawOptions = {}, payloadConfig: SanitizedConfig): Promise<Options> => {
const payloadVersion = toVersion(options.payloadVersion || (await getPayloadVersion()));
const payloadVersion = options.payloadVersion ? toVersion(options.payloadVersion) : await getPayloadVersion();

return {
payloadVersion,
access: {
Expand Down
2 changes: 2 additions & 0 deletions packages/openapi/src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export { getDescription, getPlural, getSingular } from './get-description';
export { getSingularSchemaName, getPluralSchemaName } from './get-schema-name';
export { merge } from './merge';
export * from './version';
export * from './supported';
31 changes: 31 additions & 0 deletions packages/openapi/src/utils/supported.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { OpenAPIV3 } from 'openapi-types';
import { entityToJSONSchema } from 'payload/utilities';
import { Options } from '../options';
import createBaseConfig from '../base-config';
import { Version, greaterOrEqual, toVersion } from './version';

export const isSupported = (version?: Version) => {
if (
!version ||
greaterOrEqual(toVersion('1.6.0'), version) ||
(greaterOrEqual(version, toVersion('1.9.3')) && greaterOrEqual(toVersion('1.10.1'), version))
) {
return typeof entityToJSONSchema === 'function';
}

// If the version is not any of the known unsupported versions, it should supported.
// If it's not, we might have a bug and we don't want to hide that.
return true;
};

export const getUnsupportedSchema = (options: Options): OpenAPIV3.Document => {
const base = createBaseConfig(options);
return {
...base,
info: {
...base.info,
description: `OpenAPI documentation is not supported for this version of Payload.<br/>
Please make sure you are using payload version 1.6.1 or higher, but not between 1.9.3 and 1.10.1.`,
},
};
};
35 changes: 35 additions & 0 deletions packages/openapi/src/utils/version.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import path from 'path';

export interface Version {
major: number;
minor: number;
patch: number;
}

export const toVersion = (versionString: string): Version => {
const parts = versionString.split('.');

return {
major: Number(parts[0]),
minor: Number(parts[1]),
patch: Number(parts[2]),
};
};

export const greaterOrEqual = (a: Version, b: Version) =>
a.major > b.major || (a.major === b.major && (a.minor > b.minor || (a.minor === b.minor && a.patch >= b.patch)));

export const supports = (initialVersion: Version | undefined, currentVersion: Version | undefined) => {
if (!initialVersion || !currentVersion) return true;

return greaterOrEqual(currentVersion, initialVersion);
};

export const getPayloadVersion = async (): Promise<Version | undefined> => {
try {
const { version } = await import(path.join(process.cwd(), 'node_modules/payload/package.json'));
return version ? toVersion(version) : undefined;
} catch (e) {
return undefined;
}
};
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import loadConfig from 'payload/dist/config/load';
import parser from '@apidevtools/swagger-parser';
import type { OpenAPIV3 } from 'openapi-types';

import { createDocument } from '../src';
import { createDocument } from '../../src';
import expectedSchema from './basic.expected.json';

describe('basic tests', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import loadConfig from 'payload/dist/config/load';
import parser from '@apidevtools/swagger-parser';
import type { OpenAPIV3 } from 'openapi-types';

import { createDocument } from '../src';
import { createDocument } from '../../src';
import expectedSchema from './named-interfaces.expected.json';

describe('basic tests', () => {
Expand Down
31 changes: 31 additions & 0 deletions packages/openapi/test/unit/supported.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { toVersion, isSupported } from '../../src/utils';

jest.mock('payload/utilities', () => ({
entityToJSONSchema: 'not a function',
}));

describe('isSupported tests', () => {
it('is supported if version is 1.6.1', () => {
const version = toVersion('1.6.1');

const supported = isSupported(version);

expect(supported).toBe(true);
});

it('might not be supported if version is smaller than 1.6.1', () => {
const version = toVersion('1.6.0');

const supported = isSupported(version);

expect(supported).toBe(false);
});

it('is not supported if version is between 1.9.3 and 1.10.1', () => {
expect(isSupported(toVersion('1.9.3'))).toBe(false);
expect(isSupported(toVersion('1.9.4'))).toBe(false);
expect(isSupported(toVersion('1.9.5'))).toBe(false);
expect(isSupported(toVersion('1.10.0'))).toBe(false);
expect(isSupported(toVersion('1.10.1'))).toBe(false);
});
});
91 changes: 91 additions & 0 deletions packages/openapi/test/unit/version.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { greaterOrEqual, supports, toVersion } from '../../src/utils';

describe('version tests', () => {
it('constructs a version', () => {
const versionString = '1.2.3';
const version = toVersion(versionString);

expect(version).toEqual({
major: 1,
minor: 2,
patch: 3,
});
});

it('is greater when the major version is greater', () => {
const a = toVersion('2.0.0');
const b = toVersion('1.0.0');

expect(greaterOrEqual(a, b)).toBe(true);
expect(greaterOrEqual(b, a)).toBe(false);
});

it('is greater when the minor version is greater', () => {
const a = toVersion('1.1.0');
const b = toVersion('1.0.0');

expect(greaterOrEqual(a, b)).toBe(true);
expect(greaterOrEqual(b, a)).toBe(false);
});

it('is greater when the patch version is greater', () => {
const a = toVersion('1.0.1');
const b = toVersion('1.0.0');

expect(greaterOrEqual(a, b)).toBe(true);
expect(greaterOrEqual(b, a)).toBe(false);
});

it('is greater or equal when the versions are equal', () => {
const a = toVersion('1.0.0');
const b = toVersion('1.0.0');

expect(greaterOrEqual(a, b)).toBe(true);
expect(greaterOrEqual(b, a)).toBe(true);
});

it('is supported if version is undefined', () => {
const initialVersion = toVersion('1.0.0');
const currentVersion = undefined;

const isSupportyed = supports(initialVersion, currentVersion);

expect(isSupportyed).toBe(true);
});

it('is supported if version is greater', () => {
const initialVersion = toVersion('1.0.0');
const currentVersion = toVersion('1.1.0');

const isSupportyed = supports(initialVersion, currentVersion);

expect(isSupportyed).toBe(true);
});

it('is not supported if version is less', () => {
const initialVersion = toVersion('1.1.0');
const currentVersion = toVersion('1.0.0');

const isSupportyed = supports(initialVersion, currentVersion);

expect(isSupportyed).toBe(false);
});

it('is supported if version is equal', () => {
const initialVersion = toVersion('1.0.0');
const currentVersion = toVersion('1.0.0');

const isSupportyed = supports(initialVersion, currentVersion);

expect(isSupportyed).toBe(true);
});

it('is supported if initial version is undefined', () => {
const initialVersion = undefined;
const currentVersion = toVersion('1.0.0');

const isSupportyed = supports(initialVersion, currentVersion);

expect(isSupportyed).toBe(true);
});
});

0 comments on commit 939e291

Please sign in to comment.