1
0
Fork 0
mirror of synced 2024-06-29 19:41:03 +12:00

injecting plugins into child apps

This commit is contained in:
michael shanks 2019-07-09 07:29:50 +01:00
parent 1590b584d4
commit 991bd69671
23 changed files with 260 additions and 56 deletions

View file

@ -1,19 +1,19 @@
const Koa = require('koa'); const Koa = require('koa');
const app = new Koa(); const app = new Koa();
const getMasterAppInternal = require("./utilities/masterAppInternal");
const router = require("./middleware/routers"); const router = require("./middleware/routers");
const koaBody = require('koa-body'); const koaBody = require('koa-body');
const initialiseRuntimePackages = require("./initialise/initialiseRuntimePackages"); const initialiseRuntimePackages = require("./initialise/initialiseRuntimePackages");
module.exports = async (config) => { module.exports = async (budibaseContext) => {
const { config } = budibaseContext;
app.keys = config.keys; app.keys = config.keys;
app.context.master = await getMasterAppInternal(config); app.context.master = budibaseContext.master;
app.context.getAppPackage = await initialiseRuntimePackages( app.context.getAppPackage = await initialiseRuntimePackages(
config, budibaseContext,
app.context.master, app.context.master,
config.latestAppsPath config.latestAppsPath
) );
app.use(koaBody({ multipart : true })); app.use(koaBody({ multipart : true }));
app.use(router(config, app).routes()); app.use(router(config, app).routes());
return app.listen(); return app.listen();

View file

@ -74,6 +74,16 @@
"getInitialValue": "default", "getInitialValue": "default",
"getUndefinedValue": "default" "getUndefinedValue": "default"
}, },
{
"name": "createdByMaster",
"type": "bool",
"typeOptions": {
"allowNulls": false
},
"label": "Created by Master",
"getInitialValue": "default",
"getUndefinedValue": "default"
},
{ {
"name": "instance", "name": "instance",
"type": "reference", "type": "reference",
@ -273,6 +283,18 @@
"getInitialValue": "default", "getInitialValue": "default",
"getUndefinedValue": "default" "getUndefinedValue": "default"
}, },
{
"name": "instanceKey",
"type": "string",
"typeOptions": {
"maxLength": null,
"values": null,
"allowDeclaredValuesOnly": false
},
"label": "Instance Key",
"getInitialValue": "default",
"getUndefinedValue": "default"
},
{ {
"name": "instanceVersion", "name": "instanceVersion",
"type": "string", "type": "string",
@ -467,7 +489,7 @@
"actionName": "create_user", "actionName": "create_user",
"eventName": "recordApi:save:onRecordCreated", "eventName": "recordApi:save:onRecordCreated",
"optionsCreator": "return ({ user:context.record, apis });", "optionsCreator": "return ({ user:context.record, apis });",
"condition": "context.record.type === \"user\"" "condition": "context.record.type === \"user\" && context.record.createdByMaster === true"
} }
] ]
} }

View file

@ -11,7 +11,8 @@ const { createWriteStream } = require("fs");
const { applictionVersionPackage } = require("../../utilities/createAppPackage"); const { applictionVersionPackage } = require("../../utilities/createAppPackage");
const { getApisWithFullAccess } = require("../../utilities/budibaseApi"); const { getApisWithFullAccess } = require("../../utilities/budibaseApi");
module.exports = (config) => { module.exports = (context) => {
const { config } = context;
const datastoreModule = require(`../../../datastores/datastores/${config.datastore}`); const datastoreModule = require(`../../../datastores/datastores/${config.datastore}`);
return ({ return ({
initialiseInstance : async ({ instance, apis }) => { initialiseInstance : async ({ instance, apis }) => {
@ -36,9 +37,8 @@ module.exports = (config) => {
await downloadAppPackage(apis, instance, application.name, versionId); await downloadAppPackage(apis, instance, application.name, versionId);
const dbConfig = await createInstanceDb( const dbConfig = await createInstanceDb(
config, context,
datastoreModule, datastoreModule,
config.datastoreConfig,
application, application,
instance instance
); );
@ -67,10 +67,11 @@ module.exports = (config) => {
last, last,
]); ]);
const appPackage = applictionVersionPackage( const appPackage = await applictionVersionPackage(
config, context,
application.name, application.name,
versionId); versionId,
instance.key);
const instanceApis = await getApisWithFullAccess( const instanceApis = await getApisWithFullAccess(
datastoreModule.getDatastore( datastoreModule.getDatastore(

View file

@ -1,4 +1,6 @@
const app = require("./app"); const app = require("./app");
const config = require("./config"); const config = require("./config");
const buildAppContext = require("./initialise/buildAppContext");
app(config); buildAppContext(config, true)
.then((appContext) => app(appContext));

View file

@ -0,0 +1,11 @@
const getMasterAppInternal = require("../utilities/masterAppInternal");
module.exports = async (config, masterIsCreated) => {
const context = { config };
if(!masterIsCreated) return context;
const master = await getMasterAppInternal(context);
context.master = master;
return context;
};

View file

@ -3,19 +3,18 @@ const {
setupDatastore, setupDatastore,
common common
} = require("budibase-core"); } = require("budibase-core");
const constructHierarchy = require("../utilities/constructHierarchy");
const getDatabaseManager = require("../utilities/databaseManager"); const getDatabaseManager = require("../utilities/databaseManager");
const masterDbAppDefinition = require("../appPackages/master/appDefinition.json");
const { applictionVersionPackage } = require("../utilities/createAppPackage"); const { applictionVersionPackage } = require("../utilities/createAppPackage");
const { last } = require("lodash/fp"); const { last } = require("lodash/fp");
const {$,splitKey} = common; const {$,splitKey} = common;
module.exports = async (config, datastoreModule, rootDatastoreConfig, app, instance) => { module.exports = async (context, datastoreModule, app, instance) => {
try { try {
const databaseManager = getDatabaseManager( const databaseManager = getDatabaseManager(
datastoreModule, rootDatastoreConfig); datastoreModule,
context.config.datastoreConfig);
await databaseManager.createEmptyInstanceDb( await databaseManager.createEmptyInstanceDb(
app.id, instance.id); app.id, instance.id);
@ -31,8 +30,8 @@ module.exports = async (config, datastoreModule, rootDatastoreConfig, app, insta
last last
]); ]);
const appPackage = applictionVersionPackage( const appPackage = await applictionVersionPackage(
config, app.name, versionId context, app.name, versionId, instance.key
); );
await initialiseData( await initialiseData(

View file

@ -6,9 +6,11 @@ const masterDbAppDefinition = require("../appPackages/master/appDefinition.json"
const masterDbAccessLevels = require("../appPackages/master/access_levels.json"); const masterDbAccessLevels = require("../appPackages/master/access_levels.json");
const { masterAppPackage } = require("../utilities/createAppPackage"); const { masterAppPackage } = require("../utilities/createAppPackage");
module.exports = async (datastoreModule, rootDatastoreConfig, username, password, budibaseConfig) => { module.exports = async (context, datastoreModule, username, password) => {
try { try {
const databaseManager = getDatabaseManager(datastoreModule, rootDatastoreConfig); const { config } = context;
const databaseManager = getDatabaseManager(
datastoreModule, config.datastoreConfig);
await databaseManager.createEmptyMasterDb(); await databaseManager.createEmptyMasterDb();
const masterDbConfig = databaseManager.masterDatastoreConfig; const masterDbConfig = databaseManager.masterDatastoreConfig;
@ -19,15 +21,20 @@ module.exports = async (datastoreModule, rootDatastoreConfig, username, password
await initialiseData(datastore, await initialiseData(datastore,
constructHierarchy(masterDbAppDefinition)); constructHierarchy(masterDbAppDefinition));
const masterPackage = masterAppPackage(context);
const bbMaster = await getApisWithFullAccess( const bbMaster = await getApisWithFullAccess(
datastore, masterAppPackage(budibaseConfig)); datastore, masterPackage);
await bbMaster.authApi.saveAccessLevels(masterDbAccessLevels); await bbMaster.authApi.saveAccessLevels(masterDbAccessLevels);
const user = bbMaster.authApi.getNewUser(); const user = bbMaster.authApi.getNewUser();
user.name = username; user.name = username;
user.accessLevels= ["owner"]; user.accessLevels= ["owner"];
await bbMaster.authApi.createUser(user, password); await bbMaster.authApi.createUser(user, password);
return await getApisForUser(datastore, masterAppPackage(budibaseConfig), username, password); return await getApisForUser(
datastore,
masterPackage,
username,
password);
} catch(e) { } catch(e) {
throw e; throw e;
} }

View file

@ -4,7 +4,7 @@ const readline = require('readline');
const { promisify } = require('util'); const { promisify } = require('util');
const { mkdir, rimraf } = require("../utilities/fsawait"); const { mkdir, rimraf } = require("../utilities/fsawait");
const budibaseConfig = require("../config"); const budibaseConfig = require("../config");
const buildAppContext = require("../initialise/buildAppContext");
readline.Interface.prototype.question[promisify.custom] = function(prompt) { readline.Interface.prototype.question[promisify.custom] = function(prompt) {
@ -67,10 +67,10 @@ const question = async (q) => {
await mkdir(rootconfig.rootPath); await mkdir(rootconfig.rootPath);
} }
const appContext = await buildAppContext(budibaseConfig, false);
await create( await create(
datastoreModule, appContext,
rootconfig, datastoreModule,
username, username,
password, password);
budibaseConfig)
})() })()

View file

@ -15,7 +15,7 @@ const copyfolder = (source, destination) =>
}); });
module.exports = async (config, bbMaster, latestAppsFolder) => { module.exports = async (context, bbMaster, latestAppsFolder) => {
// create runtime folder // create runtime folder
// copy master into /master/latest // copy master into /master/latest
@ -42,7 +42,7 @@ module.exports = async (config, bbMaster, latestAppsFolder) => {
const apps = { const apps = {
"_master": masterAppPackage(config) "_master": masterAppPackage(context)
} }
return ((appName, versionId) => { return ((appName, versionId) => {

View file

@ -52,7 +52,6 @@ module.exports = (app, appName) => {
name: testUserName, name: testUserName,
accessLevels:["owner"], accessLevels:["owner"],
enabled:true enabled:true
}, },
password: testPassword password: testPassword
}) })

View file

@ -86,7 +86,7 @@ module.exports = (app) => {
user1_instance1 = master.recordApi user1_instance1 = master.recordApi
.getNew(`${newAppKey}/users`, "user"); .getNew(`${newAppKey}/users`, "user");
user1_instance1.name = app.credentials.testApp.username; user1_instance1.name = app.credentials.testApp.username;
user1_instance1.createdByMaster = true;
/*const lookupResponse = await app.get(`/_master/api/lookup_field/${user1_instance1.key}?fields=instance`) /*const lookupResponse = await app.get(`/_master/api/lookup_field/${user1_instance1.key}?fields=instance`)
.set("cookie", app.credentials._master.cookie) .set("cookie", app.credentials._master.cookie)

View file

@ -4,7 +4,7 @@ const createMasterDb = require("../initialise/createMasterDb");
const request = require("supertest"); const request = require("supertest");
const fs = require("fs"); const fs = require("fs");
const { masterAppPackage } = require("../utilities/createAppPackage"); const { masterAppPackage } = require("../utilities/createAppPackage");
const buildAppContext = require("../initialise/buildAppContext");
var enableDestroy = require('server-destroy'); var enableDestroy = require('server-destroy');
const masterOwnerName = "test_master"; const masterOwnerName = "test_master";
@ -58,7 +58,8 @@ module.exports = () => {
start: async () => { start: async () => {
try { try {
await reInitialize(); await reInitialize();
server = await app(config); const budibaseContext = await buildAppContext(config, true);
server = await app(budibaseContext);
} catch(e) { } catch(e) {
console.log(e.message); console.log(e.message);
} }
@ -85,7 +86,7 @@ module.exports = () => {
name: "testApp" name: "testApp"
}, },
destroy: () => server.destroy(), destroy: () => server.destroy(),
masterAppPackage: masterAppPackage(config) masterAppPackage: masterAppPackage({ config })
}) })
}; };
@ -110,12 +111,13 @@ const reInitialize = async () => {
await mkdir(config.datastoreConfig.rootPath); await mkdir(config.datastoreConfig.rootPath);
const datastoreModule = require("../../datastores/datastores/" + config.datastore); const datastoreModule = require("../../datastores/datastores/" + config.datastore);
const budibaseContext = await buildAppContext(config, false);
await createMasterDb( await createMasterDb(
budibaseContext,
datastoreModule, datastoreModule,
config.datastoreConfig,
masterOwnerName, masterOwnerName,
masterOwnerPassword , masterOwnerPassword
config
); );
} }

View file

@ -1,5 +1,9 @@
const crypto = require("../nodeCrypto"); const crypto = require("../nodeCrypto");
const {getAppApis, getTemplateApi} = require("budibase-core"); const {getAppApis, getTemplateApi} = require("budibase-core");
const getDatastore = require("./datastore");
const { masterAppPackage } = require("../utilities/createAppPackage");
const getDatabaseManager = require("../utilities/databaseManager");
const constructHierarchy = (datastore, appDefinition) => { const constructHierarchy = (datastore, appDefinition) => {
appDefinition.hierarchy = getTemplateApi({datastore}) appDefinition.hierarchy = getTemplateApi({datastore})
@ -62,4 +66,20 @@ module.exports.getApisForSession = async (datastore, appPackage, session) => {
bb.asUser(user); bb.asUser(user);
return bb; return bb;
}
module.exports.getMasterApisWithFullAccess = async (context) => {
const { config } = context;
const datastoreModule = getDatastore(config);
const databaseManager = getDatabaseManager(
datastoreModule,
config.datastoreConfig);
const masterDatastore = datastoreModule.getDatastore(
databaseManager.masterDatastoreConfig);
return await module.exports.getApisWithFullAccess(
masterDatastore, masterAppPackage(context));
} }

View file

@ -2,7 +2,9 @@ const { join } = require("path");
const constructHierarchy = require("./constructHierarchy"); const constructHierarchy = require("./constructHierarchy");
const { common } = require("budibase-core"); const { common } = require("budibase-core");
const { getRuntimePackageDirectory } = require("../utilities/runtimePackages"); const { getRuntimePackageDirectory } = require("../utilities/runtimePackages");
const createAppPackage = (config, appPath) => { const injectPlugins = require("./injectedPlugins");
const createAppPackage = (context, appPath) => {
const appDefModule = require( const appDefModule = require(
join(appPath, "appDefinition.json")); join(appPath, "appDefinition.json"));
@ -16,14 +18,15 @@ const createAppPackage = (config, appPath) => {
return ({ return ({
appDefinition: appDefModule, appDefinition: appDefModule,
behaviourSources: pluginsModule(config), behaviourSources: pluginsModule(context),
appPath, appPath,
accessLevels accessLevels
}) })
} }
module.exports.masterAppPackage = (config) => { module.exports.masterAppPackage = (context) => {
const standardPackage = createAppPackage(config, "../appPackages/master"); const { config } = context;
const standardPackage = createAppPackage(context, "../appPackages/master");
const customizeMaster = config && config.customizeMaster const customizeMaster = config && config.customizeMaster
? config.customizeMaster ? config.customizeMaster
@ -35,7 +38,7 @@ module.exports.masterAppPackage = (config) => {
]); ]);
const plugins = require("../appPackages/master/plugins.js") const plugins = require("../appPackages/master/plugins.js")
(config); (context);
return ({ return ({
appDefinition, appDefinition,
@ -46,11 +49,17 @@ module.exports.masterAppPackage = (config) => {
}); });
} }
module.exports.applictionVersionPackage = (config, appname, versionId) => { module.exports.applictionVersionPackage = async (context, appname, versionId, instanceKey) => {
const pkg = createAppPackage( const pkg = createAppPackage(
config, context,
join("..", getRuntimePackageDirectory(appname, versionId)) join("..", getRuntimePackageDirectory(appname, versionId))
); );
pkg.appDefinition = constructHierarchy(pkg.appDefinition); pkg.appDefinition = constructHierarchy(pkg.appDefinition);
await injectPlugins(
pkg,
context.master,
appname,
instanceKey
);
return pkg; return pkg;
} }

View file

@ -0,0 +1,17 @@
module.exports = ({ masterAppInternal, instanceKey }) => async ({ user }) => {
const { bbMaster } = masterAppInternal;
const masterUser = bbMaster.recordApi
.getNew(`${newAppKey}/users`, "user");
masterUser.name = user.name;
masterUser.createdByMaster = false;
masterUser.instance = await bbMaster.recordApi
.load(instanceKey);
masterUser.active = user.enabled;
await bbMaster.recordApi.save(masterUser);
}

View file

@ -0,0 +1,6 @@
module.exports = ({ masterAppInternal, app }) => async ({ username }) => {
await masterAppInternal.disableUser(
app, username
);
}

View file

@ -0,0 +1,6 @@
module.exports = ({ masterAppInternal, app }) => async ({ username }) => {
await masterAppInternal.enableUser(
app, username
);
}

View file

@ -0,0 +1,77 @@
const createUser = require("./createUser");
const enableUser = require("./enableUser");
const disableUser = require("./disableUser");
module.exports = async (appPackage, masterAppInternal, instanceKey, appName) => {
const plugin = await constructPlugin(
masterAppInternal,
appName,
instanceKey
);
appPackage.behaviourSources._injected = plugin;
createActions(appPackage);
createTriggers(appPackage);
}
const createTriggers = (appPackage) => {
const appDef = appPackage.appDefinition;
appDef.triggers.push({
actionName: 'createUser',
eventName: 'authApi:createUser:onComplete',
optionsCreator: 'return {user:context.user};',
condition: ''
});
appDef.triggers.push({
actionName: 'enableUser',
eventName: 'authApi:enableUser:onComplete',
optionsCreator: 'return {username:context.username};',
condition: ''
});
appDef.triggers.push({
actionName: 'disableUser',
eventName: 'authApi:disableUser:onComplete',
optionsCreator: 'return {username:context.username};',
condition: ''
});
}
const createActions = (appPackage) => {
const appDef = appPackage.appDefinition;
appDef.actions.createUser = {
name: "createUser",
behaviourSource: '_injected',
behaviourName: 'createUser',
initialOptions: {}
};
appDef.actions.createUser = {
name: "enableUser",
behaviourSource: '_injected',
behaviourName: 'enableUser',
initialOptions: {}
};
appDef.actions.createUser = {
name: "disableUser",
behaviourSource: '_injected',
behaviourName: 'disableUser',
initialOptions: {}
};
}
const constructPlugin = async (masterAppInternal, appName, instanceKey) => {
const app = await masterAppInternal.getApplication(appName);
const initialiseObj = {
masterAppInternal, app, instanceKey
};
return ({
createUser:createUser(initialiseObj),
enableUser:enableUser(initialiseObj),
disableUser:disableUser(initialiseObj)
});
}

View file

@ -1,27 +1,29 @@
const {getApisWithFullAccess, getApisForSession} = require("./budibaseApi"); const {
getApisWithFullAccess,
getApisForSession,
getMasterApisWithFullAccess
} = require("./budibaseApi");
const getDatastore = require("./datastore"); const getDatastore = require("./datastore");
const getDatabaseManager = require("./databaseManager"); const getDatabaseManager = require("./databaseManager");
const {$, splitKey} = require("budibase-core").common; const {$, splitKey} = require("budibase-core").common;
const { keyBy, last } = require("lodash/fp"); const { keyBy, last } = require("lodash/fp");
const {unauthorized} = require("./exceptions");
const { masterAppPackage, applictionVersionPackage } = require("../utilities/createAppPackage"); const { masterAppPackage, applictionVersionPackage } = require("../utilities/createAppPackage");
const isMaster = appname => appname === "_master"; const isMaster = appname => appname === "_master";
module.exports = async (config) => { module.exports = async (context) => {
const { config } = context;
const datastoreModule = getDatastore(config); const datastoreModule = getDatastore(config);
const databaseManager = getDatabaseManager( const databaseManager = getDatabaseManager(
datastoreModule, datastoreModule,
config.datastoreConfig); config.datastoreConfig);
const masterDatastore = datastoreModule.getDatastore( const masterDatastore = datastoreModule.getDatastore(
databaseManager.masterDatastoreConfig); databaseManager.masterDatastoreConfig);
const bb = await getApisWithFullAccess( const bb = await getMasterApisWithFullAccess(context);
masterDatastore, masterAppPackage(config));
let applications; let applications;
const loadApplications = async () => const loadApplications = async () =>
@ -100,9 +102,11 @@ module.exports = async (config) => {
]); ]);
const dsConfig = JSON.parse(instance.datastoreconfig); const dsConfig = JSON.parse(instance.datastoreconfig);
const appPackage = await applictionVersionPackage(
context, appname, versionId, instance.key);
const bbInstance = await getApisWithFullAccess( const bbInstance = await getApisWithFullAccess(
datastoreModule.getDatastore(dsConfig), datastoreModule.getDatastore(dsConfig),
applictionVersionPackage(config, appname, versionId) appPackage
); );
const authUser = await bbInstance.authApi.authenticate(username, password); const authUser = await bbInstance.authApi.authenticate(username, password);
@ -115,6 +119,7 @@ module.exports = async (config) => {
bb.recordApi.setCustomId(session, sessionId); bb.recordApi.setCustomId(session, sessionId);
session.user_json = JSON.stringify(authUser); session.user_json = JSON.stringify(authUser);
session.instanceDatastoreConfig = instance.datastoreconfig; session.instanceDatastoreConfig = instance.datastoreconfig;
session.instanceKey = instance.key;
session.username = username; session.username = username;
session.instanceVersion = instance.version.key; session.instanceVersion = instance.version.key;
await bb.recordApi.save(session); await bb.recordApi.save(session);
@ -128,7 +133,7 @@ module.exports = async (config) => {
const session = await bb.recordApi.load(`/sessions/${customId}`); const session = await bb.recordApi.load(`/sessions/${customId}`);
return await getApisForSession( return await getApisForSession(
masterDatastore, masterDatastore,
masterAppPackage(config), masterAppPackage(context),
session); session);
} catch(_) { } catch(_) {
@ -147,9 +152,12 @@ module.exports = async (config) => {
last last
]); ]);
const appPackage = await applictionVersionPackage(
context, appname, versionId, session.instanceKey);
return await getApisForSession( return await getApisForSession(
instanceDatastore, instanceDatastore,
applictionVersionPackage(config, appname, versionId), appPackage,
session); session);
} catch(_) { } catch(_) {
return null; return null;
@ -190,10 +198,13 @@ module.exports = async (config) => {
splitKey, splitKey,
last last
]); ]);
const appPackage = await applictionVersionPackage(
context, appname, versionId, user.instanceKey);
return await getApisWithFullAccess( return await getApisWithFullAccess(
instanceDatastore, instanceDatastore,
applictionVersionPackage(config, appname, versionId)); appPackage);
} }
}; };
@ -230,6 +241,19 @@ module.exports = async (config) => {
} }
} }
const disableUser = async (app, username) => {
await removeSessionsForUser(appName, username);
const userInMaster = await getUser(bb, app.id, username);
userInMaster.active = false;
await bb.recordApi.save(userInMaster);
}
const enableUser = async (app, username) => {
const userInMaster = await getUser(bb, app.id, username);
userInMaster.active = true;
await bb.recordApi.save(userInMaster);
}
return ({ return ({
getApplication, getApplication,
getSession, getSession,
@ -238,6 +262,8 @@ module.exports = async (config) => {
getInstanceApiForSession, getInstanceApiForSession,
getFullAccessInstanceApiForUsername, getFullAccessInstanceApiForUsername,
removeSessionsForUser, removeSessionsForUser,
disableUser,
enableUser,
bbMaster:bb bbMaster:bb
}); });