Skip to content

Commit

Permalink
refactor(rubygems): Extract v1 API handling (#23474)
Browse files Browse the repository at this point in the history
  • Loading branch information
zharinov committed Jul 23, 2023
1 parent 9e77d59 commit 0e2083e
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 45 deletions.
30 changes: 30 additions & 0 deletions lib/modules/datasource/rubygems/common.ts
@@ -0,0 +1,30 @@
import { assignKeys } from '../../../util/assign-keys';
import type { Http, SafeJsonError } from '../../../util/http';
import type { AsyncResult } from '../../../util/result';
import { joinUrlParts as join } from '../../../util/url';
import type { Release, ReleaseResult } from '../types';
import { GemMetadata, GemVersions } from './schema';

export function getV1Releases(
http: Http,
registryUrl: string,
packageName: string
): AsyncResult<ReleaseResult, SafeJsonError> {
const fileName = `${packageName}.json`;
const versionsUrl = join(registryUrl, '/api/v1/versions', fileName);
const metadataUrl = join(registryUrl, '/api/v1/gems', fileName);

const addMetadata = (releases: Release[]): Promise<ReleaseResult> =>
http
.getJsonSafe(metadataUrl, GemMetadata)
.transform((metadata) =>
assignKeys({ releases } as ReleaseResult, metadata, [
'changelogUrl',
'sourceUrl',
'homepage',
])
)
.unwrap({ releases });

return http.getJsonSafe(versionsUrl, GemVersions).transform(addMetadata);
}
63 changes: 25 additions & 38 deletions lib/modules/datasource/rubygems/metadata-cache.ts
@@ -1,10 +1,10 @@
import hasha from 'hasha';
import { logger } from '../../../logger';
import * as packageCache from '../../../util/cache/package';
import type { Http } from '../../../util/http';
import { joinUrlParts, parseUrl } from '../../../util/url';
import { AsyncResult, Result } from '../../../util/result';
import { parseUrl } from '../../../util/url';
import type { ReleaseResult } from '../types';
import { GemMetadata, GemVersions } from './schema';
import { getV1Releases } from './common';

interface CacheRecord {
hash: string;
Expand All @@ -19,39 +19,21 @@ export class MetadataCache {
packageName: string,
versions: string[]
): Promise<ReleaseResult> {
const hash = hasha(versions, { algorithm: 'sha256' });
const cacheNs = `datasource-rubygems`;
const cacheKey = `metadata-cache:${registryUrl}:${packageName}`;
const oldCache = await packageCache.get<CacheRecord>(cacheNs, cacheKey);
if (oldCache?.hash === hash) {
return oldCache.data;
}

try {
const { body: releases } = await this.http.getJson(
joinUrlParts(registryUrl, '/api/v1/versions', `${packageName}.json`),
GemVersions
);

const { body: metadata } = await this.http.getJson(
joinUrlParts(registryUrl, '/api/v1/gems', `${packageName}.json`),
GemMetadata
);

const data: ReleaseResult = { releases };

if (metadata.changelogUrl) {
data.changelogUrl = metadata.changelogUrl;
}

if (metadata.sourceUrl) {
data.sourceUrl = metadata.sourceUrl;
}

if (metadata.homepage) {
data.homepage = metadata.homepage;
}
const hash = hasha(versions, { algorithm: 'sha256' });

const loadCache = (): AsyncResult<ReleaseResult, unknown> =>
Result.wrapNullable(
packageCache.get<CacheRecord>(cacheNs, cacheKey),
'cache-not-found'
).transform((cache) => {
return hash === cache.hash
? Result.ok(cache.data)
: Result.err('cache-outdated');
});

const saveCache = async (data: ReleaseResult): Promise<ReleaseResult> => {
const registryHostname = parseUrl(registryUrl)?.hostname;
if (registryHostname === 'rubygems.org') {
const newCache: CacheRecord = { hash, data };
Expand All @@ -66,10 +48,15 @@ export class MetadataCache {
}

return data;
} catch (err) {
logger.debug({ err }, 'Rubygems: failed to fetch metadata');
const releases = versions.map((version) => ({ version }));
return { releases };
}
};

return await loadCache()
.catch(() =>
getV1Releases(this.http, registryUrl, packageName).transform(saveCache)
)
.catch(() =>
Result.ok({ releases: versions.map((version) => ({ version })) })
)
.unwrapOrThrow();
}
}
14 changes: 7 additions & 7 deletions lib/modules/datasource/rubygems/schema.ts
Expand Up @@ -17,10 +17,10 @@ export const MarshalledVersionInfo = LooseArray(
export const GemMetadata = z
.object({
name: z.string(),
version: z.string().nullish().catch(null),
changelog_uri: z.string().nullish().catch(null),
homepage_uri: z.string().nullish().catch(null),
source_code_uri: z.string().nullish().catch(null),
version: z.string().optional().catch(undefined),
changelog_uri: z.string().optional().catch(undefined),
homepage_uri: z.string().optional().catch(undefined),
source_code_uri: z.string().optional().catch(undefined),
})
.transform(
({
Expand All @@ -44,9 +44,9 @@ export const GemVersions = LooseArray(
.object({
number: z.string(),
created_at: z.string(),
platform: z.string().nullable().catch(null),
ruby_version: z.string().nullable().catch(null),
rubygems_version: z.string().nullable().catch(null),
platform: z.string().optional().catch(undefined),
ruby_version: z.string().optional().catch(undefined),
rubygems_version: z.string().optional().catch(undefined),
metadata: z
.object({
changelog_uri: z.string().optional().catch(undefined),
Expand Down

0 comments on commit 0e2083e

Please sign in to comment.