From 43e8aa10181c8fb68aabee4e6edfe7d0f7ffdca2 Mon Sep 17 00:00:00 2001
From: Hermann <hermann.blum@mavt.ethz.ch>
Date: Sat, 11 May 2019 13:25:27 +0200
Subject: [PATCH] Simplify Config and parse more settings from the openapi
 schema

---
 .eslintrc.js                   |   2 +-
 src/auth.js                    |  45 ++++++++---
 src/blacklist/editBlacklist.js |   4 +-
 src/events/editEvent.js        |   6 --
 src/events/viewEvent.js        |   5 --
 src/groups/editGroup.js        |   4 +-
 src/jobs/editJob.js            |   3 -
 src/layout.js                  |  65 +++++++--------
 src/relationlistcontroller.js  |   4 +-
 src/resourceConfig.json        | 139 ---------------------------------
 src/studydocs/editDoc.js       |  18 +----
 src/users/editUser.js          |   2 -
 src/users/userTool.js          |   6 +-
 src/views/editView.js          |  25 +-----
 14 files changed, 83 insertions(+), 245 deletions(-)
 delete mode 100644 src/resourceConfig.json

diff --git a/.eslintrc.js b/.eslintrc.js
index f561085..697167a 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -7,7 +7,7 @@ module.exports = {
       "browser": true,
     },
     "rules": {
-        "no-console": 0,
+        "no-console": 1,
         "class-methods-use-this": 0,
         "prefer-destructuring": 1,
         "no-underscore-dangle": 0,
diff --git a/src/auth.js b/src/auth.js
index 1327234..fe3d61e 100644
--- a/src/auth.js
+++ b/src/auth.js
@@ -5,13 +5,13 @@ import { Snackbar } from 'polythene-mithril';
 // eslint-disable-next-line import/extensions
 import { apiUrl, ownUrl, oAuthID } from 'networkConfig';
 import * as localStorage from './localStorage';
-import config from './resourceConfig.json';
 
 // Object which stores the current login-state
 const APISession = {
   authenticated: false,
   token: '',
   userID: null,
+  schema: null,
   rights: {
     users: [],
     joboffers: [],
@@ -19,6 +19,10 @@ const APISession = {
   },
 };
 
+if (!APISession.schema) {
+  m.request(`${apiUrl}/docs/api-docs`).then((schema) => { APISession.schema = schema; });
+}
+
 const amivapi = axios.create({
   baseURL: apiUrl,
   headers: { 'Content-Type': 'application/json' },
@@ -59,14 +63,12 @@ export function checkAuthenticated() {
     else {
       // let's see if we have a stored token
       const token = localStorage.get('token');
-      console.log(`found this token: ${token}`);
       if (token !== '') {
         // check of token is valid
         checkToken(token).then((session) => {
           APISession.token = token;
           APISession.authenticated = true;
           APISession.userID = session.user;
-          console.log(APISession);
           amivapi.get('/', {
             headers: { 'Content-Type': 'application/json', Authorization: token },
           }).then((response) => {
@@ -128,6 +130,22 @@ export function getUserRights() {
   return APISession.rights;
 }
 
+export function getSchema() {
+  return APISession.schema;
+}
+
+// Mapper for resource vs schema-object names
+const objectNameForResource = {
+  users: 'User',
+  groupmemberships: 'Group Membership',
+  groups: 'Group',
+  eventsignups: 'Event Signup',
+  events: 'Event',
+  studydocuments: 'Study Document',
+  joboffers: 'Job Offer',
+  blacklist: 'Blacklist',
+};
+
 export class ResourceHandler {
   /* Handler to get and manipulate resource items
    *
@@ -139,16 +157,24 @@ export class ResourceHandler {
   constructor(resource, searchKeys = false) {
     this.resource = resource;
     this.rights = [];
-    // special case for users
+    this.schema = JSON.parse(JSON.stringify(getSchema().definitions[
+      objectNameForResource[this.resource]]));
+    // readOnly fields should be removed before patch
+    this.noPatchKeys = Object.keys(this.schema.properties).filter(key =>
+      this.schema.properties[key].readOnly);
+    // any field that is a string can be searched
+    const possibleSearchKeys = Object.keys(this.schema.properties).filter((key) => {
+      const field = this.schema.properties[key];
+      return field.type === 'string' && field.format !== 'objectid' &&
+        field.format !== 'date-time' && !key.startsWith('_');
+    });
+    // special case for users, we don't allow reverse search by legi or rfid
     if (resource === 'users') this.searchKeys = ['firstname', 'lastname', 'nethz'];
-    else this.searchKeys = searchKeys || config[resource].searchKeys;
-    this.noPatchKeys = [
-      '_etag', '_id', '_created', '_links', '_updated',
-      ...(config[resource].notPatchableKeys || [])];
+    else this.searchKeys = searchKeys || possibleSearchKeys;
     checkAuthenticated().then(() => {
       // again special case for users
       if (resource === 'users' && APISession.isUserAdmin) {
-        this.searchKeys = searchKeys || config[resource].searchKeys;
+        this.searchKeys = searchKeys || possibleSearchKeys;
       }
     });
   }
@@ -217,6 +243,7 @@ export class ResourceHandler {
   }
 
   networkError(e) {
+    // eslint-disable-next-line no-console
     console.log(e);
     Snackbar.show({ title: 'Network error, try again.', style: { color: 'red' } });
   }
diff --git a/src/blacklist/editBlacklist.js b/src/blacklist/editBlacklist.js
index ee71bb4..129f8c2 100644
--- a/src/blacklist/editBlacklist.js
+++ b/src/blacklist/editBlacklist.js
@@ -2,7 +2,6 @@ import m from 'mithril';
 import { TextField } from 'polythene-mithril';
 import { ListSelect, DatalistController } from 'amiv-web-ui-components';
 import { ResourceHandler } from '../auth';
-import { loadingScreen } from '../layout';
 import EditView from '../views/editView';
 
 class NanoController {
@@ -48,7 +47,6 @@ export default class NewBlacklist extends EditView {
   }
 
   view() {
-    if (!this.form.schema) return m(loadingScreen);
     return this.layout([
       m('div', { style: { display: 'flex' } }, [
         m(TextField, { label: 'User: ', disabled: true, style: { width: '160px' } }),
@@ -57,7 +55,7 @@ export default class NewBlacklist extends EditView {
           selection: this.form.data.user,
           listTileAttrs: user => Object.assign({}, { title: `${user.firstname} ${user.lastname}` }),
           selectedText: user => `${user.firstname} ${user.lastname}`,
-          onSelect: (data) => { console.log('data'); this.form.data.user = data; },
+          onSelect: (data) => { this.form.data.user = data; },
         })),
       ]),
       ...this.form.renderSchema(['reason', 'start_time', 'price']),
diff --git a/src/events/editEvent.js b/src/events/editEvent.js
index fdcb17e..6ea3748 100644
--- a/src/events/editEvent.js
+++ b/src/events/editEvent.js
@@ -9,7 +9,6 @@ import { TabsCSS, ButtonCSS } from 'polythene-css';
 import { apiUrl, ownUrl } from 'networkConfig';
 import { ResourceHandler } from '../auth';
 import { colors } from '../style';
-import { loadingScreen } from '../layout';
 import { icons } from '../views/elements';
 import EditView from '../views/editView';
 
@@ -69,7 +68,6 @@ export default class newEvent extends EditView {
     // proposition URL-link decoder
     if (this.rightSubmit && m.route.param('proposition')) {
       const data = JSON.parse(window.atob(m.route.param('proposition')));
-      console.log(data);
       this.form.data = data;
     }
     if (this.form.data.priority === 10) this.form.data.high_priority = true;
@@ -243,8 +241,6 @@ export default class newEvent extends EditView {
   }
 
   view() {
-    if (!this.form.schema) return m(loadingScreen);
-
     // load image urls from the API data
     ['thumbnail', 'poster', 'infoscreen'].forEach((key) => {
       const img = this.form.data[`img_${key}`];
@@ -254,8 +250,6 @@ export default class newEvent extends EditView {
       }
     });
 
-    console.log(this.form.errors, this.form.valid);
-
     // Define the number of Tabs and their titles
     const titles = ['Event Description', 'When and Where?', 'Signups', 'Internal Info'];
     if (this.rightSubmit) titles.push('Images');
diff --git a/src/events/viewEvent.js b/src/events/viewEvent.js
index 60c3d8f..c2f35f3 100644
--- a/src/events/viewEvent.js
+++ b/src/events/viewEvent.js
@@ -6,7 +6,6 @@ import { DropdownCard, DatalistController, Chip } from 'amiv-web-ui-components';
 // eslint-disable-next-line import/extensions
 import { apiUrl } from 'networkConfig';
 import ItemView from '../views/itemView';
-import { eventsignups as signupConfig } from '../resourceConfig.json';
 import TableView from '../views/tableView';
 import RelationlistController from '../relationlistcontroller';
 import { dateFormatter } from '../utils';
@@ -236,7 +235,6 @@ class ParticipantsTable {
         m(TableView, {
           tableHeight: '275px',
           controller: this.ctrl,
-          keys: signupConfig.tableKeys,
           tileContent: data => this.itemRow(data),
           clickOnRows: false,
           titles: [
@@ -271,7 +269,6 @@ export default class viewEvent extends ItemView {
 
   cloneEvent() {
     const event = Object.assign({}, this.data);
-    console.log(event);
 
     const eventInfoToDelete = [
       '_id',
@@ -284,7 +281,6 @@ export default class viewEvent extends ItemView {
       '__proto__',
     ];
     const now = new Date();
-    console.log(`${now.toISOString().slice(0, -5)}Z`);
     if (event.time_end < `${now.toISOString().slice(0, -5)}Z`) {
       eventInfoToDelete.push(...[
         'time_advertising_end',
@@ -299,7 +295,6 @@ export default class viewEvent extends ItemView {
       delete event[key];
     });
 
-    console.log(event);
     this.controller.changeModus('new');
     this.controller.data = event;
   }
diff --git a/src/groups/editGroup.js b/src/groups/editGroup.js
index b354ea0..dee6678 100644
--- a/src/groups/editGroup.js
+++ b/src/groups/editGroup.js
@@ -4,7 +4,6 @@ import { ListSelect, DatalistController, Select } from 'amiv-web-ui-components';
 // eslint-disable-next-line import/extensions
 import { apiUrl } from 'networkConfig';
 import { ResourceHandler } from '../auth';
-import { loadingScreen } from '../layout';
 import EditView from '../views/editView';
 
 
@@ -88,7 +87,6 @@ export default class NewGroup extends EditView {
   }
 
   view() {
-    if (!this.form.schema) return m(loadingScreen);
     return this.layout([
       ...this.form.renderSchema(['name', 'allow_self_enrollment', 'requires_storage']),
       m('div', { style: { display: 'flex' } }, [
@@ -98,7 +96,7 @@ export default class NewGroup extends EditView {
           selection: this.form.data.moderator,
           listTileAttrs: user => Object.assign({}, { title: `${user.firstname} ${user.lastname}` }),
           selectedText: user => `${user.firstname} ${user.lastname}`,
-          onSelect: (data) => { console.log('data'); this.form.data.moderator = data; },
+          onSelect: (data) => { this.form.data.moderator = data; },
         })),
       ]),
       m(PermissionEditor, {
diff --git a/src/jobs/editJob.js b/src/jobs/editJob.js
index daae250..bc3352c 100644
--- a/src/jobs/editJob.js
+++ b/src/jobs/editJob.js
@@ -1,12 +1,10 @@
 import m from 'mithril';
 import { FileInput } from 'amiv-web-ui-components';
-import { loadingScreen } from '../layout';
 import EditView from '../views/editView';
 
 
 export default class newJob extends EditView {
   beforeSubmit() {
-    console.log(this.form.data);
     // remove all unchanged files
     if (this.form.data.pdf !== undefined &&
         (this.form.data.pdf === null || 'upload_date' in this.form.data.pdf)) {
@@ -26,7 +24,6 @@ export default class newJob extends EditView {
   }
 
   view() {
-    if (!this.form.schema) return m(loadingScreen);
     return this.layout([
       ...this.form.renderSchema(['company']),
       m(FileInput, this.form.bind({
diff --git a/src/layout.js b/src/layout.js
index 36c4ce0..02b069f 100644
--- a/src/layout.js
+++ b/src/layout.js
@@ -12,7 +12,7 @@ import {
 } from 'polythene-mithril';
 import { styler } from 'polythene-core-css';
 import { icons } from './views/elements';
-import { deleteSession, getUserRights } from './auth';
+import { deleteSession, getUserRights, getSchema } from './auth';
 import { colors } from './style';
 
 const layoutStyle = [
@@ -93,8 +93,40 @@ class Menupoint {
   }
 }
 
+export class loadingScreen {
+  view() {
+    return m('div', {
+      style: {
+        height: '100%',
+        width: '100%',
+        display: 'flex',
+        'flex-direction': 'column',
+        'justify-content': 'center',
+        'align-items': 'center',
+        'animation-name': 'popup',
+        'animation-duration': '2000ms',
+      },
+    }, m('div', { style: { height: '5vh', 'font-size': '4em' } }, 'Loading...'), m('div', {
+      style: {
+        height: '20vh',
+        width: '20vh',
+        'animation-name': 'spin',
+        'animation-duration': '2500ms',
+        'animation-iteration-count': 'infinite',
+        'animation-timing-function': 'linear',
+      },
+    }, m('div', {
+      style: { height: '20vh', width: '20vh', display: 'inline-block' },
+    }, m(SVG, {
+      style: { width: 'inherit', height: 'inherit' },
+      content: m.trust(icons.amivWheel),
+    }))));
+  }
+}
+
 export class Layout {
   view({ children }) {
+    if (!getSchema()) return m(loadingScreen);
     const userRights = getUserRights();
     return m('div', [
       m('div.wrapper-main.smooth', [
@@ -177,37 +209,6 @@ export class Layout {
   }
 }
 
-export class loadingScreen {
-  view() {
-    return m('div', {
-      style: {
-        height: '100%',
-        width: '100%',
-        display: 'flex',
-        'flex-direction': 'column',
-        'justify-content': 'center',
-        'align-items': 'center',
-        'animation-name': 'popup',
-        'animation-duration': '2000ms',
-      },
-    }, m('div', { style: { height: '5vh', 'font-size': '4em' } }, 'Loading...'), m('div', {
-      style: {
-        height: '20vh',
-        width: '20vh',
-        'animation-name': 'spin',
-        'animation-duration': '2500ms',
-        'animation-iteration-count': 'infinite',
-        'animation-timing-function': 'linear',
-      },
-    }, m('div', {
-      style: { height: '20vh', width: '20vh', display: 'inline-block' },
-    }, m(SVG, {
-      style: { width: 'inherit', height: 'inherit' },
-      content: m.trust(icons.amivWheel),
-    }))));
-  }
-}
-
 export class Error404 {
   view() {
     return m('div', {
diff --git a/src/relationlistcontroller.js b/src/relationlistcontroller.js
index ced9580..2439245 100644
--- a/src/relationlistcontroller.js
+++ b/src/relationlistcontroller.js
@@ -71,8 +71,6 @@ export default class RelationlistController {
         // update total number of pages
         this.totalPages = Math.ceil(data._meta.total / 10);
 
-        console.log(data._items.map(item => item._id));
-
         const itemsWithoutRelation = data._items.filter(item => !(this.secondaryKey in item));
         const itemsWithRelation = data._items.filter(item => (this.secondaryKey in item));
 
@@ -115,7 +113,6 @@ export default class RelationlistController {
         // save totalPages as a constant to avoid race condition with pages added during this
         // process
         const { totalPages } = this;
-        console.log(totalPages);
 
         if (totalPages === 1) {
           resolve(firstPage);
@@ -128,6 +125,7 @@ export default class RelationlistController {
             // look if all pages were collected
             const missingPages = Array.from(new Array(totalPages), (x, i) => i + 1).filter(i =>
               !(i in pages));
+            // eslint-disable-next-line no-console
             console.log('missingPages', missingPages);
             if (missingPages.length === 0) {
               // collect all the so-far loaded pages in order (sorted keys)
diff --git a/src/resourceConfig.json b/src/resourceConfig.json
deleted file mode 100644
index de3a0db..0000000
--- a/src/resourceConfig.json
+++ /dev/null
@@ -1,139 +0,0 @@
-{
-    "apiUrl": "https://api-dev.amiv.ethz.ch/",
-    "events": {
-        "keyDescriptors": {
-            "title_de": "German Title",
-            "title_en": "English Title",
-            "location": "Location",
-            "show_website": "Event is shown on the website",
-            "priority": "Priority",
-            "time_end": "Ending time",
-            "time_register_end": "Deadline for registration",
-            "time_start": "Starting time",
-            "spots": "Spots available",
-            "allow_email_signup": "Event open for non-AMIV members",
-            "price": "Price",
-            "signup_count": "Signed-up participants",
-            "catchphrase_en": "Catchphrase in English. Announce and Website.",
-            "catchphrase_de": "Schlagwort auf Deutsch",
-            "description_de": "Beschreibung auf Deutsch",
-            "description_en": "Description in English",
-            "img_banner": "Banner as png",
-            "img_poster": "Poster as png",
-            "img_thumbnail": "Thumbnail as png",
-            "show_infoscreen": "Does the event show on the infoscreen?",
-            "img_infoscreen": "Infoscreen as png",
-            "time_advertising_end": "Advertisment ends on",
-            "time_advertising_start": "Advertisement starts on",
-            "selection_strategy": "TODO what is this?",
-            "show_announce": "Does it belong to announce?"
-        },
-        "tableKeys": [
-            "title_de",
-            "time_start",
-            "time_end",
-            "time_register_end",
-            "show_website",
-            "priority"
-        ],
-        "notPatchableKeys": [
-            "signup_count",
-            "unaccepted_count"
-        ],
-        "searchKeys": [
-            "title_de",
-            "title_en",
-            "location"
-        ]
-    },
-    "users": {
-        "keyDescriptors": {
-            "legi": "Legi",
-            "firstname": "First Name",
-            "lastname": "Last Name",
-            "rfid": "RFID",
-            "phone": "Phone",
-            "nethz": "nethz",
-            "gender": "Gender",
-            "department": "Department",
-            "email": "Email"
-        },
-        "tableKeys": [
-            "firstname",
-            "lastname",
-            "nethz",
-            "legi",
-            "membership"
-        ],
-        "searchKeys": [
-            "firstname",
-            "lastname",
-            "nethz",
-            "legi",
-            "email"
-        ],
-        "notPatchableKeys": [
-            "password_set"
-        ]
-    },
-    "joboffers":{
-        "keyDescriptors": {
-            "company": "Company",
-            "email": "Email",
-            "description_en": "Job description",
-            "description_de": "Job Beschreibung",
-            "logo": "Logo as png",
-            "pdf": "PDF provided by company",
-            "time_end": "Application deadline",
-            "title_de": "Stelle auf Deutsch",
-            "title_en": "Position title in English",
-            "show_website": "Is the job listed on the website?",
-            "_id":"Job ID."
-        },
-        "tableKeys": [
-            "title_de",
-            "time_end",
-            "show_website"
-        ]
-    },
-    "groups": {
-        "keyDescriptors": {
-            "name": "Name"
-        },
-        "searchKeys": ["name"],
-        "patchableKeys": ["name"]
-    },
-    "groupmemberships": {
-        "patchableKeys": ["user", "group"]
-    },
-    "eventsignups": {
-        "patchableKeys": ["event"],
-        "tableKeys": [
-            "_created",
-            "user.lastname",
-            "user.firstname",
-            "email"
-        ],
-        "searchKeys": []
-    },
-    "sessions": {
-        "searchKeys": []
-    },
-    "studydocuments": {
-        "searchKeys": [
-            "title",
-            "lecture",
-            "professor",
-            "author",
-            "uploader",
-            "department"
-        ],
-        "notPatchableKeys": ["uploader"]
-    },
-    "blacklist": {
-        "searchKeys": [
-          "reason",
-          "price"
-        ]
-    }
-}
diff --git a/src/studydocs/editDoc.js b/src/studydocs/editDoc.js
index f7bd2f3..12b53ac 100644
--- a/src/studydocs/editDoc.js
+++ b/src/studydocs/editDoc.js
@@ -1,31 +1,22 @@
 import m from 'mithril';
 import { FileInput } from 'amiv-web-ui-components';
 import { Button, List, ListTile, Snackbar } from 'polythene-mithril';
-// eslint-disable-next-line import/extensions
-import { apiUrl } from 'networkConfig';
 import EditView from '../views/editView';
-import { loadingScreen } from '../layout';
+import { getSchema } from '../auth';
 
 
 export default class editDoc extends EditView {
   // constructor zu file upload
   constructor(vnode) {
+    // remove the files list as it is impossible to validate
+    const docSchema = getSchema().definitions['Study Document'];
+    delete docSchema.properties.files;
     super(vnode);
     if (!('files' in this.form.data)) {
       this.form.data.files = [{ name: 'add file' }];
     }
   }
 
-  oninit() {
-    // load schema
-    m.request(`${apiUrl}/docs/api-docs`).then((schema) => {
-      // remove the files list as it is impossible to validate
-      const docSchema = schema.definitions['Study Document'];
-      delete docSchema.properties.files;
-      this.form.setSchema(docSchema);
-    }).catch((error) => { console.log(error); });
-  }
-
   beforeSubmit() {
     // check if there are files uploaded
     const files = [];
@@ -51,7 +42,6 @@ export default class editDoc extends EditView {
   }
 
   view() {
-    if (!this.form.schema) return m(loadingScreen);
     return this.layout([
       m('h3', 'Add a New Studydocument'),
       this.form._renderField('semester', {
diff --git a/src/users/editUser.js b/src/users/editUser.js
index 5d90b29..8099bae 100644
--- a/src/users/editUser.js
+++ b/src/users/editUser.js
@@ -1,6 +1,5 @@
 import m from 'mithril';
 import { TextInput } from 'amiv-web-ui-components';
-import { loadingScreen } from '../layout';
 import EditView from '../views/editView';
 
 export default class UserEdit extends EditView {
@@ -11,7 +10,6 @@ export default class UserEdit extends EditView {
 
 
   view() {
-    if (!this.form.schema) return m(loadingScreen);
     return this.layout([
       ...this.form.renderSchema(['lastname', 'firstname', 'email', 'nethz', 'legi']),
       m(TextInput, this.form.bind({
diff --git a/src/users/userTool.js b/src/users/userTool.js
index 08a4d56..2bf11ee 100644
--- a/src/users/userTool.js
+++ b/src/users/userTool.js
@@ -3,7 +3,6 @@ import { DatalistController } from 'amiv-web-ui-components';
 import EditUser from './editUser';
 import ViewUser from './viewUser';
 import TableView from '../views/tableView';
-import { users as config } from '../resourceConfig.json';
 import ItemController from '../itemcontroller';
 import { loadingScreen } from '../layout';
 import { ResourceHandler } from '../auth';
@@ -31,10 +30,11 @@ export class UserTable {
     );
   }
   view() {
+    const tableKeys = ['firstname', 'lastname', 'nethz', 'legi', 'membership'];
     return m(TableView, {
       controller: this.ctrl,
-      keys: config.tableKeys,
-      titles: config.tableKeys.map(key => config.keyDescriptors[key] || key),
+      keys: tableKeys,
+      titles: tableKeys.map(key => this.handler.schema.properties[key].title || key),
       filters: [[
         { name: 'not members', query: { membership: 'none' } },
         { name: 'regular members', query: { membership: 'regular' } },
diff --git a/src/views/editView.js b/src/views/editView.js
index 6a8b15a..3c71fe9 100644
--- a/src/views/editView.js
+++ b/src/views/editView.js
@@ -1,24 +1,10 @@
 import m from 'mithril';
 import { IconButton, Toolbar, ToolbarTitle, Button } from 'polythene-mithril';
 import { Form } from 'amiv-web-ui-components';
-// eslint-disable-next-line import/extensions
-import { apiUrl } from 'networkConfig';
 import ItemView from './itemView';
 import { icons } from './elements';
 import { colors } from '../style';
 
-// Mapper for resource vs schema-object names
-const objectNameForResource = {
-  users: 'User',
-  groupmemberships: 'Group Membership',
-  groups: 'Group',
-  eventsignups: 'Event Signup',
-  events: 'Event',
-  studydocuments: 'Study Document',
-  joboffers: 'Job Offer',
-  blacklist: 'Blacklist',
-};
-
 export default class EditView extends ItemView {
   /**
    * Extension of ItemView to edit a data item
@@ -38,13 +24,7 @@ export default class EditView extends ItemView {
     const validInitially = this.controller.modus === 'edit';
     // start a form to collect the submit data
     this.form = new Form({}, validInitially, 4, Object.assign({}, this.controller.data));
-  }
-
-  oninit() {
-    // load schema
-    m.request(`${apiUrl}/docs/api-docs`).then((schema) => {
-      this.form.setSchema(schema.definitions[objectNameForResource[this.resource]]);
-    }).catch((error) => { console.log(error); });
+    this.form.setSchema(this.handler.schema);
   }
 
   /**
@@ -66,7 +46,6 @@ export default class EditView extends ItemView {
       request.then((response) => {
         resolve(response);
       }).catch((error) => {
-        console.log(error);
         // Process the API error
         if ('_issues' in error) {
           // there are problems with some fields, display them
@@ -74,10 +53,12 @@ export default class EditView extends ItemView {
             this.form.errors[field] = [error._issues[field]];
             this.form.valid = false;
           });
+          // eslint-disable-next-line no-console
           console.log(this.form.errors);
           m.redraw();
           reject(error);
         } else {
+          // eslint-disable-next-line no-console
           console.log(error);
         }
       });
-- 
GitLab