@data-fair/catalog-data-fair 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/download.ts +83 -22
- package/lib/imports.ts +10 -3
- package/lib/prepare.ts +16 -4
- package/package.json +3 -2
- package/types/catalogConfig/.type/index.d.ts +5 -0
- package/types/catalogConfig/.type/index.js +19 -2
- package/types/catalogConfig/.type/validate.js +17 -5
- package/types/catalogConfig/schema.json +22 -3
- package/types/datafairSchemas/.type/index.d.ts +13 -0
- package/types/datafairSchemas/schema.json +32 -1
- package/types/importConfig/.type/index.js +1 -0
- package/types/importConfig/.type/validate.js +1 -1
- package/types/importConfig/schema.json +2 -1
package/lib/download.ts
CHANGED
|
@@ -4,6 +4,7 @@ import axios from '@data-fair/lib-node/axios.js'
|
|
|
4
4
|
import * as fs from 'fs'
|
|
5
5
|
import { join } from 'path'
|
|
6
6
|
import { Transform } from 'stream'
|
|
7
|
+
import slugify from 'slugify'
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Retrieves a resource by first fetching its metadata and then downloading the actual resource.
|
|
@@ -15,8 +16,8 @@ import { Transform } from 'stream'
|
|
|
15
16
|
export const getResource = async (context: GetResourceContext<DataFairConfig>): ReturnType<CatalogPlugin['getResource']> => {
|
|
16
17
|
context.log.step('Import de la ressource')
|
|
17
18
|
|
|
18
|
-
const resource = await getMetaData(context)
|
|
19
|
-
resource.filePath = await downloadResource(context, resource)
|
|
19
|
+
const { resource, file } = await getMetaData(context)
|
|
20
|
+
resource.filePath = await downloadResource(context, file, resource)
|
|
20
21
|
|
|
21
22
|
return resource
|
|
22
23
|
}
|
|
@@ -27,11 +28,12 @@ export const getResource = async (context: GetResourceContext<DataFairConfig>):
|
|
|
27
28
|
* @param resourceId the dataset Id to fetch fields from
|
|
28
29
|
* @returns the Resource corresponding to the id by this configuration
|
|
29
30
|
*/
|
|
30
|
-
const getMetaData = async ({ catalogConfig, resourceId, log }: GetResourceContext<DataFairConfig>): Promise<Resource> => {
|
|
31
|
+
const getMetaData = async ({ catalogConfig, resourceId, log, secrets }: GetResourceContext<DataFairConfig>): Promise<{ resource: Resource, file: boolean }> => {
|
|
31
32
|
let dataset: DataFairDataset
|
|
32
33
|
try {
|
|
33
34
|
const url = `${catalogConfig.url}/data-fair/api/v1/datasets/${resourceId}`
|
|
34
|
-
const
|
|
35
|
+
const config = secrets.apiKey ? { headers: { 'x-apiKey': secrets.apiKey } } : undefined
|
|
36
|
+
const res = (await axios.get(url, config))
|
|
35
37
|
if (res.status !== 200) {
|
|
36
38
|
throw new Error(`HTTP error : ${res.status}, ${res.data}`)
|
|
37
39
|
}
|
|
@@ -42,16 +44,34 @@ const getMetaData = async ({ catalogConfig, resourceId, log }: GetResourceContex
|
|
|
42
44
|
throw new Error(`Erreur lors de la récuperation de la resource DataFair. ${e instanceof Error ? e.message : e}`)
|
|
43
45
|
}
|
|
44
46
|
|
|
47
|
+
dataset.schema = (dataset.schema ?? []).map((field) => {
|
|
48
|
+
if (field['x-extension']) {
|
|
49
|
+
return {
|
|
50
|
+
...field,
|
|
51
|
+
key: slugify.default(field.key.replace(/^_/, ''), { lower: true, strict: true, replacement: '_' }), // Ensure no leading underscore
|
|
52
|
+
'x-extension': undefined, // Remove x-extension property if it exists
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return field
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
let size: number | undefined
|
|
59
|
+
if (dataset.storage?.dataFiles && dataset.storage.dataFiles.length > 0) {
|
|
60
|
+
// get the last elt of the array to get the /full file size
|
|
61
|
+
const lastFile = dataset.storage.dataFiles[dataset.storage.dataFiles.length - 1]
|
|
62
|
+
size = lastFile.size
|
|
63
|
+
}
|
|
64
|
+
|
|
45
65
|
const resource: Resource = {
|
|
46
66
|
id: resourceId,
|
|
47
67
|
title: dataset.title,
|
|
48
68
|
description: dataset.description,
|
|
49
69
|
format: 'csv',
|
|
50
|
-
origin:
|
|
70
|
+
origin: dataset.page,
|
|
51
71
|
frequency: dataset.frequency,
|
|
52
72
|
image: dataset.image,
|
|
53
73
|
keywords: dataset.keywords,
|
|
54
|
-
size
|
|
74
|
+
size,
|
|
55
75
|
schema: dataset.schema,
|
|
56
76
|
filePath: '',
|
|
57
77
|
}
|
|
@@ -62,7 +82,8 @@ const getMetaData = async ({ catalogConfig, resourceId, log }: GetResourceContex
|
|
|
62
82
|
href: dataset.license.href ?? '',
|
|
63
83
|
}
|
|
64
84
|
}
|
|
65
|
-
|
|
85
|
+
|
|
86
|
+
return { resource, file: !!dataset.file }
|
|
66
87
|
}
|
|
67
88
|
|
|
68
89
|
/**
|
|
@@ -72,12 +93,14 @@ const getMetaData = async ({ catalogConfig, resourceId, log }: GetResourceContex
|
|
|
72
93
|
* @param res - the metadatas about the resource.
|
|
73
94
|
* @returns A promise resolving to the file path of the downloaded CSV.
|
|
74
95
|
*/
|
|
75
|
-
const downloadResource = async (context: GetResourceContext<DataFairConfig>, res: Resource): Promise<string> => {
|
|
96
|
+
const downloadResource = async (context: GetResourceContext<DataFairConfig>, file: boolean, res: Resource): Promise<string> => {
|
|
76
97
|
const filePath = join(context.tmpDir, `${context.resourceId}.csv`)
|
|
77
98
|
try {
|
|
78
|
-
if (
|
|
99
|
+
if (file && !context.importConfig.fields?.length && !context.importConfig.filters?.length) {
|
|
100
|
+
await context.log.task('downloading', 'Téléchargement en cours...', res.size || NaN)
|
|
79
101
|
await downloadResourceFile(filePath, context)
|
|
80
102
|
} else {
|
|
103
|
+
await context.log.task('downloading', 'Téléchargement en cours...', NaN)
|
|
81
104
|
await downloadResourceLines(filePath, context)
|
|
82
105
|
}
|
|
83
106
|
return filePath
|
|
@@ -97,32 +120,54 @@ const downloadResource = async (context: GetResourceContext<DataFairConfig>, res
|
|
|
97
120
|
* @returns A promise that resolves when the file is successfully downloaded and saved.
|
|
98
121
|
* @throws If there is an error writing the file or fetching the dataset.
|
|
99
122
|
*/
|
|
100
|
-
const downloadResourceFile = async (filePath: string, { catalogConfig, resourceId, log }: GetResourceContext<DataFairConfig>): Promise<void> => {
|
|
123
|
+
const downloadResourceFile = async (filePath: string, { catalogConfig, resourceId, log, secrets }: GetResourceContext<DataFairConfig>): Promise<void> => {
|
|
101
124
|
const url = `${catalogConfig.url}/data-fair/api/v1/datasets/${resourceId}/full`
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
const fileStream = fs.createWriteStream(filePath)
|
|
125
|
+
const headers = secrets.apiKey ? { 'x-apiKey': secrets.apiKey } : undefined
|
|
105
126
|
|
|
106
|
-
const response = await axios.get(url, { responseType: 'stream' })
|
|
127
|
+
const response = await axios.get(url, { responseType: 'stream', headers })
|
|
107
128
|
|
|
108
129
|
if (response.status !== 200) {
|
|
109
130
|
throw new Error(`Error while fetching data: HTTP ${response.statusText}`)
|
|
110
131
|
}
|
|
111
132
|
|
|
112
|
-
|
|
133
|
+
let downloaded = 0
|
|
134
|
+
let lastLogTime = Date.now()
|
|
135
|
+
const logInterval = 500 // ms
|
|
136
|
+
|
|
137
|
+
await new Promise<void>((resolve, reject) => {
|
|
138
|
+
const fileStream = fs.createWriteStream(filePath, { encoding: 'binary' }) // Ensure binary encoding
|
|
139
|
+
|
|
140
|
+
response.data.on('data', (chunk: Buffer) => {
|
|
141
|
+
downloaded += chunk.length
|
|
142
|
+
const now = Date.now()
|
|
143
|
+
if (now - lastLogTime > logInterval) {
|
|
144
|
+
lastLogTime = now
|
|
145
|
+
log.progress('downloading', downloaded)
|
|
146
|
+
.catch(err => console.warn('Progress logging failed:', err))
|
|
147
|
+
}
|
|
148
|
+
})
|
|
149
|
+
|
|
113
150
|
response.data.pipe(fileStream)
|
|
151
|
+
|
|
114
152
|
fileStream.on('finish', () => {
|
|
153
|
+
fileStream.close()
|
|
115
154
|
resolve()
|
|
116
155
|
})
|
|
156
|
+
|
|
117
157
|
response.data.on('error', (err: any) => {
|
|
118
|
-
|
|
158
|
+
fileStream.destroy()
|
|
159
|
+
fs.unlink(filePath, () => { })
|
|
119
160
|
reject(err)
|
|
120
161
|
})
|
|
162
|
+
|
|
121
163
|
fileStream.on('error', (err) => {
|
|
122
|
-
|
|
164
|
+
response.data.destroy()
|
|
165
|
+
fs.unlink(filePath, () => { })
|
|
123
166
|
reject(err)
|
|
124
167
|
})
|
|
125
168
|
})
|
|
169
|
+
|
|
170
|
+
await log.progress('downloading', downloaded, downloaded)
|
|
126
171
|
}
|
|
127
172
|
|
|
128
173
|
/**
|
|
@@ -135,13 +180,15 @@ const downloadResourceFile = async (filePath: string, { catalogConfig, resourceI
|
|
|
135
180
|
* @returns A promise that resolves when the file is successfully downloaded and saved.
|
|
136
181
|
* @throws If there is an error writing the file or fetching the dataset.
|
|
137
182
|
*/
|
|
138
|
-
const downloadResourceLines = async (destFile: string, { catalogConfig, resourceId, importConfig, log }: GetResourceContext<DataFairConfig> & { importConfig: ImportConfig }) => {
|
|
139
|
-
let url: string | null = `${catalogConfig.url}/data-fair/api/v1/datasets/${resourceId}/lines?format=csv&size=
|
|
183
|
+
const downloadResourceLines = async (destFile: string, { catalogConfig, resourceId, importConfig, secrets, log }: GetResourceContext<DataFairConfig> & { importConfig: ImportConfig }): Promise<void> => {
|
|
184
|
+
let url: string | null = `${catalogConfig.url}/data-fair/api/v1/datasets/${resourceId}/lines?format=csv&size=5000`
|
|
140
185
|
|
|
141
186
|
if (importConfig.fields) {
|
|
142
187
|
url += '&select=' + importConfig.fields.map(field => field.key).join(',')
|
|
143
188
|
}
|
|
144
189
|
|
|
190
|
+
const headers = secrets.apiKey ? { 'x-apiKey': secrets.apiKey } : undefined
|
|
191
|
+
|
|
145
192
|
if (importConfig.filters) {
|
|
146
193
|
importConfig.filters.forEach((filter) => {
|
|
147
194
|
switch (filter.type) {
|
|
@@ -160,13 +207,14 @@ const downloadResourceLines = async (destFile: string, { catalogConfig, resource
|
|
|
160
207
|
})
|
|
161
208
|
}
|
|
162
209
|
|
|
163
|
-
|
|
210
|
+
let downloaded = 0
|
|
211
|
+
let pendingLogPromise: Promise<void> | null = null
|
|
212
|
+
|
|
164
213
|
const writer = fs.createWriteStream(destFile)
|
|
165
214
|
let isFirstChunk = true
|
|
166
215
|
|
|
167
216
|
while (url) {
|
|
168
|
-
|
|
169
|
-
const response = await axios.get(url, { responseType: 'stream' })
|
|
217
|
+
const response = await axios.get(url, { responseType: 'stream', headers })
|
|
170
218
|
if (response.status !== 200) {
|
|
171
219
|
throw new Error(`Error while fetching data: HTTP ${response.statusText}`)
|
|
172
220
|
}
|
|
@@ -189,6 +237,18 @@ const downloadResourceLines = async (destFile: string, { catalogConfig, resource
|
|
|
189
237
|
}
|
|
190
238
|
}))
|
|
191
239
|
}
|
|
240
|
+
|
|
241
|
+
stream.on('data', (chunk: Buffer) => {
|
|
242
|
+
downloaded += chunk.length
|
|
243
|
+
|
|
244
|
+
// Only start a new log promise if there isn't one already running
|
|
245
|
+
if (!pendingLogPromise) {
|
|
246
|
+
pendingLogPromise = log.progress('downloading', downloaded)
|
|
247
|
+
.catch(err => console.warn('Progress logging failed:', err))
|
|
248
|
+
.finally(() => { pendingLogPromise = null })
|
|
249
|
+
}
|
|
250
|
+
})
|
|
251
|
+
|
|
192
252
|
stream.pipe(writer, { end: false })
|
|
193
253
|
stream.on('end', () => {
|
|
194
254
|
const linkHeader = response.headers.link
|
|
@@ -203,6 +263,7 @@ const downloadResourceLines = async (destFile: string, { catalogConfig, resource
|
|
|
203
263
|
})
|
|
204
264
|
})
|
|
205
265
|
}
|
|
266
|
+
await log.progress('downloading', downloaded, downloaded)
|
|
206
267
|
writer.end()
|
|
207
268
|
}
|
|
208
269
|
|
package/lib/imports.ts
CHANGED
|
@@ -14,12 +14,18 @@ const prepareCatalog = (dataFairDatasets: DataFairDataset[]): ResourceList => {
|
|
|
14
14
|
const catalog: ResourceList = []
|
|
15
15
|
|
|
16
16
|
for (const dataFairDataset of dataFairDatasets) {
|
|
17
|
+
let size: number | undefined
|
|
18
|
+
if (dataFairDataset.storage?.dataFiles && dataFairDataset.storage.dataFiles.length > 0) {
|
|
19
|
+
const lastFile = dataFairDataset.storage.dataFiles[dataFairDataset.storage.dataFiles.length - 1]
|
|
20
|
+
size = lastFile.size
|
|
21
|
+
}
|
|
17
22
|
catalog.push({
|
|
18
23
|
id: dataFairDataset.id,
|
|
19
24
|
title: dataFairDataset.title,
|
|
20
25
|
format: 'csv',
|
|
21
|
-
size
|
|
26
|
+
size,
|
|
22
27
|
type: 'resource',
|
|
28
|
+
origin: dataFairDataset.page
|
|
23
29
|
} as ResourceList[number])
|
|
24
30
|
}
|
|
25
31
|
return catalog
|
|
@@ -38,9 +44,10 @@ export const listResources = async (config: ListResourcesContext<DataFairConfig,
|
|
|
38
44
|
|
|
39
45
|
let data: DataFairCatalog
|
|
40
46
|
const url = `${config.catalogConfig.url}/data-fair/api/v1/catalog/datasets`
|
|
47
|
+
const headers = config.secrets.apiKey ? { 'x-apiKey': config.secrets.apiKey } : undefined
|
|
41
48
|
try {
|
|
42
|
-
const res = (await axios.get(url, { params: dataFairParams }))
|
|
43
|
-
if (res.status !== 200) {
|
|
49
|
+
const res = (await axios.get(url, { params: dataFairParams, headers }))
|
|
50
|
+
if (res.status !== 200 || typeof res.data !== 'object') {
|
|
44
51
|
throw new Error(`HTTP error : ${res.status}, ${res.data}`)
|
|
45
52
|
}
|
|
46
53
|
data = res.data
|
package/lib/prepare.ts
CHANGED
|
@@ -4,15 +4,27 @@ import type { DataFairConfig } from '#types'
|
|
|
4
4
|
import axios from '@data-fair/lib-node/axios.js'
|
|
5
5
|
|
|
6
6
|
export default async ({ catalogConfig, capabilities, secrets }: PrepareContext<DataFairConfig, DataFairCapabilities>) => {
|
|
7
|
-
//
|
|
8
|
-
|
|
7
|
+
// set the apiKey in the secrets field if it exists
|
|
8
|
+
const apiKey = catalogConfig.apiKey
|
|
9
|
+
if (apiKey && apiKey !== '*************************') {
|
|
10
|
+
secrets.apiKey = apiKey
|
|
11
|
+
catalogConfig.apiKey = '*************************'
|
|
12
|
+
} else if (secrets?.apiKey && (!apiKey || apiKey === '')) {
|
|
13
|
+
delete secrets.apiKey
|
|
14
|
+
} else {
|
|
15
|
+
// The secret is already set, do nothing
|
|
16
|
+
}
|
|
9
17
|
|
|
10
18
|
// test the url
|
|
11
19
|
try {
|
|
12
|
-
|
|
20
|
+
if (!catalogConfig.url) {
|
|
21
|
+
throw new Error('URL du catalogue non définie')
|
|
22
|
+
}
|
|
23
|
+
const config = secrets.apiKey ? { headers: { 'x-apiKey': secrets.apiKey } } : undefined
|
|
24
|
+
await axios.get(catalogConfig.url + '/data-fair/api/v1/catalog/datasets?size=1&select=id', config)
|
|
13
25
|
} catch (e) {
|
|
14
26
|
console.error('Erreur URL pendant la configuration : ', e instanceof Error ? e.message : e)
|
|
15
|
-
throw new Error(
|
|
27
|
+
throw new Error(`Configuration invalide, veuillez vérifier l’URL du catalogue et la clé API si nécessaire (${e instanceof Error ? e.message : e})`)
|
|
16
28
|
}
|
|
17
29
|
|
|
18
30
|
return {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@data-fair/catalog-data-fair",
|
|
3
3
|
"description": "A simple Data Fair plugin for the Data Fair catalogs service.",
|
|
4
|
-
"version": "0.1
|
|
4
|
+
"version": "0.2.1",
|
|
5
5
|
"main": "index.ts",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"scripts": {
|
|
@@ -31,7 +31,8 @@
|
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@data-fair/lib-node": "^2.8.2",
|
|
33
33
|
"@data-fair/lib-utils": "^1.6.0",
|
|
34
|
-
"prom-client": "^15.1.3"
|
|
34
|
+
"prom-client": "^15.1.3",
|
|
35
|
+
"slugify": "^1.6.6"
|
|
35
36
|
},
|
|
36
37
|
"devDependencies": {
|
|
37
38
|
"@commitlint/cli": "^19.8.0",
|
|
@@ -6,9 +6,14 @@ export const schemaExports: string[]
|
|
|
6
6
|
* The url of the catalog
|
|
7
7
|
*/
|
|
8
8
|
export type URL = string;
|
|
9
|
+
/**
|
|
10
|
+
* The Data Fair API key to access the catalog. You can create one from the 'Settings' tab in Data Fair, under the 'API Keys' section.
|
|
11
|
+
*/
|
|
12
|
+
export type APIKeyOptional = string;
|
|
9
13
|
|
|
10
14
|
export type DataFairConfig = {
|
|
11
15
|
url: URL;
|
|
16
|
+
apiKey?: APIKeyOptional;
|
|
12
17
|
}
|
|
13
18
|
|
|
14
19
|
|
|
@@ -38,7 +38,7 @@ export const schema = {
|
|
|
38
38
|
"title": "URL",
|
|
39
39
|
"description": "The url of the catalog",
|
|
40
40
|
"x-i18n-description": {
|
|
41
|
-
"fr": "L'URL du
|
|
41
|
+
"fr": "L'URL du portail où le catalogue est publié. Par exemple: `https://opendata.koumoul.com`."
|
|
42
42
|
},
|
|
43
43
|
"pattern": "^https?://.*[^/]$",
|
|
44
44
|
"errorMessage": "The URL must start with http:// or https:// and must not end with `/`.",
|
|
@@ -46,8 +46,25 @@ export const schema = {
|
|
|
46
46
|
"fr": "L'URL doit commencer par http:// ou https:// et ne pas se terminer par `/`."
|
|
47
47
|
},
|
|
48
48
|
"examples": [
|
|
49
|
-
"https://
|
|
49
|
+
"https://opendata.koumoul.com"
|
|
50
50
|
]
|
|
51
|
+
},
|
|
52
|
+
"apiKey": {
|
|
53
|
+
"type": "string",
|
|
54
|
+
"title": "API Key (Optional)",
|
|
55
|
+
"x-i18n-title": {
|
|
56
|
+
"fr": "Clé API (Optionnelle)"
|
|
57
|
+
},
|
|
58
|
+
"description": "The Data Fair API key to access the catalog. You can create one from the 'Settings' tab in Data Fair, under the 'API Keys' section.",
|
|
59
|
+
"x-i18n-description": {
|
|
60
|
+
"fr": "La clé API Data Fair pour accéder au catalogue. Vous pouvez en créer depuis l'onglet 'Paramètres' de Data Fair, dans la section 'Clés d'API'."
|
|
61
|
+
},
|
|
62
|
+
"layout": {
|
|
63
|
+
"props": {
|
|
64
|
+
"type": "password",
|
|
65
|
+
"autocomplete": "new-password"
|
|
66
|
+
}
|
|
67
|
+
}
|
|
51
68
|
}
|
|
52
69
|
}
|
|
53
70
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"use strict";
|
|
5
5
|
export const validate = validate14;
|
|
6
6
|
export default validate14;
|
|
7
|
-
const schema16 = {"$id":"https://github.com/data-fair/catalog-data-fair/catalog-config","x-exports":["types","validate","schema"],"title":"DataFairConfig","type":"object","additionalProperties":false,"required":["url"],"properties":{"url":{"type":"string","title":"URL","description":"The url of the catalog","x-i18n-description":{"fr":"L'URL du
|
|
7
|
+
const schema16 = {"$id":"https://github.com/data-fair/catalog-data-fair/catalog-config","x-exports":["types","validate","schema"],"title":"DataFairConfig","type":"object","additionalProperties":false,"required":["url"],"properties":{"url":{"type":"string","title":"URL","description":"The url of the catalog","x-i18n-description":{"fr":"L'URL du portail où le catalogue est publié. Par exemple: `https://opendata.koumoul.com`."},"pattern":"^https?://.*[^/]$","errorMessage":"The URL must start with http:// or https:// and must not end with `/`.","x-i18n-errorMessage":{"fr":"L'URL doit commencer par http:// ou https:// et ne pas se terminer par `/`."},"examples":["https://opendata.koumoul.com"]},"apiKey":{"type":"string","title":"API Key (Optional)","x-i18n-title":{"fr":"Clé API (Optionnelle)"},"description":"The Data Fair API key to access the catalog. You can create one from the 'Settings' tab in Data Fair, under the 'API Keys' section.","x-i18n-description":{"fr":"La clé API Data Fair pour accéder au catalogue. Vous pouvez en créer depuis l'onglet 'Paramètres' de Data Fair, dans la section 'Clés d'API'."},"layout":{"props":{"type":"password","autocomplete":"new-password"}}}}};
|
|
8
8
|
const pattern0 = new RegExp("^https?://.*[^/]$", "u");
|
|
9
9
|
|
|
10
10
|
function validate14(data, {instancePath="", parentData, parentDataProperty, rootData=data}={}){
|
|
@@ -23,7 +23,7 @@ vErrors.push(err0);
|
|
|
23
23
|
errors++;
|
|
24
24
|
}
|
|
25
25
|
for(const key0 in data){
|
|
26
|
-
if(!(key0 === "url")){
|
|
26
|
+
if(!((key0 === "url") || (key0 === "apiKey"))){
|
|
27
27
|
const err1 = {instancePath,schemaPath:"#/additionalProperties",keyword:"additionalProperties",params:{additionalProperty: key0},message:"must NOT have additional properties"};
|
|
28
28
|
if(vErrors === null){
|
|
29
29
|
vErrors = [err1];
|
|
@@ -86,9 +86,9 @@ vErrors = emErrs1;
|
|
|
86
86
|
errors = emErrs1.length;
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
const err7 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "
|
|
89
|
+
if(data.apiKey !== undefined){
|
|
90
|
+
if(typeof data.apiKey !== "string"){
|
|
91
|
+
const err7 = {instancePath:instancePath+"/apiKey",schemaPath:"#/properties/apiKey/type",keyword:"type",params:{type: "string"},message:"must be string"};
|
|
92
92
|
if(vErrors === null){
|
|
93
93
|
vErrors = [err7];
|
|
94
94
|
}
|
|
@@ -97,6 +97,18 @@ vErrors.push(err7);
|
|
|
97
97
|
}
|
|
98
98
|
errors++;
|
|
99
99
|
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
const err8 = {instancePath,schemaPath:"#/type",keyword:"type",params:{type: "object"},message:"must be object"};
|
|
104
|
+
if(vErrors === null){
|
|
105
|
+
vErrors = [err8];
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
vErrors.push(err8);
|
|
109
|
+
}
|
|
110
|
+
errors++;
|
|
111
|
+
}
|
|
100
112
|
validate14.errors = vErrors;
|
|
101
113
|
return errors === 0;
|
|
102
114
|
}
|
|
@@ -17,14 +17,33 @@
|
|
|
17
17
|
"title": "URL",
|
|
18
18
|
"description": "The url of the catalog",
|
|
19
19
|
"x-i18n-description": {
|
|
20
|
-
"fr": "L'URL du
|
|
20
|
+
"fr": "L'URL du portail où le catalogue est publié. Par exemple: `https://opendata.koumoul.com`."
|
|
21
21
|
},
|
|
22
22
|
"pattern": "^https?://.*[^/]$",
|
|
23
23
|
"errorMessage": "The URL must start with http:// or https:// and must not end with `/`.",
|
|
24
24
|
"x-i18n-errorMessage": {
|
|
25
25
|
"fr": "L'URL doit commencer par http:// ou https:// et ne pas se terminer par `/`."
|
|
26
26
|
},
|
|
27
|
-
"examples": [
|
|
27
|
+
"examples": [
|
|
28
|
+
"https://opendata.koumoul.com"
|
|
29
|
+
]
|
|
30
|
+
},
|
|
31
|
+
"apiKey": {
|
|
32
|
+
"type": "string",
|
|
33
|
+
"title": "API Key (Optional)",
|
|
34
|
+
"x-i18n-title": {
|
|
35
|
+
"fr": "Clé API (Optionnelle)"
|
|
36
|
+
},
|
|
37
|
+
"description": "The Data Fair API key to access the catalog. You can create one from the 'Settings' tab in Data Fair, under the 'API Keys' section.",
|
|
38
|
+
"x-i18n-description": {
|
|
39
|
+
"fr": "La clé API Data Fair pour accéder au catalogue. Vous pouvez en créer depuis l'onglet 'Paramètres' de Data Fair, dans la section 'Clés d'API'."
|
|
40
|
+
},
|
|
41
|
+
"layout": {
|
|
42
|
+
"props": {
|
|
43
|
+
"type": "password",
|
|
44
|
+
"autocomplete": "new-password"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
28
47
|
}
|
|
29
48
|
}
|
|
30
|
-
}
|
|
49
|
+
}
|
|
@@ -210,6 +210,19 @@ export type DataFairDataset = {
|
|
|
210
210
|
*/
|
|
211
211
|
storage?: {
|
|
212
212
|
size?: number;
|
|
213
|
+
/**
|
|
214
|
+
* Le tableau de résultats.
|
|
215
|
+
*/
|
|
216
|
+
dataFiles?: {
|
|
217
|
+
key?: string;
|
|
218
|
+
size?: number;
|
|
219
|
+
name?: string;
|
|
220
|
+
mimetype?: string;
|
|
221
|
+
updatedAt?: string;
|
|
222
|
+
title?: string;
|
|
223
|
+
url?: string;
|
|
224
|
+
[k: string]: unknown;
|
|
225
|
+
}[];
|
|
213
226
|
[k: string]: unknown;
|
|
214
227
|
};
|
|
215
228
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$id": "https://github.com/data-fair/catalog-data-fair/
|
|
2
|
+
"$id": "https://github.com/data-fair/catalog-data-fair/catalog-schemas",
|
|
3
3
|
"x-exports": [
|
|
4
4
|
"types"
|
|
5
5
|
],
|
|
@@ -347,6 +347,37 @@
|
|
|
347
347
|
"properties": {
|
|
348
348
|
"size": {
|
|
349
349
|
"type": "integer"
|
|
350
|
+
},
|
|
351
|
+
"dataFiles": {
|
|
352
|
+
"type": "array",
|
|
353
|
+
"description": "Le tableau de résultats.",
|
|
354
|
+
"items": {
|
|
355
|
+
"type": "object",
|
|
356
|
+
"properties": {
|
|
357
|
+
"key": {
|
|
358
|
+
"type": "string"
|
|
359
|
+
},
|
|
360
|
+
"size": {
|
|
361
|
+
"type": "number"
|
|
362
|
+
},
|
|
363
|
+
"name": {
|
|
364
|
+
"type": "string"
|
|
365
|
+
},
|
|
366
|
+
"mimetype": {
|
|
367
|
+
"type": "string"
|
|
368
|
+
},
|
|
369
|
+
"updatedAt": {
|
|
370
|
+
"type": "string",
|
|
371
|
+
"format": "date-time"
|
|
372
|
+
},
|
|
373
|
+
"title": {
|
|
374
|
+
"type": "string"
|
|
375
|
+
},
|
|
376
|
+
"url": {
|
|
377
|
+
"type": "string"
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
350
381
|
}
|
|
351
382
|
}
|
|
352
383
|
},
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"use strict";
|
|
5
5
|
export const validate = validate14;
|
|
6
6
|
export default validate14;
|
|
7
|
-
const schema16 = {"$id":"https://github.com/data-fair/catalog-data-fair/import-config","x-exports":["types","validate","schema"],"title":"Configuration de l'import","type":"object","additionalProperties":false,"properties":{"fields":{"type":"array","default":[],"title":"Colonnes du jeu de données","description":"La liste des colonnes à importer. Si ce champ est vide, toutes les colonnes seront importées.","items":{"type":"object","properties":{"key":{"type":"string"},"label":{"type":"string"},"type":{"type":"string"}}},"layout":{"getItems":{"url":"${context.catalogConfig.url}/data-fair/api/v1/datasets/${context.resourceId}/schema?calculated=false","itemTitle":"item.label","itemKey":"item.key"}}},"filters":{"$ref":"#/$defs/filters"}},"$defs":{"filters":{"title":"Filtres prédéfinis","type":"array","default":[],"items":{"type":"object","default":{"type":"in"},"required":["type"],"oneOf":[{"title":"Restreindre à des valeurs précises","required":["field","values","type"],"properties":{"type":{"const":"in"},"field":{"$ref":"#/$defs/filterField"},"values":{"type":"array","title":"Valeurs","description":"Importe seulement des lignes où la valeur du champ indiqué est égale à l'une des valeurs renseignées.<br>*Toutes les valeurs peuvent ne pas être affichées, écrivez pour rechercher un champ particulier.*","items":{"type":"string"},"layout":{"getItems":{"url":"${context.catalogConfig.url}/data-fair/api/v1/datasets/${context.resourceId}/values/${parent.data.field.key}?q={q}&q_mode=complete&size=100&stringify=true"},"props":{"clearable":true}},"default":[]}}},{"title":"Exclure des valeurs","required":["field","values","type"],"properties":{"type":{"const":"nin"},"field":{"$ref":"#/$defs/filterField"},"values":{"type":"array","title":"Valeurs à exclure","description":"Importe seulement des lignes où la valeur du champ indiqué est différente des valeurs renseignées.<br>*Toutes les valeurs peuvent ne pas être affichées, écrivez pour rechercher un champ particulier.*","items":{"type":"string"},"layout":{"getItems":{"url":"${context.catalogConfig.url}/data-fair/api/v1/datasets/${context.resourceId}/values/${parent.data.field.key}?q={q}&q_mode=complete&size=100&stringify=true"},"props":{"clearable":true}},"default":[]}}},{"title":"Supérieur à une valeur","required":["field","value","type"],"properties":{"type":{"const":"gte"},"field":{"$ref":"#/$defs/filterField"},"value":{"type":"string","title":"Valeur minimale","description":"Importe seulement des lignes où la valeur du champ indiqué est supérieur ou égale à la valeur renseignée. La valeur peut être un nombre, une chaîne de caractère (*ordre alphanumérique*), une date.<br>*Toutes les valeurs peuvent ne pas être affichées, écrivez pour rechercher un champ particulier.*","layout":{"comp":"combobox","getItems":{"url":"${context.catalogConfig.url}/data-fair/api/v1/datasets/${context.resourceId}/values/${parent.data.field.key}?q={q}&q_mode=complete&size=100&stringify=true&sort=asc"}}}}},{"title":"Inférieur à une valeur","required":["field","value","type"],"properties":{"type":{"const":"lte"},"field":{"$ref":"#/$defs/filterField"},"value":{"type":"string","title":"Valeur maximale","description":"Importe seulement les lignes où la valeur du champ indiqué est inférieur à la valeur renseignée. La valeur peut être un nombre, une chaîne de caractère (*ordre alphanumérique*), une date.<br>*Toutes les valeurs peuvent ne pas être affichées, écrivez pour rechercher un champ particulier.*","layout":{"comp":"combobox","getItems":{"url":"${context.catalogConfig.url}/data-fair/api/v1/datasets/${context.resourceId}/values/${parent.data.field.key}?q={q}&q_mode=complete&size=100&stringify=true&sort=asc"}}}}},{"title":"Commence par les caractères","required":["field","value","type"],"properties":{"type":{"const":"starts"},"field":{"$ref":"#/$defs/filterFieldString"},"value":{"type":"string","description":"Importe seulement les lignes où le champ (colonne) indiqué commence par une chaîne de caractères précise. La chaîne de caractères est sensible à la casse.<br>Seuls les champs (colonnes) ayant pour valeurs des chaînes de caractères sont acceptées.<br>*Toutes les valeurs peuvent ne pas être affichées, écrivez pour rechercher un champ particulier.*","title":"Commence par les caractères","layout":{"comp":"combobox","getItems":{"url":"${context.catalogConfig.url}/data-fair/api/v1/datasets/${context.resourceId}/values/${parent.data.field.key}?q={q}&q_mode=complete&size=100&stringify=true"}}}}}]}},"filterField":{"type":"object","title":"Colonne de filtre","properties":{"key":{"type":"string"},"label":{"type":"string"},"type":{"type":"string"}},"layout":{"getItems":{"url":"${context.catalogConfig.url}/data-fair/api/v1/datasets/${context.resourceId}/schema?calculated=false","itemTitle":"item.label","itemKey":"item.key"}}},"filterFieldString":{"type":"object","title":"Colonne de filtre","properties":{"key":{"type":"string"},"label":{"type":"string"},"type":{"type":"string"}},"layout":{"getItems":{"url":"${context.catalogConfig.url}/data-fair/api/v1/datasets/${context.resourceId}/schema?calculated=false&type=string","itemTitle":"item.label","itemKey":"item.key"}}}}};
|
|
7
|
+
const schema16 = {"$id":"https://github.com/data-fair/catalog-data-fair/import-config","x-exports":["types","validate","schema"],"title":"Configuration de l'import","type":"object","additionalProperties":false,"properties":{"fields":{"type":"array","default":[],"title":"Colonnes du jeu de données","description":"La liste des colonnes à importer. Si ce champ est vide, toutes les colonnes seront importées.","items":{"type":"object","properties":{"key":{"type":"string"},"label":{"type":"string"},"type":{"type":"string"}}},"layout":{"density":"compact","getItems":{"url":"${context.catalogConfig.url}/data-fair/api/v1/datasets/${context.resourceId}/schema?calculated=false","itemTitle":"item.label","itemKey":"item.key"}}},"filters":{"$ref":"#/$defs/filters"}},"$defs":{"filters":{"title":"Filtres prédéfinis","type":"array","default":[],"items":{"type":"object","default":{"type":"in"},"required":["type"],"oneOf":[{"title":"Restreindre à des valeurs précises","required":["field","values","type"],"properties":{"type":{"const":"in"},"field":{"$ref":"#/$defs/filterField"},"values":{"type":"array","title":"Valeurs","description":"Importe seulement des lignes où la valeur du champ indiqué est égale à l'une des valeurs renseignées.<br>*Toutes les valeurs peuvent ne pas être affichées, écrivez pour rechercher un champ particulier.*","items":{"type":"string"},"layout":{"getItems":{"url":"${context.catalogConfig.url}/data-fair/api/v1/datasets/${context.resourceId}/values/${parent.data.field.key}?q={q}&q_mode=complete&size=100&stringify=true"},"props":{"clearable":true}},"default":[]}}},{"title":"Exclure des valeurs","required":["field","values","type"],"properties":{"type":{"const":"nin"},"field":{"$ref":"#/$defs/filterField"},"values":{"type":"array","title":"Valeurs à exclure","description":"Importe seulement des lignes où la valeur du champ indiqué est différente des valeurs renseignées.<br>*Toutes les valeurs peuvent ne pas être affichées, écrivez pour rechercher un champ particulier.*","items":{"type":"string"},"layout":{"getItems":{"url":"${context.catalogConfig.url}/data-fair/api/v1/datasets/${context.resourceId}/values/${parent.data.field.key}?q={q}&q_mode=complete&size=100&stringify=true"},"props":{"clearable":true}},"default":[]}}},{"title":"Supérieur à une valeur","required":["field","value","type"],"properties":{"type":{"const":"gte"},"field":{"$ref":"#/$defs/filterField"},"value":{"type":"string","title":"Valeur minimale","description":"Importe seulement des lignes où la valeur du champ indiqué est supérieur ou égale à la valeur renseignée. La valeur peut être un nombre, une chaîne de caractère (*ordre alphanumérique*), une date.<br>*Toutes les valeurs peuvent ne pas être affichées, écrivez pour rechercher un champ particulier.*","layout":{"comp":"combobox","getItems":{"url":"${context.catalogConfig.url}/data-fair/api/v1/datasets/${context.resourceId}/values/${parent.data.field.key}?q={q}&q_mode=complete&size=100&stringify=true&sort=asc"}}}}},{"title":"Inférieur à une valeur","required":["field","value","type"],"properties":{"type":{"const":"lte"},"field":{"$ref":"#/$defs/filterField"},"value":{"type":"string","title":"Valeur maximale","description":"Importe seulement les lignes où la valeur du champ indiqué est inférieur à la valeur renseignée. La valeur peut être un nombre, une chaîne de caractère (*ordre alphanumérique*), une date.<br>*Toutes les valeurs peuvent ne pas être affichées, écrivez pour rechercher un champ particulier.*","layout":{"comp":"combobox","getItems":{"url":"${context.catalogConfig.url}/data-fair/api/v1/datasets/${context.resourceId}/values/${parent.data.field.key}?q={q}&q_mode=complete&size=100&stringify=true&sort=asc"}}}}},{"title":"Commence par les caractères","required":["field","value","type"],"properties":{"type":{"const":"starts"},"field":{"$ref":"#/$defs/filterFieldString"},"value":{"type":"string","description":"Importe seulement les lignes où le champ (colonne) indiqué commence par une chaîne de caractères précise. La chaîne de caractères est sensible à la casse.<br>Seuls les champs (colonnes) ayant pour valeurs des chaînes de caractères sont acceptées.<br>*Toutes les valeurs peuvent ne pas être affichées, écrivez pour rechercher un champ particulier.*","title":"Commence par les caractères","layout":{"comp":"combobox","getItems":{"url":"${context.catalogConfig.url}/data-fair/api/v1/datasets/${context.resourceId}/values/${parent.data.field.key}?q={q}&q_mode=complete&size=100&stringify=true"}}}}}]}},"filterField":{"type":"object","title":"Colonne de filtre","properties":{"key":{"type":"string"},"label":{"type":"string"},"type":{"type":"string"}},"layout":{"getItems":{"url":"${context.catalogConfig.url}/data-fair/api/v1/datasets/${context.resourceId}/schema?calculated=false","itemTitle":"item.label","itemKey":"item.key"}}},"filterFieldString":{"type":"object","title":"Colonne de filtre","properties":{"key":{"type":"string"},"label":{"type":"string"},"type":{"type":"string"}},"layout":{"getItems":{"url":"${context.catalogConfig.url}/data-fair/api/v1/datasets/${context.resourceId}/schema?calculated=false&type=string","itemTitle":"item.label","itemKey":"item.key"}}}}};
|
|
8
8
|
const schema17 = {"title":"Filtres prédéfinis","type":"array","default":[],"items":{"type":"object","default":{"type":"in"},"required":["type"],"oneOf":[{"title":"Restreindre à des valeurs précises","required":["field","values","type"],"properties":{"type":{"const":"in"},"field":{"$ref":"#/$defs/filterField"},"values":{"type":"array","title":"Valeurs","description":"Importe seulement des lignes où la valeur du champ indiqué est égale à l'une des valeurs renseignées.<br>*Toutes les valeurs peuvent ne pas être affichées, écrivez pour rechercher un champ particulier.*","items":{"type":"string"},"layout":{"getItems":{"url":"${context.catalogConfig.url}/data-fair/api/v1/datasets/${context.resourceId}/values/${parent.data.field.key}?q={q}&q_mode=complete&size=100&stringify=true"},"props":{"clearable":true}},"default":[]}}},{"title":"Exclure des valeurs","required":["field","values","type"],"properties":{"type":{"const":"nin"},"field":{"$ref":"#/$defs/filterField"},"values":{"type":"array","title":"Valeurs à exclure","description":"Importe seulement des lignes où la valeur du champ indiqué est différente des valeurs renseignées.<br>*Toutes les valeurs peuvent ne pas être affichées, écrivez pour rechercher un champ particulier.*","items":{"type":"string"},"layout":{"getItems":{"url":"${context.catalogConfig.url}/data-fair/api/v1/datasets/${context.resourceId}/values/${parent.data.field.key}?q={q}&q_mode=complete&size=100&stringify=true"},"props":{"clearable":true}},"default":[]}}},{"title":"Supérieur à une valeur","required":["field","value","type"],"properties":{"type":{"const":"gte"},"field":{"$ref":"#/$defs/filterField"},"value":{"type":"string","title":"Valeur minimale","description":"Importe seulement des lignes où la valeur du champ indiqué est supérieur ou égale à la valeur renseignée. La valeur peut être un nombre, une chaîne de caractère (*ordre alphanumérique*), une date.<br>*Toutes les valeurs peuvent ne pas être affichées, écrivez pour rechercher un champ particulier.*","layout":{"comp":"combobox","getItems":{"url":"${context.catalogConfig.url}/data-fair/api/v1/datasets/${context.resourceId}/values/${parent.data.field.key}?q={q}&q_mode=complete&size=100&stringify=true&sort=asc"}}}}},{"title":"Inférieur à une valeur","required":["field","value","type"],"properties":{"type":{"const":"lte"},"field":{"$ref":"#/$defs/filterField"},"value":{"type":"string","title":"Valeur maximale","description":"Importe seulement les lignes où la valeur du champ indiqué est inférieur à la valeur renseignée. La valeur peut être un nombre, une chaîne de caractère (*ordre alphanumérique*), une date.<br>*Toutes les valeurs peuvent ne pas être affichées, écrivez pour rechercher un champ particulier.*","layout":{"comp":"combobox","getItems":{"url":"${context.catalogConfig.url}/data-fair/api/v1/datasets/${context.resourceId}/values/${parent.data.field.key}?q={q}&q_mode=complete&size=100&stringify=true&sort=asc"}}}}},{"title":"Commence par les caractères","required":["field","value","type"],"properties":{"type":{"const":"starts"},"field":{"$ref":"#/$defs/filterFieldString"},"value":{"type":"string","description":"Importe seulement les lignes où le champ (colonne) indiqué commence par une chaîne de caractères précise. La chaîne de caractères est sensible à la casse.<br>Seuls les champs (colonnes) ayant pour valeurs des chaînes de caractères sont acceptées.<br>*Toutes les valeurs peuvent ne pas être affichées, écrivez pour rechercher un champ particulier.*","title":"Commence par les caractères","layout":{"comp":"combobox","getItems":{"url":"${context.catalogConfig.url}/data-fair/api/v1/datasets/${context.resourceId}/values/${parent.data.field.key}?q={q}&q_mode=complete&size=100&stringify=true"}}}}}]}};
|
|
9
9
|
const schema18 = {"type":"object","title":"Colonne de filtre","properties":{"key":{"type":"string"},"label":{"type":"string"},"type":{"type":"string"}},"layout":{"getItems":{"url":"${context.catalogConfig.url}/data-fair/api/v1/datasets/${context.resourceId}/schema?calculated=false","itemTitle":"item.label","itemKey":"item.key"}}};
|
|
10
10
|
const schema22 = {"type":"object","title":"Colonne de filtre","properties":{"key":{"type":"string"},"label":{"type":"string"},"type":{"type":"string"}},"layout":{"getItems":{"url":"${context.catalogConfig.url}/data-fair/api/v1/datasets/${context.resourceId}/schema?calculated=false&type=string","itemTitle":"item.label","itemKey":"item.key"}}};
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
}
|
|
30
30
|
},
|
|
31
31
|
"layout": {
|
|
32
|
+
"density": "compact",
|
|
32
33
|
"getItems": {
|
|
33
34
|
"url": "${context.catalogConfig.url}/data-fair/api/v1/datasets/${context.resourceId}/schema?calculated=false",
|
|
34
35
|
"itemTitle": "item.label",
|
|
@@ -249,4 +250,4 @@
|
|
|
249
250
|
}
|
|
250
251
|
}
|
|
251
252
|
}
|
|
252
|
-
}
|
|
253
|
+
}
|