feat(environment): github releases (#3426)
This commit is contained in:
parent
d4f5643799
commit
2f4bc9484b
@ -1,3 +1,5 @@
|
||||
export * from './use-environment';
|
||||
export * from './use-links';
|
||||
export * from './use-node-health';
|
||||
export * from './use-vega-releases';
|
||||
export * from './use-vega-release';
|
||||
|
40137
libs/environment/src/hooks/mocks/github-releases.ts
Normal file
40137
libs/environment/src/hooks/mocks/github-releases.ts
Normal file
File diff suppressed because one or more lines are too long
41
libs/environment/src/hooks/use-vega-release.spec.ts
Normal file
41
libs/environment/src/hooks/use-vega-release.spec.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
import fetchMock from 'fetch-mock';
|
||||
import {
|
||||
GITHUB_VEGA_DEV_RELEASES,
|
||||
GITHUB_VEGA_RELEASES,
|
||||
} from './use-vega-releases';
|
||||
import {
|
||||
GITHUB_VEGA_RELEASES_DATA,
|
||||
GITHUB_VEGA_DEV_RELEASES_DATA,
|
||||
} from './mocks/github-releases';
|
||||
import { useVegaRelease } from './use-vega-release';
|
||||
|
||||
describe('useVegaRelease', () => {
|
||||
beforeEach(() => {
|
||||
fetchMock.get(GITHUB_VEGA_RELEASES, GITHUB_VEGA_RELEASES_DATA);
|
||||
fetchMock.get(GITHUB_VEGA_DEV_RELEASES, GITHUB_VEGA_DEV_RELEASES_DATA);
|
||||
});
|
||||
afterEach(() => {
|
||||
fetchMock.reset();
|
||||
});
|
||||
|
||||
it('should return release information by given tag', async () => {
|
||||
const { result } = renderHook(() => useVegaRelease('v0.70.1'));
|
||||
await waitFor(() => {
|
||||
expect(result.current).toBeTruthy();
|
||||
expect(result.current?.tagName).toEqual('v0.70.1');
|
||||
expect(result.current?.htmlUrl).toEqual(
|
||||
'https://github.com/vegaprotocol/vega/releases/tag/v0.70.1'
|
||||
);
|
||||
expect(result.current?.name).toEqual('v0.70.1');
|
||||
expect(result.current?.isDraft).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
it('should return undefined when a release cannot be found', async () => {
|
||||
const { result } = renderHook(() => useVegaRelease('v0.70.1'));
|
||||
await waitFor(() => {
|
||||
expect(result.current).toEqual(undefined);
|
||||
});
|
||||
});
|
||||
});
|
9
libs/environment/src/hooks/use-vega-release.ts
Normal file
9
libs/environment/src/hooks/use-vega-release.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { useMemo } from 'react';
|
||||
import { useVegaReleases } from './use-vega-releases';
|
||||
|
||||
export const useVegaRelease = (tag: string, includeDevReleases = false) => {
|
||||
const { data } = useVegaReleases();
|
||||
return useMemo(() => {
|
||||
return data?.find((r) => r.tagName === tag);
|
||||
}, [data, tag]);
|
||||
};
|
58
libs/environment/src/hooks/use-vega-releases.spec.ts
Normal file
58
libs/environment/src/hooks/use-vega-releases.spec.ts
Normal file
@ -0,0 +1,58 @@
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
import fetchMock from 'fetch-mock';
|
||||
import {
|
||||
GITHUB_VEGA_DEV_RELEASES,
|
||||
GITHUB_VEGA_RELEASES,
|
||||
useVegaReleases,
|
||||
} from './use-vega-releases';
|
||||
import {
|
||||
GITHUB_VEGA_RELEASES_DATA,
|
||||
GITHUB_VEGA_DEV_RELEASES_DATA,
|
||||
} from './mocks/github-releases';
|
||||
|
||||
describe('useVegaReleases', () => {
|
||||
afterEach(() => {
|
||||
fetchMock.reset();
|
||||
});
|
||||
|
||||
it('should return an empty list when request is unsuccessful', async () => {
|
||||
fetchMock.get(GITHUB_VEGA_RELEASES, 404);
|
||||
fetchMock.get(GITHUB_VEGA_DEV_RELEASES, 404);
|
||||
|
||||
const { result } = renderHook(() => useVegaReleases());
|
||||
expect(result.current.loading).toBeTruthy();
|
||||
await waitFor(() => {
|
||||
expect(result.current.loading).toBeFalsy();
|
||||
expect(result.current.data).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should return a list of releases', async () => {
|
||||
fetchMock.get(GITHUB_VEGA_RELEASES, GITHUB_VEGA_RELEASES_DATA);
|
||||
fetchMock.get(GITHUB_VEGA_DEV_RELEASES, GITHUB_VEGA_DEV_RELEASES_DATA);
|
||||
|
||||
const { result } = renderHook(() => useVegaReleases());
|
||||
expect(result.current.loading).toBeTruthy();
|
||||
await waitFor(() => {
|
||||
expect(result.current.loading).toBeFalsy();
|
||||
expect(result.current).not.toEqual([]);
|
||||
const data = result.current.data;
|
||||
expect(data?.[0].tagName).toEqual('v0.70.3');
|
||||
expect(data?.[0].htmlUrl).toEqual(
|
||||
'https://github.com/vegaprotocol/vega/releases/tag/v0.70.3'
|
||||
);
|
||||
expect(data?.[1].tagName).toEqual('v0.70.2');
|
||||
expect(data?.[2].tagName).toEqual('v0.70.1');
|
||||
});
|
||||
});
|
||||
|
||||
it('should return a list of releases including dev releases', async () => {
|
||||
fetchMock.get(GITHUB_VEGA_RELEASES, GITHUB_VEGA_RELEASES_DATA);
|
||||
fetchMock.get(GITHUB_VEGA_DEV_RELEASES, GITHUB_VEGA_DEV_RELEASES_DATA);
|
||||
const { result } = renderHook(() => useVegaReleases(true));
|
||||
await waitFor(() => {
|
||||
expect(result.current.data?.length).toBeGreaterThan(30);
|
||||
expect(fetchMock.calls().length).toEqual(2);
|
||||
});
|
||||
});
|
||||
});
|
124
libs/environment/src/hooks/use-vega-releases.ts
Normal file
124
libs/environment/src/hooks/use-vega-releases.ts
Normal file
@ -0,0 +1,124 @@
|
||||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import z from 'zod';
|
||||
|
||||
export const GITHUB_VEGA_RELEASES =
|
||||
'https://api.github.com/repos/vegaprotocol/vega/releases';
|
||||
export const GITHUB_VEGA_DEV_RELEASES =
|
||||
'https://api.github.com/repos/vegaprotocol/vega-dev-releases/releases';
|
||||
|
||||
const GithubReleaseSchema = z.object({
|
||||
url: z.string(),
|
||||
html_url: z.string(),
|
||||
id: z.number(),
|
||||
tag_name: z.string(),
|
||||
name: z.string().nullable(),
|
||||
draft: z.boolean(),
|
||||
created_at: z.string().datetime(),
|
||||
});
|
||||
|
||||
const GithubReleasesSchema = z.array(GithubReleaseSchema);
|
||||
|
||||
type ReleaseInfo = {
|
||||
id: number;
|
||||
name: string;
|
||||
tagName: string;
|
||||
htmlUrl: string;
|
||||
url: string;
|
||||
isDraft: boolean;
|
||||
createdAt: Date;
|
||||
isDevRelease: boolean;
|
||||
};
|
||||
|
||||
const toReleaseInfo = (
|
||||
releaseData: z.infer<typeof GithubReleaseSchema>,
|
||||
isDevRelease: boolean
|
||||
): ReleaseInfo => ({
|
||||
id: releaseData.id,
|
||||
name: releaseData.name || '',
|
||||
tagName: releaseData.tag_name,
|
||||
htmlUrl: releaseData.html_url,
|
||||
url: releaseData.url,
|
||||
isDraft: releaseData.draft,
|
||||
createdAt: new Date(releaseData.created_at),
|
||||
isDevRelease,
|
||||
});
|
||||
|
||||
enum ReleaseFeed {
|
||||
// @ts-ignore TS18033 - allowed in 5.0
|
||||
Vega = GITHUB_VEGA_RELEASES,
|
||||
// @ts-ignore TS18033
|
||||
VegaDev = GITHUB_VEGA_DEV_RELEASES,
|
||||
}
|
||||
|
||||
const fetchReleases = async (feed: ReleaseFeed) => {
|
||||
const response = await fetch(String(feed), {
|
||||
cache: 'force-cache',
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const json = await response.json();
|
||||
const data = GithubReleasesSchema.parse(json);
|
||||
return data.map((d) => toReleaseInfo(d, feed === ReleaseFeed.VegaDev));
|
||||
}
|
||||
|
||||
return [];
|
||||
};
|
||||
|
||||
type State = {
|
||||
loading: boolean;
|
||||
data: ReleaseInfo[] | null;
|
||||
error: Error | null | undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves a list of vega releases from github.
|
||||
* First element is the newest.
|
||||
*/
|
||||
export const useVegaReleases = (includeDevReleases = false) => {
|
||||
const [state, setState] = useState<State>({
|
||||
loading: true,
|
||||
data: null,
|
||||
error: null,
|
||||
});
|
||||
|
||||
const fetchData = useCallback(() => {
|
||||
let mounted = true;
|
||||
|
||||
Promise.all(
|
||||
includeDevReleases
|
||||
? [fetchReleases(ReleaseFeed.Vega), fetchReleases(ReleaseFeed.VegaDev)]
|
||||
: [fetchReleases(ReleaseFeed.Vega)]
|
||||
)
|
||||
.then(([vega, vegaDev]) => {
|
||||
if (mounted) {
|
||||
setState({
|
||||
loading: false,
|
||||
data: (vegaDev ? vega.concat(vegaDev) : vega).sort(
|
||||
(a, b) => b.createdAt.valueOf() - a.createdAt.valueOf()
|
||||
),
|
||||
error: null,
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
if (mounted) {
|
||||
setState({
|
||||
loading: false,
|
||||
data: null,
|
||||
error: err,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
mounted = false;
|
||||
};
|
||||
}, [includeDevReleases, setState]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, [fetchData]);
|
||||
|
||||
return state;
|
||||
};
|
Loading…
Reference in New Issue
Block a user