[npm] - Last update badge added (#10641)
* Added npm last update badge * extended NpmBase class instead of BaseJsonService. * added scoped packages to last update. * introduced additionalQueryParamSchema this is to add other query params schema, other than the one present in NpmBase. * removed version query param * in absence of modified date, it'll fetch created. * removed version query param. * added dist-tags. * Update services/npm/npm-last-update.service.js Co-authored-by: jNullj <15849761+jNullj@users.noreply.github.com> * refactored handle method for dist-tags. * Update services/npm/npm-last-update.service.js Co-authored-by: chris48s <chris48s@users.noreply.github.com> * added date validation check. * added date validation check. * added date validation check. --------- Co-authored-by: jNullj <15849761+jNullj@users.noreply.github.com> Co-authored-by: chris48s <chris48s@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
ad82f7647a
commit
8c7872a666
@@ -143,4 +143,25 @@ export default class NpmBase extends BaseJsonService {
|
||||
|
||||
return this.constructor._validate(packageData, packageDataSchema)
|
||||
}
|
||||
|
||||
async fetch({ registryUrl, scope, packageName, schema }) {
|
||||
registryUrl = registryUrl || this.constructor.defaultRegistryUrl
|
||||
let url
|
||||
|
||||
if (scope === undefined) {
|
||||
url = `${registryUrl}/${packageName}`
|
||||
} else {
|
||||
const scoped = this.constructor.encodeScopedPackage({
|
||||
scope,
|
||||
packageName,
|
||||
})
|
||||
url = `${registryUrl}/${scoped}`
|
||||
}
|
||||
|
||||
return this._requestJson({
|
||||
url,
|
||||
schema,
|
||||
httpErrors: { 404: 'package not found' },
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
106
services/npm/npm-last-update.service.js
Normal file
106
services/npm/npm-last-update.service.js
Normal file
@@ -0,0 +1,106 @@
|
||||
import Joi from 'joi'
|
||||
import dayjs from 'dayjs'
|
||||
import { InvalidResponse, NotFound, pathParam, queryParam } from '../index.js'
|
||||
import { formatDate } from '../text-formatters.js'
|
||||
import { age as ageColor } from '../color-formatters.js'
|
||||
import NpmBase, { packageNameDescription } from './npm-base.js'
|
||||
|
||||
const updateResponseSchema = Joi.object({
|
||||
time: Joi.object({
|
||||
created: Joi.string().required(),
|
||||
modified: Joi.string().required(),
|
||||
})
|
||||
.pattern(Joi.string().required(), Joi.string().required())
|
||||
.required(),
|
||||
'dist-tags': Joi.object()
|
||||
.pattern(Joi.string().required(), Joi.string().required())
|
||||
.required(),
|
||||
}).required()
|
||||
|
||||
export class NpmLastUpdate extends NpmBase {
|
||||
static category = 'activity'
|
||||
|
||||
static route = this.buildRoute('npm/last-update', { withTag: true })
|
||||
|
||||
static openApi = {
|
||||
'/npm/last-update/{packageName}': {
|
||||
get: {
|
||||
summary: 'NPM Last Update',
|
||||
parameters: [
|
||||
pathParam({
|
||||
name: 'packageName',
|
||||
example: 'verdaccio',
|
||||
packageNameDescription,
|
||||
}),
|
||||
queryParam({
|
||||
name: 'registry_uri',
|
||||
example: 'https://registry.npmjs.com',
|
||||
}),
|
||||
],
|
||||
},
|
||||
},
|
||||
'/npm/last-update/{packageName}/{tag}': {
|
||||
get: {
|
||||
summary: 'NPM Last Update (with dist tag)',
|
||||
parameters: [
|
||||
pathParam({
|
||||
name: 'packageName',
|
||||
example: 'verdaccio',
|
||||
packageNameDescription,
|
||||
}),
|
||||
pathParam({
|
||||
name: 'tag',
|
||||
example: 'next-8',
|
||||
}),
|
||||
queryParam({
|
||||
name: 'registry_uri',
|
||||
example: 'https://registry.npmjs.com',
|
||||
}),
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
static defaultBadgeData = { label: 'last updated' }
|
||||
|
||||
static render({ date }) {
|
||||
return {
|
||||
message: formatDate(date),
|
||||
color: ageColor(date),
|
||||
}
|
||||
}
|
||||
|
||||
async handle(namedParams, queryParams) {
|
||||
const { scope, packageName, tag, registryUrl } =
|
||||
this.constructor.unpackParams(namedParams, queryParams)
|
||||
|
||||
const packageData = await this.fetch({
|
||||
registryUrl,
|
||||
scope,
|
||||
packageName,
|
||||
schema: updateResponseSchema,
|
||||
})
|
||||
|
||||
let date
|
||||
|
||||
if (tag) {
|
||||
const tagVersion = packageData['dist-tags'][tag]
|
||||
|
||||
if (!tagVersion) {
|
||||
throw new NotFound({ prettyMessage: 'tag not found' })
|
||||
}
|
||||
|
||||
date = dayjs(packageData.time[tagVersion])
|
||||
} else {
|
||||
const timeKey = packageData.time.modified ? 'modified' : 'created'
|
||||
|
||||
date = dayjs(packageData.time[timeKey])
|
||||
}
|
||||
|
||||
if (!date.isValid) {
|
||||
throw new InvalidResponse({ prettyMessage: 'invalid date' })
|
||||
}
|
||||
|
||||
return this.constructor.render({ date })
|
||||
}
|
||||
}
|
||||
53
services/npm/npm-last-update.tester.js
Normal file
53
services/npm/npm-last-update.tester.js
Normal file
@@ -0,0 +1,53 @@
|
||||
import { isFormattedDate } from '../test-validators.js'
|
||||
import { createServiceTester } from '../tester.js'
|
||||
|
||||
export const t = await createServiceTester()
|
||||
|
||||
t.create('last updated date (valid package)')
|
||||
.get('/verdaccio.json')
|
||||
.expectBadge({
|
||||
label: 'last updated',
|
||||
message: isFormattedDate,
|
||||
})
|
||||
|
||||
t.create('last updated date (invalid package)')
|
||||
.get('/not-a-package.json')
|
||||
.expectBadge({
|
||||
label: 'last updated',
|
||||
message: 'package not found',
|
||||
})
|
||||
|
||||
t.create('last update from custom repository (valid scenario)')
|
||||
.get('/verdaccio.json?registry_uri=https://registry.npmjs.com')
|
||||
.expectBadge({
|
||||
label: 'last updated',
|
||||
message: isFormattedDate,
|
||||
})
|
||||
|
||||
t.create('last update scoped package (valid scenario)')
|
||||
.get('/@npm/types.json')
|
||||
.expectBadge({
|
||||
label: 'last updated',
|
||||
message: isFormattedDate,
|
||||
})
|
||||
|
||||
t.create('last update scoped package (invalid scenario)')
|
||||
.get('/@not-a-scoped-package/not-a-valid-package.json')
|
||||
.expectBadge({
|
||||
label: 'last updated',
|
||||
message: 'package not found',
|
||||
})
|
||||
|
||||
t.create('last updated date with tag (valid scenario)')
|
||||
.get('/verdaccio/latest.json')
|
||||
.expectBadge({
|
||||
label: 'last updated',
|
||||
message: isFormattedDate,
|
||||
})
|
||||
|
||||
t.create('last updated date (invalid tag)')
|
||||
.get('/verdaccio/not-a-valid-tag.json')
|
||||
.expectBadge({
|
||||
label: 'last updated',
|
||||
message: 'tag not found',
|
||||
})
|
||||
Reference in New Issue
Block a user