Skip to content

Commit

Permalink
refactor(github): Use schema validation for GraphQL (#20519)
Browse files Browse the repository at this point in the history
  • Loading branch information
zharinov committed Feb 24, 2023
1 parent 78a384b commit eb8a02c
Show file tree
Hide file tree
Showing 7 changed files with 96 additions and 71 deletions.
14 changes: 9 additions & 5 deletions lib/util/github/graphql/datasource-fetcher.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,14 @@ const adapter: GithubGraphqlDatasourceAdapter<
version,
releaseTimestamp,
foo,
}: TestAdapterInput): TestAdapterOutput => ({
version,
releaseTimestamp,
bar: foo,
}),
}: TestAdapterInput): TestAdapterOutput | null =>
version && releaseTimestamp && foo
? {
version,
releaseTimestamp,
bar: foo,
}
: null,
};

function resp(
Expand Down Expand Up @@ -208,6 +211,7 @@ describe('util/github/graphql/datasource-fetcher', () => {
resp(false, [
{ version: v3, releaseTimestamp: t3, foo: '3' },
{ version: v2, releaseTimestamp: t2, foo: '2' },
{} as never,
{ version: v1, releaseTimestamp: t1, foo: '1' },
])
);
Expand Down
8 changes: 7 additions & 1 deletion lib/util/github/graphql/datasource-fetcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,8 +239,14 @@ export class GithubGraphqlDatasourceFetcher<
const resultItems: ResultItem[] = [];
for (const node of queryResult.nodes) {
const item = this.datasourceAdapter.transform(node);
// istanbul ignore if: will be tested later
if (!item) {
logger.once.info(
{
packageName: `${this.repoOwner}/${this.repoName}`,
baseUrl: this.baseUrl,
},
`GitHub GraphQL datasource: skipping empty item`
);
continue;
}
resultItems.push(item);
Expand Down
14 changes: 8 additions & 6 deletions lib/util/github/graphql/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,14 @@ describe('util/github/graphql/index', () => {
payload: {
nodes: [
{
id: 123,
name: 'name',
description: 'description',
version: '1.2.3',
releaseTimestamp: '2024-09-24',
isDraft: false,
isPrerelease: false,
url: 'https://example.com',
id: 123,
name: 'name',
description: 'description',
},
],
},
Expand All @@ -69,12 +71,12 @@ describe('util/github/graphql/index', () => {

expect(res).toEqual([
{
id: 123,
name: 'name',
description: 'description',
version: '1.2.3',
releaseTimestamp: '2024-09-24',
url: 'https://example.com',
id: 123,
name: 'name',
description: 'description',
},
]);
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { GithubGraphqlRelease } from '../types';
import { adapter } from './releases-query-adapter';
import type { GithubGraphqlRelease } from './releases-query-adapter';

const item: GithubGraphqlRelease = {
version: '1.2.3',
Expand Down Expand Up @@ -28,6 +28,10 @@ describe('util/github/graphql/query-adapters/releases-query-adapter', () => {
expect(adapter.transform({ ...item, isDraft: true })).toBeNull();
});

it('handles invalid items', () => {
expect(adapter.transform({} as never)).toBeNull();
});

it('marks prereleases as unstable', () => {
expect(adapter.transform({ ...item, isPrerelease: true })).toMatchObject({
isStable: false,
Expand Down
36 changes: 31 additions & 5 deletions lib/util/github/graphql/query-adapters/releases-query-adapter.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { z } from 'zod';
import type {
GithubGraphqlDatasourceAdapter,
GithubGraphqlRelease,
GithubReleaseItem,
} from '../types';
import { prepareQuery } from '../util';
Expand Down Expand Up @@ -30,7 +30,24 @@ const query = prepareQuery(`
}
`);

const GithubGraphqlRelease = z.object({
version: z.string(),
releaseTimestamp: z.string(),
isDraft: z.boolean(),
isPrerelease: z.boolean(),
url: z.string(),
id: z.number().nullable(),
name: z.string().nullable(),
description: z.string().nullable(),
});
export type GithubGraphqlRelease = z.infer<typeof GithubGraphqlRelease>;

function transform(item: GithubGraphqlRelease): GithubReleaseItem | null {
const releaseItem = GithubGraphqlRelease.safeParse(item);
if (!releaseItem.success) {
return null;
}

const {
version,
releaseTimestamp,
Expand All @@ -40,7 +57,7 @@ function transform(item: GithubGraphqlRelease): GithubReleaseItem | null {
id,
name,
description,
} = item;
} = releaseItem.data;

if (isDraft) {
return null;
Expand All @@ -50,11 +67,20 @@ function transform(item: GithubGraphqlRelease): GithubReleaseItem | null {
version,
releaseTimestamp,
url,
id,
name,
description,
};

if (id) {
result.id = id;
}

if (name) {
result.name = name;
}

if (description) {
result.description = description;
}

if (isPrerelease) {
result.isStable = false;
}
Expand Down
47 changes: 33 additions & 14 deletions lib/util/github/graphql/query-adapters/tags-query-adapter.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,30 @@
import type {
GithubGraphqlDatasourceAdapter,
GithubGraphqlTag,
GithubTagItem,
} from '../types';
import { z } from 'zod';
import type { GithubGraphqlDatasourceAdapter, GithubTagItem } from '../types';
import { prepareQuery } from '../util';

const key = 'github-tags-datasource-v2';

const GithubGraphqlTag = z.object({
version: z.string(),
target: z.union([
z.object({
type: z.literal('Commit'),
oid: z.string(),
releaseTimestamp: z.string(),
}),
z.object({
type: z.literal('Tag'),
target: z.object({
oid: z.string(),
}),
tagger: z.object({
releaseTimestamp: z.string(),
}),
}),
]),
});
export type GithubGraphqlTag = z.infer<typeof GithubGraphqlTag>;

const query = prepareQuery(`
refs(
first: $count
Expand Down Expand Up @@ -41,16 +59,17 @@ const query = prepareQuery(`
}`);

function transform(item: GithubGraphqlTag): GithubTagItem | null {
const { version, target } = item;
if (target.type === 'Commit') {
const { oid: hash, releaseTimestamp } = target;
return { version, gitRef: version, hash, releaseTimestamp };
} else if (target.type === 'Tag') {
const { oid: hash } = target.target;
const { releaseTimestamp } = target.tagger;
return { version, gitRef: version, hash, releaseTimestamp };
const res = GithubGraphqlTag.safeParse(item);
if (!res.success) {
return null;
}
return null;
const { version, target } = item;
const releaseTimestamp =
target.type === 'Commit'
? target.releaseTimestamp
: target.tagger.releaseTimestamp;
const hash = target.type === 'Commit' ? target.oid : target.target.oid;
return { version, gitRef: version, hash, releaseTimestamp };
}

export const adapter: GithubGraphqlDatasourceAdapter<
Expand Down
42 changes: 3 additions & 39 deletions lib/util/github/graphql/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,51 +58,15 @@ export interface GithubPackageConfig {
registryUrl?: string;
}

/**
* GraphQL shape for releases
*/
export interface GithubGraphqlRelease {
version: string;
releaseTimestamp: string;
isDraft: boolean;
isPrerelease: boolean;
url: string;
id: number;
name: string;
description: string;
}

/**
* Result of GraphQL response transformation for releases (via adapter)
*/
export interface GithubReleaseItem extends GithubDatasourceItem {
isStable?: boolean;
url: string;
id: number;
name: string;
description: string;
}

/**
* GraphQL shape for tags
*/
export interface GithubGraphqlTag {
version: string;
target:
| {
type: 'Commit';
oid: string;
releaseTimestamp: string;
}
| {
type: 'Tag';
target: {
oid: string;
};
tagger: {
releaseTimestamp: string;
};
};
id?: number;
name?: string;
description?: string;
}

/**
Expand Down

0 comments on commit eb8a02c

Please sign in to comment.