update from RIFT as of 696b75d2fe9fb046261b08c616f1bcf6c0b54a9b third try
[osm/UI.git] / skyquake / plugins / admin / src / store / ModelStore.js
1 /*
2 *
3 * Copyright 2016 RIFT.IO Inc
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 */
18 import Alt from 'widgets/skyquake_container/skyquakeAltInstance';
19 import yang from '../yang/leaf-utils';
20
21 import {
22 schemaActions,
23 schemaSource
24 } from 'source/schema'
25 import {
26 modelActions,
27 modelSource
28 } from 'source/model'
29 import adminActions from '../adminActions'
30
31 function pullOutWantedProperty(path, result) {
32 if (!result) {
33 return null;
34 }
35 const nodes = path.split('/');
36 if (nodes[0] === 'project') {
37 nodes.shift(); // get rid of top level as it was not sent back
38 result = nodes.reduce((data, node) => data[node], result);
39 }
40 return result;
41 }
42
43 function findItemIndex(list, key, keyValue) {
44 const keyPath = Array.isArray(keyValue) ? keyValue : key.length > 1 ? JSON.parse(keyValue) : [keyValue];
45 if (key.length > 1) {
46 return list.findIndex(item => {
47 return key.every((k, i) => item[k] === keyPath[i]);
48 });
49 } else {
50 const leaf = key[0];
51 const match = keyPath[0];
52 return list.findIndex(item => {
53 return item[leaf] === match;
54 });
55 }
56 }
57
58 function getItemFromList(list, key, keyValue) {
59 return list[findItemIndex(list, key, keyValue)];
60 }
61
62 function getElement(path, store) {
63 return path.reduce((parent, node, index) => {
64 const element = Object.assign({}, parent);
65 if (parent.type === 'list') {
66 element.type = 'list-entry'
67 element.value = getItemFromList(parent.value, parent.schema.key, node);
68 } else {
69 element.schema = parent.schema.properties.find(property => property.name === node)
70 element.type = element.schema.type;
71 element.value = (parent.value && parent.value[node]) || (parent.value[node] = {});
72 }
73 return element;
74 }, {
75 value: store.data,
76 schema: store.schema[store.path],
77 type: store.schema[store.path].type
78 });
79 }
80
81 function massageData(data, schema){
82 return Object.keys(data).reduce((o, name) => {
83 let input = data[name];
84 let output = null;
85 if (yang.isLeafEmpty(input.property)) {
86 output = {type: 'leaf_empty', data: input.value.length ? 'set' : ''}
87 } else if (yang.isLeafList(input.property)) {
88 const newList = Array.isArray(input.value) ? input.value : input.value.split(',');
89 const oldList = input.currentValue ? (Array.isArray(input.currentValue) ? input.currentValue : input.currentValue.split(',')) : [];
90 const addList = newList.reduce((list, v) => {
91 v = v.trim();
92 if (v) {
93 const i = oldList.indexOf(v);
94 if (i === -1) {
95 list.push(v);
96 } else {
97 oldList.splice(i, 1);
98 }
99 }
100 return list;
101 }, [])
102 output = {type: 'leaf_list', data: {}};
103 addList.length && (output.data.add = addList);
104 oldList.length && (output.data.remove = oldList);
105 } else {
106 output = input.value;
107 }
108 o[name] = output;
109 return o
110 }, {})
111 }
112
113 class ModelStore {
114 constructor(path) {
115 this.state = {
116 path,
117 isLoading: false
118 };
119 this.bindActions(adminActions);
120 this.registerAsync(modelSource);
121 this.bindActions(modelActions);
122 this.registerAsync(schemaSource);
123 this.bindActions(schemaActions);
124 this.exportPublicMethods({
125 get: this.get,
126 update: this.update,
127 create: this.create,
128 'delete': this.remove
129 })
130 }
131
132 get = () => {
133 Promise.all([
134 new Promise((resolve, reject) => {
135 this.getInstance().loadSchema(this.state.path)
136 .then(result => resolve(result))
137 .catch(error => reject(error))
138 }),
139 new Promise((resolve, reject) => {
140 const result = this.getInstance().loadModel(this.state.path)
141 .then(result => resolve(pullOutWantedProperty(this.state.path, result)))
142 .catch(error => reject(error))
143 })
144 ]).then((results) => {
145 this.setState({
146 isLoading: false,
147 path: this.state.path,
148 schema: results[0][this.state.path],
149 data: results[1] || {}
150 });
151 }).catch((errors) => {
152 this.setState({
153 isLoading: false,
154 error: {
155 get: errors
156 }
157 })
158 })
159 this.setState({
160 isLoading: true,
161 path: this.state.path
162 })
163 }
164 update = (path, obj) => {
165 const e = getElement(path, this.state);
166 obj = massageData(obj, e.schema);
167 this.getInstance().updateModel(path, obj)
168 .then((response) => {
169 const errors = [];
170 const target = getElement(path, this.state);
171 for (const name in response.result) {
172 if (response.result[name].success) {
173 target.value[name] = response.result[name].value
174 } else {
175 errors.push(response.result[name]);
176 }
177 }
178 this.setState({
179 updatingPath: null,
180 data: this.state.data
181 });
182 errors && this.setState({
183 error: {
184 update: errors
185 }
186 });
187 }).catch((errors) => {
188 this.setState({
189 updatingPath: null,
190 error: {
191 update: errors
192 }
193 })
194 })
195 this.setState({
196 updatingPath: path,
197 })
198 }
199
200 create = (path, obj) => {
201 const e = getElement(path, this.state);
202 obj = massageData(obj, e.schema);
203 this.getInstance().createModel(path, obj)
204 .then((response) => {
205 const createList = () => {
206 const parentPath = path.slice();
207 const name = parentPath.pop();
208 const list = [];
209 const parent = getElement(parentPath, this.state)
210 if (parent.value) parent.value[name] = [];
211 return parent.value[name];
212 }
213 const errors = [];
214 const target = getElement(path, this.state);
215 const list = target.value || createList();
216 list.unshift(response.data);
217 this.setState({
218 updatingPath: null,
219 data: this.state.data
220 });
221 errors && this.setState({
222 error: {
223 update: errors
224 }
225 });
226 }).catch((errors) => {
227 this.setState({
228 updatingPath: null,
229 error: {
230 update: errors
231 }
232 })
233 })
234 this.setState({
235 updatingPath: path,
236 })
237 }
238
239 remove = (path, obj) => {
240 this.getInstance().deleteModel(path)
241 .then((response) => {
242 path = path.slice();
243 const id = path.pop();
244 const list = getElement(path, this.state);
245 const index = findItemIndex(list.value, list.schema.key, id);
246 list.value.splice(index, 1);
247 this.setState({
248 updatingPath: null,
249 data: this.state.data
250 });
251 }).catch((errors) => {
252 this.setState({
253 updatingPath: null,
254 error: {
255 'delete': errors
256 }
257 })
258 })
259 this.setState({
260 updatingPath: path,
261 })
262 }
263 }
264
265 export default ModelStore