blob: b2d966a7ed227e88c7c890e64c43ba297a997f27 [file] [log] [blame]
Matteo Scandolod58d5042016-12-16 16:59:21 -08001import * as _ from 'lodash';
2import * as pluralize from 'pluralize';
Matteo Scandolocb466ed2017-01-04 17:16:24 -08003import {IXosTableColumn, IXosTableCfg} from '../../table/table';
4import {IModeldef} from '../../../datasources/rest/modeldefs.rest';
Matteo Scandolo80c3a652017-01-06 10:48:31 -08005import {IXosFormConfig, IXosFormInput} from '../../form/form';
Matteo Scandolo0a8b02e2017-01-06 14:43:36 -08006import {IXosAuthService} from '../../../datasources/rest/auth.rest';
Matteo Scandolo04964232017-01-07 12:53:46 -08007import {IModelStoreService} from '../../../datasources/stores/model.store';
Matteo Scandolod58d5042016-12-16 16:59:21 -08008
9export interface IXosModelDefsField {
10 name: string;
11 type: string;
Matteo Scandolocb466ed2017-01-04 17:16:24 -080012 validators?: any;
Matteo Scandolo04964232017-01-07 12:53:46 -080013 hint?: string;
14 relation?: {
15 model: string;
16 type: string;
17 };
Matteo Scandolod58d5042016-12-16 16:59:21 -080018}
19
20export interface IXosConfigHelpersService {
Matteo Scandoloee655a12016-12-19 15:38:43 -080021 excluded_fields: string[];
Matteo Scandolocb466ed2017-01-04 17:16:24 -080022 modelFieldsToColumnsCfg(fields: IXosModelDefsField[], baseUrl: string): IXosTableColumn[]; // TODO use a proper interface
Matteo Scandolo80c3a652017-01-06 10:48:31 -080023 modelToTableCfg(model: IModeldef, baseUrl: string): IXosTableCfg;
24 modelFieldToInputCfg(fields: IXosModelDefsField[]): IXosFormInput[];
25 modelToFormCfg(model: IModeldef): IXosFormConfig;
Matteo Scandolod58d5042016-12-16 16:59:21 -080026 pluralize(string: string, quantity?: number, count?: boolean): string;
27 toLabel(string: string, pluralize?: boolean): string;
Matteo Scandoloe0d71ea2016-12-19 11:56:12 -080028 toLabels(string: string[], pluralize?: boolean): string[];
Matteo Scandolod58d5042016-12-16 16:59:21 -080029}
30
31export class ConfigHelpers {
Matteo Scandolo04964232017-01-07 12:53:46 -080032 static $inject = ['toastr', 'AuthService', 'ModelStore'];
Matteo Scandolod58d5042016-12-16 16:59:21 -080033
Matteo Scandoloee655a12016-12-19 15:38:43 -080034 excluded_fields = [
35 'created',
36 'updated',
37 'enacted',
38 'policed',
39 'backend_register',
40 'deleted',
41 'write_protect',
42 'lazy_blocked',
43 'no_sync',
44 'no_policy',
45 'omf_friendly',
46 'enabled',
Matteo Scandolod62ea792016-12-22 14:02:28 -080047 'validators',
Matteo Scandolo80c3a652017-01-06 10:48:31 -080048 'password',
49 'backend_need_delete',
50 'backend_need_reap'
Matteo Scandoloee655a12016-12-19 15:38:43 -080051 ];
52
Matteo Scandolocb466ed2017-01-04 17:16:24 -080053 constructor(
Matteo Scandolo0a8b02e2017-01-06 14:43:36 -080054 private toastr: ng.toastr.IToastrService,
Matteo Scandolo04964232017-01-07 12:53:46 -080055 private AuthService: IXosAuthService,
56 private ModelStore: IModelStoreService
Matteo Scandolocb466ed2017-01-04 17:16:24 -080057 ) {
Matteo Scandolod58d5042016-12-16 16:59:21 -080058 pluralize.addIrregularRule('xos', 'xosses');
59 pluralize.addPluralRule(/slice$/i, 'slices');
Matteo Scandolo80c3a652017-01-06 10:48:31 -080060 pluralize.addSingularRule(/slice$/i, 'slice');
Matteo Scandolod58d5042016-12-16 16:59:21 -080061 }
62
Matteo Scandolo1c5905f2017-01-04 17:41:15 -080063 public pluralize(string: string, quantity?: number, count?: boolean): string {
Matteo Scandolod58d5042016-12-16 16:59:21 -080064 return pluralize(string, quantity, count);
65 }
66
Matteo Scandolo1c5905f2017-01-04 17:41:15 -080067 public toLabels(strings: string[], pluralize?: boolean): string[] {
Matteo Scandoloe0d71ea2016-12-19 11:56:12 -080068 if (angular.isArray(strings)) {
69 return _.map(strings, s => {
Matteo Scandolod58d5042016-12-16 16:59:21 -080070 return this.toLabel(s, pluralize);
71 });
72 }
Matteo Scandoloe0d71ea2016-12-19 11:56:12 -080073 }
74
Matteo Scandolo1c5905f2017-01-04 17:41:15 -080075 public toLabel(string: string, pluralize?: boolean): string {
Matteo Scandolod58d5042016-12-16 16:59:21 -080076
77 if (pluralize) {
78 string = this.pluralize(string);
79 }
80
81 string = this.fromCamelCase(string);
82 string = this.fromSnakeCase(string);
83 string = this.fromKebabCase(string);
84
85 return this.capitalizeFirst(string);
86 }
87
Matteo Scandolo1c5905f2017-01-04 17:41:15 -080088 public modelToTableCfg(model: IModeldef, baseUrl: string): IXosTableCfg {
Matteo Scandolocb466ed2017-01-04 17:16:24 -080089 const cfg = {
90 columns: this.modelFieldsToColumnsCfg(model.fields, baseUrl),
91 filter: 'fulltext',
92 order: {field: 'id', reverse: false},
93 actions: [
94 {
95 label: 'delete',
96 icon: 'remove',
97 color: 'red',
98 cb: (item) => {
99 let obj = angular.copy(item);
100
101 item.$delete()
102 .then((res) => {
103 if (res.status === 404) {
104 // TODO understand why it does not go directly in catch
105 throw new Error();
106 }
107 this.toastr.info(`${model.name} ${obj.name} succesfully deleted`);
108 })
109 .catch(() => {
110 this.toastr.error(`Error while deleting ${obj.name}`);
111 });
112 }
113 }
114 ]
115 };
116 return cfg;
117 }
118
Matteo Scandolo1c5905f2017-01-04 17:41:15 -0800119 public modelFieldsToColumnsCfg(fields: IXosModelDefsField[], baseUrl: string): IXosTableColumn[] {
Matteo Scandoloee655a12016-12-19 15:38:43 -0800120
Matteo Scandolo231de262017-01-04 16:33:14 -0800121 const columns = _.map(fields, (f) => {
Matteo Scandoloee655a12016-12-19 15:38:43 -0800122 if (this.excluded_fields.indexOf(f.name) > -1) {
Matteo Scandolod58d5042016-12-16 16:59:21 -0800123 return;
124 }
125 const col: IXosTableColumn = {
126 label: this.toLabel(f.name),
127 prop: f.name
128 };
129
Matteo Scandoloa8a6fbb2016-12-21 16:59:08 -0800130 if (f.name === 'id' || f.name === 'name') {
Matteo Scandolo99ac9d92017-01-03 13:58:19 -0800131 // NOTE can we find a better method to generalize the route?
Matteo Scandoloee655a12016-12-19 15:38:43 -0800132 col.link = item => `#/core${baseUrl.replace(':id?', item.id)}`;
133 }
134
Matteo Scandolo04964232017-01-07 12:53:46 -0800135 // if the field identify a relation, create a link
136 if (f.relation && f.relation.type === 'many_to_one') {
137 // TODO read the related model name and replace the value, use the xosTable format method?
138 col.type = 'custom';
139 col.formatter = item => {
140 this.populateRelated(item, item[f.name], f);
141 return item[f.name];
142 };
143 col.link = item => `#${this.urlFromCoreModel(f.relation.model)}/${item[f.name]}`;
144 }
145
Matteo Scandolod58d5042016-12-16 16:59:21 -0800146 if (f.name === 'backend_status') {
147 col.type = 'icon';
148 col.formatter = (item) => {
149 if (item.backend_status.indexOf('1') > -1) {
150 return 'check';
151 }
152 if (item.backend_status.indexOf('2') > -1) {
153 return 'exclamation-circle';
154 }
155 if (item.backend_status.indexOf('0') > -1) {
156 return 'clock-o';
157 }
158 };
159 }
Matteo Scandoloa8a6fbb2016-12-21 16:59:08 -0800160
Matteo Scandolod58d5042016-12-16 16:59:21 -0800161 return col;
162 })
163 .filter(v => angular.isDefined(v));
164
Matteo Scandolo231de262017-01-04 16:33:14 -0800165 return columns;
Matteo Scandolod58d5042016-12-16 16:59:21 -0800166 };
167
Matteo Scandolo1c5905f2017-01-04 17:41:15 -0800168 public urlFromCoreModel(name: string): string {
169 return `/core/${this.pluralize(name.toLowerCase())}`;
170 }
171
Matteo Scandolo80c3a652017-01-06 10:48:31 -0800172 public modelFieldToInputCfg(fields: IXosModelDefsField[]): IXosFormInput[] {
173
174 return _.map(fields, f => {
175 return {
176 name: f.name,
177 label: this.toLabel(f.name),
178 type: f.type,
179 validators: f.validators
180 };
181 })
182 .filter(f => this.excluded_fields.indexOf(f.name) === -1);
183 }
184
185 public modelToFormCfg(model: IModeldef): IXosFormConfig {
186 return {
187 formName: `${model.name}Form`,
Matteo Scandolo0a8b02e2017-01-06 14:43:36 -0800188 exclude: ['backend_status', 'creator'],
Matteo Scandolo80c3a652017-01-06 10:48:31 -0800189 actions: [{
190 label: 'Save',
191 class: 'success',
192 icon: 'ok',
193 cb: (item, form) => {
Matteo Scandolo0a8b02e2017-01-06 14:43:36 -0800194 const model = angular.copy(item);
195
196 // TODO remove ManyToMany relations and save them separately (how??)
197 delete item.networks;
198
199 // adding userId as creator
200 item.creator = this.AuthService.getUser().id;
201
Matteo Scandolo80c3a652017-01-06 10:48:31 -0800202 item.$save()
Matteo Scandolo0a8b02e2017-01-06 14:43:36 -0800203 .then((res) => {
204 if (res.status === 403 || res.status === 405 || res.status === 500) {
205 // TODO understand why 405 does not go directly in catch (it may be realted to ng-rest-gw)
206 throw new Error();
207 }
208 this.toastr.success(`${model.name} succesfully saved`);
Matteo Scandolo80c3a652017-01-06 10:48:31 -0800209 })
210 .catch(err => {
Matteo Scandolo0a8b02e2017-01-06 14:43:36 -0800211 // TODO keep the edited model
212 this.toastr.error(`Error while saving ${model.name}`);
Matteo Scandolo80c3a652017-01-06 10:48:31 -0800213 });
214 }
215 }],
216 inputs: this.modelFieldToInputCfg(model.fields)
217 };
218 }
219
Matteo Scandolod58d5042016-12-16 16:59:21 -0800220 private fromCamelCase(string: string): string {
221 return string.split(/(?=[A-Z])/).map(w => w.toLowerCase()).join(' ');
222 }
223
224 private fromSnakeCase(string: string): string {
225 return string.split('_').join(' ').trim();
226 }
227
228 private fromKebabCase(string: string): string {
229 return string.split('-').join(' ').trim();
230 }
231
232 private capitalizeFirst(string: string): string {
233 return string.slice(0, 1).toUpperCase() + string.slice(1);
234 }
Matteo Scandolo04964232017-01-07 12:53:46 -0800235
236 private populateRelated(item: any, fk: string, field: IXosModelDefsField): any {
237 // if the relation is not defined return
238 if (!fk || angular.isUndefined(fk) || fk === null) {
239 return;
240 }
241 this.ModelStore.query(field.relation.model)
242 .subscribe(res => {
243 if (angular.isDefined(res) && angular.isDefined(fk)) {
244 let ri = _.find(res, {id: fk});
245 if (angular.isDefined(ri)) {
246 item[`${field.name}-formatted`] = angular.isDefined(ri.name) ? ri.name : ri.humanReadableName;
247 }
248 }
249 });
250 }
Matteo Scandolod58d5042016-12-16 16:59:21 -0800251}