From 225f7bf6e54bbc9a86d44cc1005d41e57d0eedc9 Mon Sep 17 00:00:00 2001 From: Jordan GAZEAU Date: Tue, 11 Oct 2022 00:19:43 +0200 Subject: [PATCH] feat: add jsdelivr asset manager --- README.md | 7 + src/cli/bmycCli.ts | 15 +- src/model/assetManagers/assetManagerType.ts | 1 + src/model/assetManagers/jsdelivr.ts | 70 +++++++++ src/model/assetManagers/unpkg.ts | 6 +- src/model/configurationFile/asset.ts | 2 + .../tempSubdir/bmyc-github-content-ok.js | 0 .../tempSubdir/bmyc-unpkg-axios-ok.js | 0 test/model/assetManager/test-jsdelivr.ts | 134 ++++++++++++++++++ test/model/assetManager/test-unpkg.ts | 2 +- test/resources/.bmycconfig-incorrect.json | 2 +- test/resources/.bmycconfig-specific.json | 22 ++- test/resources/.bmycconfig.json | 22 ++- .../cdnjs-sample-unexisting-file.json | 2 +- test/resources/cdnjs-sample-valid.json | 4 +- test/resources/config-ok.json | 16 ++- .../jsdelivr-sample-unexisting-cdn.json | 6 + .../jsdelivr-sample-unexisting-file.json | 6 + .../jsdelivr-sample-unexisting-package.json | 6 + test/resources/jsdelivr-sample-valid.json | 6 + .../unpkg-sample-unexisting-file.json | 2 +- test/resources/unpkg-sample-valid.json | 4 +- 22 files changed, 309 insertions(+), 26 deletions(-) create mode 100644 src/model/assetManagers/jsdelivr.ts create mode 100644 tempDirectory/tempSubdir/bmyc-github-content-ok.js create mode 100644 tempDirectory/tempSubdir/bmyc-unpkg-axios-ok.js create mode 100644 test/model/assetManager/test-jsdelivr.ts create mode 100644 test/resources/jsdelivr-sample-unexisting-cdn.json create mode 100644 test/resources/jsdelivr-sample-unexisting-file.json create mode 100644 test/resources/jsdelivr-sample-unexisting-package.json create mode 100644 test/resources/jsdelivr-sample-valid.json diff --git a/README.md b/README.md index 734e76d..93c91a5 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ In that case, Bmyc should just fit your needs. * [github](https://github.com/) (Sources from repository) * [cdnjs](https://cdnjs.com/) * [unpkg](https://unpkg.com/) + * [jsdelivr](https://www.jsdelivr.com/) ## How to use it? @@ -52,6 +53,12 @@ Examples: bmyc --config "./myconfig.json" Use specific configuration file bmyc --summary-pr "./summary-pr.md" Generate markdown summary results to describe a Pull Request +Package managers available: + - cdnjs + - github + - unpkg + - jsdelivr + Additional information: GitHub: https://github.com/jgazeau/bmyc.git Documentation: https://github.com/jgazeau/bmyc#readme diff --git a/src/cli/bmycCli.ts b/src/cli/bmycCli.ts index b0300de..9776ae0 100644 --- a/src/cli/bmycCli.ts +++ b/src/cli/bmycCli.ts @@ -2,6 +2,7 @@ import kleur = require('kleur'); import {hideBin} from 'yargs/helpers'; import {getOutputWidth} from '../utils/helpers'; import {IArgumentsParser, ICliArguments} from './iArgumentsParser'; +import {AssetManagerType} from '../model/assetManagers/assetManagerType'; import { CLI_USAGE, CONFIG_OPTION, @@ -92,7 +93,9 @@ export class BmycCli { }) .wrap(getOutputWidth()) .epilog( - `Additional information: + `Package managers available: +${this.printAvailablePackages(2)} +Additional information: GitHub: ${kleur.green('https://github.com/jgazeau/bmyc.git')} Documentation: ${kleur.blue('https://github.com/jgazeau/bmyc#readme')} Issues: ${kleur.red('https://github.com/jgazeau/bmyc/issues')} @@ -104,4 +107,14 @@ export class BmycCli { this.parser.argv; return Promise.resolve(BmycCli.cliArgs); } + + private printAvailablePackages(offset: number): string { + let availablePackages = ''; + Object.values(AssetManagerType).forEach(entry => { + availablePackages = `${availablePackages}${' '.repeat( + offset + )}- ${entry}\n`; + }); + return availablePackages; + } } diff --git a/src/model/assetManagers/assetManagerType.ts b/src/model/assetManagers/assetManagerType.ts index 1c6cffd..f82a5a4 100644 --- a/src/model/assetManagers/assetManagerType.ts +++ b/src/model/assetManagers/assetManagerType.ts @@ -2,4 +2,5 @@ export enum AssetManagerType { CDNJS = 'cdnjs', GITHUB = 'github', UNPKG = 'unpkg', + JSDELIVR = 'jsdelivr', } diff --git a/src/model/assetManagers/jsdelivr.ts b/src/model/assetManagers/jsdelivr.ts new file mode 100644 index 0000000..d387308 --- /dev/null +++ b/src/model/assetManagers/jsdelivr.ts @@ -0,0 +1,70 @@ +/* eslint-disable @typescript-eslint/no-explicit-any*/ +import axios from 'axios'; +import {AssetManager} from './assetManager'; +import {IsDefined, IsString} from 'class-validator'; +import {unknownLatestVersionError} from '../bmycError'; + +const JSDELIVR_API_URL = 'https://data.jsdelivr.com/v1'; +const JSDELIVR_DATA_URL = 'https://cdn.jsdelivr.net'; +const JSDELIVR_PACKAGE_PATH = 'package'; + +export class Jsdelivr extends AssetManager { + @IsDefined() + @IsString() + private cdn: string; + /* c8 ignore start */ + public get _cdn(): string { + return this.cdn; + } + public set _cdn(value: string) { + this.cdn = value; + } + /* c8 ignore stop */ + + @IsDefined() + @IsString() + private package: string; + /* c8 ignore start */ + public get _package(): string { + return this.package; + } + public set _package(value: string) { + this.package = value; + } + /* c8 ignore stop */ + + @IsDefined() + @IsString() + private filePath: string; + /* c8 ignore start */ + public get _filePath(): string { + return this.filePath; + } + public set _filePath(value: string) { + this.filePath = value; + } + /* c8 ignore stop */ + + getLatestVersion(): Promise { + return axios({ + method: 'get', + url: `${JSDELIVR_API_URL}/${JSDELIVR_PACKAGE_PATH}/${this.cdn}/${this.package}`, + }).then((response: any) => { + const version = response.data.tags.latest; + if (version) { + return Promise.resolve(version); + } else { + throw unknownLatestVersionError(`${this.cdn}/${this.package}`); + } + }); + } + + getContent(assetVersion: string): Promise { + return axios({ + method: 'get', + url: `${JSDELIVR_DATA_URL}/${this.cdn}/${this.package}@${assetVersion}/${this.filePath}`, + }).then((response: any) => { + return Promise.resolve(response.data); + }); + } +} diff --git a/src/model/assetManagers/unpkg.ts b/src/model/assetManagers/unpkg.ts index 9281bbc..93e200c 100644 --- a/src/model/assetManagers/unpkg.ts +++ b/src/model/assetManagers/unpkg.ts @@ -4,7 +4,7 @@ import {AssetManager} from './assetManager'; import {IsDefined, IsString} from 'class-validator'; import {unknownLatestVersionError} from '../bmycError'; -const UNPKG_URL = 'https://unpkg.com'; +const UNPKG_API_URL = 'https://unpkg.com'; export class Unpkg extends AssetManager { @IsDefined() @@ -34,7 +34,7 @@ export class Unpkg extends AssetManager { getLatestVersion(): Promise { return axios({ method: 'get', - url: `${UNPKG_URL}/${this.library}/`, + url: `${UNPKG_API_URL}/${this.library}/`, }).then((response: any) => { // Unpkg should redirect to the latest version of the package by default let version = response.request.res.responseUrl.replace( @@ -53,7 +53,7 @@ export class Unpkg extends AssetManager { getContent(assetVersion: string): Promise { return axios({ method: 'get', - url: `${UNPKG_URL}/${this.library}@${assetVersion}/${this.filePath}`, + url: `${UNPKG_API_URL}/${this.library}@${assetVersion}/${this.filePath}`, }).then((response: any) => { return Promise.resolve(response.data); }); diff --git a/src/model/configurationFile/asset.ts b/src/model/configurationFile/asset.ts index c5c6be4..d06fd8e 100644 --- a/src/model/configurationFile/asset.ts +++ b/src/model/configurationFile/asset.ts @@ -5,6 +5,7 @@ import {Unpkg} from '../assetManagers/unpkg'; import {Cdnjs} from '../assetManagers/cdnjs'; import {Github} from '../assetManagers/github'; import {Exclude, Type} from 'class-transformer'; +import {Jsdelivr} from '../assetManagers/jsdelivr'; import {AssetManagerType} from '../assetManagers/assetManagerType'; import {AssetManager as AssetManager} from '../assetManagers/assetManager'; import {AssetManagerValidator as AssetManagerValidator} from '../assetManagers/assetManagerValidator'; @@ -77,6 +78,7 @@ export class Asset { {value: Cdnjs, name: AssetManagerType.CDNJS}, {value: Github, name: AssetManagerType.GITHUB}, {value: Unpkg, name: AssetManagerType.UNPKG}, + {value: Jsdelivr, name: AssetManagerType.JSDELIVR}, ], }, }) diff --git a/tempDirectory/tempSubdir/bmyc-github-content-ok.js b/tempDirectory/tempSubdir/bmyc-github-content-ok.js new file mode 100644 index 0000000..e69de29 diff --git a/tempDirectory/tempSubdir/bmyc-unpkg-axios-ok.js b/tempDirectory/tempSubdir/bmyc-unpkg-axios-ok.js new file mode 100644 index 0000000..e69de29 diff --git a/test/model/assetManager/test-jsdelivr.ts b/test/model/assetManager/test-jsdelivr.ts new file mode 100644 index 0000000..dac09b5 --- /dev/null +++ b/test/model/assetManager/test-jsdelivr.ts @@ -0,0 +1,134 @@ +/* eslint-disable @typescript-eslint/no-explicit-any*/ +import 'reflect-metadata'; +import * as path from 'path'; +import * as fs from 'fs-extra'; +import {expect} from 'chai'; +import {PathLike} from 'fs-extra'; +import {testResourcesPath} from '../../testUtils/const'; +import {setChaiAsPromised} from '../../testUtils/helpers'; +import {deserializeObject} from '../../../src/utils/helpers'; +import {Jsdelivr} from '../../../src/model/assetManagers/jsdelivr'; +import {ConfigurationError} from '../../../src/model/configurationError'; + +const JSDELIVR_SAMPLE_UNEXISTING_CDN: PathLike = path.join( + testResourcesPath, + 'jsdelivr-sample-unexisting-cdn.json' +); +const JSDELIVR_SAMPLE_UNEXISTING_PACKAGE: PathLike = path.join( + testResourcesPath, + 'jsdelivr-sample-unexisting-package.json' +); +const JSDELIVR_SAMPLE_UNEXISTING_FILE: PathLike = path.join( + testResourcesPath, + 'jsdelivr-sample-unexisting-file.json' +); +const JSDELIVR_SAMPLE_VALID: PathLike = path.join( + testResourcesPath, + 'jsdelivr-sample-valid.json' +); + +describe('Jsdelivr AssetManager tests', () => { + it('Jsdelivr should implement getLatestVersion', () => { + const jsdelivr: Jsdelivr = new Jsdelivr(); + expect(jsdelivr.getLatestVersion).to.be.instanceof(Function); + }); + it('Jsdelivr should implement getContent', () => { + const jsdelivr: Jsdelivr = new Jsdelivr(); + expect(jsdelivr.getContent).to.be.instanceof(Function); + }); + it('Jsdelivr should throw a ConfigurationError when name not set', () => { + const tempInput: any = JSON.parse( + fs.readFileSync(JSDELIVR_SAMPLE_VALID, 'utf8') + ); + delete tempInput.name; + const input: string = JSON.stringify(tempInput); + expect(() => { + deserializeObject(input, Jsdelivr); + }).to.throw(ConfigurationError); + }); + it('Jsdelivr should throw a ConfigurationError when cdn not set', () => { + const tempInput: any = JSON.parse( + fs.readFileSync(JSDELIVR_SAMPLE_VALID, 'utf8') + ); + delete tempInput.cdn; + const input: string = JSON.stringify(tempInput); + expect(() => { + deserializeObject(input, Jsdelivr); + }).to.throw(ConfigurationError); + }); + it('Jsdelivr should throw a ConfigurationError when package not set', () => { + const tempInput: any = JSON.parse( + fs.readFileSync(JSDELIVR_SAMPLE_VALID, 'utf8') + ); + delete tempInput.package; + const input: string = JSON.stringify(tempInput); + expect(() => { + deserializeObject(input, Jsdelivr); + }).to.throw(ConfigurationError); + }); + it('Jsdelivr should throw a ConfigurationError when filePath not set', () => { + const tempInput: any = JSON.parse( + fs.readFileSync(JSDELIVR_SAMPLE_VALID, 'utf8') + ); + delete tempInput.filePath; + const input: string = JSON.stringify(tempInput); + expect(() => { + deserializeObject(input, Jsdelivr); + }).to.throw(ConfigurationError); + }); + it('getLatestVersion should return the latest version', () => { + setChaiAsPromised(); + const input: string = fs.readFileSync(JSDELIVR_SAMPLE_VALID, 'utf8'); + const jsdelivr: Jsdelivr = deserializeObject(input, Jsdelivr); + return expect(jsdelivr.getLatestVersion()).to.eventually.match( + /^[0-9]+\.[0-9]+\.[0-9]+([.-][a-zA-Z0-9]+)*$/ + ); + }); + it('getLatestVersion should throw a Error when unexisting cdn', () => { + setChaiAsPromised(); + const input: string = fs.readFileSync( + JSDELIVR_SAMPLE_UNEXISTING_CDN, + 'utf8' + ); + const jsdelivr: Jsdelivr = deserializeObject(input, Jsdelivr); + return expect(jsdelivr.getLatestVersion()).to.eventually.be.rejectedWith( + Error, + '400' + ); + }); + it('getLatestVersion should throw a Error when unexisting package', () => { + setChaiAsPromised(); + const input: string = fs.readFileSync( + JSDELIVR_SAMPLE_UNEXISTING_PACKAGE, + 'utf8' + ); + const jsdelivr: Jsdelivr = deserializeObject(input, Jsdelivr); + return expect(jsdelivr.getLatestVersion()).to.eventually.be.rejectedWith( + Error, + '404' + ); + }); + it('getContent should return the latest content', () => { + setChaiAsPromised(); + const input: string = fs.readFileSync(JSDELIVR_SAMPLE_VALID, 'utf8'); + const jsdelivr: Jsdelivr = deserializeObject(input, Jsdelivr); + return jsdelivr.getLatestVersion().then(latestVersion => { + return jsdelivr.getContent(latestVersion).then(content => { + expect(content).to.not.be.empty; + }); + }); + }); + it('getContent should throw a Error when unexisting file', () => { + setChaiAsPromised(); + const input: string = fs.readFileSync( + JSDELIVR_SAMPLE_UNEXISTING_FILE, + 'utf8' + ); + const jsdelivr: Jsdelivr = deserializeObject(input, Jsdelivr); + return jsdelivr.getLatestVersion().then(latestVersion => { + return expect( + jsdelivr.getContent(latestVersion) + ).to.eventually.be.rejectedWith(Error, '404'); + }); + }); +}); diff --git a/test/model/assetManager/test-unpkg.ts b/test/model/assetManager/test-unpkg.ts index a96c5e6..276b11c 100644 --- a/test/model/assetManager/test-unpkg.ts +++ b/test/model/assetManager/test-unpkg.ts @@ -92,7 +92,7 @@ describe('Unpkg AssetManager tests', () => { }); }); }); - it('getContent should throw a BmycError when unexisting file', () => { + it('getContent should throw a Error when unexisting file', () => { setChaiAsPromised(); const input: string = fs.readFileSync(UNPKG_SAMPLE_UNEXISTING_FILE, 'utf8'); const unpkg: Unpkg = deserializeObject(input, Unpkg); diff --git a/test/resources/.bmycconfig-incorrect.json b/test/resources/.bmycconfig-incorrect.json index 80a16d3..2f25793 100644 --- a/test/resources/.bmycconfig-incorrect.json +++ b/test/resources/.bmycconfig-incorrect.json @@ -4,7 +4,7 @@ "localPath": "test/temp/bmyc-cdnjs-incorrect-unknown.min.js", "assetManager": { "name": "cdnjs", - "library": "typescript", + "library": "axios", "fileName": "unknown.min.js" } }, diff --git a/test/resources/.bmycconfig-specific.json b/test/resources/.bmycconfig-specific.json index 1327a85..1ba70c0 100644 --- a/test/resources/.bmycconfig-specific.json +++ b/test/resources/.bmycconfig-specific.json @@ -1,11 +1,11 @@ [ { "name": "asset1", - "localPath": "test/temp/bmyc-cdnjs-specific-typescript.min.js", + "localPath": "test/temp/bmyc-cdnjs-specific-axios.min.js", "assetManager": { "name": "cdnjs", - "library": "typescript", - "fileName": "typescript.min.js" + "library": "axios", + "fileName": "axios.min.js" } }, { @@ -20,11 +20,21 @@ }, { "name": "asset3", - "localPath": "test/temp/bmyc-unpkg-typescript-ok.js", + "localPath": "test/temp/bmyc-unpkg-axios-ok.js", "assetManager": { "name": "unpkg", - "library": "typescript", - "filePath": "bin/tsc" + "library": "axios", + "filePath": "dist/axios.min.js" + } + }, + { + "name": "asset4", + "localPath": "test/temp/bmyc-jsdelivr-axios-ok.js", + "assetManager": { + "name": "jsdelivr", + "cdn": "npm", + "package": "axios", + "filePath": "dist/axios.min.js" } } ] \ No newline at end of file diff --git a/test/resources/.bmycconfig.json b/test/resources/.bmycconfig.json index 58f2a37..602a2f3 100644 --- a/test/resources/.bmycconfig.json +++ b/test/resources/.bmycconfig.json @@ -1,11 +1,11 @@ [ { "name": "asset1", - "localPath": "tempDirectory/tempSubdir/bmyc-cdnjs-typescript.min.js", + "localPath": "tempDirectory/tempSubdir/bmyc-cdnjs-axios.min.js", "assetManager": { "name": "cdnjs", - "library": "typescript", - "fileName": "typescript.min.js" + "library": "axios", + "fileName": "axios.min.js" } }, { @@ -20,11 +20,21 @@ }, { "name": "asset3", - "localPath": "tempDirectory/tempSubdir/bmyc-unpkg-typescript-ok.js", + "localPath": "tempDirectory/tempSubdir/bmyc-unpkg-axios-ok.js", "assetManager": { "name": "unpkg", - "library": "typescript", - "filePath": "bin/tsc" + "library": "axios", + "filePath": "dist/axios.min.js" + } + }, + { + "name": "asset4", + "localPath": "tempDirectory/tempSubdir/bmyc-jsdelivr-axios-ok.js", + "assetManager": { + "name": "jsdelivr", + "cdn": "npm", + "package": "axios", + "filePath": "dist/axios.min.js" } } ] \ No newline at end of file diff --git a/test/resources/cdnjs-sample-unexisting-file.json b/test/resources/cdnjs-sample-unexisting-file.json index 585bce8..cc00e7c 100644 --- a/test/resources/cdnjs-sample-unexisting-file.json +++ b/test/resources/cdnjs-sample-unexisting-file.json @@ -1,5 +1,5 @@ { "name": "cdnjs", - "library": "typescript", + "library": "axios", "fileName": "unknown.min.js" } \ No newline at end of file diff --git a/test/resources/cdnjs-sample-valid.json b/test/resources/cdnjs-sample-valid.json index c2612f2..cf22052 100644 --- a/test/resources/cdnjs-sample-valid.json +++ b/test/resources/cdnjs-sample-valid.json @@ -1,5 +1,5 @@ { "name": "cdnjs", - "library": "typescript", - "fileName": "typescript.min.js" + "library": "axios", + "fileName": "axios.min.js" } \ No newline at end of file diff --git a/test/resources/config-ok.json b/test/resources/config-ok.json index e494041..e8f6020 100644 --- a/test/resources/config-ok.json +++ b/test/resources/config-ok.json @@ -30,8 +30,20 @@ "localPath": "./path/asset3.min.js", "assetManager": { "name": "unpkg", - "library": "typescript", - "filePath": "bin/tsc" + "library": "axios", + "filePath": "dist/axios.min.js" + } + }, + { + "package": "package4", + "hold": false, + "name": "asset4", + "localPath": "./path/asset4.min.js", + "assetManager": { + "name": "jsdelivr", + "cdn": "npm", + "package": "axios", + "filePath": "dist/axios.min.js" } } ] \ No newline at end of file diff --git a/test/resources/jsdelivr-sample-unexisting-cdn.json b/test/resources/jsdelivr-sample-unexisting-cdn.json new file mode 100644 index 0000000..b9e4c6b --- /dev/null +++ b/test/resources/jsdelivr-sample-unexisting-cdn.json @@ -0,0 +1,6 @@ +{ + "name": "jsdelivr", + "cdn": "unknown_cdn", + "package": "axios", + "filePath": "dist/axios.min.js" +} \ No newline at end of file diff --git a/test/resources/jsdelivr-sample-unexisting-file.json b/test/resources/jsdelivr-sample-unexisting-file.json new file mode 100644 index 0000000..b5fad2a --- /dev/null +++ b/test/resources/jsdelivr-sample-unexisting-file.json @@ -0,0 +1,6 @@ +{ + "name": "jsdelivr", + "cdn": "npm", + "package": "axios", + "filePath": "dist/unknown.min.js" +} \ No newline at end of file diff --git a/test/resources/jsdelivr-sample-unexisting-package.json b/test/resources/jsdelivr-sample-unexisting-package.json new file mode 100644 index 0000000..ec01aad --- /dev/null +++ b/test/resources/jsdelivr-sample-unexisting-package.json @@ -0,0 +1,6 @@ +{ + "name": "jsdelivr", + "cdn": "npm", + "package": "unknown_package", + "filePath": "dist/axios.min.js" +} \ No newline at end of file diff --git a/test/resources/jsdelivr-sample-valid.json b/test/resources/jsdelivr-sample-valid.json new file mode 100644 index 0000000..07c580f --- /dev/null +++ b/test/resources/jsdelivr-sample-valid.json @@ -0,0 +1,6 @@ +{ + "name": "jsdelivr", + "cdn": "npm", + "package": "axios", + "filePath": "dist/axios.min.js" +} \ No newline at end of file diff --git a/test/resources/unpkg-sample-unexisting-file.json b/test/resources/unpkg-sample-unexisting-file.json index 8d1e275..bb32153 100644 --- a/test/resources/unpkg-sample-unexisting-file.json +++ b/test/resources/unpkg-sample-unexisting-file.json @@ -1,5 +1,5 @@ { "name": "unpkg", - "library": "typescript", + "library": "axios", "filePath": "unknown.js" } \ No newline at end of file diff --git a/test/resources/unpkg-sample-valid.json b/test/resources/unpkg-sample-valid.json index 6663245..6a2ac1b 100644 --- a/test/resources/unpkg-sample-valid.json +++ b/test/resources/unpkg-sample-valid.json @@ -1,5 +1,5 @@ { "name": "unpkg", - "library": "typescript", - "filePath": "bin/tsc" + "library": "axios", + "filePath": "dist/axios.min.js" } \ No newline at end of file