Skip to content

Commit c5a1914

Browse files
JeromeBuguillermau
andauthored
breaking change multi source (#390)
Co-authored-by: Guillaume Bourdat <[email protected]>
1 parent 41ce691 commit c5a1914

File tree

108 files changed

+5186
-2467
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

108 files changed

+5186
-2467
lines changed

.vscode/launch.json

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
"cwd": "${workspaceFolder}/api",
2323
"runtimeArgs": [
2424
"run",
25-
"import"
25+
"job:import"
2626
],
2727
},
2828
{
@@ -33,7 +33,28 @@
3333
"cwd": "${workspaceFolder}/api",
3434
"runtimeArgs": [
3535
"run",
36-
"update"
36+
"job:update"
37+
],
38+
},
39+
{
40+
"type": "node",
41+
"request": "launch",
42+
"name": "Fullcheck",
43+
"runtimeExecutable": "yarn",
44+
"cwd": "${workspaceFolder}",
45+
"runtimeArgs": [
46+
"fullcheck"
47+
],
48+
},
49+
{
50+
"type": "node",
51+
"request": "launch",
52+
"name": "Import From Inner Identifers",
53+
"runtimeExecutable": "yarn",
54+
"cwd": "${workspaceFolder}/api",
55+
"runtimeArgs": [
56+
"run",
57+
"job:import-from-inner-identifiers"
3758
],
3859
}
3960
]

api/package.json

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,28 @@
1010
"types": "dist/src/lib/index.d.ts",
1111
"scripts": {
1212
"migrate": "dotenv -e ../.env -- kysely migrate",
13-
"migrate:down": "dotenv -e ../.env -- kysely migrate down",
1413
"db:up": "yarn migrate latest",
1514
"test": "vitest --watch=false --no-file-parallelism",
1615
"dev": "yarn build && yarn start",
1716
"generate-translation-schema": "node scripts/generate-translation-schema.js",
1817
"build": "tsc && cp -r src/customization dist/src/",
1918
"start": "yarn db:up && dotenv -e ../.env -- node dist/src/entrypoints/start-api.js",
20-
"update": "dotenv -e ../.env -- node dist/src/entrypoints/update.js",
19+
"job:import": "dotenv -e ../.env -- node dist/src/entrypoints/import.js",
20+
"job:import-from-inner-identifiers": "dotenv -e ../.env -- node dist/src/entrypoints/import-from-inner-identifiers.js",
21+
"job:update": "dotenv -e ../.env -- node dist/src/entrypoints/update.js",
2122
"update-then-wait": "./update-then-wait.sh",
22-
"import": "dotenv -e ../.env -- node dist/src/entrypoints/import.js",
2323
"_format": "prettier \"**/*.{ts,tsx,json,md}\"",
2424
"format": "yarn run _format --write",
2525
"format:check": "yarn run _format --list-different",
2626
"link-in-web": "ts-node --skipProject scripts/link-in-app.ts sill-web",
2727
"db:seed": "yarn build && dotenv -e ../.env -- node dist/scripts/seed.js",
2828
"typecheck": "tsc --noEmit",
29-
"dev:db": "docker compose -f ../docker-compose.resources.yml up -d",
30-
"dev:reset": "docker compose -f ../docker-compose.resources.yml down && rm -rf ../docker-data && docker compose -f ../docker-compose.resources.yml up -d",
29+
"dev:db:up": "docker compose -f ../docker-compose.resources.yml up -d",
30+
"dev:db:down": "docker compose -f ../docker-compose.resources.yml down",
31+
"dev:db:flush": "rm -rf ../docker-data",
32+
"dev:db:reset": "yarn dev:db:down && yarn dev:db:flush && yarn dev:db:up",
33+
"dev:dbs:mig:up": "dotenv -e ../.env -- kysely migrate up",
34+
"dev:dbs:mig:down": "dotenv -e ../.env -- kysely migrate down",
3135
"knip": "knip"
3236
},
3337
"author": "DINUM",
@@ -73,7 +77,7 @@
7377
"deepmerge": "^4.3.1",
7478
"generate-schema": "^2.6.0",
7579
"jsdom": "^26.0.0",
76-
"kysely": "^0.27.4",
80+
"kysely": "^0.28.2",
7781
"kysely-ctl": "^0.12.0",
7882
"pg": "^8.11.5",
7983
"tsafe": "^1.6.6",

api/scripts/seed.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { Database } from "../src/core/adapters/dbApi/kysely/kysely.database";
1111
import { createPgDialect } from "../src/core/adapters/dbApi/kysely/kysely.dialect";
1212
import { SoftwareFormData, Source } from "../src/lib/ApiTypes";
1313
import { OmitFromExisting } from "../src/core/utils";
14+
import { makeCreateSofware } from "../src/core/usecases/createSoftware";
1415

1516
const seed = async () => {
1617
const dbUrl = process.env.DATABASE_URL;
@@ -35,7 +36,7 @@ const seed = async () => {
3536
console.info("Adding source");
3637
const source = {
3738
slug: "wikidata",
38-
description: null,
39+
description: undefined,
3940
url: "https://www.wikidata.org/",
4041
kind: "wikidata",
4142
priority: 1
@@ -50,6 +51,8 @@ const seed = async () => {
5051
sub: null
5152
};
5253

54+
const UCCreateSofware = makeCreateSofware(dbApi);
55+
5356
console.info("Adding user");
5457
const userId = await dbApi.user.add(someUser);
5558

@@ -63,7 +66,6 @@ const seed = async () => {
6366
},
6467
externalIdForSource: undefined,
6568
sourceSlug: "wikidata",
66-
comptoirDuLibreId: undefined,
6769
softwareLicense: "MIT",
6870
softwareMinimalVersion: "18.0.0",
6971
similarSoftwareExternalDataIds: [],
@@ -82,7 +84,6 @@ const seed = async () => {
8284
},
8385
externalIdForSource: undefined,
8486
sourceSlug: "wikidata",
85-
comptoirDuLibreId: undefined,
8687
softwareLicense: "GPL-2.0",
8788
softwareMinimalVersion: "2.0.0",
8889
similarSoftwareExternalDataIds: [],
@@ -102,7 +103,6 @@ const seed = async () => {
102103
},
103104
externalIdForSource: undefined,
104105
sourceSlug: "wikidata",
105-
comptoirDuLibreId: undefined,
106106
softwareLicense: "Apache-2.0",
107107
softwareMinimalVersion: "4.1.0",
108108
similarSoftwareExternalDataIds: [],
@@ -121,7 +121,6 @@ const seed = async () => {
121121
},
122122
externalIdForSource: undefined,
123123
sourceSlug: "wikidata",
124-
comptoirDuLibreId: undefined,
125124
softwareLicense: "GPL-2.0",
126125
softwareMinimalVersion: "3.0.0",
127126
similarSoftwareExternalDataIds: [],
@@ -140,7 +139,6 @@ const seed = async () => {
140139
},
141140
externalIdForSource: undefined,
142141
sourceSlug: "wikidata",
143-
comptoirDuLibreId: undefined,
144142
softwareLicense: "GPL-3.0",
145143
softwareMinimalVersion: "2.10.0",
146144
similarSoftwareExternalDataIds: [],
@@ -159,7 +157,6 @@ const seed = async () => {
159157
},
160158
externalIdForSource: "Q110492908",
161159
sourceSlug: "wikidata",
162-
comptoirDuLibreId: 461,
163160
softwareLicense: "MIT",
164161
softwareMinimalVersion: "0.26.25",
165162
similarSoftwareExternalDataIds: [],
@@ -173,7 +170,7 @@ const seed = async () => {
173170
];
174171

175172
for (const formData of softwarePackagesFormData) {
176-
await dbApi.software.create({ userId, formData });
173+
await UCCreateSofware({ userId, formData });
177174
}
178175

179176
// Add instances for Onyxia
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// SPDX-FileCopyrightText: 2021-2025 DINUM <[email protected]>
2+
// SPDX-FileCopyrightText: 2024-2025 Université Grenoble Alpes
3+
// SPDX-License-Identifier: MIT
4+
5+
import memoize from "memoizee";
6+
7+
import { GetSoftwareExternalData, SoftwareExternalData } from "../../ports/GetSoftwareExternalData";
8+
import { Source } from "../../usecases/readWriteSillData";
9+
import { SchemaOrganization } from "../dbApi/kysely/kysely.database";
10+
import { identifersUtils } from "../../../tools/identifiersTools";
11+
import { getCnllPrestatairesSill } from "../getCnllPrestatairesSill";
12+
import { CnllPrestatairesSill } from "../../ports/GetCnllPrestatairesSill";
13+
14+
export const getCNLLSoftwareExternalData: GetSoftwareExternalData = memoize(
15+
async ({
16+
externalId,
17+
source
18+
}: {
19+
externalId: string;
20+
source: Source;
21+
}): Promise<SoftwareExternalData | undefined> => {
22+
if (source.kind !== "CNLL") throw new Error("This source if not compatible with CNLL Adapter");
23+
24+
const cnllProviders = await getCnllPrestatairesSill();
25+
26+
const providersForExternalId = cnllProviders.find(element => element.sill_id.toString() === externalId);
27+
28+
if (!providersForExternalId) return undefined;
29+
30+
return formatCNLLProvidersToExternalData(providersForExternalId, source);
31+
}
32+
);
33+
34+
const cnllProviderToCMProdivers = (provider: CnllPrestatairesSill.Prestataire): SchemaOrganization => {
35+
return {
36+
"@type": "Organization" as const,
37+
name: provider.nom,
38+
url: provider.url ?? undefined,
39+
identifiers: [
40+
identifersUtils.makeSIRENIdentifier({
41+
SIREN: provider.siren,
42+
additionalType: "Organization"
43+
})
44+
]
45+
};
46+
};
47+
48+
const formatCNLLProvidersToExternalData = (
49+
cnllProdivers: CnllPrestatairesSill,
50+
source: Source
51+
): SoftwareExternalData => ({
52+
externalId: cnllProdivers.sill_id.toString(),
53+
sourceSlug: source.slug,
54+
developers: [],
55+
label: { "fr": cnllProdivers.nom },
56+
description: { "fr": "" },
57+
isLibreSoftware: true,
58+
logoUrl: undefined,
59+
websiteUrl: undefined,
60+
sourceUrl: undefined,
61+
documentationUrl: undefined,
62+
license: undefined,
63+
softwareVersion: undefined,
64+
keywords: [],
65+
programmingLanguages: [],
66+
applicationCategories: [],
67+
publicationTime: undefined,
68+
referencePublications: [],
69+
identifiers: [
70+
identifersUtils.makeCNLLIdentifier({
71+
cNNLId: cnllProdivers.sill_id.toString()
72+
})
73+
],
74+
providers: cnllProdivers.prestataires.map(prodiver => cnllProviderToCMProdivers(prodiver))
75+
});
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// SPDX-FileCopyrightText: 2021-2025 DINUM <[email protected]>
2+
// SPDX-FileCopyrightText: 2024-2025 Université Grenoble Alpes
3+
// SPDX-License-Identifier: MIT
4+
5+
import { SecondarySourceGateway } from "../../ports/SourceGateway";
6+
import { getCNLLSoftwareExternalData } from "./getExternalData";
7+
8+
export const cnllSourceGateway: SecondarySourceGateway = {
9+
sourceType: "ComptoirDuLibre",
10+
sourceProfile: "Secondary",
11+
softwareExternalData: {
12+
getById: getCNLLSoftwareExternalData
13+
}
14+
};
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
// SPDX-FileCopyrightText: 2021-2025 DINUM <[email protected]>
2+
// SPDX-FileCopyrightText: 2024-2025 Université Grenoble Alpes
3+
// SPDX-License-Identifier: MIT
4+
5+
import memoize from "memoizee";
6+
7+
import { GetSoftwareExternalData, SoftwareExternalData } from "../../ports/GetSoftwareExternalData";
8+
import { Source } from "../../usecases/readWriteSillData";
9+
import { comptoirDuLibreApi } from "../comptoirDuLibreApi";
10+
import { ComptoirDuLibre } from "../../ports/ComptoirDuLibreApi";
11+
import { SchemaOrganization } from "../dbApi/kysely/kysely.database";
12+
import { identifersUtils } from "../../../tools/identifiersTools";
13+
14+
export const getCDLSoftwareExternalData: GetSoftwareExternalData = memoize(
15+
async ({
16+
externalId,
17+
source
18+
}: {
19+
externalId: string;
20+
source: Source;
21+
}): Promise<SoftwareExternalData | undefined> => {
22+
const comptoirAPi = await comptoirDuLibreApi.getComptoirDuLibre();
23+
24+
const comptoirSoftware = comptoirAPi.softwares.find(softwareItem => softwareItem.id.toString() === externalId);
25+
26+
if (!comptoirSoftware) return undefined;
27+
28+
return formatCDLSoftwareToExternalData(comptoirSoftware, source);
29+
},
30+
{
31+
maxAge: 3 * 3600 * 1000
32+
}
33+
);
34+
35+
const cdlProviderToCMProdivers = (provider: ComptoirDuLibre.Provider): SchemaOrganization => {
36+
return {
37+
"@type": "Organization" as const,
38+
name: provider.name,
39+
url: provider.external_resources.website ?? undefined,
40+
identifiers: [
41+
identifersUtils.makeCDLIdentifier({
42+
cdlId: provider.id.toString(),
43+
url: provider.url,
44+
additionalType: "Organization"
45+
})
46+
]
47+
};
48+
};
49+
50+
const formatCDLSoftwareToExternalData = (
51+
cdlSoftwareItem: ComptoirDuLibre.Software,
52+
source: Source
53+
): SoftwareExternalData => {
54+
const splittedCNLLUrl = !Array.isArray(cdlSoftwareItem.external_resources.cnll)
55+
? cdlSoftwareItem.external_resources.cnll.url.split("/")
56+
: undefined;
57+
58+
return {
59+
externalId: cdlSoftwareItem.id.toString(),
60+
sourceSlug: source.slug,
61+
developers: [],
62+
label: { "fr": cdlSoftwareItem.name },
63+
description: { "fr": "" },
64+
isLibreSoftware: true,
65+
//
66+
logoUrl: undefined, // Use scrapper ?
67+
websiteUrl: cdlSoftwareItem.external_resources.website ?? undefined,
68+
sourceUrl: cdlSoftwareItem.external_resources.repository ?? undefined,
69+
documentationUrl: undefined,
70+
license: cdlSoftwareItem.licence,
71+
softwareVersion: undefined,
72+
keywords: [],
73+
programmingLanguages: [],
74+
applicationCategories: [],
75+
publicationTime: undefined,
76+
referencePublications: [],
77+
identifiers: [
78+
identifersUtils.makeCDLIdentifier({
79+
cdlId: cdlSoftwareItem.id.toString(),
80+
url: cdlSoftwareItem.url,
81+
additionalType: "Software"
82+
}),
83+
...(!Array.isArray(cdlSoftwareItem.external_resources.cnll) && splittedCNLLUrl
84+
? [
85+
identifersUtils.makeCNLLIdentifier({
86+
cNNLId: splittedCNLLUrl[splittedCNLLUrl.length - 1],
87+
url: cdlSoftwareItem.external_resources.cnll.url
88+
})
89+
]
90+
: []),
91+
...(!Array.isArray(cdlSoftwareItem.external_resources.framalibre)
92+
? [
93+
identifersUtils.makeFramaIndentifier({
94+
framaLibreId: cdlSoftwareItem.external_resources.framalibre.slug,
95+
url: cdlSoftwareItem.external_resources.framalibre.url,
96+
additionalType: "Software"
97+
})
98+
]
99+
: []),
100+
...(!Array.isArray(cdlSoftwareItem.external_resources.wikidata)
101+
? [
102+
identifersUtils.makeWikidataIdentifier({
103+
wikidataId: cdlSoftwareItem.external_resources.wikidata.id,
104+
url: cdlSoftwareItem.external_resources.wikidata.url,
105+
additionalType: "Software"
106+
})
107+
]
108+
: [])
109+
],
110+
providers: cdlSoftwareItem.providers.map(prodiver => cdlProviderToCMProdivers(prodiver))
111+
};
112+
};

0 commit comments

Comments
 (0)