1
0
Fork 0
mirror of synced 2024-06-27 02:20:35 +12:00

creating new apps, instances and users now working

This commit is contained in:
michael shanks 2019-07-05 16:56:53 +01:00
parent 2781a9fabb
commit 60deb10b45
14 changed files with 600 additions and 59 deletions

@ -1 +1 @@
Subproject commit 11c29ab2c467ea7aea0161f331dab07694ccd0ab
Subproject commit 804483d3a3210936743f3b57a032783296eab06c

View file

@ -3,8 +3,8 @@ const fs = require("fs");
const {join} = require("path");
const readFile = promisify(fs.readFile);
const writeFile = (path, content) =>
promisify(fs.writeFile)(path, content, "utf8");
const writeFile = (path, content, overwrite) =>
promisify(fs.writeFile)(path, content, {encoding:"utf8", flag: overwrite ? "w" : "wx"});
const access = promisify(fs.access);
const mkdir = promisify(fs.mkdir);
const rmdir = promisify(fs.rmdir);
@ -16,10 +16,16 @@ const stat = promisify(fs.stat);
const updateFile = root => async (path, file) =>
await writeFile(
join(root,path),
file
file,
true
);
const createFile = updateFile;
const createFile = root => async (path, file) =>
await writeFile(
join(root,path),
file,
false
);
const loadFile = root => async (path) =>
await readFile(

View file

@ -53,7 +53,7 @@
"type": "record",
"fields": [
{
"name": "unique_name",
"name": "name",
"type": "string",
"typeOptions": {
"maxLength": 200,
@ -329,7 +329,7 @@
{
"name": "user_name_lookup",
"type": "index",
"map": "return {name:record.name, instanceDatastoreConfig:instance.datastoreconfig};",
"map": "return ({name:record.name, instanceKey:record.instance.key ? record.instance.key : '', instanceDatastoreConfig:record.instance.datastoreconfig ? record.instance.datastoreconfig : 'nothing'});",
"filter": "",
"indexType": "ancestor",
"getShardName": "return record.name.substring(0,2)",
@ -448,8 +448,8 @@
{
"actionName": "initialise_instance",
"eventName": "recordApi:save:onRecordCreated",
"optionsCreator": "return { instance:context.record, apis };",
"condition": "record.type === \"instance\""
"optionsCreator": "return ({ instance:context.record, apis });",
"condition": "context.record.type === \"instance\""
},
{
"actionName": "create_user",

View file

@ -1,7 +1,7 @@
const { tmpdir } = require("os");
const { join } = require("path");
const uuid = require("uuid/v1");
const { take, takeRight } = require("lodash/fp");
const { take, takeRight, last } = require("lodash/fp");
const { splitKey, $, joinKey } = require("budibase-core").common;
const { unzipTarGzPackageToRuntime } = require("../../utilities/targzAppPackage");
const { getRuntimePackageDirectory } = require("../../utilities/runtimePackages");
@ -32,24 +32,25 @@ module.exports = (config) => {
const versionId = $(instance.version.key, [
splitKey,
takeRight(1),
joinKey
last
]);
const runtimeDir = getRuntimePackageDirectory(
application.name,
versionId);
if(!await exists(runtimeDir))
await downloadAppPackage(apis, instance, application.name, versionId);
instance.datastoreconfig = JSON.stringify(dbConfig);
instance.isNew = false;
await apis.recordApi.save(instance);
instance.transactionId = "";
await apis.recordApi.save(instance);
},
createNewUser: async ({user, apis}) => {
const instance = apis.recordApi.load(user.instance.key);
const instance = await apis.recordApi.load(user.instance.key);
const appKey = $(instance.key, [
splitKey,
@ -61,16 +62,17 @@ module.exports = (config) => {
const versionId = $(instance.version.key, [
splitKey,
takeRight(1),
joinKey
last,
]);
const appPackage = applictionVersionPackage(
config,
application.name,
versionId);
const instanceApis = getApisWithFullAccess(
datastoreModule.getDatastore(instance.datastoreconfig),
const instanceApis = await getApisWithFullAccess(
datastoreModule.getDatastore(
JSON.parse(instance.datastoreconfig)),
appPackage);
const authUser = instanceApis.authApi.getNewUser();

View file

@ -325,10 +325,10 @@
{
"name": "user_name_lookup",
"type": "index",
"map": "return {name:record.name, instanceDatastoreConfig:instance.datastoreconfig};",
"map": "return {name:record.unique_name, instanceDatastoreConfig:instance.datastoreconfig};",
"filter": "",
"indexType": "ancestor",
"getShardName": "return record.name.substring(0,2)",
"getShardName": "return record.unique_name.substring(0,2)",
"getSortKey": "record.id",
"aggregateGroups": [],
"allowedRecordNodeIds": [
@ -438,7 +438,7 @@
{
"actionName": "output_to_file",
"eventName": "authApi:createUser:onComplete",
"optionsCreator": "return { filename: filename:'tempaccess' + context.user.name, content:context.result.tempCode };",
"optionsCreator": "return { filename:'tempaccess' + context.user.name, content:context.result.tempCode };",
"condition": ""
}
]

View file

@ -165,6 +165,21 @@ module.exports = (config, app) => {
);
ctx.response.status = StatusCodes.OK;
})
.get("/:appname/api/lookup_field/*", async (ctx) => {
const recordKey = getRecordKey(ctx.params.appname, ctx.request.path)
const fields = ctx.query.fields.split(",")
const recordContext = await ctx.instance.recordApi.getContext(
recordKey
);
const allContext = [];
for(let field of fields) {
allContext.push(
await recordContext.referenceOptions(field)
);
}
ctx.body = allContext;
ctx.response.status = StatusCodes.OK;
})
.get("/:appname/api/record/*", async (ctx) => {
ctx.body = await ctx.instance.recordApi.load(
getRecordKey(ctx.params.appname, ctx.request.path)
@ -198,8 +213,9 @@ module.exports = (config, app) => {
const getRecordKey = (appname, wholePath) =>
wholePath
.replace(`/${appname}/api/record/`, "")
.replace(`/${appname}/api/files/`, "");
.replace(`/${appname}/api/files/`, "")
.replace(`/${appname}/api/lookup_field/`, "")
.replace(`/${appname}/api/record/`, "");
return router;
}

View file

@ -0,0 +1,445 @@
{
"hierarchy": {
"name": "root",
"type": "root",
"children": [
{
"name": "application",
"type": "record",
"fields": [
{
"name": "name",
"type": "string",
"typeOptions": {
"maxLength": 500,
"values": null,
"allowDeclaredValuesOnly": false
},
"label": "Name",
"getInitialValue": "default",
"getUndefinedValue": "default"
},
{
"name": "domain",
"type": "string",
"typeOptions": {
"maxLength": 500,
"values": null,
"allowDeclaredValuesOnly": false
},
"label": "domain",
"getInitialValue": "default",
"getUndefinedValue": "default"
},
{
"name": "application_resolve_strategy",
"type": "string",
"typeOptions": {
"maxLength": 100,
"values": [
"domain",
"path"
],
"allowDeclaredValuesOnly": true
},
"label": "Resolve Application By",
"getInitialValue": "default",
"getUndefinedValue": "default"
}
],
"children": [
{
"name": "user",
"type": "record",
"fields": [
{
"name": "unique_name",
"type": "string",
"typeOptions": {
"maxLength": 200,
"values": null,
"allowDeclaredValuesOnly": false
},
"label": "Name (unique)",
"getInitialValue": "default",
"getUndefinedValue": "default"
},
{
"name": "active",
"type": "bool",
"typeOptions": {
"allowNulls": false
},
"label": "Is Active",
"getInitialValue": "default",
"getUndefinedValue": "default"
},
{
"name": "instance",
"type": "reference",
"typeOptions": {
"indexNodeKey": "/applications/1-{id}/allinstances",
"reverseIndexNodeKeys": [
"/applications/1-{id}/instances/2-{id}/users_on_this_instance"
],
"displayValue": "name"
},
"label": "Instance",
"getInitialValue": "default",
"getUndefinedValue": "default"
}
],
"children": [],
"validationRules": [],
"nodeId": 8,
"indexes": [],
"allidsShardFactor": "64",
"collectionName": "users",
"isSingle": false
},
{
"name": "instance",
"type": "record",
"fields": [
{
"name": "name",
"type": "string",
"typeOptions": {
"maxLength": 1000,
"values": null,
"allowDeclaredValuesOnly": false
},
"label": "Name",
"getInitialValue": "default",
"getUndefinedValue": "default"
},
{
"name": "active",
"type": "bool",
"typeOptions": {
"allowNulls": false
},
"label": "Is Active",
"getInitialValue": "default",
"getUndefinedValue": "default"
},
{
"name": "version",
"type": "reference",
"typeOptions": {
"indexNodeKey": "/applications/1-{id}/all_versions",
"reverseIndexNodeKeys": [
"/applications/1-{id}/versions/3-{id}/instances_on_this_version"
],
"displayValue": "name"
},
"label": "Version",
"getInitialValue": "default",
"getUndefinedValue": "default"
},
{
"name": "datastoreconfig",
"type": "string",
"typeOptions": {
"maxLength": 1000,
"values": null,
"allowDeclaredValuesOnly": false
},
"label": "Datastore Config",
"getInitialValue": "default",
"getUndefinedValue": "default"
}
],
"children": [],
"validationRules": [],
"nodeId": 2,
"indexes": [
{
"name": "users_on_this_instance",
"type": "index",
"map": "return {...record};",
"filter": "",
"indexType": "reference",
"getShardName": "",
"getSortKey": "record.id",
"aggregateGroups": [],
"allowedRecordNodeIds": [],
"nodeId": 15
}
],
"allidsShardFactor": 1,
"collectionName": "instances",
"isSingle": false
},
{
"name": "version",
"type": "record",
"fields": [
{
"name": "name",
"type": "string",
"typeOptions": {
"maxLength": 200,
"values": null,
"allowDeclaredValuesOnly": false
},
"label": "Name",
"getInitialValue": "default",
"getUndefinedValue": "default"
},
{
"name": "package",
"type": "file",
"typeOptions": {},
"label": "Package",
"getInitialValue": "default",
"getUndefinedValue": "default"
}
],
"children": [],
"validationRules": [],
"nodeId": 3,
"indexes": [
{
"name": "instances_for_this_version",
"type": "index",
"map": "return {name:record.name};",
"filter": "",
"indexType": "ancestor",
"getShardName": "",
"getSortKey": "record.id",
"aggregateGroups": [],
"allowedRecordNodeIds": [],
"nodeId": 9
},
{
"name": "instances_on_this_version",
"type": "index",
"map": "return {...record};",
"filter": "",
"indexType": "reference",
"getShardName": "",
"getSortKey": "record.id",
"aggregateGroups": [],
"allowedRecordNodeIds": [],
"nodeId": 10
}
],
"allidsShardFactor": 1,
"collectionName": "versions",
"isSingle": false
},
{
"name": "session",
"type": "record",
"fields": [
{
"name": "created",
"type": "number",
"typeOptions": {
"minValue": 0,
"maxValue": 99999999999999,
"decimalPlaces": 0
},
"label": "Created",
"getInitialValue": "default",
"getUndefinedValue": "default"
},
{
"name": "user_json",
"type": "string",
"typeOptions": {
"maxLength": null,
"values": null,
"allowDeclaredValuesOnly": false
},
"label": "User Json",
"getInitialValue": "default",
"getUndefinedValue": "default"
},
{
"name": "instanceDatastoreConfig",
"type": "string",
"typeOptions": {
"maxLength": null,
"values": null,
"allowDeclaredValuesOnly": false
},
"label": "Instance Datastore Config",
"getInitialValue": "default",
"getUndefinedValue": "default"
},
{
"name": "username",
"type": "string",
"typeOptions": {
"maxLength": null,
"values": null,
"allowDeclaredValuesOnly": false
},
"label": "User",
"getInitialValue": "default",
"getUndefinedValue": "default"
}
],
"children": [],
"validationRules": [],
"nodeId": 16,
"indexes": [],
"allidsShardFactor": 1,
"collectionName": "sessions",
"isSingle": false
}
],
"validationRules": [],
"nodeId": 1,
"indexes": [
{
"name": "allinstances",
"type": "index",
"map": "return {...record};",
"filter": "",
"indexType": "ancestor",
"getShardName": "",
"getSortKey": "record.id",
"aggregateGroups": [],
"allowedRecordNodeIds": [
2
],
"nodeId": 23
},
{
"name": "sessions_by_user",
"type": "index",
"map": "return {username:record.username};",
"filter": "",
"indexType": "ancestor",
"getShardName": "return record.username.substring(0,2)",
"getSortKey": "record.id",
"aggregateGroups": [],
"allowedRecordNodeIds": [
16
],
"nodeId": 24
},
{
"name": "user_name_lookup",
"type": "index",
"map": "return {name:record.unique_name, instanceDatastoreConfig:instance.datastoreconfig};",
"filter": "",
"indexType": "ancestor",
"getShardName": "return record.unique_name.substring(0,2)",
"getSortKey": "record.id",
"aggregateGroups": [],
"allowedRecordNodeIds": [
8
],
"nodeId": 25
},
{
"name": "all_versions",
"type": "index",
"map": "return {...record};",
"filter": "",
"indexType": "ancestor",
"getShardName": "",
"getSortKey": "record.id",
"aggregateGroups": [],
"allowedRecordNodeIds": [
3
],
"nodeId": 26
}
],
"allidsShardFactor": 64,
"collectionName": "applications",
"isSingle": false
},
{
"name": "mastersession",
"type": "record",
"fields": [
{
"name": "user_json",
"type": "string",
"typeOptions": {
"maxLength": 10000,
"values": null,
"allowDeclaredValuesOnly": false
},
"label": "User Json",
"getInitialValue": "default",
"getUndefinedValue": "default"
},
{
"name": "username",
"type": "string",
"typeOptions": {
"maxLength": null,
"values": null,
"allowDeclaredValuesOnly": false
},
"label": "User",
"getInitialValue": "default",
"getUndefinedValue": "default"
}
],
"children": [],
"validationRules": [],
"nodeId": 17,
"indexes": [],
"allidsShardFactor": 64,
"collectionName": "sessions",
"isSingle": false
}
],
"pathMaps": [],
"indexes": [
{
"name": "all_applications",
"type": "index",
"map": "return {...record};",
"filter": "",
"indexType": "ancestor",
"getShardName": "",
"getSortKey": "record.id",
"aggregateGroups": [],
"allowedRecordNodeIds": [
1
],
"nodeId": 22
},
{
"name": "mastersessions_by_user",
"type": "index",
"map": "return {username:record.username};",
"filter": "",
"indexType": "ancestor",
"getShardName": "return record.username.substring(0,2)",
"getSortKey": "record.id",
"aggregateGroups": [],
"allowedRecordNodeIds": [
17
],
"nodeId": 27
}
],
"nodeId": 0
},
"actions": {
"output_to_file": {
"name": "output_to_file",
"behaviourSource": "main",
"behaviourName": "outputToFile",
"initialOptions": {}
}
},
"triggers": [
{
"actionName": "output_to_file",
"eventName": "authApi:createUser:onComplete",
"optionsCreator": "return { filename:'tempaccess' + context.user.name, content:context.result.tempCode };",
"condition": ""
}
]
}

View file

@ -0,0 +1,9 @@
const fs = require("fs");
module.exports = (config) => ({
main: {
outputToFile : ({filename, content}) => {
fs.writeFile(`./tests/.data/${filename}`, content, {encoding:"utf8"});
}
}
})

File diff suppressed because one or more lines are too long

View file

@ -6,6 +6,7 @@ const masterAppDefinition = constructHierarchy(
require("../appPackages/master/appDefinition.json"));
const {getApisWithFullAccess} = require("../utilities/budibaseApi");
const { createTarGzPackage } = require("../utilities/targzAppPackage");
const { timeout } = require("./helpers");
module.exports = (app) => {
@ -40,6 +41,7 @@ module.exports = (app) => {
const version1 = master.recordApi
.getNew(`${newAppKey}/versions`, "version");
version1.name = "v1";
version1.defaultAccessLevel = "owner";
version1Key = version1.key;
const { path, size } = await createTarGzPackage(app.config, "testApp");
@ -57,26 +59,62 @@ module.exports = (app) => {
});
let instance1Key;
let instance1;
it("should be able to create new instance of app", async () => {
const master = await getmaster();
const instance1 = master.recordApi
instance1 = master.recordApi
.getNew(`${newAppKey}/instances`, "instance");
instance1.name = "instance 1";
instance1.active = true;
instance1.version = {key:version1Key, name:"v1", defaultAccessLevel:"owner"};
instance1Key = instance1.key;
await app.post(`/_master/api/record/${instance1.key}`, instance1)
.set("cookie", app.masterAuth.cookie)
.expect(statusCodes.OK);
const loadInstanceResponse = await app.get(`/_master/api/record/${instance1.key}`)
.set("cookie", app.masterAuth.cookie)
.expect(statusCodes.OK);
instance1 = loadInstanceResponse.body;
});
it("should be able to create new user on instance", async () => {
let user1_instance1;
it("should be able to create new user on instance, via master", async () => {
const master = await getmaster();
user1_instance1 = master.recordApi
.getNew(`${newAppKey}/users`, "user");
user1_instance1.name = "testAppUser1";
/*const lookupResponse = await app.get(`/_master/api/lookup_field/${user1_instance1.key}?fields=instance`)
.set("cookie", app.masterAuth.cookie)
.expect(statusCodes.OK);
*/
user1_instance1.instance = instance1;
user1_instance1.active = true;
//await timeout(100);
await app.post(`/_master/api/record/${user1_instance1.key}`, user1_instance1)
.set("cookie", app.masterAuth.cookie)
.expect(statusCodes.OK);
});
it("should be able to set password for new user using temporary code", async () => {
const testUserTempCode = await readFile(`./tests/.data/tempaccess${user1_instance1.name}`, "utf8");
user1_instance1.password = "user1_instance1_password";
await app.post("/testApp/api/setPasswordFromTemporaryCode", {
username: user1_instance1.name,
tempCode:testUserTempCode,
newPassword:user1_instance1.password
})
.expect(statusCodes.OK);
await app.post("/testApp/api/authenticate", {
username: user1_instance1.name,
password: user1_instance1.password
})
.expect(statusCodes.OK);
})
}

View file

@ -1,14 +1,24 @@
const { join } = require("path");
const constructHierarchy = require("./constructHierarchy");
const { common } = require("budibase-core");
const createAppPackage = (appPath) => ({
appDefinition: require(join(appPath, "appDefinition.json")),
behaviourSources: require(join(appPath, "plugins.js")),
appPath
});
const { getRuntimePackageDirectory } = require("../utilities/runtimePackages");
const createAppPackage = (config, appPath) => {
const appDefModule = require(
join(appPath, "appDefinition.json"));
const pluginsModule = require(
join(appPath, "plugins.js"));
return ({
appDefinition: appDefModule,
behaviourSources: pluginsModule(config),
appPath
})
}
module.exports.masterAppPackage = (config) => {
const standardPackage = createAppPackage("../appPackages/master");
const standardPackage = createAppPackage(config, "../appPackages/master");
const customizeMaster = config && config.customizeMaster
? config.customizeMaster
@ -31,8 +41,11 @@ module.exports.masterAppPackage = (config) => {
});
}
module.exports.applictionVersionPackage = (appname, versionId) => {
const pkg = createAppPackage(`../runtimePackages/${appname}/${versionId}`);
pkg.appDefinition = constructHierarchy(appDefinition);
module.exports.applictionVersionPackage = (config, appname, versionId) => {
const pkg = createAppPackage(
config,
join("..", getRuntimePackageDirectory(appname, versionId))
);
pkg.appDefinition = constructHierarchy(pkg.appDefinition);
return pkg;
}

View file

@ -88,22 +88,20 @@ module.exports = async (config) => {
const app = await getApplication(appname);
const userInMaster = await bb.indexApi.listItems(
`/applications/${app.id}/users_by_name`,
{name:username}
).find(u => u.name === username);
const userInMaster = await getUser(bb, app.id, username);
const instance = await bb.recordApi.load(
userInMaster.instance.key);
userInMaster.instanceKey);
const versionId = $(instance.version.key, [
splitKey,
last
]);
const dsConfig = JSON.parse(instance.datastoreconfig);
const bbInstance = await getApisWithFullAccess(
datastoreModule.getDatastore(instance.datastoreconfig),
applictionVersionPackage(appname, versionId)
datastoreModule.getDatastore(dsConfig),
applictionVersionPackage(config, appname, versionId)
);
const authUser = await bbInstance.authApi.authenticate(username, password);
@ -140,10 +138,11 @@ module.exports = async (config) => {
const customId = bb.recordApi.customId("session", sessionId);
try {
const session = await bb.recordApi.load(`/applications/${app.id}/sessions/${customId}`);
const instanceDatastore = getInstanceDatastore(session.instanceDatastoreConfig)
const dsConfig = JSON.parse(session.instanceDatastoreConfig);
const instanceDatastore = getInstanceDatastore(dsConfig)
return await getApisForSession(
instanceDatastore,
applictionVersionPackage(appname, session.instanceVersion),
applictionVersionPackage(config, appname, session.instanceVersion),
session);
} catch(_) {
return null;
@ -151,6 +150,19 @@ module.exports = async (config) => {
}
};
const getUser = async (bb, appId, username ) => {
const matches = await bb.indexApi.listItems(
`/applications/${appId}/user_name_lookup`,
{
rangeStartParams:{name:username},
rangeEndParams:{name:username},
searchPhrase:`name:${username}`
}
);
if(matches.length !== 1) return;
return matches[0];
}
const getFullAccessInstanceApiForUsername = async (appname, username) => {
if(isMaster(appname)) {
@ -158,20 +170,21 @@ module.exports = async (config) => {
}
else {
const app = await getApplication(appname);
const matches = bb.indexApi.listItems(
`/applications/${app.id}/user_name_lookup`,
{
rangeStartParams:{name:username},
rangeEndParams:{name:username},
searchPhrase:`name:${username}`
}
);
if(matches.length !== 1) return;
const user = await getUser(bb, app.id, username);
const dsConfig = JSON.parse(user.instanceDatastoreConfig);
const instanceDatastore = getInstanceDatastore(
matches[0].instanceDatastoreConfig);
dsConfig
);
return await getApisWithFullAccess(instanceDatastore);
const versionId = $((await bb.recordApi.load(user.instanceKey)).version.key, [
splitKey,
last
]);
return await getApisWithFullAccess(
instanceDatastore,
applictionVersionPackage(config, appname, versionId));
}
};