Skip to content

Commit

Permalink
fix: registry index URL (#935)
Browse files Browse the repository at this point in the history
* fix: registry index URL

Signed-off-by: Oleksii Orel <oorel@redhat.com>
  • Loading branch information
olexii4 authored Sep 28, 2023
1 parent e42268d commit 333d6cf
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 43 deletions.
2 changes: 1 addition & 1 deletion .deps/prod.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
| [`@babel/runtime@7.22.10`](https://github.com/babel/babel.git) | MIT | #8730 |
| [`@devfile/api@2.2.1-alpha-1667236163`](https://github.com/devfile/api.git) | Apache-2.0 | clearlydefined |
| `@eclipse-che/api@7.72.0` | EPL-2.0 | ecd.che |
| [`@eclipse-che/che-devworkspace-generator@7.75.0-next-50585f6`](git+https://github.com/eclipse-che/che-devfile-registry.git) | EPL-2.0 | ecd.che |
| [`@eclipse-che/che-devworkspace-generator@7.75.0-next-bbd66d3`](git+https://github.com/eclipse-che/che-devfile-registry.git) | EPL-2.0 | ecd.che |
| [`@eclipse-che/common@7.75.0-next`](https://github.com/eclipse-che/che-dashboard) | EPL-2.0 | ecd.che |
| [`@eclipse-che/dashboard-backend@7.75.0-next`](https://github.com/eclipse-che/che-dashboard) | EPL-2.0 | ecd.che |
| [`@eclipse-che/dashboard-frontend@7.75.0-next`](git://github.com/eclipse/che-dashboard.git) | EPL-2.0 | ecd.che |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,3 @@
display: inline-block;
padding-top: 8px;
}

Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,20 @@ describe('fetch registry metadata', () => {
describe('external registry', () => {
const baseUrl = 'https://eclipse-che.github.io/che-devfile-registry/7.71.0/';

it('should fetch the deprecated indexUrl as a second try', async () => {
mockSessionStorageServiceGet.mockReturnValue(undefined);

mockFetchData.mockRejectedValueOnce(new Error('Unsupported index URL'));
mockFetchData.mockResolvedValueOnce([]);

await fetchRegistryMetadata(baseUrl, true);

expect(mockFetchData.mock.calls).toEqual([
[`https://eclipse-che.github.io/che-devfile-registry/7.71.0/index`],
[`https://eclipse-che.github.io/che-devfile-registry/7.71.0/devfiles/index.json`],
]);
});

it('should fetch registry metadata', async () => {
const metadata = {
displayName: 'java-maven',
Expand All @@ -83,12 +97,12 @@ describe('fetch registry metadata', () => {
SessionStorageKey.EXTERNAL_REGISTRIES,
);
expect(mockFetchData).toBeCalledWith(
'https://eclipse-che.github.io/che-devfile-registry/7.71.0/devfiles/index.json',
'https://eclipse-che.github.io/che-devfile-registry/7.71.0/index',
);
expect(mockSessionStorageServiceUpdate).toHaveBeenCalledWith(
SessionStorageKey.EXTERNAL_REGISTRIES,
JSON.stringify({
'https://eclipse-che.github.io/che-devfile-registry/7.71.0/devfiles/index.json': {
'https://eclipse-che.github.io/che-devfile-registry/7.71.0/index': {
metadata: [metadata],
lastFetched: 1555555555555,
},
Expand Down Expand Up @@ -138,14 +152,14 @@ describe('fetch registry metadata', () => {
SessionStorageKey.EXTERNAL_REGISTRIES,
);
expect(mockFetchData).toBeCalledWith(
'https://eclipse-che.github.io/che-devfile-registry/7.71.0/devfiles/index.json',
'https://eclipse-che.github.io/che-devfile-registry/7.71.0/index',
);
expect(mockSessionStorageServiceUpdate).not.toHaveBeenCalled();
expect(errorMessage).toEqual(
'Failed to fetch devfiles metadata from registry URL: https://eclipse-che.github.io/che-devfile-registry/7.71.0/, reason: Error: Returns type is not array.',
'Failed to fetch devfiles metadata from registry URL: https://eclipse-che.github.io/che-devfile-registry/7.71.0/, reason: Returned value is not array.',
);
expect(console.error).toBeCalledWith(
'Failed to fetch devfiles metadata from registry URL: https://eclipse-che.github.io/che-devfile-registry/7.71.0/, reason: Error: Returns type is not array.',
'Failed to fetch devfiles metadata from registry URL: https://eclipse-che.github.io/che-devfile-registry/7.71.0/, reason: Returned value is not array.',
);
});

Expand Down Expand Up @@ -177,13 +191,13 @@ describe('fetch registry metadata', () => {
SessionStorageKey.EXTERNAL_REGISTRIES,
);
expect(mockFetchData).toBeCalledWith(
'https://eclipse-che.github.io/che-devfile-registry/7.71.0/devfiles/index.json',
'https://eclipse-che.github.io/che-devfile-registry/7.71.0/index',
);
expect(console.warn).toBeCalledTimes(2);
expect(mockSessionStorageServiceUpdate).toHaveBeenCalledWith(
SessionStorageKey.EXTERNAL_REGISTRIES,
JSON.stringify({
'https://eclipse-che.github.io/che-devfile-registry/7.71.0/devfiles/index.json': {
'https://eclipse-che.github.io/che-devfile-registry/7.71.0/index': {
metadata: [metadata],
lastFetched: 1555555555555,
},
Expand All @@ -207,7 +221,7 @@ describe('fetch registry metadata', () => {
mockFetchData.mockResolvedValue([metadata]);
mockSessionStorageServiceGet.mockReturnValue(
JSON.stringify({
'https://eclipse-che.github.io/che-devfile-registry/7.71.0/devfiles/index.json': {
'https://eclipse-che.github.io/che-devfile-registry/7.71.0/index': {
metadata: [metadata],
lastFetched: time,
},
Expand Down Expand Up @@ -239,7 +253,7 @@ describe('fetch registry metadata', () => {
mockFetchData.mockResolvedValue([metadata]);
mockSessionStorageServiceGet.mockReturnValue(
JSON.stringify({
'https://eclipse-che.github.io/che-devfile-registry/7.71.0/devfiles/index.json': {
'https://eclipse-che.github.io/che-devfile-registry/7.71.0/index': {
metadata: [metadata],
lastFetched: time,
},
Expand All @@ -252,12 +266,12 @@ describe('fetch registry metadata', () => {
SessionStorageKey.EXTERNAL_REGISTRIES,
);
expect(mockFetchData).toBeCalledWith(
'https://eclipse-che.github.io/che-devfile-registry/7.71.0/devfiles/index.json',
'https://eclipse-che.github.io/che-devfile-registry/7.71.0/index',
);
expect(mockSessionStorageServiceUpdate).toHaveBeenCalledWith(
SessionStorageKey.EXTERNAL_REGISTRIES,
JSON.stringify({
'https://eclipse-che.github.io/che-devfile-registry/7.71.0/devfiles/index.json': {
'https://eclipse-che.github.io/che-devfile-registry/7.71.0/index': {
metadata: [metadata],
lastFetched: time + elapsedTime,
},
Expand Down Expand Up @@ -319,10 +333,10 @@ describe('fetch registry metadata', () => {
expect(mockFetchData).toBeCalledWith('https://registry.devfile.io/index');
expect(mockSessionStorageServiceUpdate).not.toHaveBeenCalled();
expect(errorMessage).toEqual(
'Failed to fetch devfiles metadata from registry URL: https://registry.devfile.io/, reason: Error: Returns type is not array.',
'Failed to fetch devfiles metadata from registry URL: https://registry.devfile.io/, reason: Returned value is not array.',
);
expect(console.error).toBeCalledWith(
'Failed to fetch devfiles metadata from registry URL: https://registry.devfile.io/, reason: Error: Returns type is not array.',
'Failed to fetch devfiles metadata from registry URL: https://registry.devfile.io/, reason: Returned value is not array.',
);
});

Expand Down
90 changes: 63 additions & 27 deletions packages/dashboard-frontend/src/services/registry/devfiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,16 @@ const EXPIRATION_TIME_FOR_STORED_METADATA = 60 * 60 * 1000; // expiration time i

import { fetchData } from './fetchData';
import SessionStorageService, { SessionStorageKey } from '../session-storage';
import common from '@eclipse-che/common';

export class DevfileMetaDataIsNotArrayError extends Error {
public location: string;

constructor(location: string) {
super('Returned value is not array.');
this.location = location;
}
}

function createURL(url: string, baseUrl: string): URL {
if (/^\/(\w+)/.test(url)) {
Expand Down Expand Up @@ -106,50 +116,75 @@ export function updateObjectLinks(object: any, baseUrl): any {
return object;
}

export function getRegistryIndexUrl(registryUrl: string, isExternal: boolean): URL {
export function getRegistryIndexLocations(registryUrl: string, isExternal: boolean): Array<string> {
registryUrl = registryUrl[registryUrl.length - 1] === '/' ? registryUrl : registryUrl + '/';

const registryIndexLocations: Array<string> = [];
if (isExternal) {
if (new URL(registryUrl).host === 'registry.devfile.io') {
return new URL('index', registryUrl);
const indexUrl = new URL('index', registryUrl);
registryIndexLocations.push(indexUrl.href);

const deprecatedIndexUrl = new URL('devfiles/index.json', registryUrl);
registryIndexLocations.push(deprecatedIndexUrl.href);
} else {
if (registryUrl.endsWith('/getting-started-sample/')) {
const indexUrl = new URL(registryUrl.slice(0, -1));
registryIndexLocations.push(indexUrl.href);
} else {
const indexUrl = new URL('devfiles/index.json', registryUrl);
registryIndexLocations.push(indexUrl.href);
}
} else if (registryUrl.endsWith('/getting-started-sample/')) {
return new URL(registryUrl.replace(/\/$/, ''));
}
return new URL('devfiles/index.json', registryUrl);

return registryIndexLocations;
}

export async function fetchRegistryMetadata(
registryUrl: string,
isExternal: boolean,
): Promise<che.DevfileMetaData[]> {
try {
const registryIndexUrl = getRegistryIndexUrl(registryUrl, isExternal);
const registryIndexLocations = getRegistryIndexLocations(registryUrl, isExternal);
if (isExternal) {
const devfileMetaDataArr = getExternalRegistryMetadataFromStorage(registryIndexUrl.href);
const devfileMetaDataArr = getExternalRegistryMetadataFromStorage(registryIndexLocations[0]);
if (devfileMetaDataArr !== undefined) {
return devfileMetaDataArr;
}
}
const data = await fetchData(registryIndexUrl.href);
const devfileMetaDataArr: che.DevfileMetaData[] = isExternal
? []
: (data as che.DevfileMetaData[]);

if (isExternal) {
if (!Array.isArray(data)) {
throw new Error('Returns type is not array.');
}
data.forEach(val => {
if (isDevfileMetaData(val)) {
devfileMetaDataArr.push(val);
const devfileMetaDataArr: che.DevfileMetaData[] = [];
for (const [index, location] of registryIndexLocations.entries()) {
try {
const data = await fetchData(location);
if (Array.isArray(data)) {
data.forEach(metadata => {
if (metadata.url) {
if (!metadata.links) {
metadata.links = {};
}
metadata.links.v2 = metadata.url;
delete metadata.url;
}
if (isDevfileMetaData(metadata)) {
devfileMetaDataArr.push(metadata);
} else {
console.warn(
`Returns type from registry URL: ${registryUrl} is not DevfileMetaData.`,
metadata,
);
}
});
break;
} else {
console.warn(
`Returns type from registry URL: ${registryUrl} is not DevfileMetaData.`,
val,
);
throw new DevfileMetaDataIsNotArrayError(location);
}
} catch (e) {
if (
e instanceof DevfileMetaDataIsNotArrayError ||
index === registryIndexLocations.length - 1
) {
throw e;
}
});
}
}

const val = devfileMetaDataArr.map(meta => {
Expand All @@ -159,12 +194,13 @@ export async function fetchRegistryMetadata(
return meta;
});
if (isExternal) {
setExternalRegistryMetadataToStorage(registryIndexUrl.href, val);
setExternalRegistryMetadataToStorage(registryIndexLocations[0], val);
}
return val;
} catch (error) {
const errorMessage =
`Failed to fetch devfiles metadata from registry URL: ${registryUrl}, reason: ` + error;
`Failed to fetch devfiles metadata from registry URL: ${registryUrl}, reason: ` +
common.helpers.errors.getMessage(error);
console.error(errorMessage);
throw errorMessage;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,9 @@ describe('Devfile registries', () => {
store.dispatch(
devfileRegistriesStore.actionCreators.requestRegistriesMetadata(locations, false),
),
).rejects.toMatch('Failed to fetch devfiles metadata from registry');
).rejects.toMatch(
'Failed to fetch devfiles metadata from registry URL: http://example.com/location3, reason: Unexpected error. Check DevTools console and network tabs for more information.',
);

const actions = store.getActions();

Expand Down

0 comments on commit 333d6cf

Please sign in to comment.