Skip to content

Commit 9ac2223

Browse files
committed
fix: get repositories using xml endpoint
Signed-off-by: Mouad BANI <mouad-mb@outlook.com>
1 parent cdd8bf0 commit 9ac2223

3 files changed

Lines changed: 55 additions & 3 deletions

File tree

services/apps/packages_worker/src/nuget/client.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
} from './types'
99

1010
const SERVICE_INDEX_URL = 'https://api.nuget.org/v3/index.json'
11+
const FLAT_CONTAINER_BASE_URL = 'https://api.nuget.org/v3-flatcontainer'
1112

1213
interface ServiceIndexResource {
1314
'@id': string
@@ -144,3 +145,29 @@ export async function fetchRegistration(
144145
throw err
145146
}
146147
}
148+
149+
// The registration API never exposes the nuspec <repository> element — it must be read from the
150+
// raw nuspec XML, served by the flat-container endpoint.
151+
export async function fetchNuspec(
152+
packageId: string,
153+
version: string,
154+
): Promise<string | NuGetFetchError> {
155+
const lowerId = packageId.toLowerCase()
156+
const lowerVersion = version.toLowerCase()
157+
158+
try {
159+
const resp = await axios.get<string>(
160+
`${FLAT_CONTAINER_BASE_URL}/${lowerId}/${lowerVersion}/${lowerId}.nuspec`,
161+
{
162+
responseType: 'text',
163+
transformResponse: (data) => data,
164+
timeout: 15000,
165+
},
166+
)
167+
return resp.data
168+
} catch (err) {
169+
const classified = classifyError(err)
170+
if (classified) return classified
171+
throw err
172+
}
173+
}

services/apps/packages_worker/src/nuget/normalize.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { XMLParser } from 'fast-xml-parser'
2+
13
import { canonicalizeRepoUrl } from '../utils/canonicalizeRepoUrl'
24

35
import {
@@ -28,6 +30,18 @@ function parsePublishedDate(published: string | undefined): Date | null {
2830
return !isNaN(date.getTime()) && date.getUTCFullYear() > 1900 ? date : null
2931
}
3032

33+
const nuspecParser = new XMLParser({ ignoreAttributes: false, attributeNamePrefix: '@_' })
34+
35+
export function parseNuspecRepositoryUrl(nuspecXml: string): string | null {
36+
try {
37+
const doc = nuspecParser.parse(nuspecXml)
38+
const url = doc?.package?.metadata?.repository?.['@_url']
39+
return typeof url === 'string' && url.trim() !== '' ? url.trim() : null
40+
} catch {
41+
return null
42+
}
43+
}
44+
3145
const SCM_HOSTS = ['github.com', 'gitlab.com', 'bitbucket.org']
3246

3347
function isScmUrl(url: string | undefined): boolean {
@@ -56,6 +70,7 @@ export function normalizeNuGetPackage(
5670
packageId: string,
5771
searchResult: NuGetSearchItem | null,
5872
registration: NuGetRegistrationIndex,
73+
nuspecXml?: string | null,
5974
): NormalizedNuGetPackage {
6075
const allLeaves = registration.items.flatMap((page) => page.items ?? [])
6176
const allEntries: NuGetCatalogEntry[] = allLeaves.map((leaf) => leaf.catalogEntry)
@@ -88,7 +103,9 @@ export function normalizeNuGetPackage(
88103
...(latestEntry ? [latestEntry] : []),
89104
]
90105
: [...allEntries].reverse()
91-
const nuspecRepoUrl = entriesForRepo.find((e) => e.repository?.url)?.repository?.url
106+
const catalogRepoUrl = entriesForRepo.find((e) => e.repository?.url)?.repository?.url
107+
const fetchedNuspecRepoUrl = nuspecXml ? parseNuspecRepositoryUrl(nuspecXml) : null
108+
const nuspecRepoUrl = fetchedNuspecRepoUrl ?? catalogRepoUrl
92109
const declaredRepositoryUrl = nuspecRepoUrl ?? null
93110
const repo =
94111
(nuspecRepoUrl ? canonicalizeRepoUrl(nuspecRepoUrl) : null) ??

services/apps/packages_worker/src/nuget/runNuGetEnrichmentLoop.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { getServiceChildLogger } from '@crowd/logging'
1717

1818
import { getNuGetConfig } from '../config'
1919

20-
import { fetchRegistration, fetchSearch } from './client'
20+
import { fetchNuspec, fetchRegistration, fetchSearch } from './client'
2121
import { normalizeNuGetPackage } from './normalize'
2222
import { BatchResult, isNuGetFetchError } from './types'
2323

@@ -98,7 +98,15 @@ async function processPackage(
9898

9999
const searchItem = isNuGetFetchError(searchResult) ? null : searchResult
100100

101-
const normalized = normalizeNuGetPackage(packageId, searchItem, registrationResult)
101+
const preliminary = normalizeNuGetPackage(packageId, searchItem, registrationResult)
102+
103+
let nuspecXml: string | null = null
104+
if (preliminary.latestVersion) {
105+
const nuspecResult = await fetchNuspec(packageId, preliminary.latestVersion)
106+
nuspecXml = isNuGetFetchError(nuspecResult) ? null : nuspecResult
107+
}
108+
109+
const normalized = normalizeNuGetPackage(packageId, searchItem, registrationResult, nuspecXml)
102110

103111
await withDeadlockRetry(() =>
104112
qx.tx(async (t) => {

0 commit comments

Comments
 (0)