refactored Generic API
This commit is contained in:
parent
caeb47aee9
commit
a1d1cbac5d
@ -1,4 +1,3 @@
|
||||
from cookbook.models import SearchFields
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -730,7 +730,6 @@
|
||||
searchKeywords: function (query) {
|
||||
this.keywords_loading = true
|
||||
this.$http.get("{% url 'api:keyword-list' %}" + '?query=' + query + '&limit=10').then((response) => {
|
||||
console.log(response.data)
|
||||
this.keywords = response.data.results;
|
||||
this.keywords_loading = false
|
||||
}).catch((err) => {
|
||||
@ -780,7 +779,7 @@
|
||||
searchFoods: function (query) {
|
||||
this.foods_loading = true
|
||||
this.$http.get("{% url 'api:food-list' %}" + '?query=' + query + '&limit=10').then((response) => {
|
||||
this.foods = response.data
|
||||
this.foods = response.data.results
|
||||
|
||||
if (this.recipe !== undefined) {
|
||||
for (let s of this.recipe.steps) {
|
||||
|
@ -66,7 +66,6 @@ from cookbook.serializer import (FoodSerializer, IngredientSerializer,
|
||||
|
||||
|
||||
class StandardFilterMixin(ViewSetMixin):
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = self.queryset
|
||||
query = self.request.query_params.get('query', None)
|
||||
|
@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<div id="app" style="margin-bottom: 4vh">
|
||||
<generic-modal-form
|
||||
:model="this_model"
|
||||
:model="this_model.name"
|
||||
:action="this_action"/>
|
||||
<generic-split-lists
|
||||
:list_name="this_model"
|
||||
:list_name="this_model.name"
|
||||
@reset="resetList"
|
||||
@get-list="getFoods"
|
||||
@item-action="startAction"
|
||||
@ -13,7 +13,7 @@
|
||||
<generic-horizontal-card
|
||||
v-for="f in foods" v-bind:key="f.id"
|
||||
:model=f
|
||||
:model_name="this_model"
|
||||
:model_name="this_model.name"
|
||||
:draggable="true"
|
||||
:merge="true"
|
||||
:move="true"
|
||||
@ -28,7 +28,7 @@
|
||||
<template v-slot:cards-right>
|
||||
<generic-horizontal-card v-for="f in foods2" v-bind:key="f.id"
|
||||
:model=f
|
||||
:model_name="this_model"
|
||||
:model_name="this_model.name"
|
||||
:draggable="true"
|
||||
:merge="true"
|
||||
:move="true"
|
||||
@ -90,7 +90,7 @@
|
||||
:title="this.$t('Delete_Food')"
|
||||
:ok-title="this.$t('Delete')"
|
||||
:cancel-title="this.$t('Cancel')"
|
||||
@ok="deleteThis(this_item.id, this_model)">
|
||||
@ok="deleteThis(this_item.id)">
|
||||
{{this.$t("delete_confimation", {'kw': this_item.name})}}
|
||||
</b-modal>
|
||||
<!-- move modal -->
|
||||
@ -141,7 +141,9 @@ import {BootstrapVue} from 'bootstrap-vue'
|
||||
|
||||
import 'bootstrap-vue/dist/bootstrap-vue.css'
|
||||
|
||||
import {ToastMixin, ApiMixin, CardMixin} from "@/utils/utils";
|
||||
import {ApiMixin, CardMixin, ToastMixin} from "@/utils/utils";
|
||||
import {Models, Actions} from "@/utils/models";
|
||||
import {StandardToasts} from "@/utils/utils";
|
||||
|
||||
import GenericSplitLists from "@/components/GenericSplitLists";
|
||||
import GenericHorizontalCard from "@/components/GenericHorizontalCard";
|
||||
@ -152,11 +154,11 @@ Vue.use(BootstrapVue)
|
||||
|
||||
export default {
|
||||
name: 'FoodListView',
|
||||
mixins: [ToastMixin, ApiMixin, CardMixin],
|
||||
mixins: [ApiMixin, CardMixin, ToastMixin],
|
||||
components: {GenericHorizontalCard, GenericMultiselect, GenericSplitLists, GenericModalForm},
|
||||
data() {
|
||||
return {
|
||||
this_model: 'Food',
|
||||
this_model: Models.FOOD,
|
||||
this_action:'',
|
||||
foods: [],
|
||||
foods2: [],
|
||||
@ -249,7 +251,7 @@ export default {
|
||||
getFoods: function(params, callback) {
|
||||
let column = params?.column ?? 'left'
|
||||
|
||||
this.genericAPI(this.this_model, 'list', params).then((result) => {
|
||||
this.genericAPI(this.this_model, Actions.LIST, params).then((result) => {
|
||||
if (result.data.results.length){
|
||||
if (column ==='left') {
|
||||
this.foods = this.foods.concat(result.data.results)
|
||||
@ -266,18 +268,18 @@ export default {
|
||||
callback(result.data.count < (column==="left" ? this.foods.length : this.foods2.length))
|
||||
}).catch((err) => {
|
||||
console.log(err)
|
||||
this.makeToast(this.$t('Error'), err.bodyText, 'danger')
|
||||
StandardToasts.makeStandardToast(StandardToasts.FAIL_FETCH)
|
||||
})
|
||||
},
|
||||
getThis: function(id, callback){
|
||||
return this.genericAPI(this.this_model, 'retrieve', {'id': id})
|
||||
return this.genericAPI(this.this_model, Actions.FETCH, {'id': id})
|
||||
},
|
||||
saveFood: function () {
|
||||
let food = {...this.this_item}
|
||||
food.supermarket_category = this.this_item.supermarket_category?.id ?? null
|
||||
food.recipe = this.this_item.recipe?.id ?? null
|
||||
if (!food?.id) { // if there is no item id assume it's a new item
|
||||
this.genericAPI(this.this_model, 'create', food).then((result) => {
|
||||
this.genericAPI(this.this_model, Actions.CREATE, food).then((result) => {
|
||||
// place all new foods at the top of the list - could sort instead
|
||||
this.foods = [result.data].concat(this.foods)
|
||||
// this creates a deep copy to make sure that columns stay independent
|
||||
@ -286,20 +288,24 @@ export default {
|
||||
} else {
|
||||
this.foods2 = []
|
||||
}
|
||||
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_CREATE)
|
||||
}).catch((err) => {
|
||||
console.log(err)
|
||||
StandardToasts.makeStandardToast(StandardToasts.FAIL_CREATE)
|
||||
})
|
||||
} else {
|
||||
this.genericAPI(this.this_model, 'updatePartial', food).then((result) => {
|
||||
this.refreshObject(this.this_item.id)
|
||||
this.genericAPI(this.this_model, Actions.UPDATE, food).then((result) => {
|
||||
this.refreshObject(food.id)
|
||||
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_UPDATE)
|
||||
}).catch((err) => {
|
||||
console.log(err)
|
||||
StandardToasts.makeStandardToast(StandardToasts.FAIL_UPDATE)
|
||||
})
|
||||
}
|
||||
this.this_item = {...this.blank_item}
|
||||
},
|
||||
moveFood: function (source_id, target_id) {
|
||||
this.genericAPI(this.this_model, 'move', {'source': source_id, 'target': target_id}).then((result) => {
|
||||
this.genericAPI(this.this_model, Actions.MOVE, {'source': source_id, 'target': target_id}).then((result) => {
|
||||
if (target_id === 0) {
|
||||
let food = this.findCard(source_id, this.foods) || this.findCard(source_id, this.foods2)
|
||||
this.foods = [food].concat(this.destroyCard(source_id, this.foods)) // order matters, destroy old card before adding it back in at root
|
||||
@ -310,6 +316,8 @@ export default {
|
||||
this.foods2 = this.destroyCard(source_id, this.foods2)
|
||||
this.refreshObject(target_id)
|
||||
}
|
||||
// TODO make standard toast
|
||||
this.makeToast(this.$t('Success'), 'Succesfully moved food', 'success')
|
||||
}).catch((err) => {
|
||||
// TODO none of the error checking works because the openapi generated functions don't throw an error?
|
||||
// or i'm capturing it incorrectly
|
||||
@ -318,7 +326,7 @@ export default {
|
||||
})
|
||||
},
|
||||
mergeFood: function (source_id, target_id) {
|
||||
this.genericAPI(this.this_model, 'merge', {'source': source_id, 'target': target_id}).then((result) => {
|
||||
this.genericAPI(this.this_model, Actions.MERGE, {'source': source_id, 'target': target_id}).then((result) => {
|
||||
this.foods = this.destroyCard(source_id, this.foods)
|
||||
this.foods2 = this.destroyCard(source_id, this.foods2)
|
||||
this.refreshObject(target_id)
|
||||
@ -326,6 +334,8 @@ export default {
|
||||
console.log('Error', err)
|
||||
this.makeToast(this.$t('Error'), err.bodyText, 'danger')
|
||||
})
|
||||
// TODO make standard toast
|
||||
this.makeToast(this.$t('Success'), 'Succesfully merged food', 'success')
|
||||
},
|
||||
getChildren: function(col, food){
|
||||
let parent = {}
|
||||
@ -333,7 +343,7 @@ export default {
|
||||
'root': food.id,
|
||||
'pageSize': 200
|
||||
}
|
||||
this.genericAPI(this.this_model, 'list', options).then((result) => {
|
||||
this.genericAPI(this.this_model, Actions.LIST, options).then((result) => {
|
||||
parent = this.findCard(food.id, col === 'left' ? this.foods : this.foods2)
|
||||
if (parent) {
|
||||
Vue.set(parent, 'children', result.data.results)
|
||||
@ -352,7 +362,7 @@ export default {
|
||||
'pageSize': 200
|
||||
}
|
||||
|
||||
this.genericAPI('recipe', 'list', options).then((result) => {
|
||||
this.genericAPI(Models.RECIPE, Actions.LIST, options).then((result) => {
|
||||
parent = this.findCard(food.id, col === 'left' ? this.foods : this.foods2)
|
||||
if (parent) {
|
||||
Vue.set(parent, 'recipes', result.data.results)
|
||||
@ -366,6 +376,7 @@ export default {
|
||||
})
|
||||
},
|
||||
refreshObject: function(id){
|
||||
console.log('refresh object', id)
|
||||
this.getThis(id).then(result => {
|
||||
this.refreshCard(result.data, this.foods)
|
||||
this.refreshCard({...result.data}, this.foods2)
|
||||
@ -382,12 +393,13 @@ export default {
|
||||
this.this_item.icon = icon
|
||||
},
|
||||
deleteThis: function(id, model) {
|
||||
this.genericAPI(this.this_model, 'destroy', {'id': id}).then((result) => {
|
||||
this.genericAPI(this.this_model, Actions.DELETE, {'id': id}).then((result) => {
|
||||
this.foods = this.destroyCard(id, this.foods)
|
||||
this.foods2 = this.destroyCard(id, this.foods2)
|
||||
StandardToasts.makeStandardToast(StandardToasts.SUCCESS_DELETE)
|
||||
}).catch((err) => {
|
||||
console.log(err)
|
||||
this.makeToast(this.$t('Error'), err.bodyText, 'danger')
|
||||
StandardToasts.makeStandardToast(StandardToasts.FAIL_DELETE)
|
||||
})
|
||||
},
|
||||
}
|
||||
|
@ -107,6 +107,7 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
Button: function(e) {
|
||||
console.log(typeof({}), typeof([]), typeof('this'), typeof(1))
|
||||
this.action='new'
|
||||
this.$bvModal.show('modal')
|
||||
},
|
||||
|
103
vue/src/utils/models.js
Normal file
103
vue/src/utils/models.js
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Utility CLASS to define model configurations
|
||||
* */
|
||||
|
||||
// TODO this needs rethought and simplified
|
||||
// maybe a function that returns a single dictionary based on action?
|
||||
export class Models {
|
||||
// Arrays correspond to ORDERED list of parameters required by ApiApiFactory
|
||||
// Inner arrays are used to construct a dictionary of key:value pairs
|
||||
// MODEL configurations will override MODEL_TYPE configurations with will override ACTION configurations
|
||||
|
||||
// MODEL_TYPES - inherited by MODELS, inherits and takes precedence over ACTIONS
|
||||
static TREE = {
|
||||
'list': {
|
||||
'params': ['query', 'root', 'tree', 'page', 'pageSize'],
|
||||
'config': {
|
||||
'root': {
|
||||
'default': {
|
||||
'function': 'CONDITIONAL',
|
||||
'check': 'query',
|
||||
'operator': 'not_exist',
|
||||
'true': 0,
|
||||
'false': undefined
|
||||
}
|
||||
},
|
||||
'tree': {'default': undefined},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// MODELS - inherits and takes precedence over MODEL_TYPES and ACTIONS
|
||||
static FOOD = {
|
||||
'name': 'Food', // *OPTIONAL: parameters will be built model -> model_type -> default
|
||||
'model_type': this.TREE, // *OPTIONAL* model specific params for api, if not present will attempt modeltype_create then default_create
|
||||
// REQUIRED: unordered array of fields that can be set during create
|
||||
'create': {
|
||||
// if not defined partialUpdate will use the same parameters, prepending 'id'
|
||||
'params': [['name', 'description', 'recipe', 'ignore_shopping', 'supermarket_category']]
|
||||
},
|
||||
|
||||
}
|
||||
static KEYWORD = {}
|
||||
static UNIT = {}
|
||||
static RECIPE = {}
|
||||
static SHOPPING_LIST = {}
|
||||
static RECIPE = {
|
||||
'name': 'Recipe',
|
||||
'list': {
|
||||
'params': ['query', 'keywords', 'foods', 'books', 'keywordsOr', 'foodsOr', 'booksOr', 'internal', 'random', '_new', 'page', 'pageSize', 'options'],
|
||||
'config': {
|
||||
'foods': {'type':'string'},
|
||||
'keywords': {'type': 'string'},
|
||||
'books': {'type': 'string'},
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class Actions {
|
||||
static CREATE = {
|
||||
"function": "create"
|
||||
}
|
||||
static UPDATE = {
|
||||
"function": "partialUpdate",
|
||||
}
|
||||
static DELETE = {
|
||||
"function": "destroy",
|
||||
'params': ['id']
|
||||
}
|
||||
static FETCH = {
|
||||
"function": "retrieve",
|
||||
'params': ['id']
|
||||
}
|
||||
static LIST = {
|
||||
"function": "list",
|
||||
"suffix": "s",
|
||||
"params": ['query', 'page', 'pageSize'],
|
||||
"config": {
|
||||
'query': {'default':undefined},
|
||||
'page': {'default': 1},
|
||||
'pageSize': {'default': 25}
|
||||
}
|
||||
|
||||
}
|
||||
static MERGE = {
|
||||
"function": "merge",
|
||||
'params': ['source', 'target'],
|
||||
"config": {
|
||||
'source': {'type':'string'},
|
||||
'target': {'type': 'string'}
|
||||
}
|
||||
}
|
||||
static MOVE = {
|
||||
"function": "move",
|
||||
'params': ['source', 'target'],
|
||||
"config": {
|
||||
'source': {'type':'string'},
|
||||
'target': {'type': 'string'}
|
||||
}
|
||||
}
|
||||
}
|
@ -162,145 +162,120 @@ import axios from "axios";
|
||||
axios.defaults.xsrfCookieName = 'csrftoken'
|
||||
axios.defaults.xsrfHeaderName = "X-CSRFTOKEN"
|
||||
export const ApiMixin = {
|
||||
data() {
|
||||
return {
|
||||
api_settings: {
|
||||
// TODO consider moving this to an API type dictionary that contains api types with details on the function call name, suffix, etc
|
||||
'suffix': { // if OpenApiGenerator adds a suffix to model name in function calls
|
||||
'list': 's'
|
||||
},
|
||||
// model specific settings, when not provided will use defaults instead
|
||||
'food': { // must be lowercase
|
||||
// *REQUIRED* name is name of the model used for translations and looking up APIFactory
|
||||
'name': 'Food',
|
||||
// *OPTIONAL: parameters will be built model -> model_type -> default
|
||||
'model_type': 'tree',
|
||||
// *OPTIONAL* model specific params for api, if not present will attempt modeltype_create then default_create
|
||||
// an array will create a dict of name:value pairs
|
||||
'create': [['name', 'description', 'recipe', 'ignore_shopping', 'supermarket_category']], // required: unordered array of fields that can be set during create
|
||||
// *OPTIONAL* model specific params for api, includes ordered list of parameters
|
||||
// and an unordered array that will be converted to a dictionary and passed as the 2nd param
|
||||
'partialUpdate': ['id', // required: ordered array of list params to patch existing object
|
||||
['name', 'description', 'recipe', 'ignore_shopping', 'supermarket_category'] // must include ordered array of field names that can be updated
|
||||
],
|
||||
// *OPTIONAL* provide model specific typing
|
||||
// 'typing': {},
|
||||
},
|
||||
'keyword': {},
|
||||
'unit': {},
|
||||
'recipe': {
|
||||
'name': 'Recipe',
|
||||
'list': ['query', 'keywords', 'foods', 'books', 'keywordsOr', 'foodsOr', 'booksOr', 'internal', 'random', '_new', 'page', 'pageSize', 'options'],
|
||||
'typing': {
|
||||
'list': {
|
||||
'foods': 'string',
|
||||
'keywords': 'string',
|
||||
'books': 'string',
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* constructs OpenAPI Generator function using named parameters
|
||||
* @param {string} model string to define which model API to use
|
||||
* @param {string} api string to define which of the API functions to use
|
||||
* @param {object} options dictionary to define all of the parameters necessary to use API
|
||||
*/
|
||||
genericAPI: function(model, action, options) {
|
||||
let setup = getConfig(model, action)
|
||||
let func = setup.function
|
||||
let config = setup?.config ?? {}
|
||||
let params = setup?.params ?? []
|
||||
console.log('config', config, 'params', params)
|
||||
let parameters = []
|
||||
|
||||
},
|
||||
// collection of default attributes for model_type TREE. All settings except typing and values will be overwritten by model values
|
||||
'tree': {
|
||||
'values': {
|
||||
'root': 'getFunction()', // if default value is exactly 'getFunction()' will call getFunction(model_type, param, params)
|
||||
'tree': undefined,
|
||||
},
|
||||
'list': ['query', 'root', 'tree', 'page', 'pageSize'], // ordered array of list params for tree
|
||||
'typing': {
|
||||
'move': {
|
||||
'source': 'string',
|
||||
'target': 'string',
|
||||
}
|
||||
}
|
||||
},
|
||||
// collection of global defaults. All settings except typing and values will be overwritten by model_type or model values
|
||||
'default': {
|
||||
'list': ['query', 'page', 'pageSize'], // ordered array of list params for default listApi
|
||||
'destroy': ['id'], // ordered array of list params for default deleteApi
|
||||
'retrieve': ['id'], // ordered array of list params for default retrieveApi
|
||||
'merge': ['source', 'target'], // ordered array of list params for default mergeApi
|
||||
'move': ['source', 'target'], // ordered array of list params for default moveApi
|
||||
'create': [], // ordered array of list params for default createApi
|
||||
'partialUpdate': [], // ordered array of list params for default updateApi
|
||||
'values': {
|
||||
'query': undefined, // default values for list API
|
||||
'page': 1,
|
||||
'pageSize': 25,
|
||||
},
|
||||
'typing': { // optional settings to force type on parameters
|
||||
'merge': {
|
||||
'source': 'string',
|
||||
'target': 'string',
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* constructs OpenAPI Generator function using named parameters
|
||||
* @param {string} model string to define which model API to use
|
||||
* @param {string} api string to define which of the API functions to use
|
||||
* @param {object} options dictionary to define all of the parameters necessary to use API
|
||||
*/
|
||||
genericAPI: function(model, api, options) {
|
||||
model = model.toLowerCase()
|
||||
// construct settings for this model and this api - values are assigned in order (default is overwritten by api overwritten by model)
|
||||
let settings = {...this.api_settings?.default ?? {}, ...this.api_settings?.[this.api_settings?.[model]?.model_type] ?? {}, ...this.api_settings[model]};
|
||||
// values and typing are also dicts and need to be merged,
|
||||
settings.values = {...this.api_settings?.default?.values ?? {},
|
||||
...this.api_settings?.[this.api_settings?.[model]?.model_type]?.values ?? {},
|
||||
...this.api_settings[model].values}
|
||||
settings.typing = {...this.api_settings?.default?.typing?.[api] ?? {},
|
||||
...this.api_settings?.[this.api_settings?.[model]?.model_type]?.typing?.[api] ?? {},
|
||||
...this.api_settings[model].typing?.[api]}
|
||||
let func = api + settings.name + (this.api_settings.suffix?.[api] ?? '')
|
||||
// does model params exist?
|
||||
let params = []
|
||||
let this_value = undefined
|
||||
settings[api].forEach(function (item, index) {
|
||||
|
||||
if (Array.isArray(item)) {
|
||||
this_value = {}
|
||||
for (const [k, v] of Object.entries(options)) { // filters options dict based on valus in array, I'm sure there's a better way to do this
|
||||
if (item.includes(k)) {
|
||||
this_value[k] = v
|
||||
let this_value = undefined
|
||||
params.forEach(function (item, index) {
|
||||
if (Array.isArray(item)) {
|
||||
this_value = {}
|
||||
// if the value is an array, convert it to a dictionary of key:value
|
||||
// filtered based on OPTIONS passed
|
||||
// maybe map/reduce is better?
|
||||
for (const [k, v] of Object.entries(options)) {
|
||||
if (item.includes(k)) {
|
||||
this_value[k] = formatParam(config?.[k], v)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this_value = options?.[item] ?? undefined
|
||||
if (this_value) {this_value = formatParam(config?.[item], this_value)}
|
||||
}
|
||||
} else {
|
||||
this_value = options?.[item] ?? settings.values?.[item] ?? undefined // set the value or apply default
|
||||
}
|
||||
if (this_value ==='getFunction()') {
|
||||
this_value = getFunction(item, settings?.model_type, options)
|
||||
}
|
||||
if (Object.keys(settings?.typing).includes(item)) {
|
||||
switch (settings.typing[item]) {
|
||||
case 'string':
|
||||
if (this_value) {this_value = String(this_value)}
|
||||
break;
|
||||
// if no value is found so far, get the default if it exists
|
||||
if (!this_value) {
|
||||
this_value = getDefault(config?.[item], options)
|
||||
}
|
||||
}
|
||||
params.push(this_value)
|
||||
});
|
||||
|
||||
let apiClient = new ApiApiFactory()
|
||||
return apiClient[func](...params)
|
||||
parameters.push(this_value)
|
||||
});
|
||||
|
||||
console.log(func, 'parameters', parameters, 'passed options', options)
|
||||
let apiClient = new ApiApiFactory()
|
||||
return apiClient[func](...parameters)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Utility functions to calculate default value
|
||||
* */
|
||||
export function getFunction(item, model_type, params) {
|
||||
if (item==='root' && model_type==='tree') {
|
||||
if ((!params?.query ?? undefined) || params?.query?.length == 0) {
|
||||
return 0
|
||||
// /*
|
||||
// * local functions for ApiMixin
|
||||
// * */
|
||||
function formatParam(config, value) {
|
||||
if (config) {
|
||||
for (const [k, v] of Object.entries(config)) {
|
||||
switch(k) {
|
||||
case 'type':
|
||||
switch(v) {
|
||||
case 'string':
|
||||
value = String(value)
|
||||
break;
|
||||
case 'integer':
|
||||
value = parseInt(value)
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
||||
function getDefault(config, options) {
|
||||
let value = undefined
|
||||
value = config?.default ?? undefined
|
||||
if (typeof(value) === 'object') {
|
||||
let condition = false
|
||||
switch(value.function) {
|
||||
// CONDITIONAL case requires 4 keys:
|
||||
// - check: which other OPTIONS key to check against
|
||||
// - operator: what type of operation to perform
|
||||
// - true: what value to assign when true
|
||||
// - false: what value to assign when false
|
||||
case 'CONDITIONAL':
|
||||
switch(value.operator) {
|
||||
case 'not_exist':
|
||||
condition = (
|
||||
(!options?.[value.check] ?? undefined)
|
||||
|| options?.[value.check]?.length == 0
|
||||
)
|
||||
if (condition) {
|
||||
value = value.true
|
||||
} else {
|
||||
value = value.false
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
function getConfig(model, action) {
|
||||
let f = action.function
|
||||
// if not defined partialUpdate will use params from create
|
||||
if (f === 'partialUpdate' && !model?.[f]?.params) {
|
||||
model[f] = {'params': [...['id'], ...model.create.params]}
|
||||
}
|
||||
|
||||
let config = {
|
||||
'name': model.name,
|
||||
'function': f + model.name + action?.suffix
|
||||
}
|
||||
// spread operator merges dictionaries - last item in list takes precedence
|
||||
config = {...config, ...action, ...model.model_type?.[f], ...model?.[f]}
|
||||
// nested dictionaries are not merged - so merge again on any nested keys
|
||||
config.config = {...action?.config, ...model.model_type?.[f]?.config, ...model?.[f]?.config}
|
||||
config.function = config.function + config.name + (config?.suffix ?? '') // parens are required to force optional chaining to evaluate before concat
|
||||
return config
|
||||
}
|
||||
|
||||
|
||||
@ -365,4 +340,5 @@ export const CardMixin = {
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user