@data-fair/catalog-onegeo-suite 0.1.4 → 0.1.8
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/index.ts +1 -2
- package/lib/capabilities.ts +0 -1
- package/lib/imports.ts +83 -70
- package/lib/list.ts +15 -9
- package/package.json +1 -1
- package/types/importConfig/.type/index.js +24 -4
- package/types/index.ts +0 -1
- package/types/importConfig/index.ts +0 -1
- package/types/importConfig/schema.json +0 -47
package/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type CatalogPlugin from '@data-fair/types-catalogs'
|
|
2
|
-
import {
|
|
2
|
+
import { configSchema, assertConfigValid, type OneGeoSuiteConfig } from '#types'
|
|
3
3
|
import { type OneGeoCapabilities, capabilities } from './lib/capabilities.ts'
|
|
4
4
|
import i18n from './lib/i18n.ts'
|
|
5
5
|
|
|
@@ -30,7 +30,6 @@ const plugin: CatalogPlugin<OneGeoSuiteConfig, OneGeoCapabilities> = {
|
|
|
30
30
|
capabilities
|
|
31
31
|
},
|
|
32
32
|
|
|
33
|
-
importConfigSchema,
|
|
34
33
|
configSchema,
|
|
35
34
|
assertConfigValid
|
|
36
35
|
}
|
package/lib/capabilities.ts
CHANGED
package/lib/imports.ts
CHANGED
|
@@ -4,65 +4,38 @@ import { apiList, formatsList, sortList } from './list.ts'
|
|
|
4
4
|
|
|
5
5
|
import axios from '@data-fair/lib-node/axios.js'
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
format = sortList(catalog._source['metadata-fr'].link.find((x: Link) => { return x.service === service && x.formats.find((y: string) => { return formatsList.includes(y) }) }).formats, formatsList)[0]
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
if (!catalog) {
|
|
20
|
-
throw Error(`resource ${service} not found for ${resourceId} in ${catalogConfig.url}`)
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// filter links by format and service
|
|
24
|
-
const source: Link = catalog._source['metadata-fr'].link.find((x: Link) => {
|
|
25
|
-
return x.service === service || x.url === service
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
// table of format for make WFS url
|
|
29
|
-
const wfsTable: Record<string, string> = {
|
|
30
|
-
CSV: 'csv',
|
|
31
|
-
JSON: 'application/json',
|
|
32
|
-
GeoJSON: 'application/json',
|
|
33
|
-
'Shapefile (zip)': 'SHAPE-ZIP',
|
|
34
|
-
'SHAPE-ZIP': 'SHAPE-ZIP',
|
|
35
|
-
KML: 'kml',
|
|
36
|
-
}
|
|
37
|
-
// table of format
|
|
38
|
-
const extensionTable: Record<string, string> = {
|
|
39
|
-
CSV: '.csv',
|
|
40
|
-
GeoJSON: '.geojson',
|
|
41
|
-
JSON: '.json',
|
|
42
|
-
'Shapefile (zip)': '.zip',
|
|
43
|
-
'SHAPE-ZIP': '.zip',
|
|
44
|
-
KML: '.kml',
|
|
45
|
-
'Excel non structuré': '.xlsx',
|
|
46
|
-
'Microsoft Excel': '.xls',
|
|
47
|
-
}
|
|
7
|
+
// table of format -> extension
|
|
8
|
+
const wfsTable: Record<string, string> = {
|
|
9
|
+
CSV: 'csv',
|
|
10
|
+
JSON: 'application/json',
|
|
11
|
+
GeoJSON: 'application/json',
|
|
12
|
+
'Shapefile (zip)': 'SHAPE-ZIP',
|
|
13
|
+
'SHAPE-ZIP': 'SHAPE-ZIP',
|
|
14
|
+
KML: 'kml',
|
|
15
|
+
}
|
|
48
16
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
17
|
+
// table of format -> extension
|
|
18
|
+
const extensionTable: Record<string, string> = {
|
|
19
|
+
CSV: '.csv',
|
|
20
|
+
GeoJSON: '.geojson',
|
|
21
|
+
JSON: '.json',
|
|
22
|
+
'Shapefile (zip)': '.zip',
|
|
23
|
+
'SHAPE-ZIP': '.zip',
|
|
24
|
+
KML: '.kml',
|
|
25
|
+
'Excel non structuré': '.xlsx',
|
|
26
|
+
'Microsoft Excel': '.xls',
|
|
27
|
+
}
|
|
59
28
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
29
|
+
export const getResource = async ({
|
|
30
|
+
catalogConfig,
|
|
31
|
+
resourceId,
|
|
32
|
+
tmpDir,
|
|
33
|
+
log
|
|
34
|
+
}: GetResourceContext<OneGeoSuiteConfig>): ReturnType<CatalogPlugin['getResource']> => {
|
|
35
|
+
const catalog = (await axios.get(new URL(`fr/indexer/elastic/_search/?q=uuid.keyword:${resourceId}%20AND%20is_metadata:true`, catalogConfig.url).href)).data.hits.hits[0]
|
|
36
|
+
if (!catalog) throw Error(`resource not found for ${resourceId} in ${catalogConfig.url}`)
|
|
65
37
|
|
|
38
|
+
// get origine url
|
|
66
39
|
const axiosPortail = axios.create({
|
|
67
40
|
validateStatus: function (status) {
|
|
68
41
|
return status >= 200 && status < 500
|
|
@@ -77,19 +50,57 @@ export const getResource = async ({ catalogConfig, importConfig, resourceId, tmp
|
|
|
77
50
|
}
|
|
78
51
|
const origin = portail ? `${catalogConfig.url}/${portail}/jeux-de-donnees/${catalog._source.slug}/info` : ''
|
|
79
52
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
53
|
+
const links: Link[] = catalog._source['metadata-fr'].link.filter((x: Link) => {
|
|
54
|
+
return apiList.includes(x.service) && x.formats.find((y: string) => {
|
|
55
|
+
return formatsList.includes(y)
|
|
56
|
+
})
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
// list all url possible
|
|
60
|
+
let downloadUrls: { url: string, format: string, service: string | undefined, description: string | undefined }[] = []
|
|
61
|
+
|
|
62
|
+
for (const link of links) {
|
|
63
|
+
const formats = link.formats.filter((f: string) => {
|
|
64
|
+
return formatsList.includes(f)
|
|
84
65
|
})
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
66
|
+
for (const format of formats) {
|
|
67
|
+
if (link.service === 'WS' && extensionTable[format]) {
|
|
68
|
+
downloadUrls.push({ url: `${link.url}/${link.name}/all${extensionTable[format]}`, format, service: link.service, description: link.description })
|
|
69
|
+
} else if (link.service === undefined) {
|
|
70
|
+
downloadUrls.push({ url: `${link.url}`, format, service: link.service, description: link.description })
|
|
71
|
+
} else if (link.service === 'WFS' && wfsTable[format]) {
|
|
72
|
+
downloadUrls.push({ url: `${link.url}?SERVICE=WFS&VERSION=2.0.0&request=GetFeature&typename=${link.name}&outputFormat=${wfsTable[format]}`, format, service: link.service, description: link.description })
|
|
73
|
+
}
|
|
88
74
|
}
|
|
75
|
+
}
|
|
76
|
+
downloadUrls = sortList(downloadUrls, apiList, (x: any) => { return x.service })
|
|
77
|
+
downloadUrls = sortList(downloadUrls, formatsList, (x: any) => { return x.format })
|
|
89
78
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
79
|
+
// Download the resource
|
|
80
|
+
const fs = await import('node:fs')
|
|
81
|
+
const path = await import('path')
|
|
82
|
+
let response
|
|
83
|
+
let format: string
|
|
84
|
+
let description: string | undefined
|
|
85
|
+
|
|
86
|
+
for (const downloadUrl of downloadUrls) {
|
|
87
|
+
await log.step(`Downloading the file ${downloadUrl.url}; format: ${downloadUrl.format}; service: ${downloadUrl.service}`)
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
response = await axios.get(downloadUrl.url, {
|
|
91
|
+
responseType: 'stream',
|
|
92
|
+
})
|
|
93
|
+
if (response.headers['content-type'] === 'text/html') {
|
|
94
|
+
response = undefined
|
|
95
|
+
throw Error('return HTML page')
|
|
96
|
+
}
|
|
97
|
+
format = downloadUrl.format
|
|
98
|
+
description = downloadUrl.description
|
|
99
|
+
await log.info(`Get file with ${downloadUrl.url} successfully! ${response.status}`)
|
|
100
|
+
} catch (e) {
|
|
101
|
+
await log.warning(`Downloading fail with this url: ${downloadUrl}; ${e}`)
|
|
102
|
+
}
|
|
103
|
+
if (response) break
|
|
93
104
|
}
|
|
94
105
|
|
|
95
106
|
if (!response) {
|
|
@@ -97,7 +108,7 @@ export const getResource = async ({ catalogConfig, importConfig, resourceId, tmp
|
|
|
97
108
|
}
|
|
98
109
|
|
|
99
110
|
// Create a filename
|
|
100
|
-
const fileName = catalog._source.slug + extensionTable[format]
|
|
111
|
+
const fileName = catalog._source.slug + extensionTable[format!]
|
|
101
112
|
const filePath = path.join(tmpDir, fileName)
|
|
102
113
|
await log.info(`Downloading resource to ${fileName}`)
|
|
103
114
|
|
|
@@ -140,9 +151,9 @@ export const getResource = async ({ catalogConfig, importConfig, resourceId, tmp
|
|
|
140
151
|
id: resourceId,
|
|
141
152
|
slug: catalog._source.slug,
|
|
142
153
|
title: catalog._source['metadata-fr'].title,
|
|
143
|
-
description:
|
|
154
|
+
description: description ?? catalog._source['metadata-fr'].abstratc,
|
|
144
155
|
filePath,
|
|
145
|
-
format
|
|
156
|
+
format: format!,
|
|
146
157
|
frequency,
|
|
147
158
|
license: {
|
|
148
159
|
href: '',
|
|
@@ -150,7 +161,9 @@ export const getResource = async ({ catalogConfig, importConfig, resourceId, tmp
|
|
|
150
161
|
},
|
|
151
162
|
keywords: catalog._source['metadata-fr'].keyword,
|
|
152
163
|
updatedAt: catalog._source['metadata-fr'].lastUpdateDate ?? undefined,
|
|
153
|
-
image: catalog._source['metadata-fr'].image.find((x: { type: string, url: string | null }) => {
|
|
164
|
+
image: catalog._source['metadata-fr'].image.find((x: { type: string, url: string | null }) => {
|
|
165
|
+
return x.type === 'thumbnail' && !!x.url
|
|
166
|
+
})?.url ?? null,
|
|
154
167
|
origin
|
|
155
168
|
}
|
|
156
169
|
}
|
package/lib/list.ts
CHANGED
|
@@ -7,7 +7,7 @@ type ResourceList = Awaited<ReturnType<CatalogPlugin['list']>>['results']
|
|
|
7
7
|
|
|
8
8
|
export const apiList: Array<string | undefined> = ['WS', 'WFS', undefined]
|
|
9
9
|
export const formatsList = [
|
|
10
|
-
'
|
|
10
|
+
'GeoJSON', 'SHAPE-ZIP', 'Shapefile (zip)', 'CSV', 'JSON', 'Excel non structuré', 'Microsoft Excel', 'KML']
|
|
11
11
|
|
|
12
12
|
const extensionTable: Record<string, string> = {
|
|
13
13
|
CSV: '.csv',
|
|
@@ -20,13 +20,13 @@ const extensionTable: Record<string, string> = {
|
|
|
20
20
|
'Microsoft Excel': '.xls',
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
export const sortList = (formats: any[], reference: any[]) => {
|
|
23
|
+
export const sortList = (formats: any[], reference: any[], func = (x: any) => { return x }) => {
|
|
24
24
|
return [...formats].sort((a, b) =>
|
|
25
|
-
(reference.indexOf(a) === -1 ? reference.length : reference.indexOf(a)) -
|
|
26
|
-
(reference.indexOf(b) === -1 ? reference.length : reference.indexOf(b))
|
|
25
|
+
(reference.indexOf(func(a)) === -1 ? reference.length : reference.indexOf(func(a))) -
|
|
26
|
+
(reference.indexOf(func(b)) === -1 ? reference.length : reference.indexOf(func(b)))
|
|
27
27
|
)
|
|
28
28
|
}
|
|
29
|
-
const baseReqDataset = (input: string = '*', size: number =
|
|
29
|
+
const baseReqDataset = (input: string = '*', size: number = 500, from: number = 1) => {
|
|
30
30
|
return {
|
|
31
31
|
from: (from - 1) * size,
|
|
32
32
|
size: Math.min(size, 10000),
|
|
@@ -47,7 +47,7 @@ const baseReqDataset = (input: string = '*', size: number = 100, from: number =
|
|
|
47
47
|
}
|
|
48
48
|
}]
|
|
49
49
|
}
|
|
50
|
-
}, { term: { is_metadata: true } }, {
|
|
50
|
+
}, { term: { is_metadata: true } }, { term: { 'editorial-metadata.defaultPermissionLevel': 3 } }, {
|
|
51
51
|
bool: {
|
|
52
52
|
should: [
|
|
53
53
|
...apiList.filter((x: any) => { return x }).map((x: any) => { return { term: { 'metadata-fr.link.service.keyword': x } } }),
|
|
@@ -87,7 +87,7 @@ const countReq = (input: string = '*') => {
|
|
|
87
87
|
}
|
|
88
88
|
}]
|
|
89
89
|
}
|
|
90
|
-
}, { term: { is_metadata: true } }, {
|
|
90
|
+
}, { term: { is_metadata: true } }, { term: { 'editorial-metadata.defaultPermissionLevel': 3 } }, {
|
|
91
91
|
bool: {
|
|
92
92
|
should: [
|
|
93
93
|
...apiList.filter((x: any) => x).map((x: any) => ({ term: { 'metadata-fr.link.service.keyword': x } })),
|
|
@@ -121,7 +121,13 @@ const countReq = (input: string = '*') => {
|
|
|
121
121
|
export const list = async ({ catalogConfig, params }: ListContext<OneGeoSuiteConfig, OneGeoCapabilities>): ReturnType<CatalogPlugin['list']> => {
|
|
122
122
|
const url = catalogConfig.url
|
|
123
123
|
const listResources = async (params: Record<any, any>) => {
|
|
124
|
-
|
|
124
|
+
let catalogs
|
|
125
|
+
try {
|
|
126
|
+
catalogs = (await axios.post(new URL('fr/indexer/elastic/_search/', url).href, baseReqDataset(params.q || '*', params.size, params.page))).data.hits.hits
|
|
127
|
+
} catch (e) {
|
|
128
|
+
// @ts-ignore
|
|
129
|
+
throw Error(`Axios error: ${e?.status ?? ''} ${e?.message}`)
|
|
130
|
+
}
|
|
125
131
|
const count = (await axios.post(new URL('fr/indexer/elastic/_search/', url).href, countReq(params.q))).data.aggregations.unique_datasets.value
|
|
126
132
|
const res = []
|
|
127
133
|
|
|
@@ -151,7 +157,7 @@ export const list = async ({ catalogConfig, params }: ListContext<OneGeoSuiteCon
|
|
|
151
157
|
formats = formats.map(x => extensionTable[x]?.slice(1) ?? x)
|
|
152
158
|
|
|
153
159
|
res.push({
|
|
154
|
-
id: `${catalog.
|
|
160
|
+
id: `${catalog._source.uuid}`,
|
|
155
161
|
title: catalog._source['metadata-fr'].title,
|
|
156
162
|
description: sources[0].description,
|
|
157
163
|
format: formats.slice(0, 3).join(', ') + (formats.length ? '..' : ''),
|
package/package.json
CHANGED
|
@@ -29,8 +29,8 @@ export const schema = {
|
|
|
29
29
|
"layout": {
|
|
30
30
|
"cols": 6,
|
|
31
31
|
"getItems": {
|
|
32
|
-
"url": "${context.catalogConfig.url}/fr/indexer/elastic/_search/?q=
|
|
33
|
-
"itemsResults": "(function() { const links = data.hits.hits[0]._source['metadata-fr'].link; return links.filter(l => l.formats.find(f => ['CSV', '
|
|
32
|
+
"url": "${context.catalogConfig.url}/fr/indexer/elastic/_search/?q=uuid.keyword:${context.resourceId}%20AND%20is_metadata:true",
|
|
33
|
+
"itemsResults": "(function() { const links = data.hits.hits[0]._source['metadata-fr'].link; return links.filter(l => l.formats.find(f => ['CSV', 'Excel non structuré', 'Microsoft Excel','ZIP', 'Shapefile (zip)', 'SHAPE-ZIP', 'GeoJSON', 'JSON', 'KML'].includes(f)) && ['WS', 'WFS', undefined].includes(l.service)).map((l, i) => [l, i] ); })()",
|
|
34
34
|
"itemTitle": "item[0].service?? 'Local N°' + (item[1] + 1)",
|
|
35
35
|
"itemValue": "item[0].service?? item[0].url"
|
|
36
36
|
}
|
|
@@ -39,12 +39,32 @@ export const schema = {
|
|
|
39
39
|
"format": {
|
|
40
40
|
"type": "string",
|
|
41
41
|
"title": "Format",
|
|
42
|
+
"description": "Import format, if no service is set, the possible formats are unknown.",
|
|
43
|
+
"x-i18n-description": {
|
|
44
|
+
"fr": "Format d’importation, si aucun service n’est défini, les formats possibles sont inconnus."
|
|
45
|
+
},
|
|
42
46
|
"layout": {
|
|
43
47
|
"if": "parent.data.service != null",
|
|
44
48
|
"cols": 6,
|
|
45
49
|
"getItems": {
|
|
46
|
-
"url": "${context.catalogConfig.url}/fr/indexer/elastic/_search/?q=${parent.data.service}&q=
|
|
47
|
-
"itemsResults": "(function(){const service = data.hits.hits[0]._source['metadata-fr'].link.find(x => x.service === parent.data.service || x.url === parent.data.service); let formats = service.formats; if (['WS', 'WFS'
|
|
50
|
+
"url": "${context.catalogConfig.url}/fr/indexer/elastic/_search/?q=${parent.data.service}&q=uuid.keyword:${context.resourceId}%20AND%20is_metadata:true",
|
|
51
|
+
"itemsResults": "(function(){ const service = data.hits.hits[0]._source['metadata-fr'].link.find(x => x.service === parent.data.service || x.url === parent.data.service); let formats = service.formats; if (['WS', 'WFS'].includes(service.service) && !formats.includes('CSV')) {formats.push('CSV');} return formats.filter(f => ['CSV', 'Excel non structuré', 'Microsoft Excel','ZIP', 'Shapefile (zip)', 'SHAPE-ZIP', 'GeoJSON', 'JSON', 'KML'].includes(f));})()"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"format2": {
|
|
56
|
+
"type": "string",
|
|
57
|
+
"title": "Format",
|
|
58
|
+
"description": "Import format, if no service is set, the possible formats are unknown.",
|
|
59
|
+
"x-i18n-description": {
|
|
60
|
+
"fr": "Format d’importation, si aucun service n’est défini, les formats possibles sont inconnus."
|
|
61
|
+
},
|
|
62
|
+
"layout": {
|
|
63
|
+
"if": "parent.data.service == null",
|
|
64
|
+
"cols": 6,
|
|
65
|
+
"getItems": {
|
|
66
|
+
"url": "https://httpbin.org/get",
|
|
67
|
+
"itemsResults": "(function(){return ['CSV', 'Excel non structuré', 'Microsoft Excel','ZIP', 'Shapefile (zip)', 'SHAPE-ZIP', 'GeoJSON', 'JSON', 'KML']})()"
|
|
48
68
|
}
|
|
49
69
|
}
|
|
50
70
|
}
|
package/types/index.ts
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './.type/index.js'
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$id": "https://github.com/data-fair/catalog-onegeo-suite/import-config",
|
|
3
|
-
"x-exports": [
|
|
4
|
-
"schema"
|
|
5
|
-
],
|
|
6
|
-
"title": "ImportConfig",
|
|
7
|
-
"type": "object",
|
|
8
|
-
"additionalProperties": false,
|
|
9
|
-
"required": [],
|
|
10
|
-
"properties": {
|
|
11
|
-
"service": {
|
|
12
|
-
"type": "string",
|
|
13
|
-
"title": "Web Service",
|
|
14
|
-
"x-i18n-title": {
|
|
15
|
-
"fr": "Service Web"
|
|
16
|
-
},
|
|
17
|
-
"description": "This resource is available from multiple sources (web services, local files). Select the source you want to use for the import.",
|
|
18
|
-
"x-i18n-description": {
|
|
19
|
-
"fr": "Cette ressource est disponible via plusieurs sources (services web, fichiers locaux). Sélectionnez la source que vous souhaitez utiliser pour l'import."
|
|
20
|
-
},
|
|
21
|
-
"layout": {
|
|
22
|
-
"cols": 6,
|
|
23
|
-
"getItems": {
|
|
24
|
-
"url": "${context.catalogConfig.url}/fr/indexer/elastic/_search/?q=_id:${context.resourceId}",
|
|
25
|
-
"itemsResults": "(function() { const links = data.hits.hits[0]._source['metadata-fr'].link; return links.filter(l => l.formats.find(f => ['CSV', 'ODS', 'Excel non structuré', 'Microsoft Excel','ZIP', 'Shapefile (zip)', 'GeoJSON', 'JSON', 'XML', 'GML', 'KML'].includes(f)) || ['WS', 'AFS', 'WFS'].includes(l.service)).map((l, i) => [l, i] ); })()",
|
|
26
|
-
"itemTitle": "item[0].service?? 'Local N°' + (item[1] + 1)",
|
|
27
|
-
"itemValue": "item[0].service?? item[0].url"
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
},
|
|
31
|
-
"format": {
|
|
32
|
-
"type": "string",
|
|
33
|
-
"title": "Format",
|
|
34
|
-
"layout": {
|
|
35
|
-
"if": "parent.data.service != null",
|
|
36
|
-
"cols": 6,
|
|
37
|
-
"getItems": {
|
|
38
|
-
"url": "${context.catalogConfig.url}/fr/indexer/elastic/_search/?q=${parent.data.service}&q=_id:${context.resourceId}",
|
|
39
|
-
"itemsResults": "(function(){const service = data.hits.hits[0]._source['metadata-fr'].link.find(x => x.service === parent.data.service || x.url === parent.data.service); let formats = service.formats; if (['WS', 'WFS', 'AFS'].includes(service.service) && !formats.includes('CSV')) {formats.push('CSV');} return formats; })()"
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
},
|
|
44
|
-
"layout": {
|
|
45
|
-
"title": null
|
|
46
|
-
}
|
|
47
|
-
}
|