-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
238 additions
and
0 deletions.
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,201 @@ | ||
import {LibraryArtifact, MetadataResponse} from "../../types"; | ||
import {findLatestVersion} from "./versionUtils"; | ||
|
||
export type GeneratedCode = { | ||
gradle: string; | ||
maven: string; | ||
sbt: string; | ||
pip: string; | ||
}; | ||
|
||
export type ArtifactInfo = { | ||
lib: LibraryArtifact; | ||
code: GeneratedCode; | ||
}; | ||
|
||
export interface JFrogArtifactoryError { | ||
status: number; | ||
message: string; | ||
} | ||
|
||
export interface Errors { | ||
errors: JFrogArtifactoryError[]; | ||
} | ||
|
||
export interface RepositoryDetails { | ||
key: string; | ||
packageType: string; | ||
rclass: string; | ||
} | ||
|
||
export interface PropertiesInfo { | ||
'pypi.version': string[]; | ||
} | ||
|
||
export interface PropsResponse { | ||
properties: PropertiesInfo; | ||
} | ||
|
||
export interface VersionsPropsListResponse { | ||
results: PropsResponse[]; | ||
} | ||
|
||
export async function getErrorMessage(response: Response) { | ||
return ((await response.json()) as Errors).errors[0].message; | ||
} | ||
|
||
export async function getRepositoryType( | ||
fetch: { | ||
(input: RequestInfo | URL, init?: RequestInit): Promise<Response>; | ||
(input: RequestInfo | URL, init?: RequestInit): Promise<Response>; | ||
}, | ||
url: string, | ||
{ repo }: LibraryArtifact, | ||
) { | ||
const response = await fetch(`${url}artifactory/api/repositories/${repo}`); | ||
if (response.status === 404) { | ||
throw new Error(`Repository ${repo} was not found`); | ||
} else { | ||
if (response.status !== 200) { | ||
throw new Error( | ||
`Cannot get repository ${repo} detail info ` + | ||
(await getErrorMessage(response)), | ||
); | ||
} else { | ||
return (await response.json()) as RepositoryDetails; | ||
} | ||
} | ||
} | ||
|
||
export async function getMavenLatestVersion( | ||
fetch: { | ||
(input: RequestInfo | URL, init?: RequestInit): Promise<Response>; | ||
(input: RequestInfo | URL, init?: RequestInit): Promise<Response>; | ||
}, | ||
url: string, | ||
{ group, artifact, repo }: LibraryArtifact, | ||
) { | ||
const response = await fetch( | ||
`${url}artifactory/api/search/latestVersion?g=${group}&a=${artifact}&repos=${repo}`, | ||
); | ||
if (response.status === 404) { | ||
return undefined; | ||
} else { | ||
if (response.status !== 200) { | ||
throw new Error( | ||
`Error getting latest version ` + (await getErrorMessage(response)), | ||
); | ||
} else { | ||
return await response.text(); | ||
} | ||
} | ||
} | ||
|
||
export function getMetadataVersionQuery(artifact: string) { | ||
const query = { | ||
query: | ||
'query ($filter: VersionFilter!, $first: Int, $orderBy: VersionOrder) { versions (filter: $filter, first: $first, orderBy: $orderBy) { edges { node { name, created, modified, package { id }, repos { name, type, leadFilePath }, licenses { name, source }, size, stats { downloadCount }, vulnerabilities { critical, high, medium, low, info, unknown, skipped }, files { name, lead, size, md5, sha1, sha256, mimeType } } } } }', | ||
variables: { | ||
filter: { | ||
packageId: `docker://${artifact}`, | ||
name: '*', | ||
ignorePreRelease: false, | ||
}, | ||
first: 1, | ||
orderBy: { | ||
field: 'NAME_SEMVER', | ||
direction: 'DESC', | ||
}, | ||
}, | ||
}; | ||
return JSON.stringify(query); | ||
} | ||
|
||
export function removeDockerVersion(artifact: string): string { | ||
const versionSeparator = artifact.indexOf(':'); | ||
if (versionSeparator > 0) { | ||
return artifact.substring(0, versionSeparator); | ||
} | ||
return artifact; | ||
} | ||
|
||
export function extractArtifactFromFullDockerName(artifact: string) :string { | ||
// remove domain path | ||
artifact = removeDockerVersion(artifact); | ||
const dotPosition = artifact.lastIndexOf('.'); | ||
const slashPosition = artifact.indexOf('/'); | ||
if (dotPosition >= 0 && dotPosition < slashPosition) { | ||
return artifact.substring(slashPosition +1); | ||
} else { | ||
return artifact; | ||
} | ||
} | ||
|
||
export async function getDockerLatestVersion( | ||
fetch: { | ||
(input: RequestInfo | URL, init?: RequestInit): Promise<Response>; | ||
(input: RequestInfo | URL, init?: RequestInit): Promise<Response>; | ||
}, | ||
url: string, | ||
{ artifact }: LibraryArtifact, | ||
) { | ||
|
||
const response = await fetch(`${url}/metadata/api/v1/query`, { | ||
headers: { | ||
'Content-Type': 'application/json', | ||
}, | ||
method: 'POST', | ||
body: getMetadataVersionQuery(extractArtifactFromFullDockerName(artifact)), | ||
}); | ||
if (response.status === 404) { | ||
return undefined; | ||
} else { | ||
if (!response.ok) { | ||
throw new Error( | ||
`Error getting latest version ` + (await getErrorMessage(response)), | ||
); | ||
} else { | ||
const metadataResponse = | ||
(await response.json()) as MetadataResponse; | ||
const node = metadataResponse.data.versions.edges | ||
.map(items => items.node).pop(); | ||
if (node?.name) { | ||
return {version: node.name, | ||
size: Number(node.size), | ||
statsDownload: node.stats.downloadCount, | ||
lastModified: node.modified ? new Date(node.modified) : undefined}; | ||
} | ||
return undefined; | ||
} | ||
} | ||
} | ||
|
||
export async function getPypiLatestVersion( | ||
fetch: { | ||
(input: RequestInfo | URL, init?: RequestInit): Promise<Response>; | ||
(input: RequestInfo | URL, init?: RequestInit): Promise<Response>; | ||
}, | ||
url: string, | ||
{ artifact, repo }: LibraryArtifact, | ||
) { | ||
const response = await fetch( | ||
`${url}/artifactory/api/search/prop?pypi.name=${artifact}&repos=${repo}`, | ||
); | ||
if (response.status === 404) { | ||
return undefined; | ||
} else { | ||
if (response.status !== 200) { | ||
throw new Error( | ||
`Error getting latest version ` + (await getErrorMessage(response)), | ||
); | ||
} else { | ||
const versionPropsListResponse = | ||
(await response.json()) as VersionsPropsListResponse; | ||
const versions = versionPropsListResponse.results | ||
.map(items => items.properties) | ||
.map(propertiesInfo => propertiesInfo['pypi.version'][0]) | ||
.filter(item => item !== undefined); | ||
return findLatestVersion(versions); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
export function formatDate(date: Date): string { | ||
if (!date) { | ||
return 'N/A'; | ||
} | ||
const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone; | ||
const options: Intl.DateTimeFormatOptions = { | ||
timeZone, | ||
year: 'numeric', | ||
month: 'short', // Month as 3-letter string | ||
day: 'numeric', | ||
hour: '2-digit', | ||
minute: '2-digit', | ||
hour12: false, // Use 24-hour format | ||
}; | ||
|
||
const isoString = date.toLocaleString('en-US', options); | ||
|
||
const parts = isoString.replace(',', '').split(' '); | ||
const year = parts[2]; | ||
const month = parts[0]; | ||
const day = parts[1]; | ||
const time = parts[3]; | ||
|
||
return `${day}-${month}-${year} ${time}`; | ||
} | ||
|
||
export function formatSize(sizeInBytes: number) { | ||
if (!sizeInBytes) { | ||
// null or undefined | ||
return 'N/A'; | ||
} | ||
|
||
const i = Math.floor(Math.log(sizeInBytes) / Math.log(1024)); | ||
return `${Number((sizeInBytes / Math.pow(1024, i)).toFixed(2))} ${ | ||
['B', 'kB', 'MB', 'GB', 'TB'][i] | ||
}`; | ||
} |