Auto generate admin fields

This commit is contained in:
Pierre Burgy 2017-07-06 17:51:13 +02:00
parent feaaf3a4eb
commit 1fb39080c3
6 changed files with 115 additions and 27 deletions

View File

@ -40,7 +40,7 @@ class EditFormRelation extends React.Component { // eslint-disable-line react/pr
}
// Request URL
const requestUrlSuffix = query ? '' : this.props.record.get(this.props.relation.attribute);
const requestUrlSuffix = query && this.props.record.get(this.props.relation.attribute) ? this.props.record.get(this.props.relation.attribute) : '';
const requestUrl = `/content-manager/explorer/${this.props.relation.model}/${requestUrlSuffix}`;
// Call our request helper (see 'utils/request')
@ -51,11 +51,11 @@ class EditFormRelation extends React.Component { // eslint-disable-line react/pr
.then(response => {
const options = _.isArray(response)
? _.map(response, item => ({
value: Number(item.id),
value: item.id,
label: item[this.props.relation.displayedAttribute],
}))
: [{
value: Number(response.id),
value: response.id,
label: response[this.props.relation.displayedAttribute],
}];

View File

@ -8,21 +8,33 @@
import React from 'react';
import { connect } from 'react-redux';
import { createStructuredSelector } from 'reselect';
import _ from 'lodash';
import config from '../../../../config/admin.json';
import { loadModels, updateSchema } from './actions';
import { makeSelectModels, makeSelectLoading } from './selectors';
import { makeSelectLoading } from './selectors';
const tryRequire = (path) => {
try {
return require(`containers/${path}.js`); // eslint-disable-line global-require
} catch (err) {
return null;
}
};
class App extends React.Component {
componentWillMount() {
this.props.loadModels();
this.props.updateSchema(config.admin.schema);
const config = tryRequire('../../../../config/admin.json');
if (!_.isEmpty(_.get(config, 'admin.schema'))) {
this.props.updateSchema(config.admin.schema);
} else {
this.props.loadModels();
}
}
render() {
let content = <div />;
if (this.props.models) {
if (!this.props.loading) {
// Assign plugin component to children
content = React.Children.map(this.props.children, child =>
React.cloneElement(child, {
@ -46,11 +58,8 @@ App.contextTypes = {
App.propTypes = {
children: React.PropTypes.node.isRequired,
exposedComponents: React.PropTypes.object.isRequired,
loading: React.PropTypes.bool,
loadModels: React.PropTypes.func.isRequired,
models: React.PropTypes.oneOfType([
React.PropTypes.object,
React.PropTypes.bool,
]),
updateSchema: React.PropTypes.func.isRequired,
};
@ -63,7 +72,6 @@ export function mapDispatchToProps(dispatch) {
}
const mapStateToProps = createStructuredSelector({
models: makeSelectModels(),
loading: makeSelectLoading(),
});

View File

@ -9,7 +9,7 @@ import { fromJS } from 'immutable';
import { LOAD_MODELS, LOADED_MODELS, UPDATE_SCHEMA } from './constants';
const initialState = fromJS({
loading: false,
loading: true,
models: false,
schema: false,
});
@ -17,11 +17,14 @@ const initialState = fromJS({
function appReducer(state = initialState, action) {
switch (action.type) {
case LOAD_MODELS:
return state.set('loading', true);
return state;
case LOADED_MODELS:
return state.set('loading', false).set('models', action.models);
return state
.set('models', action.models);
case UPDATE_SCHEMA:
return state.set('schema', action.schema);
return state
.set('loading', false)
.set('schema', action.schema);
default:
return state;
}

View File

@ -1,9 +1,13 @@
import _ from 'lodash';
import {takeLatest} from 'redux-saga';
import {fork, put} from 'redux-saga/effects';
import { takeLatest } from 'redux-saga';
import { fork, put, select } from 'redux-saga/effects';
import { generateSchema } from 'utils/schema';
import { loadedModels, updateSchema } from './actions';
import { LOAD_MODELS, LOADED_MODELS, UPDATE_SCHEMA } from './constants';
import { makeSelectModels } from './selectors';
import {loadedModels} from './actions';
import {LOAD_MODELS, UPDATE_SCHEMA} from './constants';
export function* getModels() {
try {
@ -26,6 +30,22 @@ export function* getModels() {
}
}
export function* modelsLoaded() {
const models = yield select(makeSelectModels());
let schema;
try {
schema = generateSchema(models);
} catch (err) {
window.Strapi.notification.error(
'An error occurred during schema generation.'
);
throw new Error(err);
}
yield put(updateSchema(schema));
}
export function* schemaUpdated(action) {
// Display the links only if the `displayed` attribute is not set to false
const displayedModels = _.pickBy(action.schema, model => (model.displayed !== false));
@ -44,6 +64,7 @@ export function* schemaUpdated(action) {
export function* defaultSaga() {
yield fork(takeLatest, LOAD_MODELS, getModels);
yield fork(takeLatest, UPDATE_SCHEMA, schemaUpdated);
yield fork(takeLatest, LOADED_MODELS, modelsLoaded);
}
// All sagas to be loaded

View File

@ -0,0 +1,56 @@
import _ from 'lodash';
import pluralize from 'pluralize';
/**
* Generate a schema according to the models
* of the Strapi application.
*
* @param models
*/
const generateSchema = (models) => {
// Init `schema` object
const schema = {};
_.forEach(models, (model, name) => {
// Model data
const schemaModel = {
label: _.upperFirst(name),
labelPlural: _.upperFirst(pluralize(name)),
orm: model.orm || 'mongoose',
};
// Fields (non relation)
schemaModel.fields = _.mapValues(_.pickBy(model.attributes, attribute =>
!attribute.model && !attribute.collection
), (value, attribute) => ({
label: _.upperFirst(attribute),
description: '',
type: value.type || 'string',
}));
// Select fields displayed in list view
schemaModel.list = _.slice(_.keys(schemaModel.fields), 0, 4);
// Model relations
schemaModel.relations = _.mapValues(_.pickBy(model.attributes, attribute =>
attribute.model
), (value, attribute) => ({
columnName: attribute,
model: value.model,
attribute,
label: _.upperFirst(attribute),
description: '',
displayedAttribute: _.findKey(models[value.model].attributes, { type: 'string' }) || 'id',
})
);
// Set the formatted model to the schema
schema[name] = schemaModel;
});
return schema;
};
export {
generateSchema,
};

View File

@ -23,7 +23,7 @@ module.exports = {
find: async ctx => {
const model = strapi.models[ctx.params.model];
const orm = _.get(strapi.plugins, ['content-manager', 'config', 'admin', 'schema', ctx.params.model, 'orm']);
const orm = _.get(strapi.plugins, ['content-manager', 'config', 'admin', 'schema', ctx.params.model, 'orm']) || model.orm;
const queries = _.get(strapi.plugins, ['content-manager', 'config', 'queries', orm]);
const primaryKey = model.primaryKey;
const {limit, skip = 0, sort = primaryKey, query, queryAttribute} = ctx.request.query;
@ -44,7 +44,7 @@ module.exports = {
count: async ctx => {
const model = strapi.models[ctx.params.model];
const orm = _.get(strapi.plugins, ['content-manager', 'config', 'admin', 'schema', ctx.params.model, 'orm']);
const orm = _.get(strapi.plugins, ['content-manager', 'config', 'admin', 'schema', ctx.params.model, 'orm']) || model.orm;
const queries = _.get(strapi.plugins, ['content-manager', 'config', 'queries', orm]);
// Count using `queries` system
@ -57,7 +57,7 @@ module.exports = {
findOne: async ctx => {
const model = strapi.models[ctx.params.model];
const orm = _.get(strapi.plugins, ['content-manager', 'config', 'admin', 'schema', ctx.params.model, 'orm']);
const orm = _.get(strapi.plugins, ['content-manager', 'config', 'admin', 'schema', ctx.params.model, 'orm']) || model.orm;
const queries = _.get(strapi.plugins, ['content-manager', 'config', 'queries', orm]);
const primaryKey = model.primaryKey;
const id = ctx.params.id;
@ -79,7 +79,7 @@ module.exports = {
create: async ctx => {
const model = strapi.models[ctx.params.model];
const orm = _.get(strapi.plugins, ['content-manager', 'config', 'admin', 'schema', ctx.params.model, 'orm']);
const orm = _.get(strapi.plugins, ['content-manager', 'config', 'admin', 'schema', ctx.params.model, 'orm']) || model.orm;
const queries = _.get(strapi.plugins, ['content-manager', 'config', 'queries', orm]);
const values = ctx.request.body;
@ -94,7 +94,7 @@ module.exports = {
update: async ctx => {
const model = strapi.models[ctx.params.model];
const orm = _.get(strapi.plugins, ['content-manager', 'config', 'admin', 'schema', ctx.params.model, 'orm']);
const orm = _.get(strapi.plugins, ['content-manager', 'config', 'admin', 'schema', ctx.params.model, 'orm']) || model.orm;
const queries = _.get(strapi.plugins, ['content-manager', 'config', 'queries', orm]);
const primaryKey = model.primaryKey;
const id = ctx.params.id;
@ -113,7 +113,7 @@ module.exports = {
delete: async ctx => {
const model = strapi.models[ctx.params.model];
const orm = _.get(strapi.plugins, ['content-manager', 'config', 'admin', 'schema', ctx.params.model, 'orm']);
const orm = _.get(strapi.plugins, ['content-manager', 'config', 'admin', 'schema', ctx.params.model, 'orm']) || model.orm;
const queries = _.get(strapi.plugins, ['content-manager', 'config', 'queries', orm]);
const primaryKey = model.primaryKey;
const id = ctx.params.id;