Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
},
"dependencies": {
"@gitbeaker/core": "^43.4.0",
"@gitbeaker/requester-utils": "^43.8.0",
"@gitbeaker/rest": "^43.4.0",
"@octokit/rest": "^22.0.0",
"@sentry/node": "^10.23.0",
Expand Down
3 changes: 2 additions & 1 deletion api/scripts/seed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ const seed = async () => {
description: undefined,
url: "https://www.wikidata.org/",
kind: "wikidata",
priority: 1
priority: 1,
configuration: undefined
} satisfies Source;
await db.insertInto("sources").values(source).execute();

Expand Down
29 changes: 17 additions & 12 deletions api/src/core/adapters/CNLL/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,25 @@
// SPDX-FileCopyrightText: 2024-2025 Université Grenoble Alpes
// SPDX-License-Identifier: MIT

import { SecondarySourceGateway } from "../../ports/SourceGateway";
import { SourceGateway } from "../../ports/SourceGateway";
import { getCNLLSoftwareExternalData } from "./getExternalData";
import { getCnllPrestatairesSill } from "../getCnllPrestatairesSill";

export const cnllSourceGateway: SecondarySourceGateway = {
sourceType: "ComptoirDuLibre",
sourceProfile: "Secondary",
softwareExternal: { getById: getCNLLSoftwareExternalData },
discoverSoftwareLinks: async () => {
const cnllProviders = await getCnllPrestatairesSill();
return cnllProviders.map(provider => ({
externalId: provider.sill_id.toString(),
softwareId: provider.sill_id,
softwareName: provider.nom
}));
export type CNLLGateway = SourceGateway & {
softwareExtra: NonNullable<SourceGateway["softwareExtra"]>;
};

export const cnllSourceGateway: CNLLGateway = {
sourceType: "CNLL",
softwareExtra: {
getSoftwareExternal: getCNLLSoftwareExternalData,
getDiscoverSoftwareLinks: async () => {
const cnllProviders = await getCnllPrestatairesSill();
return cnllProviders.map(provider => ({
externalId: provider.sill_id.toString(),
softwareId: provider.sill_id,
softwareName: provider.nom
}));
}
}
};
7 changes: 2 additions & 5 deletions api/src/core/adapters/GitHub/api/repo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,8 @@ const parseURL = (repoUrl: string | URL): { repo: string; owner: string } => {
};
};

export const repoGitHubEndpointMaker = (params: { githubPersonalAccessTokenForApiRateLimit?: string }) => {
const { githubPersonalAccessTokenForApiRateLimit } = params;
const octokit = new Octokit({
auth: githubPersonalAccessTokenForApiRateLimit
});
export const gitHubEndpointMaker = (params?: { auth?: string }) => {
const octokit = new Octokit(params?.auth ? { auth: params.auth } : {});

return {
repo: {
Expand Down
5 changes: 3 additions & 2 deletions api/src/core/adapters/GitHub/getExternalData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,16 @@ import type { GetSoftwareExternal } from "../../ports/GetSoftwareExternal";
import type { SoftwareExternal } from "../../types/SoftwareTypes";
import { Source } from "../../usecases/readWriteSillData";
import { identifersUtils } from "../../../tools/identifiersTools";
import { repoGitHubEndpointMaker } from "./api/repo";
import { gitHubEndpointMaker } from "./api/repo";

export const getGitHubSoftwareExternalData: GetSoftwareExternal = memoize(
async ({ externalId, source }: { externalId: string; source: Source }): Promise<SoftwareExternal | undefined> => {
if (source.kind !== "GitHub") throw new Error("This source if not compatible with GitHub Adapter");
if (source.url !== "https://github.com/")
throw new Error("This source doesn't allow custom url, please set it properly.");

const gitHubApi = repoGitHubEndpointMaker({});
const configApi = source?.configuration?.auth ? { auth: source?.configuration?.auth } : {};
const gitHubApi = gitHubEndpointMaker(configApi);
if (!gitHubApi) throw new Error("This GitHub url provided doesn't work.");

const repoUrl = externalId.includes("https://github.com") ? externalId : `https://github.com/${externalId}`;
Expand Down
5 changes: 3 additions & 2 deletions api/src/core/adapters/GitHub/getSofrwareFormData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import { GetSoftwareFormData } from "../../ports/GetSoftwareFormData";
import { SoftwareFormData, Source } from "../../usecases/readWriteSillData";
import { resolveOsAndPlatforms } from "../../utils";
import { repoGitHubEndpointMaker } from "./api/repo";
import { gitHubEndpointMaker } from "./api/repo";

import memoize from "memoizee";

Expand All @@ -15,7 +15,8 @@ export const getGitHubSoftwareFOrm: GetSoftwareFormData = memoize(
if (source.url !== "https://github.com/")
throw new Error("This source doesn't allow custom url, please set it properly.");

const gitHubApi = repoGitHubEndpointMaker({});
const configApi = source?.configuration?.auth ? { auth: source?.configuration?.auth } : {};
const gitHubApi = gitHubEndpointMaker(configApi);

const repoData = await gitHubApi.repo.get({ repoUrl: externalId });
if (!repoData) throw new Error(`This GitHub url (${externalId}) provided doesn't work.`);
Expand Down
5 changes: 3 additions & 2 deletions api/src/core/adapters/GitHub/getSoftwareOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import type {
SoftwareExternalDataOption
} from "../../ports/GetSoftwareExternalDataOptions";
import { Source } from "../../usecases/readWriteSillData";
import { GitHubAPI, repoGitHubEndpointMaker } from "./api/repo";
import { GitHubAPI, gitHubEndpointMaker } from "./api/repo";

const gitHubSoftwareToExternalOption =
({ source }: { source: Source }) =>
Expand All @@ -29,7 +29,8 @@ export const getGitHubSoftwareOptions: GetSoftwareExternalDataOptions = async ({
if (source.url !== "https://github.com/")
throw new Error("This source doesn't allow custom url, please set it properly.");

const gitHubApi = repoGitHubEndpointMaker({});
const configApi = source?.configuration?.auth ? { auth: source?.configuration?.auth } : {};
const gitHubApi = gitHubEndpointMaker(configApi);
const result = await gitHubApi.search.repo.searchByName(queryString);

if (!result) return [];
Expand Down
20 changes: 14 additions & 6 deletions api/src/core/adapters/GitHub/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,23 @@
// SPDX-FileCopyrightText: 2024-2025 Université Grenoble Alpes
// SPDX-License-Identifier: MIT

import { PrimarySourceGateway } from "../../ports/SourceGateway";
import { SourceGateway } from "../../ports/SourceGateway";
import { getGitHubSoftwareExternalData } from "./getExternalData";
import { getGitHubSoftwareFOrm } from "./getSofrwareFormData";
import { getGitHubSoftwareOptions } from "./getSoftwareOptions";

export const gitHubSourceGateway: PrimarySourceGateway = {
export type GitHubGateway = SourceGateway & {
softwareExtra: NonNullable<SourceGateway["softwareExtra"]>;
software: NonNullable<SourceGateway["software"]>;
};

export const gitHubSourceGateway: GitHubGateway = {
sourceType: "GitHub",
sourceProfile: "Primary",
softwareExternal: { getById: getGitHubSoftwareExternalData },
softwareOptions: { getById: getGitHubSoftwareOptions },
softwareForm: { getById: getGitHubSoftwareFOrm }
software: {
getSoftwareForm: getGitHubSoftwareFOrm,
getSoftwareOptions: getGitHubSoftwareOptions
},
softwareExtra: {
getSoftwareExternal: getGitHubSoftwareExternalData
}
};
29 changes: 0 additions & 29 deletions api/src/core/adapters/GitLab/api/project.ts

This file was deleted.

5 changes: 2 additions & 3 deletions api/src/core/adapters/GitLab/getExternalData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,13 @@ import type { SoftwareExternal } from "../../types/SoftwareTypes";
import { Source } from "../../usecases/readWriteSillData";
import { identifersUtils } from "../../../tools/identifiersTools";
import { resolveExternalReferenceToProject } from "./api/utils";
import { convertSourceConfigToBaseRequestOptions } from "../../../tools/sourceConfig";

export const getGitLabSoftwareExternalData: GetSoftwareExternal = memoize(
async ({ externalId, source }: { externalId: string; source: Source }): Promise<SoftwareExternal | undefined> => {
if (source.kind !== "GitLab") throw new Error("This source if not compatible with GitLab Adapter");

const api = new Gitlab({
host: source.url
});
const api = new Gitlab(convertSourceConfigToBaseRequestOptions(source));

const project = await resolveExternalReferenceToProject({ externalId, gitLabApi: api });
if (!project) return;
Expand Down
5 changes: 2 additions & 3 deletions api/src/core/adapters/GitLab/getSoftwareFormData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,13 @@ import { GetSoftwareFormData } from "../../ports/GetSoftwareFormData";
import { SoftwareFormData, Source } from "../../usecases/readWriteSillData";
import { resolveOsAndPlatforms } from "../../utils";
import { resolveExternalReferenceToProject } from "./api/utils";
import { convertSourceConfigToBaseRequestOptions } from "../../../tools/sourceConfig";

export const getGitLabSoftwareForm: GetSoftwareFormData = memoize(
async ({ externalId, source }: { externalId: string; source: Source }): Promise<SoftwareFormData | undefined> => {
if (source.kind !== "GitLab") throw new Error("This source if not compatible with GitLab Adapter");

const api = new Gitlab({
host: source.url
});
const api = new Gitlab(convertSourceConfigToBaseRequestOptions(source));

const project = await resolveExternalReferenceToProject({ externalId, gitLabApi: api });
if (!project) return;
Expand Down
5 changes: 2 additions & 3 deletions api/src/core/adapters/GitLab/getSoftwareOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type {
SoftwareExternalDataOption
} from "../../ports/GetSoftwareExternalDataOptions";
import { Source } from "../../usecases/readWriteSillData";
import { convertSourceConfigToBaseRequestOptions } from "../../../tools/sourceConfig";

const gitLabSoftwareToExternalOption =
({ source }: { source: Source }) =>
Expand All @@ -26,9 +27,7 @@ export const getGitLabSoftwareOptions: GetSoftwareExternalDataOptions = async ({

console.info(`GitLab doesn't support multi languages, ${language} not used`);

const api = new Gitlab({
host: source.url
});
const api = new Gitlab(convertSourceConfigToBaseRequestOptions(source));

const result = await api.Projects.all({ search: queryString });

Expand Down
20 changes: 14 additions & 6 deletions api/src/core/adapters/GitLab/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,23 @@
// SPDX-FileCopyrightText: 2024-2025 Université Grenoble Alpes
// SPDX-License-Identifier: MIT

import { PrimarySourceGateway } from "../../ports/SourceGateway";
import { SourceGateway } from "../../ports/SourceGateway";
import { getGitLabSoftwareExternalData } from "./getExternalData";
import { getGitLabSoftwareForm } from "./getSoftwareFormData";
import { getGitLabSoftwareOptions } from "./getSoftwareOptions";

export const gitLabSourceGateway: PrimarySourceGateway = {
export const gitLabSourceGateway: GitLabGateway = {
sourceType: "GitLab",
sourceProfile: "Primary",
softwareExternal: { getById: getGitLabSoftwareExternalData },
softwareOptions: { getById: getGitLabSoftwareOptions },
softwareForm: { getById: getGitLabSoftwareForm }
software: {
getSoftwareForm: getGitLabSoftwareForm,
getSoftwareOptions: getGitLabSoftwareOptions
},
softwareExtra: {
getSoftwareExternal: getGitLabSoftwareExternalData
}
};

export type GitLabGateway = SourceGateway & {
softwareExtra: NonNullable<SourceGateway["softwareExtra"]>;
software: NonNullable<SourceGateway["software"]>;
};
44 changes: 26 additions & 18 deletions api/src/core/adapters/comptoirDuLibre/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,34 @@
import { getCDLSoftwareOptions } from "./getCDLSoftwareOptions";
import { getCDLSoftwareExternalData } from "./getCDLExternalData";
import { getCDLFormData } from "./getCDLFormData";
import { PrimarySourceGateway } from "../../ports/SourceGateway";
import { SourceGateway } from "../../ports/SourceGateway";
import { comptoirDuLibreApi } from "../comptoirDuLibreApi";

export const comptoirDuLibreSourceGateway: PrimarySourceGateway = {
export type CDLGateway = SourceGateway & {
softwareExtra: NonNullable<SourceGateway["softwareExtra"]>;
software: NonNullable<SourceGateway["software"]>;
};

export const comptoirDuLibreSourceGateway: CDLGateway = {
sourceType: "ComptoirDuLibre",
sourceProfile: "Primary",
softwareExternal: { getById: getCDLSoftwareExternalData },
softwareOptions: { getById: getCDLSoftwareOptions },
softwareForm: { getById: getCDLFormData },
discoverSoftwareLinks: async () => {
const cdlData = await comptoirDuLibreApi.getComptoirDuLibre();
return cdlData.softwares
.filter(software => !Array.isArray(software.external_resources.sill))
.map(software => {
const sill = software.external_resources.sill as { id: number };
return {
externalId: software.id.toString(),
softwareId: sill.id,
softwareName: software.name
};
});
software: {
getSoftwareForm: getCDLFormData,
getSoftwareOptions: getCDLSoftwareOptions
},
softwareExtra: {
getSoftwareExternal: getCDLSoftwareExternalData,
getDiscoverSoftwareLinks: async () => {
const cdlData = await comptoirDuLibreApi.getComptoirDuLibre();
return cdlData.softwares
.filter(software => !Array.isArray(software.external_resources.sill))
.map(software => {
const sill = software.external_resources.sill as { id: number };
return {
externalId: software.id.toString(),
softwareId: sill.id,
softwareName: software.name
};
});
}
}
};
1 change: 0 additions & 1 deletion api/src/core/adapters/comptoirDuLibreApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// SPDX-FileCopyrightText: 2024-2025 Université Grenoble Alpes
// SPDX-License-Identifier: MIT

import fetch from "node-fetch";
import { type ComptoirDuLibreApi, zComptoirDuLibre } from "../ports/ComptoirDuLibreApi";
import cheerio, { type CheerioAPI, type Cheerio, type Element } from "cheerio";
import memoize from "memoizee";
Expand Down
7 changes: 7 additions & 0 deletions api/src/core/adapters/dbApi/kysely/kysely.database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,12 +140,19 @@ type SimilarExternalSoftwareExternalDataTable = {
sourceSlug: string;
};

export type SourceConfig = {
queryTimeout?: number;
auth?: string;
rateLimitRetryDuration?: number;
};

type SourcesTable = {
slug: string;
kind: ExternalDataOriginKind;
url: string;
priority: number;
description: JSONColumnType<LocalizedString> | null;
configuration: JSONColumnType<SourceConfig> | null;
};

type SoftwareAttributeDefinitionsTable = {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { Kysely } from "kysely";

export async function up(db: Kysely<any>): Promise<void> {
await db.schema.alterTable("sources").addColumn("configuration", "jsonb").execute();
}

export async function down(db: Kysely<any>): Promise<void> {
await db.schema.alterTable("sources").dropColumn("configuration").execute();
}
4 changes: 1 addition & 3 deletions api/src/core/adapters/getCnllPrestatairesSill.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
// SPDX-FileCopyrightText: 2024-2025 Université Grenoble Alpes
// SPDX-License-Identifier: MIT

import fetch from "node-fetch";
import memoize from "memoizee";
import * as https from "https";
import { z } from "zod";
import { zCnllPrestatairesSill, type GetCnllPrestatairesSill } from "../ports/GetCnllPrestatairesSill";

Expand All @@ -14,7 +12,7 @@ export const getCnllPrestatairesSill: GetCnllPrestatairesSill = memoize(
async () => {
try {
console.info("Fetching cnll prestataires sill");
const res = await fetch(url, { "agent": new https.Agent({ "rejectUnauthorized": false }) });
const res = await fetch(url);

if (res.status !== 200) {
throw new Error(`Failed to fetch ${url}`);
Expand Down
Loading