@@ -31,32 +31,47 @@ fs.ensureDirSync(tmpDir)
3131
3232tmp . setGracefulCleanup ( )
3333
34- /**
35- * For compatibility with old plugins
36- */
37- const injectPluginNameConfig = ( plugin : Plugin ) : Plugin => {
38- if ( ! plugin . pluginConfigSchema . properties ?. pluginName ) {
39- const version = plugin . distTag === 'latest' ? plugin . version : `${ plugin . distTag } - ${ plugin . version } `
40- const defaultName = plugin . name . replace ( '@data-fair/processing-' , '' ) + ' (' + version + ')'
41- plugin . pluginConfigSchema . properties = plugin . pluginConfigSchema . properties || { }
42- plugin . pluginConfigSchema . properties . pluginName = {
34+ const pluginMetadataSchema = {
35+ type : 'object' ,
36+ additionalProperties : false ,
37+ properties : {
38+ name : {
4339 type : 'string' ,
4440 title : 'Nom du plugin' ,
45- description : 'Nom du plugin affiché dans les traitements' ,
46- default : defaultName
41+ layout : {
42+ cols : 4
43+ }
44+ } ,
45+ category : {
46+ type : 'string' ,
47+ title : 'Catégorie' ,
48+ enum : config . pluginCategories ,
49+ layout : {
50+ cols : 4
51+ }
52+ } ,
53+ icon : {
54+ type : 'object' ,
55+ title : 'Icon' ,
56+ layout : {
57+ getItems : {
58+ url : 'https://koumoul.com/data-fair/api/v1/datasets/icons-mdi-latest/lines?q={q}&select=name,svg,svgPath&size=25' ,
59+ itemsResults : 'data.results' ,
60+ itemTitle : 'item.name' ,
61+ itemIcon : 'item.svg' ,
62+ itemKey : 'item.name'
63+ } ,
64+ cols : 4
65+ }
66+ } ,
67+ description : {
68+ type : 'string' ,
69+ title : 'Description du plugin'
4770 }
4871 }
49- return plugin
50- }
51-
52- const preparePluginInfo = async ( pluginInfo : Plugin ) : Promise < Plugin > => {
53- pluginInfo = injectPluginNameConfig ( pluginInfo )
54- const pluginConfigPath = path . join ( pluginsDir , pluginInfo . id + '-config.json' )
55- let customName = await fs . pathExists ( pluginConfigPath ) ? ( await fs . readJson ( pluginConfigPath ) ) . pluginName : pluginInfo . pluginConfigSchema . properties . pluginName . default
56- if ( ! customName ) customName = pluginInfo . name . replace ( '@data-fair/processing-' , '' ) + ' (' + pluginInfo . distTag + ' - ' + pluginInfo . version + ')'
57- return { ...pluginInfo , customName }
5872}
5973
74+ // Install a new plugin or update an existing one
6075router . post ( '/' , permissions . isSuperAdmin , async ( req , res ) => {
6176 const { body } = ( await import ( '#doc/plugin/post-req/index.ts' ) ) . returnValid ( req )
6277 const plugin = body as Record < string , any >
@@ -86,6 +101,8 @@ router.post('/', permissions.isSuperAdmin, async (req, res) => {
86101
87102 plugin . pluginConfigSchema = await fs . readJson ( path . join ( dir . path , 'src' , 'plugin-config-schema.json' ) )
88103 plugin . processingConfigSchema = await fs . readJson ( path . join ( dir . path , 'src' , 'processing-config-schema.json' ) )
104+
105+ // static metadata for the plugin
89106 await fs . writeFile ( path . join ( dir . path , 'plugin.json' ) , JSON . stringify ( plugin , null , 2 ) )
90107 await fs . move ( dir . path , pluginDir , { overwrite : true } )
91108 } finally {
@@ -96,14 +113,20 @@ router.post('/', permissions.isSuperAdmin, async (req, res) => {
96113 }
97114 }
98115
99- const installedPlugin : Plugin = await preparePluginInfo ( plugin as Plugin )
100- installedPlugin . access = { public : false , privateAccess : [ ] }
101- const accessFilePath = path . join ( pluginsDir , installedPlugin . id + '-access.json' )
102- if ( ! await fs . pathExists ( accessFilePath ) ) await fs . writeJson ( accessFilePath , installedPlugin . access )
103- const pluginConfigPath = path . join ( pluginsDir , installedPlugin . id + '-config.json' )
104- if ( await fs . pathExists ( pluginConfigPath ) ) installedPlugin . config = await fs . readJson ( pluginConfigPath )
116+ // set defaults access (don't overwrite if already exists (after an update))
117+ plugin . access = { public : false , privateAccess : [ ] }
118+ const accessFilePath = path . join ( pluginsDir , plugin . id + '-access.json' )
119+ if ( ! await fs . pathExists ( accessFilePath ) ) await fs . writeJson ( accessFilePath , plugin . access )
120+
121+ // return the existing config if it exists
122+ const pluginConfigPath = path . join ( pluginsDir , plugin . id + '-config.json' )
123+ if ( await fs . pathExists ( pluginConfigPath ) ) plugin . config = await fs . readJson ( pluginConfigPath )
124+
125+ // return the existing metadata if it exists
126+ const pluginMetadataPath = path . join ( pluginsDir , plugin . id + '-metadata.json' )
127+ if ( await fs . pathExists ( pluginMetadataPath ) ) plugin . metadata = await fs . readJson ( pluginMetadataPath )
105128
106- res . send ( installedPlugin )
129+ res . send ( plugin )
107130} )
108131
109132// List installed plugins (optional: privateAccess=[type]:[id])
@@ -136,7 +159,17 @@ router.get('/', async (req, res) => {
136159 } else {
137160 return res . status ( 400 ) . send ( 'privateAccess filter is required' )
138161 }
139- results . push ( await preparePluginInfo ( pluginInfo ) )
162+
163+ const pluginMetadataPath = path . join ( pluginsDir , dir + '-metadata.json' )
164+ const version = pluginInfo . distTag === 'latest' ? pluginInfo . version : `${ pluginInfo . distTag } - ${ pluginInfo . version } `
165+ pluginInfo . metadata = {
166+ name : pluginInfo . name . replace ( '@data-fair/processing-' , '' ) + ' (' + version + ')' ,
167+ description : pluginInfo . description ,
168+ ...( await fs . pathExists ( pluginMetadataPath ) ? await fs . readJson ( pluginMetadataPath ) : { } )
169+ }
170+ pluginInfo . pluginMetadataSchema = pluginMetadataSchema
171+
172+ results . push ( pluginInfo )
140173 }
141174
142175 const aggregationResult = (
@@ -160,7 +193,14 @@ router.get('/:id', async (req, res) => {
160193 await session . reqAuthenticated ( req )
161194 try {
162195 const pluginInfo : Plugin = await fs . readJson ( resolvePath ( pluginsDir , path . join ( req . params . id , 'plugin.json' ) ) )
163- res . send ( await preparePluginInfo ( pluginInfo ) )
196+ const pluginMetadataPath = path . join ( pluginsDir , req . params . id + '-metadata.json' )
197+ const version = pluginInfo . distTag === 'latest' ? pluginInfo . version : `${ pluginInfo . distTag } - ${ pluginInfo . version } `
198+ pluginInfo . metadata = {
199+ name : pluginInfo . name . replace ( '@data-fair/processing-' , '' ) + ' (' + version + ')' ,
200+ description : pluginInfo . description ,
201+ ...( await fs . pathExists ( pluginMetadataPath ) ? await fs . readJson ( pluginMetadataPath ) : { } )
202+ }
203+ res . send ( pluginInfo )
164204 } catch ( e : any ) {
165205 if ( e . code === 'ENOENT' ) res . status ( 404 ) . send ( 'Plugin not found' )
166206 else throw e
@@ -183,6 +223,14 @@ router.put('/:id/config', permissions.isSuperAdmin, async (req, res) => {
183223 res . send ( req . body )
184224} )
185225
226+ router . put ( '/:id/metadata' , permissions . isSuperAdmin , async ( req , res ) => {
227+ const validate = ajv . compile ( pluginMetadataSchema )
228+ const valid = validate ( req . body )
229+ if ( ! valid ) return res . status ( 400 ) . send ( validate . errors )
230+ await fs . writeJson ( path . join ( pluginsDir , req . params . id + '-metadata.json' ) , req . body )
231+ res . send ( req . body )
232+ } )
233+
186234router . put ( '/:id/access' , permissions . isSuperAdmin , async ( req , res ) => {
187235 await fs . writeJson ( path . join ( pluginsDir , req . params . id + '-access.json' ) , req . body )
188236 res . send ( req . body )
0 commit comments