Skip to content

Commit 433d85c

Browse files
committed
Add support for update documents
1 parent 6925e9d commit 433d85c

17 files changed

Lines changed: 584 additions & 67 deletions

File tree

OpenFlow/src/DatabaseConnection.ts

Lines changed: 189 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
import { ObjectID, Db, Binary, InsertOneWriteOpResult, DeleteWriteOpResultObject, ObjectId, MapReduceOptions } from "mongodb";
1+
import {
2+
ObjectID, Db, Binary, InsertOneWriteOpResult, DeleteWriteOpResultObject, ObjectId, MapReduceOptions, CollectionInsertOneOptions, UpdateWriteOpResult, WriteOpResult
3+
} from "mongodb";
24
import { MongoClient } from "mongodb";
35
import { Base, Rights, WellknownIds } from "./base";
46
import winston = require("winston");
@@ -171,6 +173,8 @@ export class DatabaseConnection {
171173
* Create a new document in the database
172174
* @param {T} item Item to create
173175
* @param {string} collectionname The collection to create item in
176+
* @param {number} w Write Concern ( 0:no acknowledgment, 1:Requests acknowledgment, 2: Requests acknowledgment from 2, 3:Requests acknowledgment from 3)
177+
* @param {boolean} j Ensure is written to the on-disk journal.
174178
* @param {string} jwt JWT of the user, creating the item, to ensure rights and permission
175179
* @returns Promise<T> Returns the new item added
176180
*/
@@ -188,7 +192,7 @@ export class DatabaseConnection {
188192
item._modifiedbyid = user._id;
189193
item._modified = item._created;
190194
var hasUser: Ace = item._acl.find(e => e._id === user._id);
191-
if (hasUser === null || hasUser === undefined) {
195+
if ((hasUser === null || hasUser === undefined) && item._acl.length == 0) {
192196
if (collectionname != "audit") { this._logger.debug("Adding self " + user.username + " to object " + (item.name || item._name)); }
193197
item.addRight(user._id, user.name, [Rights.full_control]);
194198
}
@@ -201,7 +205,8 @@ export class DatabaseConnection {
201205
(item as any).passwordhash = await Crypt.hash((item as any).newpassword);
202206
delete (item as any).newpassword;
203207
}
204-
var options = { writeConcern: { w: parseInt((w as any)), j: j } };
208+
// var options:CollectionInsertOneOptions = { writeConcern: { w: parseInt((w as any)), j: j } };
209+
var options: CollectionInsertOneOptions = { w: w, j: j };
205210
var result: InsertOneWriteOpResult = await this.db.collection(collectionname).insertOne(item, options);
206211
item = result.ops[0];
207212

@@ -217,61 +222,201 @@ export class DatabaseConnection {
217222
* Update entity in database
218223
* @param {T} item Item to update
219224
* @param {string} collectionname Collection containing item
225+
* @param {number} w Write Concern ( 0:no acknowledgment, 1:Requests acknowledgment, 2: Requests acknowledgment from 2, 3:Requests acknowledgment from 3)
226+
* @param {boolean} j Ensure is written to the on-disk journal.
220227
* @param {string} jwt JWT of user who is doing the update, ensuring rights
221228
* @returns Promise<T>
222229
*/
223-
async UpdateOne<T extends Base>(item: T, collectionname: string, w: number, j: boolean, jwt: string): Promise<T> {
230+
async UpdateOne<T extends Base>(query: any, item: T, collectionname: string, w: number, j: boolean, jwt: string): Promise<T> {
231+
var itemUpdate: boolean = true;
224232
if (item === null || item === undefined) { throw Error("Cannot update null item"); }
225-
if (!item.hasOwnProperty("_id")) { throw Error("Cannot update item without _id"); }
226233
await this.connect();
227234
var user: TokenUser = Crypt.verityToken(jwt);
228235
if (!this.hasAuthorization(user, item, "update")) { throw new Error("Access denied"); }
229236
var original: T = await this.getbyid<T>(item._id, collectionname, jwt);
230237
if (!original) { throw Error("item not found!"); }
231-
item._modifiedby = user.name;
232-
item._modifiedbyid = user._id;
233-
item._modified = new Date(new Date().toISOString());
234-
// now add all _ fields to the new object
235-
var keys: string[] = Object.keys(original);
236-
for (let i: number = 0; i < keys.length; i++) {
237-
let key: string = keys[i];
238-
if (key === "_created") {
239-
item[key] = new Date(original[key]);
240-
} else if (key === "_createdby" || key === "_createdbyid") {
241-
item[key] = original[key];
242-
} else if (key === "_modifiedby" || key === "_modifiedbyid" || key === "_modified") {
243-
// allready updated
244-
} else if (key.indexOf("_") === 0) {
245-
if (!item.hasOwnProperty(key)) {
246-
item[key] = original[key]; // add missing key
247-
} else if (item[key] === null) {
248-
delete item[key]; // remove key
249-
} else {
250-
// key allready exists, might been updated since last save
238+
239+
// assume empty query, means full document, else update document
240+
if (query === null || query === undefined) {
241+
// this will add an _acl so needs to be after we checked old item
242+
if (!item.hasOwnProperty("_id")) {
243+
throw Error("Cannot update item without _id");
244+
}
245+
item._modifiedby = user.name;
246+
item._modifiedbyid = user._id;
247+
item._modified = new Date(new Date().toISOString());
248+
// now add all _ fields to the new object
249+
var keys: string[] = Object.keys(original);
250+
for (let i: number = 0; i < keys.length; i++) {
251+
let key: string = keys[i];
252+
if (key === "_created") {
253+
item[key] = new Date(original[key]);
254+
} else if (key === "_createdby" || key === "_createdbyid") {
255+
item[key] = original[key];
256+
} else if (key === "_modifiedby" || key === "_modifiedbyid" || key === "_modified") {
257+
// allready updated
258+
} else if (key.indexOf("_") === 0) {
259+
if (!item.hasOwnProperty(key)) {
260+
item[key] = original[key]; // add missing key
261+
} else if (item[key] === null) {
262+
delete item[key]; // remove key
263+
} else {
264+
// key allready exists, might been updated since last save
265+
}
251266
}
252267
}
268+
item = this.ensureResource(item);
269+
this.traversejsonencode(item);
270+
item = this.encryptentity<T>(item);
271+
var hasUser: Ace = item._acl.find(e => e._id === user._id);
272+
if ((hasUser === null || hasUser === undefined) && item._acl.length == 0) {
273+
if (collectionname != "audit") { this._logger.debug("Adding self " + user.username + " to object " + (item.name || item._name)); }
274+
item.addRight(user._id, user.name, [Rights.full_control]);
275+
}
276+
} else {
277+
itemUpdate = false;
253278
}
254-
// this will add an _acl so needs to be after we checked old item
255-
item = this.ensureResource(item);
256-
this.traversejsonencode(item);
257-
item = this.encryptentity<T>(item);
258279

259-
var hasUser: Ace = item._acl.find(e => e._id === user._id);
260-
if (hasUser === null || hasUser === undefined) {
261-
if (collectionname != "audit") { this._logger.debug("Adding self " + user.username + " to object " + (item.name || item._name)); }
262-
item.addRight(user._id, user.name, [Rights.full_control]);
280+
if (collectionname === "users" && item._type === "user" && item.hasOwnProperty("newpassword")) {
281+
(item as any).passwordhash = await Crypt.hash((item as any).newpassword);
282+
delete (item as any).newpassword;
283+
}
284+
this._logger.debug("updating " + (item.name || item._name) + " in database");
285+
// await this.db.collection(collectionname).replaceOne({ _id: item._id }, item, options);
286+
287+
if (query === null || query === undefined) {
288+
query = { _id: item._id };
289+
}
290+
var _query: Object = {};
291+
if (collectionname === "files") { collectionname = "fs.files"; }
292+
if (collectionname === "fs.files") {
293+
_query = { $and: [query, this.getbasequery(jwt, "metadata._acl", [Rights.update])] };
294+
} else {
295+
if (!collectionname.endsWith("hist")) {
296+
_query = { $and: [query, this.getbasequery(jwt, "_acl", [Rights.update])] };
297+
} else {
298+
// todo: enforcer permissions when fetching hist ?
299+
_query = query;
300+
}
301+
}
302+
303+
// var options = { writeConcern: { w: parseInt((w as any)), j: j } };
304+
var options: CollectionInsertOneOptions = { w: w, j: j };
305+
var res: UpdateWriteOpResult = null;
306+
try {
307+
if (itemUpdate) {
308+
res = await this.db.collection(collectionname).updateOne(_query, { $set: item }, options);
309+
} else {
310+
res = await this.db.collection(collectionname).updateOne(_query, item, options);
311+
}
312+
// var res: ReplaceOneWriteOpResult = await this.db.collection(collectionname).replaceOne(_query, item, options);
313+
if (res.result.ok == 1) {
314+
if (w > 0) {
315+
if (res.modifiedCount == 0 || res.modifiedCount == undefined) {
316+
throw Error("item not found!");
317+
} else if (res.modifiedCount == 1) {
318+
item = item;
319+
} else {
320+
throw Error("More than one item was updated !!!");
321+
}
322+
}
323+
} else {
324+
throw Error("UpdateOne failed!!!");
325+
}
326+
item = this.decryptentity<T>(item);
327+
this.traversejsondecode(item);
328+
return item;
329+
} catch (error) {
330+
throw error;
331+
263332
}
333+
}
334+
/**
335+
* Update multiple documents in database based on update document
336+
* @param {any} query MongoDB Query
337+
* @param {T} item Update document
338+
* @param {string} collectionname Collection containing item
339+
* @param {number} w Write Concern ( 0:no acknowledgment, 1:Requests acknowledgment, 2: Requests acknowledgment from 2, 3:Requests acknowledgment from 3)
340+
* @param {boolean} j Ensure is written to the on-disk journal.
341+
* @param {string} jwt JWT of user who is doing the update, ensuring rights
342+
* @returns Promise<T>
343+
*/
344+
async UpdateMany(query: any, item: any, collectionname: string, w: number, j: boolean, jwt: string): Promise<any[]> {
345+
if (item === null || item === undefined) { throw Error("Cannot update null item"); }
346+
await this.connect();
347+
var user: TokenUser = Crypt.verityToken(jwt);
348+
if (!this.hasAuthorization(user, item, "update")) { throw new Error("Access denied"); }
264349

265350
if (collectionname === "users" && item._type === "user" && item.hasOwnProperty("newpassword")) {
266351
(item as any).passwordhash = await Crypt.hash((item as any).newpassword);
267352
delete (item as any).newpassword;
268353
}
269-
this._logger.debug("updating " + (item.name || item._name) + " in database");
270-
var options = { writeConcern: { w: parseInt((w as any)), j: j } };
271-
await this.db.collection(collectionname).replaceOne({ _id: item._id }, item, options);
272-
this.traversejsondecode(item);
273-
return item;
354+
for (let key in query) {
355+
if (key === "_id") {
356+
var id: string = query._id;
357+
delete query._id;
358+
query.$or = [{ _id: id }, { _id: safeObjectID(id) }];
359+
}
360+
}
361+
var _query: Object = {};
362+
if (collectionname === "files") { collectionname = "fs.files"; }
363+
if (collectionname === "fs.files") {
364+
_query = { $and: [query, this.getbasequery(jwt, "metadata._acl", [Rights.read])] };
365+
} else {
366+
if (!collectionname.endsWith("hist")) {
367+
_query = { $and: [query, this.getbasequery(jwt, "_acl", [Rights.read])] };
368+
} else {
369+
// todo: enforcer permissions when fetching hist ?
370+
_query = query;
371+
}
372+
}
373+
374+
if (item.$set !== undefined) {
375+
item.$set._modifiedby = user.name;
376+
item.$set._modifiedbyid = user._id;
377+
item.$set._modified = new Date(new Date().toISOString());
378+
}
379+
380+
this._logger.debug("updateMany " + (item.name || item._name) + " in database");
381+
// var options = { writeConcern: { w: parseInt((w as any)), j: j } };
382+
var options: CollectionInsertOneOptions = {};
383+
if (w > 0) { options.w = w; }
384+
var res: UpdateWriteOpResult = null;
385+
try {
386+
res = await this.db.collection(collectionname).updateMany(_query, item, options);
387+
if (res.modifiedCount == 0) {
388+
throw Error("item not found!");
389+
}
390+
if (res.result.ok == 1) {
391+
if (w > 0) {
392+
if (res.modifiedCount == 0 || res.modifiedCount == undefined) {
393+
throw Error("item not found!");
394+
} else if (res.modifiedCount == 1) {
395+
item = item;
396+
} else {
397+
throw Error("More than one item was updated !!!");
398+
}
399+
}
400+
} else {
401+
throw Error("UpdateOne failed!!!");
402+
}
403+
return null;
404+
} catch (error) {
405+
throw error;
406+
}
407+
// this.traversejsondecode(item);
408+
// return item;
274409
}
410+
/**
411+
* Insert or Update depending on document allready exists.
412+
* @param {T} item Item to insert or update
413+
* @param {string} collectionname Collection containing item
414+
* @param {string} uniqeness List of fields to combine for uniqeness
415+
* @param {number} w Write Concern ( 0:no acknowledgment, 1:Requests acknowledgment, 2: Requests acknowledgment from 2, 3:Requests acknowledgment from 3)
416+
* @param {boolean} j Ensure is written to the on-disk journal.
417+
* @param {string} jwt JWT of user who is doing the update, ensuring rights
418+
* @returns Promise<T>
419+
*/
275420
async InsertOrUpdateOne<T extends Base>(item: T, collectionname: string, uniqeness: string, w: number, j: boolean, jwt: string): Promise<T> {
276421
var query: any = null;
277422
if (uniqeness !== null && uniqeness !== undefined && uniqeness !== "") {
@@ -295,12 +440,12 @@ export class DatabaseConnection {
295440
var user: TokenUser = Crypt.verityToken(jwt);
296441
if (!this.hasAuthorization(user, item, "update")) { throw new Error("Access denied"); }
297442
var hasUser: Ace = item._acl.find(e => e._id === user._id);
298-
if (hasUser === null || hasUser === undefined) {
443+
if ((hasUser === null || hasUser === undefined) && item._acl.length == 0) {
299444
if (collectionname != "audit") { this._logger.debug("Adding self " + user.username + " to object " + (item.name || item._name)); }
300445
item.addRight(user._id, user.name, [Rights.full_control]);
301446
}
302447
if (item._id !== null && item._id !== undefined && item._id !== "") {
303-
item = await this.UpdateOne(item, collectionname, w, j, jwt);
448+
item = await this.UpdateOne(null, item, collectionname, w, j, jwt);
304449
} else {
305450
item = await this.InsertOne(item, collectionname, w, j, jwt);
306451
}
@@ -433,8 +578,11 @@ export class DatabaseConnection {
433578
};
434579
finalor.push(q2);
435580
}
581+
//
582+
if (bits.length == 1 && bits[0] == Rights.read) {
583+
return { $or: finalor.concat(isme) };
584+
}
436585
return { $or: finalor.concat() };
437-
// return { $or: finalor.concat(isme) };
438586
}
439587
/**
440588
* Ensure _type and _acs on object

OpenFlow/src/Logger.ts

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,40 @@ export class Logger {
1919
colorize: true
2020
},
2121
};
22+
const enumerateErrorFormat = winston.format(info => {
23+
if ((info.message as any) instanceof Error) {
24+
var e = (info.message as any) as Error;
25+
info.message = Object.assign({
26+
message: e.message,
27+
stack: e.stack
28+
}, info.message);
29+
}
30+
31+
if (info instanceof Error) {
32+
return Object.assign({
33+
message: info.message,
34+
stack: info.stack
35+
}, info);
36+
}
37+
38+
return info;
39+
});
2240
const logger: winston.Logger = winston.createLogger({
2341
level: "debug",
24-
format: winston.format.json(),
25-
defaultMeta: { service: "openflow" },
42+
//format: winston.format.json(),
43+
44+
format: winston.format.combine(
45+
enumerateErrorFormat(),
46+
winston.format.json()
47+
),
48+
defaultMeta: { service: "openflownodered" },
2649
transports: [
2750
// new winston.transports.File(options.file),
2851
new winston.transports.Console(options.console)
2952
]
3053
});
54+
Logger.instanse = logger;
3155
return logger;
3256
}
57+
static instanse: winston.Logger = null;
3358
}

0 commit comments

Comments
 (0)