Page Menu
Home
Sealhub
Search
Configure Global Search
Log In
Files
F1262377
D214.id721.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
13 KB
Referenced Files
None
Subscribers
None
D214.id721.diff
View Options
diff --git a/lib/app/base-chips/access-strategy-types/access-strategy-types.test.js b/lib/app/base-chips/access-strategy-types/access-strategy-types.test.js
--- a/lib/app/base-chips/access-strategy-types/access-strategy-types.test.js
+++ b/lib/app/base-chips/access-strategy-types/access-strategy-types.test.js
@@ -4,4 +4,5 @@
require("./users-who-can.subtest.js");
require("./user-referenced-in-field.subtest.js");
require("./roles.subtest.js");
+ require("./when.subtest.js");
});
diff --git a/lib/app/base-chips/access-strategy-types/logged_in.js b/lib/app/base-chips/access-strategy-types/logged_in.js
--- a/lib/app/base-chips/access-strategy-types/logged_in.js
+++ b/lib/app/base-chips/access-strategy-types/logged_in.js
@@ -8,15 +8,13 @@
if (context.user_id) {
return new Query.AllowAll();
}
- return new Query.DenyQuery();
+ return new Query.DenyAll();
},
checker_function: function(context) {
if (context.user_id) {
return Promise.resolve();
} else {
- return Promise.reject(
- "Only logged-in users can perform this action."
- );
+ return Promise.reject("Only logged-in users can perform this action.");
}
},
};
diff --git a/lib/app/base-chips/access-strategy-types/or.js b/lib/app/base-chips/access-strategy-types/or.js
--- a/lib/app/base-chips/access-strategy-types/or.js
+++ b/lib/app/base-chips/access-strategy-types/or.js
@@ -20,14 +20,7 @@
if (queries.some(query => query instanceof Query.AllowAll)) {
return new Query.AllowAll();
}
- return Promise.reduce(
- queries,
- async (aggregated, query) => {
- aggregated.addQuery(query);
- return aggregated;
- },
- new Query.Or()
- );
+ return new Query.Or(...queries);
},
item_sensitive: function(params) {
const access_strategies = parse_params(app, params);
@@ -44,23 +37,19 @@
const results = access_strategies.map(function(strategy) {
return strategy.check(context, item);
});
- return Promise.any(results).catch(
- Promise.AggregateError,
- function(aggregated_errors) {
- aggregated_errors.forEach(function(error) {
- if (!(error instanceof Error)) {
- throw error;
- }
- });
- const error_message = aggregated_errors
- .map(
- aggregated_errors =>
- aggregated_errors.message
- )
- .reduce((a, b) => `${a} ${b}`);
- return Promise.reject(error_message);
- }
- );
+ return Promise.any(results).catch(Promise.AggregateError, function(
+ aggregated_errors
+ ) {
+ aggregated_errors.forEach(function(error) {
+ if (!(error instanceof Error)) {
+ throw error;
+ }
+ });
+ const error_message = aggregated_errors
+ .map(aggregated_errors => aggregated_errors.message)
+ .reduce((a, b) => `${a} ${b}`);
+ return Promise.reject(error_message);
+ });
}
});
},
diff --git a/lib/app/base-chips/access-strategy-types/when.js b/lib/app/base-chips/access-strategy-types/when.js
new file mode 100644
--- /dev/null
+++ b/lib/app/base-chips/access-strategy-types/when.js
@@ -0,0 +1,73 @@
+"use strict";
+const Promise = require("bluebird");
+const Query = require("../../../datastore/query.js");
+
+async function construct_query(
+ app,
+ context,
+ collection_name,
+ special_filter_name,
+ when_true_name,
+ when_false_name
+) {
+ const collection = app.ChipManager.get_chip("collection", collection_name);
+ const special_filter = collection.get_named_filter(special_filter_name);
+ const when_true = new app.Sealious.AccessStrategy(app, when_true_name);
+ const when_false = new app.Sealious.AccessStrategy(app, when_false_name);
+ return new Query.Or(
+ new Query.And(
+ await special_filter.getRestrictingQuery(collection),
+ await when_true.getRestrictingQuery(context)
+ ),
+ new Query.And(
+ new Query.Not(await special_filter.getRestrictingQuery(collection)),
+ await when_false.getRestrictingQuery(context)
+ )
+ );
+}
+
+module.exports = app => ({
+ name: "when",
+ getRestrictingQuery: async function(
+ context,
+ [collection_name, special_filter_name, when_true_name, when_false_name]
+ ) {
+ return construct_query(
+ app,
+ context,
+ collection_name,
+ special_filter_name,
+ when_true_name,
+ when_false_name
+ );
+ },
+ checker_function: async function(
+ context,
+ [collection_name, special_filter_name, when_true_name, when_false_name],
+ item
+ ) {
+ try {
+ const results = await app.Datastore.aggregate(
+ item.collection_name,
+ (await construct_query(
+ app,
+ context,
+ collection_name,
+ special_filter_name,
+ when_true_name,
+ when_false_name
+ ))
+ .match({ sealious_id: item.id })
+ .toPipeline()
+ );
+ if (results.length) {
+ return Promise.resolve();
+ } else {
+ return Promise.reject("No access");
+ }
+ } catch (e) {
+ console.error(e);
+ }
+ },
+ item_sensitive: true,
+});
diff --git a/lib/app/base-chips/access-strategy-types/when.subtest.js b/lib/app/base-chips/access-strategy-types/when.subtest.js
new file mode 100644
--- /dev/null
+++ b/lib/app/base-chips/access-strategy-types/when.subtest.js
@@ -0,0 +1,68 @@
+const locreq = require("locreq")(__dirname);
+const assert = require("assert");
+const { with_stopped_app } = locreq("test_utils/with-test-app.js");
+const assert_throws_async = locreq("test_utils/assert_throws_async.js");
+const axios = require("axios");
+const matches = require("./../special_filters/matches.js");
+
+describe("when", () => {
+ async function create_resources(app) {
+ app.createChip(app.Sealious.Collection, {
+ name: "numbers",
+ fields: [{ name: "number", type: "int" }],
+ named_filters: {
+ positive: matches({ number: { ">": 0 } }),
+ negative: matches({ number: { "<": 0 } }),
+ },
+ access_strategy: {
+ default: ["when", ["numbers", "negative", "logged_in", "public"]],
+ },
+ });
+
+ await app.start();
+
+ for (let number of [-1, 0, 1]) {
+ await app.run_action(
+ new app.Sealious.SuperContext(),
+ ["collections", "numbers"],
+ "create",
+ { number }
+ );
+ }
+
+ await app.run_action(
+ new app.Sealious.SuperContext(),
+ ["collections", "users"],
+ "create",
+ { username: "user", password: "password", email: "user@example.com" }
+ );
+ }
+
+ it("should only use 'when_true' access strategy when the item passes the filter", async () =>
+ with_stopped_app(async ({ app, base_url, rest_api }) => {
+ await create_resources(app);
+ const session = await rest_api.login({
+ username: "user",
+ password: "password",
+ });
+
+ const resources_when_logged_in = await rest_api.get(
+ "/api/v1/collections/numbers?sort[body.number]=asc",
+ session
+ );
+
+ assert.equal(resources_when_logged_in.length, 3);
+ assert.equal(resources_when_logged_in[0].body.number, -1);
+ }));
+
+ it("should only use 'when_false' access strategy when the item doesn't pass the filter", async () =>
+ with_stopped_app(async ({ app, base_url, rest_api }) => {
+ await create_resources(app);
+
+ const public_resources = await rest_api.get(
+ "/api/v1/collections/numbers?sort[body.number]=asc"
+ );
+
+ assert.equal(public_resources.length, 2);
+ }));
+});
diff --git a/lib/app/base-chips/field-types/single_reference.js b/lib/app/base-chips/field-types/single_reference.js
--- a/lib/app/base-chips/field-types/single_reference.js
+++ b/lib/app/base-chips/field-types/single_reference.js
@@ -22,7 +22,7 @@
}
return collection
- .get_aggregation_stages(context, "show", { filter })
+ .get_aggregation_stages(context, "retrieve", { filter })
.then(stages => [{ $match: { sealious_id: resource_id } }, ...stages])
.then(stages => app.Datastore.aggregate(collection.name, stages))
.then(
diff --git a/lib/app/base-chips/special_filters/IsReferencedByResourcesMatching.js b/lib/app/base-chips/special_filters/IsReferencedByResourcesMatching.js
--- a/lib/app/base-chips/special_filters/IsReferencedByResourcesMatching.js
+++ b/lib/app/base-chips/special_filters/IsReferencedByResourcesMatching.js
@@ -3,6 +3,7 @@
const assert = require("assert");
const Collection = locreq("lib/chip-types/collection");
+const Query = locreq("lib/datastore/query.js");
module.exports = SpecialFilter.WithParams(
class IsReferencedByResourcesMatching extends SpecialFilter {
@@ -23,24 +24,19 @@
assert(Array.isArray(allowed_values));
assert(typeof nopass_reason === "string");
}
- getAggregationStages() {
- return [
- {
- $lookup: {
- from: this.params.collection.name,
- localField: "sealious_id",
- foreignField: `body.${this.params.referencing_field}`,
- as: "resource",
- },
+ async getRestrictingQuery() {
+ const query = new Query();
+ const lookup_id = query.lookup({
+ from: this.params.collection.name,
+ localField: "sealious_id",
+ foreignField: `body.${this.params.referencing_field}`,
+ });
+ query.match({
+ [`${lookup_id}.body.${this.params.field_to_check}`]: {
+ $in: this.params.allowed_values,
},
- {
- $match: {
- [`resource.body.${this.params.field_to_check}`]: {
- $in: this.params.allowed_values,
- },
- },
- },
- ];
+ });
+ return query;
}
getNopassReason() {
diff --git a/lib/app/base-chips/special_filters/matches.js b/lib/app/base-chips/special_filters/matches.js
--- a/lib/app/base-chips/special_filters/matches.js
+++ b/lib/app/base-chips/special_filters/matches.js
@@ -4,6 +4,7 @@
const SpecialFilter = locreq("lib/chip-types/special-filter.js");
const Collection = locreq("lib/chip-types/collection");
const SuperContext = locreq("lib/super-context.js");
+const Query = locreq("lib/datastore/query.js");
module.exports = SpecialFilter.WithParams(
class Matches extends SpecialFilter {
@@ -13,19 +14,17 @@
this.filter = params;
}
- async getAggregationStages(collection) {
- return Promise.all(
- Object.keys(this.filter).map(field_name =>
- collection.fields[field_name].get_aggregation_stages(
- new SuperContext(),
- {
- filter: this.filter,
- }
- )
- )
- ).then(stages =>
- stages.reduce((acc, curr) => acc.concat(curr), [])
- );
+ async getRestrictingQuery(collection) {
+ let stages = [];
+ for (let field_name in this.filter) {
+ const field_stages = await collection.fields[
+ field_name
+ ].get_aggregation_stages(new SuperContext(), {
+ filter: this.filter,
+ });
+ stages = stages.concat(field_stages);
+ }
+ return Query.fromStages(stages);
}
getNopassReason() {
diff --git a/lib/app/load-base-chips.js b/lib/app/load-base-chips.js
--- a/lib/app/load-base-chips.js
+++ b/lib/app/load-base-chips.js
@@ -23,6 +23,7 @@
"users-who-can",
"user-referenced-in-field",
"roles",
+ "when",
]);
BaseChips.set(FieldType, [
diff --git a/lib/chip-types/collection.js b/lib/chip-types/collection.js
--- a/lib/chip-types/collection.js
+++ b/lib/chip-types/collection.js
@@ -510,12 +510,10 @@
return Object.keys(a[0] || {})[0] === "$match" ? -1 : 1;
})
),
- Promise.all(
- named_filters.map(named_filter =>
- collection.named_filters[named_filter].getAggregationStages(
- collection
- )
- )
+ Promise.map(named_filters, async named_filter =>
+ (await collection.named_filters[named_filter].getRestrictingQuery(
+ collection
+ )).toPipeline()
),
])
.map(Promise.all)
@@ -552,6 +550,9 @@
add_special_filters: function(named_filters = []) {
return pure.add_special_filters(this, named_filters);
},
+ get_named_filter: function(filter_name) {
+ return this.named_filters[filter_name];
+ },
validate_field_values(
context,
assume_delete_value_on_missing_key,
diff --git a/lib/chip-types/special-filter.js b/lib/chip-types/special-filter.js
--- a/lib/chip-types/special-filter.js
+++ b/lib/chip-types/special-filter.js
@@ -13,7 +13,7 @@
const documents = await app.Datastore.aggregate(collection.name, [
{ $match: { sealious_id: resource_id } },
- ...this.getAggregationStages(),
+ ...this.getRestrictingQuery(),
]);
return documents.length
diff --git a/lib/datastore/query.js b/lib/datastore/query.js
--- a/lib/datastore/query.js
+++ b/lib/datastore/query.js
@@ -88,35 +88,68 @@
}
addQuery(query) {
let stages = query.dump();
+ const combined_match = {};
for (let stage of stages) {
if (stage.$lookup) {
this._lookup(stage);
} else if (stage.$match) {
- this._match(stage);
+ Object.assign(combined_match, stage.$match);
} else {
throw new Error("Unsupported query: " + Object.keys(stage));
}
}
+ if (Object.keys(combined_match).length) this.matches.push(combined_match);
}
_lookup(stage) {
const id = stage.$lookup.as;
this.lookups[id] = stage;
}
- _match(stage) {
- this.matches.push(stage);
- }
dump() {
return Object.values(this.lookups).concat({
- $match: { $or: this.matches.map(stage => stage.$match) },
+ $match: { $or: this.matches },
});
}
toPipeline() {
return Object.values(this.lookups)
.reduce((acc, stage) => this._pushToPipeline(acc, stage), [])
.concat({
- $match: { $or: this.matches.map(stage => stage.$match) },
+ $match: { $or: this.matches },
});
}
+ match(body) {
+ return Query.fromStages([{ $match: body }, ...this.toPipeline()]);
+ }
+};
+
+Query.And = class extends Query {
+ constructor(...queries) {
+ super();
+ this.stages = queries
+ .map(query => query.stages)
+ .reduce((acc, stages) => acc.concat(stages), []);
+ }
+};
+
+Query.Not = class extends Query {
+ constructor(query) {
+ super();
+ query.toPipeline().map(stage => {
+ if (!stage.$match) {
+ return stage;
+ }
+ for (let field_name in stage.$match) {
+ this.match({ [field_name]: { $not: stage.$match[field_name] } });
+ }
+ });
+ }
+};
+
+Query.fromStages = function(stages) {
+ const query = new Query();
+ for (let stage of stages) {
+ query.pipeline = query._pushToPipeline(query.stages, stage);
+ }
+ return query;
};
module.exports = Query;
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Wed, Jan 22, 20:23 (2 h, 12 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
601517
Default Alt Text
D214.id721.diff (13 KB)
Attached To
Mode
D214: 'when' access strategy - Ref T805
Attached
Detach File
Event Timeline
Log In to Comment