Skip to content

Commit fedc460

Browse files
check unverified packages when reading the directory
1 parent db58aa3 commit fedc460

File tree

5 files changed

+5460
-5333
lines changed

5 files changed

+5460
-5333
lines changed

src/registry/domain/components-cache/components-list.ts

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import semver from 'semver';
22
import pLimit from 'p-limit';
33
import getUnixUTCTimestamp from 'oc-get-unix-utc-timestamp';
4-
import { ComponentsList, Config } from '../../../types';
54
import { StorageAdapter } from 'oc-storage-adapters-utils';
5+
import eventsHandler from '../events-handler';
6+
import { ComponentsList, Config } from '../../../types';
67

78
export default function componentsList(conf: Config, cdn: StorageAdapter) {
89
const filePath = (): string =>
@@ -11,17 +12,64 @@ export default function componentsList(conf: Config, cdn: StorageAdapter) {
1112
const componentsList = {
1213
getFromJson: (): Promise<ComponentsList> => cdn.getJson(filePath(), true),
1314

14-
getFromDirectories: async (): Promise<ComponentsList> => {
15+
getFromDirectories: async (
16+
jsonList: ComponentsList | null
17+
): Promise<ComponentsList> => {
1518
const componentsInfo: Record<string, string[]> = {};
1619

20+
const checkVersion = (
21+
componentName: string,
22+
componentVersion: string
23+
) => {
24+
return cdn
25+
.getJson(
26+
// Check integrity of the package by checking existence of package.json
27+
// OC will upload always the package.json last when publishing
28+
`${conf.storage.options.componentsDir}/${componentName}/${componentVersion}/package.json`
29+
)
30+
.then(() => true)
31+
.catch(() => false);
32+
};
33+
1734
const getVersionsForComponent = async (
1835
componentName: string
1936
): Promise<string[]> => {
20-
const versions = await cdn.listSubDirectories(
37+
const allVersions = await cdn.listSubDirectories(
2138
`${conf.storage.options.componentsDir}/${componentName}`
2239
);
40+
const unCheckedVersions = allVersions.filter(
41+
version => !jsonList?.components[componentName]?.includes(version)
42+
);
43+
const limit = pLimit(cdn.maxConcurrentRequests);
44+
const invalidVersions = (
45+
await Promise.all(
46+
unCheckedVersions.map(unCheckedVersion =>
47+
limit(async () => {
48+
const isValid = await checkVersion(
49+
componentName,
50+
unCheckedVersion
51+
);
52+
53+
return isValid ? null : unCheckedVersion;
54+
})
55+
)
56+
)
57+
).filter((x): x is string => typeof x === 'string');
58+
59+
if (invalidVersions.length > 0) {
60+
eventsHandler.fire('error', {
61+
code: 'CORRUPTED_VERSION',
62+
message: `Couldn't validate the integrity of the component ${componentName} on the following versions: ${invalidVersions.join(
63+
', '
64+
)}.`
65+
});
66+
}
67+
68+
const validVersions = allVersions.filter(
69+
version => !invalidVersions.includes(version)
70+
);
2371

24-
return versions.sort(semver.compare);
72+
return validVersions.sort(semver.compare);
2573
};
2674

2775
try {
@@ -55,8 +103,8 @@ export default function componentsList(conf: Config, cdn: StorageAdapter) {
55103
}
56104
},
57105

58-
async refresh(): Promise<ComponentsList> {
59-
const components = await componentsList.getFromDirectories();
106+
async refresh(cachedList: ComponentsList): Promise<ComponentsList> {
107+
const components = await componentsList.getFromDirectories(cachedList);
60108
await componentsList.save(components);
61109

62110
return components;

src/registry/domain/components-cache/index.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ export default function componentsCache(conf: Config, cdn: StorageAdapter) {
1111

1212
const componentsList = getComponentsList(conf, cdn);
1313

14-
const poll = () =>
15-
setTimeout(async () => {
14+
const poll = () => {
15+
return setTimeout(async () => {
1616
try {
1717
const data = await componentsList.getFromJson();
1818

@@ -29,6 +29,7 @@ export default function componentsCache(conf: Config, cdn: StorageAdapter) {
2929
}
3030
refreshLoop = poll();
3131
}, conf.pollingInterval * 1000);
32+
};
3233

3334
const cacheDataAndStartPolling = (data: ComponentsList) => {
3435
cachedComponentsList = data;
@@ -55,12 +56,12 @@ export default function componentsCache(conf: Config, cdn: StorageAdapter) {
5556
},
5657

5758
async load(): Promise<ComponentsList> {
58-
const dirComponents = await componentsList
59-
.getFromDirectories()
60-
.catch(err => throwError('components_list_get', err));
6159
const jsonComponents = await componentsList
6260
.getFromJson()
6361
.catch(() => null);
62+
const dirComponents = await componentsList
63+
.getFromDirectories(jsonComponents)
64+
.catch(err => throwError('components_list_get', err));
6465

6566
if (
6667
!jsonComponents ||
@@ -78,7 +79,7 @@ export default function componentsCache(conf: Config, cdn: StorageAdapter) {
7879
async refresh(): Promise<ComponentsList> {
7980
clearTimeout(refreshLoop);
8081
try {
81-
const components = await componentsList.refresh();
82+
const components = await componentsList.refresh(cachedComponentsList);
8283
cacheDataAndStartPolling(components);
8384

8485
return components;

test/unit/registry-domain-components-cache.js

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ describe('registry : domain : components-cache', () => {
6262
describe('when initialising the cache', () => {
6363
before(done => {
6464
mockedCdn.getJson = sinon.stub();
65-
mockedCdn.getJson.rejects('not_found');
65+
mockedCdn.getJson.resolves({});
66+
mockedCdn.getJson.onFirstCall(0).rejects('not_found');
6667
mockedCdn.listSubDirectories = sinon.stub();
6768
mockedCdn.listSubDirectories.onCall(0).resolves(['hello-world']);
6869
mockedCdn.listSubDirectories.onCall(1).resolves(['1.0.0', '1.0.2']);
@@ -72,11 +73,17 @@ describe('registry : domain : components-cache', () => {
7273
componentsCache.load().finally(done);
7374
});
7475

75-
it('should try fetching the components.json', () => {
76-
expect(mockedCdn.getJson.calledOnce).to.be.true;
76+
it('should try fetching the components.json and check components', () => {
77+
expect(mockedCdn.getJson.calledThrice).to.be.true;
7778
expect(mockedCdn.getJson.args[0][0]).to.be.equal(
7879
'component/components.json'
7980
);
81+
expect(mockedCdn.getJson.args[1][0]).to.be.equal(
82+
'component/hello-world/1.0.0/package.json'
83+
);
84+
expect(mockedCdn.getJson.args[2][0]).to.be.equal(
85+
'component/hello-world/1.0.2/package.json'
86+
);
8087
});
8188

8289
it('should scan for directories to fetch components and versions', () => {
@@ -118,10 +125,13 @@ describe('registry : domain : components-cache', () => {
118125
});
119126

120127
it('should fetch the components.json', () => {
121-
expect(mockedCdn.getJson.calledOnce).to.be.true;
128+
expect(mockedCdn.getJson.calledTwice).to.be.true;
122129
expect(mockedCdn.getJson.args[0][0]).to.be.equal(
123130
'component/components.json'
124131
);
132+
expect(mockedCdn.getJson.args[1][0]).to.be.equal(
133+
'component/hello-world/2.0.0/package.json'
134+
);
125135
});
126136

127137
it('should scan for directories to fetch components and versions', () => {

tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
1414

1515
/* Language and Environment */
16-
"target": "ES2018" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
16+
"target": "ES2020" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
1717
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
1818
// "jsx": "preserve", /* Specify what JSX code is generated. */
1919
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */

0 commit comments

Comments
 (0)