diff --git a/lib/datastore/mongo-api-abstract.js b/lib/datastore/mongo-api-abstract.js --- a/lib/datastore/mongo-api-abstract.js +++ b/lib/datastore/mongo-api-abstract.js @@ -11,6 +11,7 @@ var DatabasesCommonPart = function(app, datastore, _private) { datastore.post_start = function() { + datastore.client = _private.db; const collection_names = app.ChipManager.get_all_collections(); const collections = collection_names.map(name => app.ChipManager.get_chip("collection", name) @@ -54,18 +55,13 @@ let text_indexes = [ all_indexes .filter(e => e[1] === "text") - .reduce( - (a, b) => merge(true, a, { [b[0]]: b[1] }), - {} - ), + .reduce((a, b) => merge(true, a, { [b[0]]: b[1] }), {}), ]; if (Object.keys(text_indexes[0]).length == 0) { text_indexes = []; } - const merged_indexes = text_indexes.concat( - non_text_indexes - ); + const merged_indexes = text_indexes.concat(non_text_indexes); return merged_indexes; }) @@ -100,8 +96,7 @@ new_query[attribute_name] = query[attribute_name]; } else { for (var i in query[attribute_name]) { - new_query[attribute_name + "." + i] = - query[attribute_name][i]; + new_query[attribute_name + "." + i] = query[attribute_name][i]; } } } else { @@ -117,9 +112,7 @@ //query = process_query(query); // - needed, ResourceCollection subject handles that now options = options || {}; output_options = output_options || {}; - var cursor = _private.db - .collection(collection_name) - .find(query, options); + var cursor = _private.db.collection(collection_name).find(query, options); if (output_options.sort) { cursor.sort(output_options.sort); } @@ -141,9 +134,7 @@ //console.log("aggregate", collection_name, JSON.stringify(pipeline)); options = options || {}; output_options = output_options || {}; - const cursor = _private.db - .collection(collection_name) - .aggregate(pipeline); + const cursor = _private.db.collection(collection_name).aggregate(pipeline); if (output_options.sort) { cursor.sort(output_options.sort); @@ -183,7 +174,6 @@ datastore.remove = function(collection_name, query, just_one) { query = process_query(query); - //console.log("Removing!", query); return new Promise(function(resolve, reject) { if (just_one === undefined) { just_one = 0; diff --git a/lib/subject/subject-types/single-resource-subject.js b/lib/subject/subject-types/single-resource-subject.js --- a/lib/subject/subject-types/single-resource-subject.js +++ b/lib/subject/subject-types/single-resource-subject.js @@ -6,115 +6,206 @@ const CollectionFieldSubject = require("./collection-field-subject.js"); -const SingleResource = function(app, collection, resource_id){ +const SingleResource = function(app, collection, resource_id) { this.collection = collection; this.resource_id = resource_id; this.name = "SingleResource"; - this.get_resource = function(context, args){ + this.get_resource = function(context, args) { const datastore = app.ChipManager.get_datastore_chip(); - return SingleResource.prototype.__get_resource(datastore, this.collection, this.resource_id, context, args); + return SingleResource.prototype.__get_resource( + datastore, + this.collection, + this.resource_id, + context, + args + ); }; - this.delete_resource = function(context, args){ + this.delete_resource = function(context, args) { const datastore = app.ChipManager.get_datastore_chip(); const self = this; - return SingleResource.prototype.__delete_resource(datastore, self.collection, self.resource_id, context, args); + return SingleResource.prototype.__delete_resource( + datastore, + self.collection, + self.resource_id, + context, + args + ); }; - this.edit_resource = function(context, values_to_patch, delete_empty_values){ + this.edit_resource = function(context, values_to_patch, delete_empty_values) { const datastore = app.ChipManager.get_datastore_chip(); const self = this; - return SingleResource.prototype.__edit_resource(datastore, self.collection, self.resource_id, context, values_to_patch, delete_empty_values); + return SingleResource.prototype.__edit_resource( + datastore, + self.collection, + self.resource_id, + context, + values_to_patch, + delete_empty_values + ); }; - }; SingleResource.prototype = Object.create(Subject.prototype); -SingleResource.prototype.__get_resource = function(datastore, collection, resource_id, context, args){ +SingleResource.prototype.__get_resource = function( + datastore, + collection, + resource_id, + context, + args +) { args = args || {}; - - return datastore.find(collection.name, {sealious_id: resource_id}, {}) - .then(function(db_entries){ - if (db_entries[0] === undefined){ - throw new Errors.NotFound(`${collection.name}: id ${ resource_id } not found`); - } else { - return collection.get_resource_representation(context, db_entries[0], args.format); - } - }).then(function(resource_representation){ - return collection.check_if_action_is_allowed(context, "retrieve", resource_representation) - .then(function(){ - return resource_representation; + return datastore + .find(collection.name, { sealious_id: resource_id }, {}) + .then(function(db_entries) { + if (db_entries[0] === undefined) { + throw new Errors.NotFound( + `${collection.name}: id ${resource_id} not found` + ); + } else { + return collection.get_resource_representation( + context, + db_entries[0], + args.format + ); + } + }) + .then(function(resource_representation) { + return collection + .check_if_action_is_allowed( + context, + "retrieve", + resource_representation + ) + .then(function() { + return resource_representation; + }); }); - }); }; -SingleResource.prototype.__edit_resource = function(datastore, collection, resource_id, context, values_to_patch, delete_empty_values){ +SingleResource.prototype.__edit_resource = function( + datastore, + collection, + resource_id, + context, + values_to_patch, + delete_empty_values +) { // replaces just the provided values. Equivalent of PATCH request - delete_empty_values = (delete_empty_values === undefined? false : delete_empty_values); + delete_empty_values = + delete_empty_values === undefined ? false : delete_empty_values; let resource_representation; - return SingleResource.prototype.__get_resource(datastore, collection, resource_id, context, {}) - .then(function(resource_data){ - resource_representation = resource_data; - return collection.check_if_action_is_allowed(context, "update", resource_representation); - }).then(function(){ - return collection.validate_field_values(context, delete_empty_values, values_to_patch, resource_representation.body); - }).then(function(){ - return collection.encode_field_values(context, values_to_patch, resource_representation.body); - }).then(function(encoded_values){ - const query = {}; - query.last_modified_context = context; - for (const field_name in encoded_values){ - query[`body.${ field_name}`] = encoded_values[field_name]; - } - return datastore.update(collection.name, {sealious_id: resource_id}, {$set: query}); - }).then(function(patch_result){ - if(patch_result.result.nModified !== 1){ - throw new Error("Wrong amount of resources (!=1) modified"); - } - return SingleResource.prototype.__get_resource(datastore, collection, resource_id, context, resource_id); - }); + return SingleResource.prototype + .__get_resource(datastore, collection, resource_id, context, {}) + .then(function(resource_data) { + resource_representation = resource_data; + return collection.check_if_action_is_allowed( + context, + "edit", + resource_representation + ); + }) + .then(function() { + return collection.validate_field_values( + context, + delete_empty_values, + values_to_patch, + resource_representation.body + ); + }) + .then(function() { + return collection.encode_field_values( + context, + values_to_patch, + resource_representation.body + ); + }) + .then(function(encoded_values) { + const query = {}; + query.last_modified_context = context; + for (const field_name in encoded_values) { + query[`body.${field_name}`] = encoded_values[field_name]; + } + return datastore.update( + collection.name, + { sealious_id: resource_id }, + { $set: query } + ); + }) + .then(function(patch_result) { + if (patch_result.result.nModified !== 1) { + throw new Error("Wrong amount of resources (!=1) modified"); + } + return SingleResource.prototype.__get_resource( + datastore, + collection, + resource_id, + context, + resource_id + ); + }); }; -SingleResource.prototype.__delete_resource = function(datastore, collection, resource_id, context, args){ - +SingleResource.prototype.__delete_resource = function( + datastore, + collection, + resource_id, + context, + args +) { // abstraction seems to be leaking here: should we use context or SuperContext here? - return SingleResource.prototype.__get_resource(datastore, collection, resource_id, context, {}) - .then(function(resource_representation){ - return collection.check_if_action_is_allowed(context, "delete", resource_representation); - }).then(function(){ - return datastore.remove(collection.name, { - sealious_id: resource_id, - }, {}); - }) - .then(function(data){ - return Promise.resolve(); - }); + return SingleResource.prototype + .__get_resource(datastore, collection, resource_id, context, {}) + .then(function(resource_representation) { + return collection.check_if_action_is_allowed( + context, + "delete", + resource_representation + ); + }) + .then(function() { + return datastore.remove( + collection.name, + { + sealious_id: resource_id, + }, + {} + ); + }) + .then(function(data) { + return Promise.resolve(); + }); }; -SingleResource.prototype.perform_action = function(context, action_name, args){ - switch (action_name){ - case "show": - return this.get_resource(context, args); - case "edit": - return this.edit_resource(context, args, false); - case "replace": - return this.edit_resource(context, args, true); - case "delete": - return this.delete_resource(context, args); - default: - throw new Errors.DeveloperError(`Unknown action for '${this.collection.name}' resource: '${action_name}'`); +SingleResource.prototype.perform_action = function(context, action_name, args) { + switch (action_name) { + case "show": + return this.get_resource(context, args); + case "edit": + return this.edit_resource(context, args, false); + case "replace": + return this.edit_resource(context, args, true); + case "delete": + return this.delete_resource(context, args); + default: + throw new Errors.DeveloperError( + `Unknown action for '${ + this.collection.name + }' resource: '${action_name}'` + ); } }; -SingleResource.prototype.get_child_subject = function(key){ - if (this.collection.fields[key].type.is_subject){ +SingleResource.prototype.get_child_subject = function(key) { + if (this.collection.fields[key].type.is_subject) { return new CollectionFieldSubject(this.collection, this.resource_id, key); } }; diff --git a/package.json b/package.json --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "nodemailer": "^4.6.3", "object-hash": "^1.3.0", "pattern-emitter-promise": "^1.0.3", + "qs": "^6.5.1", "request": "^2.79.0", "request-promise": "^4.0.2", "resolve": "^1.1.7", diff --git a/react-components/lib/cached-http.js b/react-components/lib/cached-http.js --- a/react-components/lib/cached-http.js +++ b/react-components/lib/cached-http.js @@ -3,6 +3,7 @@ const url = require("url"); const merge = require("merge"); const Promise = require("bluebird"); +const qs = require("qs"); Promise.config({ cancellation: true }); function CachedError() {} @@ -37,13 +38,10 @@ const promise = new Promise((resolve, reject, onCancel) => { const source = CancelToken.source(); const qp = axios - .get( - pathname, - Object.assign({}, merged_query, { - cancelToken: source.token, - options, - }) - ) + .get(`${pathname}?${qs.stringify(merged_query)}`, { + cancelToken: source.token, + options, + }) .then(response => { const data = response.data; cache[hash] = response.data; @@ -51,9 +49,7 @@ resolve(data); }) .catch(error => { - const cached_err = Object.create( - CachedError.prototype - ); + const cached_err = Object.create(CachedError.prototype); Object.assign(cached_err, error); delete pending[hash]; cache[hash] = cached_err; diff --git a/react-components/lib/collection.jsx b/react-components/lib/collection.jsx --- a/react-components/lib/collection.jsx +++ b/react-components/lib/collection.jsx @@ -3,7 +3,15 @@ const KeyValueStore = require("./stores/key-value-store.js"); const ConnectWithKeyValueStore = require("./stores/connect-with-key-value-store.jsx"); -function Collection({ collection, query_store }, component) { +function Collection( + { + collection, + query_store, + get_forced_filter = () => {}, + get_forced_format = () => {}, + }, + component +) { return class Component extends React.Component { constructor() { super(); @@ -14,10 +22,14 @@ } componentDidMount() { this.setState({ loading: true }); - CachedHttp.get( - `/api/v1/collections/${collection}`, - query_store.getQuery() - ).then(resources => { + CachedHttp.get(`/api/v1/collections/${collection}`, { + filter: Object.assign( + {}, + query_store.getQuery().filter, + get_forced_filter(this.props) + ), + format: get_forced_format(this.props), + }).then(resources => { this.setState({ resources, loading: false }); }); } @@ -27,6 +39,7 @@ query_store, resources: this.state.resources, loading: this.state.loading, + metadata: this.props.metadata, }); } };