Skip to content
Snippets Groups Projects
Unverified Commit 86543938 authored by Sergio Zharinov's avatar Sergio Zharinov Committed by GitHub
Browse files

feat(cdnjs): Add caching for CDNJS datasource (#5259)

parent 9452694b
No related branches found
No related tags found
No related merge requests found
// Datasource // Datasource
export const DATASOURCE_CARGO = 'cargo'; export const DATASOURCE_CARGO = 'cargo';
// export const DATASOURCE_CDNJS = 'cdnjs'; export const DATASOURCE_CDNJS = 'cdnjs';
export const DATASOURCE_DART = 'dart'; export const DATASOURCE_DART = 'dart';
export const DATASOURCE_DOCKER = 'docker'; export const DATASOURCE_DOCKER = 'docker';
export const DATASOURCE_GIT_SUBMODULES = 'gitSubmodules'; export const DATASOURCE_GIT_SUBMODULES = 'gitSubmodules';
......
...@@ -2,12 +2,16 @@ import { logger } from '../../logger'; ...@@ -2,12 +2,16 @@ import { logger } from '../../logger';
import got from '../../util/got'; import got from '../../util/got';
import { ReleaseResult, PkgReleaseConfig } from '../common'; import { ReleaseResult, PkgReleaseConfig } from '../common';
import { DATASOURCE_FAILURE } from '../../constants/error-messages'; import { DATASOURCE_FAILURE } from '../../constants/error-messages';
import { DATASOURCE_CDNJS } from '../../constants/data-binary-source';
interface CdnjsAsset { interface CdnjsAsset {
version: string; version: string;
files: string[]; files: string[];
} }
const cacheNamespace = `datasource-${DATASOURCE_CDNJS}`;
const cacheMinutes = 60;
interface CdnjsResponse { interface CdnjsResponse {
homepage?: string; homepage?: string;
repository?: { repository?: {
...@@ -28,7 +32,16 @@ export async function getPkgReleases({ ...@@ -28,7 +32,16 @@ export async function getPkgReleases({
const [depName, ...assetParts] = lookupName.split('/'); const [depName, ...assetParts] = lookupName.split('/');
const assetName = assetParts.join('/'); const assetName = assetParts.join('/');
const url = `https://api.cdnjs.com/libraries/${depName}`;
const cacheKey = depName;
const cachedResult = await renovateCache.get<ReleaseResult>(
cacheNamespace,
cacheKey
);
// istanbul ignore if
if (cachedResult) return cachedResult;
const url = `https://api.cdnjs.com/libraries/${depName}?fields=homepage,repository,assets`;
try { try {
const res = await got(url, { json: true }); const res = await got(url, { json: true });
...@@ -51,6 +64,8 @@ export async function getPkgReleases({ ...@@ -51,6 +64,8 @@ export async function getPkgReleases({
if (homepage) result.homepage = homepage; if (homepage) result.homepage = homepage;
if (repository && repository.url) result.sourceUrl = repository.url; if (repository && repository.url) result.sourceUrl = repository.url;
await renovateCache.set(cacheNamespace, cacheKey, result, cacheMinutes);
return result; return result;
} catch (err) { } catch (err) {
const errorData = { depName, err }; const errorData = { depName, err };
......
...@@ -3,7 +3,8 @@ import _got from '../../lib/util/got'; ...@@ -3,7 +3,8 @@ import _got from '../../lib/util/got';
import { getPkgReleases } from '../../lib/datasource/cdnjs'; import { getPkgReleases } from '../../lib/datasource/cdnjs';
import { DATASOURCE_FAILURE } from '../../lib/constants/error-messages'; import { DATASOURCE_FAILURE } from '../../lib/constants/error-messages';
const got: any = _got; const got: jest.Mock<any> = _got as any;
jest.mock('../../lib/util/got');
let res1 = fs.readFileSync( let res1 = fs.readFileSync(
'test/datasource/cdnjs/_fixtures/d3-force.json', 'test/datasource/cdnjs/_fixtures/d3-force.json',
...@@ -17,55 +18,36 @@ let res2 = fs.readFileSync( ...@@ -17,55 +18,36 @@ let res2 = fs.readFileSync(
); );
res2 = JSON.parse(res2); res2 = JSON.parse(res2);
jest.mock('../../lib/util/got');
describe('datasource/cdnjs', () => { describe('datasource/cdnjs', () => {
describe('getPkgReleases', () => { describe('getPkgReleases', () => {
beforeEach(() => { beforeEach(() => {
jest.clearAllMocks(); jest.clearAllMocks();
global.repoCache = {};
return global.renovateCache.rmAll(); return global.renovateCache.rmAll();
}); });
it('returns null for empty result', async () => { it('returns null for empty result', async () => {
got.mockReturnValueOnce(null); got.mockResolvedValueOnce(null);
expect(await getPkgReleases({ lookupName: 'foo/bar' })).toBeNull(); expect(await getPkgReleases({ lookupName: 'foo/bar' })).toBeNull();
}); });
it('returns null for missing fields', async () => { it('returns null for missing fields', async () => {
got.mockReturnValueOnce({}); got.mockResolvedValueOnce({});
expect(await getPkgReleases({ lookupName: 'foo/bar' })).toBeNull(); expect(await getPkgReleases({ lookupName: 'foo/bar' })).toBeNull();
}); });
it('returns null for 404', async () => { it('returns null for 404', async () => {
got.mockImplementationOnce(() => got.mockRejectedValueOnce({ statusCode: 404 });
Promise.reject({
statusCode: 404,
})
);
expect(await getPkgReleases({ lookupName: 'foo/bar' })).toBeNull(); expect(await getPkgReleases({ lookupName: 'foo/bar' })).toBeNull();
}); });
it('returns null for 401', async () => { it('returns null for 401', async () => {
got.mockImplementationOnce(() => got.mockRejectedValueOnce({ statusCode: 401 });
Promise.reject({
statusCode: 401,
})
);
expect(await getPkgReleases({ lookupName: 'foo/bar' })).toBeNull(); expect(await getPkgReleases({ lookupName: 'foo/bar' })).toBeNull();
}); });
it('throws for 429', async () => { it('throws for 429', async () => {
got.mockImplementationOnce(() => got.mockRejectedValueOnce({ statusCode: 429 });
Promise.reject({
statusCode: 429,
})
);
await expect( await expect(
getPkgReleases({ lookupName: 'foo/bar' }) getPkgReleases({ lookupName: 'foo/bar' })
).rejects.toThrowError(DATASOURCE_FAILURE); ).rejects.toThrowError(DATASOURCE_FAILURE);
}); });
it('throws for 5xx', async () => { it('throws for 5xx', async () => {
got.mockImplementationOnce(() => got.mockRejectedValueOnce({ statusCode: 502 });
Promise.reject({
statusCode: 502,
})
);
await expect( await expect(
getPkgReleases({ lookupName: 'foo/bar' }) getPkgReleases({ lookupName: 'foo/bar' })
).rejects.toThrowError(DATASOURCE_FAILURE); ).rejects.toThrowError(DATASOURCE_FAILURE);
...@@ -77,21 +59,17 @@ describe('datasource/cdnjs', () => { ...@@ -77,21 +59,17 @@ describe('datasource/cdnjs', () => {
expect(await getPkgReleases({ lookupName: 'foo/bar' })).toBeNull(); expect(await getPkgReleases({ lookupName: 'foo/bar' })).toBeNull();
}); });
it('returns null with wrong auth token', async () => { it('returns null with wrong auth token', async () => {
got.mockReturnValueOnce( got.mockRejectedValueOnce({ statusCode: 401 });
Promise.reject({
statusCode: 401,
})
);
const res = await getPkgReleases({ lookupName: 'foo/bar' }); const res = await getPkgReleases({ lookupName: 'foo/bar' });
expect(res).toBeNull(); expect(res).toBeNull();
}); });
it('processes real data', async () => { it('processes real data', async () => {
got.mockReturnValueOnce({ body: res1 }); got.mockResolvedValueOnce({ body: res1 });
const res = await getPkgReleases({ lookupName: 'd3-force/d3-force.js' }); const res = await getPkgReleases({ lookupName: 'd3-force/d3-force.js' });
expect(res).toMatchSnapshot(); expect(res).toMatchSnapshot();
}); });
it('filters releases by asset presence', async () => { it('filters releases by asset presence', async () => {
got.mockReturnValueOnce({ body: res2 }); got.mockResolvedValueOnce({ body: res2 });
const res = await getPkgReleases({ const res = await getPkgReleases({
lookupName: 'bulma/only/0.7.5/style.css', lookupName: 'bulma/only/0.7.5/style.css',
}); });
......
{ {
"name": "bulma",
"filename": "css/bulma.min.css",
"version": "0.8.0",
"description": "Modern CSS framework based on Flexbox",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/jgthms/bulma.git" "url": "git+https://github.com/jgthms/bulma.git"
}, },
"keywords": [
"css",
"sass",
"flexbox",
"responsive",
"framework"
],
"author": "bulma",
"homepage": "http://bulma.io", "homepage": "http://bulma.io",
"autoupdate": {
"source": "npm",
"target": "bulma"
},
"license": "MIT",
"assets": [ "assets": [
{ {
"version": "0.8.0", "version": "0.8.0",
......
{ {
"name": "d3-force",
"filename": "d3-force.min.js",
"version": "1.1.0",
"description": "Force-directed graph layout using velocity Verlet integration.",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/d3/d3-force.git" "url": "https://github.com/d3/d3-force.git"
}, },
"keywords": [
"d3",
"d3-module",
"layout",
"network",
"graph",
"force",
"verlet",
"infovis"
],
"author": "d3-force",
"homepage": "https://d3js.org/d3-force/", "homepage": "https://d3js.org/d3-force/",
"autoupdate": {
"source": "npm",
"target": "d3-force"
},
"license": "BSD-3-Clause",
"assets": [ "assets": [
{ {
"version": "1.1.0", "version": "1.1.0",
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment