UNPKG

adaptorex

Version:

Connect all your live interactive storytelling devices and software

276 lines (239 loc) 8.32 kB
/** * Organize access to adaptor:ex data collections * * data is a core plugin and will be included with every game * * @requires plugin * @requires file * * @module data/data * @copyright Lasse Marburg 2022 * @license MIT */ const plugin = require('../plugin.js') var schema = require('./schema.json') const file = require('../../file.js') const path = require('path') /** @typedef {import('../../types').AdaptorAction} Action */ /** * install request API for all custom collections the game contains */ class Data extends plugin.Plugin { constructor() { super(schema) this.name = "data" this.core = true this.collection_apis = [] } /** * create request API for each custom database * * collect files in file directory and watch for changes. */ setup(config, game) { return new Promise((resolve, reject) => { super.setup(config, game) .then(result => { this.addTemplate("addItem", (payload, action) => { const title = `data ${action.name}` let subtitle = `Add new ${payload.collection} Item` const body = [] if(payload.reference) { subtitle += ` as ${payload.reference}` } if (payload.variables) { body.push({text: JSON.stringify(payload.variables)}) } return {title, subtitle, body} }) this.addTemplate("getItem", (payload, action) => { const title = `data ${action.name}` let subtitle = `Get Item from ${payload.collection} as '${payload.reference}'` if(payload.multiple && payload.limit != 1) { subtitle = `Get Item(s) from ${payload.collection} as '${payload.reference}'` } const body = [{ text: payload.query}] if(payload.sort && payload.sort.length) { let txt = "" for(let s of payload.sort) { if(s.direction == 1) { txt += ` ${s.by} ⬆️` } else { txt += ` ${s.by} ⬇️` } } body.push({text:`sort by:${txt}`}) } if(payload.limit && payload.limit > 1 && payload.multiple) { body.push({text:`limit to ${payload.limit} Items`}) } if(payload.skip) { if(payload.skip == 1) { body.push({text:`skip first Item`}) } else { body.push({text:`skip first ${payload.skip} Items`}) } } return {title, subtitle, body} }) log.debug(this.name, "load assets filenames from " + this.game.files) this.collectFiles(this.game.files) file.watchDir(this.game.files, (e, file) => { this.collectFiles(this.game.files) log.debug(this.name, "change in file directory: " + e + " " + file) this.schemaUpdate(this.schema) }) for(let coll of this.game.setup.collections) { this.schema.definitions.collection.enum.push(coll) this.schema.definitions.collection.options.enum_titles.push(coll) this.schema.definitions.data_collection.enum.push(coll) } this.game.status.on("collection_added", data => { let updt = false if(!this.schema.definitions.collection.enum.includes(data.collection)) { this.schema.definitions.collection.enum.push(data.collection) if(data.plugin) { this.schema.definitions.collection.options.enum_titles.push(data.plugin + " " + data.name) } else { this.schema.definitions.collection.options.enum_titles.push(data.name) } updt = true } if(data.type == "data" && !this.schema.definitions.data_collection.enum.includes(data.collection)) { this.schema.definitions.data_collection.enum.push(data.collection) updt = true } if(updt) { this.schemaUpdate(this.schema) } }) return resolve(this.schema) }) .catch(err => { return reject(err) }) }) } /** * Collects all file paths from a directory and stores them in the data schema definitions under `files.anyOf[1].enum`. * * Clears the existing file enum array in the schema, walks through the specified directory to gather file paths, and then populates the enum array with those paths. * * @param {string} dir - The base directory to collect file paths from. All file paths will be relative to this directory. * @returns {string[]} - The updated `enum` array containing the relative file paths. * * @example * const fileList = this.collectFiles('./my-directory'); * log.info(fileList); * // Output: ['file1.png', 'sub-dir/file2.mp3'] */ collectFiles(dir) { this.schema.definitions.files.anyOf[1].enum = [] this.walkDirectory(dir, dir) return this.schema.definitions.files.anyOf[1].enum } /** * Recursively walks through a directory and returns a list of relative file paths in Unix-style format. * * @param {string} dir - The current directory being processed (absolute or relative path). * @param {string} baseDir - The base directory from which all file paths should be relative. */ walkDirectory(dir, baseDir) { const files = file.ls(dir) files.forEach((f) => { const filePath = path.posix.join(dir, f) if(file.isDir(filePath)) { return this.walkDirectory(filePath, baseDir) } this.schema.definitions.files.anyOf[1].enum.push(path.posix.relative(baseDir, filePath)) }) } /** * @type {Action} * * Call database update on an item */ async update(data, session) { await session.variables.update(data.item, data.update, {multiple: data.multiple || false}) } /** * @type {Action} * * Set value of variable */ async set(data, session) { await session.variables.set(data.variable, data.value, {multiple: data.multiple || false}) } /** * @type {Action} * * Increase numeric value of variable */ async increase(data, session) { await session.variables.edit("inc", data.variable, data.value, {multiple: data.multiple || false}) } /** * @type {Action} * * Add element to variable list */ async push(data, session) { if(data.duplicates) { await session.variables.push(data.variable, data.value, {multiple: data.multiple || false}) } else { await session.variables.add(data.variable, data.value, {multiple: data.multiple || false}) } } /** * @type {Action} * * Remove Element from variable list */ async pull(data, session) { await session.variables.pull(data.variable, data.value, {multiple: data.multiple || false}) } /** * @type {Action} * * Create a new Item in one of the existing data collections */ async addItem(data, session) { await session.variables.create(data.collection, data.variables, data.reference) } /** * @type {Action} * * Remove variable from item document or delete the whole document. */ async delete(data, session) { await session.variables.delete(data.variable, {multiple: data.multiple || false}) } /** * @type {Action} * * create new reference to current session in an external document. * * document can than be accessed during session via name reference. */ async getItem(data, session) { if(!adaptor.hasOwnProperties(data, ["collection", "query", "reference"])) { throw new adaptor.InvalidError("can not load reference. collection, query or reference property missing.") } let options = {multiple: data.multiple} if(data.sort && data.sort.length) { options.sort = {} for(let s of data.sort) { options.sort[s.by] = s.direction } } if(data.limit) { options.limit = parseInt(data.limit) } if(data.skip) { options.skip = parseInt(data.skip) } await session.createReference(data["collection"], data.query, data["reference"], options) session.log(`create ${data.collection} reference "${data.reference}"`) } } module.exports.Plugin = Data