lowcode-data-app
Version:
LowCode App Environment
424 lines (366 loc) • 13.3 kB
JavaScript
/* LOWCODE-DATA-APP / copyright 2024 by ma-ha https://github.com/ma-ha / MIT License */
const log = require( '../helper/log' ).logger
const apiSec = require( './api-sec' )
const dta = require( '../persistence/app-dta' )
const userDta = require( '../persistence/app-dta-user' )
const helper = require( '../helper/helper' )
const axios = require( 'axios' )
const appImport = require( './api-app-import' )
const stateImport= require( './api-models' )
exports: module.exports = {
setupAPI
}
// ============================================================================
// API:
// now we need to implement the ReST service for /products
// this should also only be available for authenticated users
let gui = null
let cfg = null
async function setupAPI( app, appCfg ) {
cfg = appCfg
let svc = app.getExpress()
gui = app
const guiAuthz = apiSec.userTenantAuthz( gui )
svc.get( '/market', getMarketAppList )
svc.get( '/market/html', getMarketItemDetails )
svc.get( '/market/prep-import', getMarketPrepImport )
svc.get( '/market/import/html', getMarketImport )
svc.get( '/market/howto/html', getMarketHowTo )
svc.get( '/market/state-model/diagram', getMarketStateModel )
setInterval( clearMarketListCache, 60*60*1000 )
}
// ============================================================================
async function getMarketAppList( req, res ) {
log.info( 'GET market', req.params.id )
let user = await userDta.getUserInfoFromReq( gui, req )
// log.info( 'user', user )
if ( ! user ) { return res.status(401).send( 'login required' ) }
if ( cfg.MARKETPLACE_SERVER ) {
res.json( await getMarketAppLisFrmDB( req, res ) )
} else {
res.json( await getMarketAppListFrmURL( req, res ) )
}
}
let marketList = null
function clearMarketListCache(){
marketList = null
}
async function getMarketAppListFrmURL( req, res ) {
log.info( 'getMarketAppListFrmURL', req.query )
let marketListURL = cfg.MARKETPLACE_URL +'/market.json'
let offers = []
let { showApp, showSM, name, standard } = getFilter( req )
try {
log.debug( 'axios', marketListURL )
if ( ! marketList ) {
let result = await axios.get( marketListURL )
log.info( 'axios', result.status )
if ( result.status != 200 ) {
log.error( 'axios.get', marketListURL, result.status )
return offers
}
marketList = result.data
}
offers = []
for ( let offer of marketList ) {
if ( name && offer.title?.toLowerCase().indexOf( name ) == -1 ) { continue }
if ( standard ) {
if ( offer.standard ) {
if ( offer.standard.toLowerCase().indexOf( standard ) == -1 ) { continue }
} else {
continue
}
}
let img = 'img/k8s-ww-conn.png'
if ( offer.type =='StateModel' ) {
if ( ! showSM ) { continue }
img = 'img/state-model.png'
} else {
if ( ! showApp ) { continue }
}
// if ( ! offer.img ) {
// img = 'img/k8s-ww-conn.png'
// if ( offer.type =='StateModel' ) {
// if ( ! showSM ) { continue }
// img = 'img/state-model.png' // TODO
// } else {
// if ( ! showApp ) { continue }
// }
// } else {
// img = encodeURI( offer.img )
// }
if ( offer.type =='StateModel' ) {
offer.img = '<a href="index.html?layout=MarketStateModelDetails-nonav&id='+offer.id+'">'+
'<img src="'+img+'"></a>'
} else {
offer.img = '<a href="index.html?layout=MarketAppDetails-nonav&id='+offer.id+'">'+
'<img src="'+img+'"></a>'
}
offers.push( offer )
}
} catch ( exc ) {
log.error( 'getAppMarketList', marketListURL, exc.message )
}
return offers
}
async function getMarketAppLisFrmDB( req, res ) {
let offers = []
let { showApp, showSM, name, standard } = getFilter( req )
if ( showApp ) {
let dbApps = await dta.getAppList( cfg.MARKETPLACE_SCOPE, [], 'marketplace' )
for ( let appId in dbApps ) {
let app = dbApps[ appId ]
let id = appId.split('/')[1]
if ( name && app.title?.toLowerCase().indexOf( name ) == -1 ) { continue }
if ( standard ) {
if ( app.standard ) {
if ( app.standard.toLowerCase().indexOf( standard ) == -1 ) { continue }
} else {
continue
}
}
offers.push({
id : id,
title : app.title,
standard : app.standard,
license : app.license,
author : ( app.by ? app.by : 'NN' ),
type : 'App',
img : '<a href="index.html?layout=MarketAppDetails-nonav&id=App/'+appId+'">'+
'<img src="img/k8s-ww-conn.png"></a>'
})
}
}
if ( showSM ) {
let dbSM = await dta.getStateModelMap( cfg.MARKETPLACE_SCOPE )
for ( let smId in dbSM ) {
let stateModel = dbSM[ smId ]
let id = smId.split('/')[1]
if ( name ) {
if ( stateModel.title ) {
if ( stateModel.title.toLowerCase().indexOf( name ) == -1 ) { continue }
} else if ( id.indexOf( name ) == -1 ) { continue }
}
if ( standard ) {
if ( stateModel.standard ) {
if ( stateModel.standard.toLowerCase().indexOf( standard ) == -1 ) { continue }
} else {
continue
}
}
offers.push({
id : id,
title : ( stateModel.title ? stateModel.title : id ),
author : ( stateModel.by ? stateModel.by : 'NN'),
type : 'StateModel',
img : '<a href="index.html?layout=MarketStateModelDetails-nonav&id=StateModel/'+smId+'">'+
'<img src="img/state-model.png"></a>'
})
}
}
offers.sort( (a,b) => { if ( a.title < b.title ) { return 1 } else { return -1 }} )
return offers
}
function getFilter( req ) {
log.info( 'getFilter', req.query.dataFilter )
let showApp = true
let showSM = true
let name = false
let standard = false
if ( req.query.dataFilter ) {
if ( req.query.dataFilter.type == 'App' ) { showSM = false }
if ( req.query.dataFilter.type == 'StateModel' ) { showApp = false }
if ( req.query.dataFilter.name != '' ) {
name = req.query.dataFilter.name.toLowerCase()
}
if ( req.query.dataFilter.standard != '*' ) {
standard = req.query.dataFilter.standard.toLowerCase()
}
}
return { showApp: showApp, showSM: showSM, name: name, standard:standard }
}
// ============================================================================
async function getMarketItemDetails( req, res ) {
log.info( 'GET market/html', req.query.id )
let user = await userDta.getUserInfoFromReq( gui, req )
// log.info( 'user', user )
if ( ! user ) { return res.status(401).send( 'login required' ) }
if ( cfg.MARKETPLACE_SERVER ) {
res.json( await getMarketItemDetailsFromDB( req.query.id ) )
} else {
res.send( await getMarketItemDetailsFromURL( req.query.id ) )
}
}
async function getMarketItemDetailsFromURL( id ) {
let importUrl = cfg.MARKETPLACE_URL +'/'+id+'.json'
let html = ''
try {
log.debug( 'axios', importUrl )
let result = await axios.get( importUrl )
log.debug( 'axios', result.data )
if ( result.status == 200 ) {
if ( result.data.state ) {
html = await preStateImport( id, result.data )
} else {
html = await prepAppImport( id, result.data )
}
}
} catch ( exc ) {
log.error( 'getMarketApp', importUrl, exc.message )
}
return html
}
async function getMarketItemDetailsFromDB( id ) {
let html = ''
if ( id.startsWith( 'App' ) ) {
let appId = id.substring( 4 )
let app = await dta.getAppById( appId )
html = await prepAppImport( id, { appId: app } )
} else if ( id.startsWith( 'StateModel' ) ) {
let smId = id.substring( 11 )
let sm = await dta.getStateModelById( smId )
html = await preStateImport( id, sm )
}
return html
}
async function getMarketStateModel( req, res ) {
log.info( 'getMarketStateModel', req.query.id )
let user = await userDta.getUserInfoFromReq( gui, req )
if ( ! user ) { return res.status(401).send( 'Login required!' )}
if ( ! cfg.MARKETPLACE_SERVER ) {
try {
log.debug( 'axios', cfg.MARKETPLACE_URL +'/'+req.query.id+'.json' )
let result = await axios.get( cfg.MARKETPLACE_URL +'/'+req.query.id+'.json' )
log.debug( 'axios', result.data )
if ( result.status == 200 ) {
// if ( result.data.state ) {
return await stateImport.getStateModelDta( res, req.query.id, result.data )
// }
}
} catch ( exc ) {
log.warn( 'getMarketStateModel', cfg.MARKETPLACE_URL +'/'+req.query.id+'.json', exc.message )
}
res.send({})
} else {
await stateImport.getStateModel( req, res )
}
}
// ============================================================================
async function prepAppImport( id, apps ) {
let html = ''
try {
for ( let appId in apps ) {
let app = apps[ appId ]
html += '<h2>App: '+ ( app.standard ? app.standard + ' ' : '' ) + app.title +'</h2>'
html += 'License: '+ ( app.license ? app.license : 'unknown' ) + '<br>'
html += app.description.replaceAll('\n','<p>')
html += '<p><b>'+ id +'</b>: <a href="market/prep-import?id='+id+'">Check import apps and entity models</a>'
for ( let entityId in app.entity ) {
let entity = app.entity[ entityId ]
log.debug( 'entity', entity )
html += '<h3>Entity: '+ ( entity.title ? entity.title+' ('+entityId+')' : entityId ) +'</h3>'
if ( entity.stateModel ) {
html += 'State Model: '+ entity.stateModel
let sm = await dta.getStateModelById( entity.stateModel )
if ( ! sm ) {
html += '<span class="error"> Warning: Not present! Import or create model first!</span>'
}
html += '<br>'
}
html += '<ul>'
for ( let propId in entity.properties ) {
let prp = entity.properties[ propId ]
let tp = prp.type
if ( tp == 'DocMap' ) {
tp += ' -> '+ prp.docMap
} else if ( tp == 'SelectRef' ) {
tp += ' -> '+ prp.selectRef
} else if ( tp == 'MultiSelectRef' ) {
tp += ' -> '+ prp.multiSelectRef
} else
html += '<li>'+ propId + ' ('+ tp +')</li>'
}
html += '</ul>'
}
}
} catch (exc) {
log.error( '', exc.message )
html = exc.message
}
return html
}
async function preStateImport( id, state ) {
let html = ''
try {
let sm = await dta.getStateModelById( id )
if ( sm ) {
html += '<span class="error"> Error: State model ='+id+' exists!</span>'
} else {
html += '<b>'+ id +'</b>: <a href="market/prep-import?id='+id+'">Check import state models</a>'
if ( state.description ) {
html += '<h2>'+ ( state.title ? state.title : id ) +'</h2>'
html += state.description
}
}
} catch (exc) {
html = exc.message
}
return html
}
let impResult = {}
async function getMarketPrepImport( req, res ) {
log.info( 'GET market/prep-import', req.query.id )
let user = await userDta.getUserInfoFromReq( gui, req )
// log.info( 'user', user )
if ( ! user ) { return res.status(401).send( 'login required' ) }
if ( cfg.MARKETPLACE_SERVER ) {
let id = req.query.id
if ( id.startsWith( 'App' ) ) {
let appId = id.substring( 4 )
let app = await dta.getAppById( appId )
let apps = {}
let idp = appId.split('/')
apps[ idp[1]+'/'+idp[2] ] = app
let prepOut = await appImport.prepJsonUpload( user.rootScopeId, apps )
impResult[ user.userId ] = prepOut
} else if ( id.startsWith( 'StateModel' ) ) {
let smId = id.substring( 11 )
let sm = await dta.getStateModelById( smId )
let prepOut = await stateImport.prepJsonStateUpload( user.rootScopeId, smId, sm )
impResult[ user.userId ] = prepOut
}
} else {
let appUrl = cfg.MARKETPLACE_URL +'/'+req.query.id+'.json'
try {
log.debug( 'axios', appUrl )
let result = await axios.get( appUrl )
// log.info( 'axios', result.data )
if ( result.status == 200 ) {
if ( result.data.state ) {
let prepOut = await stateImport.prepJsonStateUpload( user.rootScopeId, req.query.id, result.data )
impResult[ user.userId ] = prepOut
} else {
let prepOut = await appImport.prepJsonUpload( user.rootScopeId, result.data )
impResult[ user.userId ] = prepOut
}
}
} catch ( exc ) {
log.error( 'getMarketApp', appUrl, exc.message )
}
}
res.redirect( '../index.html?layout=MarketImport-nonav&id='+req.params.appId )
}
async function getMarketImport( req, res ) {
log.info( 'GET market/prep-import', req.query.id )
let user = await userDta.getUserInfoFromReq( gui, req )
let html = impResult[ user.userId ]
delete impResult[ user.userId ]
res.send( html )
}
async function getMarketHowTo( req, res ) {
if ( cfg.MARKETPLACE_HOWTO_CONTRIBUTE ) {
res.send( cfg.MARKETPLACE_HOWTO_CONTRIBUTE )
} else {
res.send( 'No contributions!' )
}
}