Skip to content

Commit

Permalink
refactor crate versions
Browse files Browse the repository at this point in the history
  • Loading branch information
nyurik committed Aug 9, 2023
1 parent ca81ff9 commit cfc3e59
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 27 deletions.
41 changes: 40 additions & 1 deletion services/crates/crates-base.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Joi from 'joi'
import { nonNegativeInteger } from '../validators.js'
import { BaseJsonService } from '../index.js'
import { BaseJsonService, InvalidParameter } from '../index.js'

const keywords = ['Rust']

Expand Down Expand Up @@ -47,6 +47,45 @@ class BaseCratesService extends BaseJsonService {
: `https://crates.io/api/v1/crates/${crate}?include=versions,downloads`
return this._requestJson({ schema, url })
}

/** get the default version for a crate using the same logic as crates.io
* this should be kept in sync with the crates.io implementation in
* https://github.com/rust-lang/crates.io/blob/20bbac9f5521cb4888ef63f8464fddb28fd973f5/app/models/crate.js#L34-L41
*
* @param {object} crate the `json.crate` response from crates.io
* @returns {string} the default version string, or throws an error
*/
static defaultVersion(crate) {
if (crate) {
if (crate.max_stable_version) {
return crate.max_stable_version
}
if (crate.max_version && crate.max_version !== '0.0.0') {
return crate.max_version
}
}
throw new InvalidParameter({
prettyMessage:
'default version not found, possibly all versions were yanked',
})
}

/** get the default version object from the `json.versions` array.
*
* @param {object} crate the `json.crate` response from crates.io
* @param {object[]} versions the `json.versions` response from crates.io
* @returns {object} the default version object, or throws an error if not found
*/
static defaultVersionObject(crate, versions) {
const lastVerNum = BaseCratesService.defaultVersion(crate)
const version = versions.find(ver => ver.num === lastVerNum)
if (!version) {
throw new InvalidParameter({
prettyMessage: 'version object not found in the versions array',
})
}
return version
}
}

export { BaseCratesService, keywords }
14 changes: 6 additions & 8 deletions services/crates/crates-downloads.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,16 +68,14 @@ export default class CratesDownloads extends BaseCratesService {
transform({ variant, json }) {
switch (variant) {
case 'dv':
let lastVer = json.version
if (json.crate) {
const lastVerNum = json.crate.max_stable_version
? json.crate.max_stable_version
: json.crate.max_version
lastVer =
json.versions.find(ver => ver.num === lastVerNum) ||
json.versions[0]
return BaseCratesService.defaultVersionObject(
json.crate,
json.versions,
).downloads
} else {
return json.version.downloads
}
return lastVer.downloads
case 'dr':
return json.crate.recent_downloads || 0
default:
Expand Down
16 changes: 4 additions & 12 deletions services/crates/crates-license.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,24 +28,16 @@ export default class CratesLicense extends BaseCratesService {
return { message }
}

static transform({ errors, version, versions, crate }) {
static transform({ errors, version, crate, versions }) {
// crates.io returns a 200 response with an errors object in
// error scenarios, e.g. https://crates.io/api/v1/crates/libc/0.1
if (errors) {
throw new InvalidResponse({ prettyMessage: errors[0].detail })
}

let license
if (version) {
license = version.license
} else {
const lastVerNum = crate.max_stable_version
? crate.max_stable_version
: crate.max_version
const lastVer =
versions.find(ver => ver.num === lastVerNum) || versions[0]
license = lastVer.license
}
const license = version
? version.license
: BaseCratesService.defaultVersionObject(crate, versions).license

if (!license) {
throw new InvalidResponse({ prettyMessage: 'invalid null license' })
Expand Down
13 changes: 10 additions & 3 deletions services/crates/crates-license.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import CratesLicense from './crates-license.service.js'
describe('CratesLicense', function () {
test(CratesLicense.transform, () => {
given({
crate: { max_stable_version: '1.0.0', max_version: '1.0.0' },
version: { num: '1.0.0', license: 'MIT' },
versions: [{ license: 'MIT/Apache 2.0' }],
versions: [{ num: '1.0.0', license: 'MIT/Apache 2.0' }],
}).expect({ license: 'MIT' })
given({
versions: [{ license: 'MIT/Apache 2.0' }],
crate: { max_stable_version: '1.0.0', max_version: '1.0.0' },
versions: [{ num: '1.0.0', license: 'MIT/Apache 2.0' }],
}).expect({ license: 'MIT/Apache 2.0' })
})

Expand All @@ -31,7 +33,12 @@ describe('CratesLicense', function () {
})

it('throws InvalidResponse on null license with latest version', function () {
expect(() => CratesLicense.transform({ versions: [{ license: null }] }))
expect(() =>
CratesLicense.transform({
crate: { max_stable_version: '1.0.0', max_version: '1.0.0' },
versions: [{ num: '1.0.0', license: null }],
}),
)
.to.throw(InvalidResponse)
.with.property('prettyMessage', 'invalid null license')
})
Expand Down
4 changes: 1 addition & 3 deletions services/crates/crates-version.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@ export default class CratesVersion extends BaseCratesService {
if (json.errors) {
throw new InvalidResponse({ prettyMessage: json.errors[0].detail })
}
return json.crate.max_stable_version
? json.crate.max_stable_version
: json.crate.max_version
return BaseCratesService.defaultVersion(json.crate)
}

async handle({ crate }) {
Expand Down

0 comments on commit cfc3e59

Please sign in to comment.