Allow groups to access queries (#36)

Allow groups to access queries (#36)

  • [WIP] group ids saving on new reports

  • Add groups to default queries, and added tab connector

  • group_ids set to empty array for default queries

  • group reports route (in & and) action

  • [WIP] created group reports show route/controller

  • Find correct query in show route

  • Removed empty array for group_ids in query file

  • Add report show view, where users can run queries

  • Removed unneeded commas from queries.rb

  • Allow non-admin group members to access reports

  • query-result component dynamic download url based on location

  • Removed accidental changes, and corrected tab size

  • Group members can add params to queries

  • Specs for new QueryController actions

  • remove “Inlude query plan” from group reports

  • Run prettier

  • return and return -> return render

Co-Authored-By: Robin Ward robin.ward@gmail.com

  • [WIP] changes from review

  • Remove weird [-1] group_ids logic, for a simply check for [] in query update action

  • Added integration tests for group report access

  • Using guardian for securing endpoints, and much improved specs

  • Update assets/javascripts/discourse/components/group-reports-nav-item.js.es6

Co-Authored-By: Robin Ward robin.ward@gmail.com

diff --git a/assets/javascripts/discourse/components/group-reports-nav-item.js.es6 b/assets/javascripts/discourse/components/group-reports-nav-item.js.es6
new file mode 100644
index 0000000..6280436
--- /dev/null
+++ b/assets/javascripts/discourse/components/group-reports-nav-item.js.es6
@@ -0,0 +1,21 @@
+import { ajax } from "discourse/lib/ajax";
+
+export default Ember.Component.extend({
+  group: null,
+  showReportsTab: false,
+
+  checkForReports() {
+    return ajax(`/g/${this.group.name}/reports`).then(response => {
+      return this.set("showReportsTab", response.queries.length > 0);
+    });
+  },
+
+  init(args) {
+    this.set("group", args.group);
+    if (this.currentUser.groups.some(g => g.id === this.group.id)) {
+      // User is a part of the group. Now check if the group has reports
+      this.checkForReports();
+    }
+    this._super(args);
+  }
+});
diff --git a/assets/javascripts/discourse/components/query-result.js.es6 b/assets/javascripts/discourse/components/query-result.js.es6
index 87d4ff8..d359c00 100644
--- a/assets/javascripts/discourse/components/query-result.js.es6
+++ b/assets/javascripts/discourse/components/query-result.js.es6
@@ -146,6 +146,12 @@ const QueryResultComponent = Ember.Component.extend({
     return this.site.get("categoriesById")[id];
   },
 
+  download_url() {
+    return this.group
+      ? `/g/${this.group.name}/reports/`
+      : "/admin/plugins/explorer/queries/";
+  },
+
   downloadResult(format) {
     // Create a frame to submit the form in (?)
     // to avoid leaving an about:blank behind
@@ -161,7 +167,7 @@ const QueryResultComponent = Ember.Component.extend({
     form.setAttribute(
       "action",
       Discourse.getURL(
-        "/admin/plugins/explorer/queries/" +
+        this.download_url() +
           this.get("query.id") +
           "/run." +
           format +
diff --git a/assets/javascripts/discourse/controllers/admin-plugins-explorer.js.es6 b/assets/javascripts/discourse/controllers/admin-plugins-explorer.js.es6
index d6bc55f..73cb79b 100644
--- a/assets/javascripts/discourse/controllers/admin-plugins-explorer.js.es6
+++ b/assets/javascripts/discourse/controllers/admin-plugins-explorer.js.es6
@@ -54,6 +54,22 @@ export default Ember.Controller.extend({
     return item || NoQuery;
   },
 
+  @computed("selectedItem", "editing")
+  selectedGroupNames(selectedItem) {
+    const groupIds = this.selectedItem.group_ids || [];
+    const groupNames = groupIds.map(id => {
+      return this.groupOptions.find(groupOption => groupOption.id == id).name;
+    });
+    return groupNames.join(", ");
+  },
+
+  @computed("groups")
+  groupOptions(groups) {
+    return groups.arrangedContent.map(g => {
+      return { id: g.id.toString(), name: g.name };
+    });
+  },
+
   @computed("selectedItem", "selectedItem.dirty")
   othersDirty(selectedItem) {
     return !!this.model.find(q => q !== selectedItem && q.dirty);
@@ -81,6 +97,7 @@ export default Ember.Controller.extend({
     this.set("loading", true);
     if (this.get("selectedItem.description") === "")
       this.set("selectedItem.description", "");
+      
     return this.selectedItem
       .save()
       .then(() => {
@@ -183,6 +200,8 @@ export default Ember.Controller.extend({
         .then(result => {
           const query = this.get("selectedItem");
           query.setProperties(result.getProperties(Query.updatePropertyNames));
+          if (!query.group_ids || !Array.isArray(query.group_ids))
+            query.set("group_ids", []);
           query.markNotDirty();
           this.set("editing", false);
         })
diff --git a/assets/javascripts/discourse/controllers/group-reports-index.js.es6 b/assets/javascripts/discourse/controllers/group-reports-index.js.es6
new file mode 100644
index 0000000..b2573e3
--- /dev/null
+++ b/assets/javascripts/discourse/controllers/group-reports-index.js.es6
@@ -0,0 +1,10 @@
+import Query from "discourse/plugins/discourse-data-explorer/discourse/models/query";
+import { ajax } from "discourse/lib/ajax";
+import {
+  default as computed,
+  observes
+} from "ember-addons/ember-computed-decorators";
+
+export default Ember.Controller.extend({
+  queries: Ember.computed.alias("model.queries")
+});
diff --git a/assets/javascripts/discourse/controllers/group-reports-show.js.es6 b/assets/javascripts/discourse/controllers/group-reports-show.js.es6
new file mode 100644
index 0000000..02f7030
--- /dev/null
+++ b/assets/javascripts/discourse/controllers/group-reports-show.js.es6
@@ -0,0 +1,44 @@
+import Query from "discourse/plugins/discourse-data-explorer/discourse/models/query";
+import { popupAjaxError } from "discourse/lib/ajax-error";
+import { ajax } from "discourse/lib/ajax";
+import {
+  default as computed,
+  observes
+} from "ember-addons/ember-computed-decorators";
+
+export default Ember.Controller.extend({
+  showResults: false,
+  explain: false,
+  loading: false,
+  results: Ember.computed.alias("model.results"),
+  hasParams: Ember.computed.gt("model.param_info.length", 0),
+
+  actions: {
+    run() {
+      this.setProperties({ loading: true, showResults: false });
+      ajax(`/g/${this.get("group.name")}/reports/${this.model.id}/run`, {
+        type: "POST",
+        data: {
+          params: JSON.stringify(this.model.params),
+          explain: this.explain
+        }
+      })
+        .then(result => {
+          this.set("results", result);
+          if (!result.success) {
+            return;
+          }
+
+          this.set("showResults", true);
+        })
+        .catch(err => {
+          if (err.jqXHR && err.jqXHR.status === 422 && err.jqXHR.responseJSON) {
+            this.set("results", err.jqXHR.responseJSON);
+          } else {
+            popupAjaxError(err);
+          }
+        })
+        .finally(() => this.set("loading", false));
+    }
+  }
+});
diff --git a/assets/javascripts/discourse/group-reports-route-map.js.es6 b/assets/javascripts/discourse/group-reports-route-map.js.es6
new file mode 100644
index 0000000..f354996
--- /dev/null
+++ b/assets/javascripts/discourse/group-reports-route-map.js.es6
@@ -0,0 +1,9 @@
+export default {
+  resource: "group",
+
+  map() {
+    this.route("reports", function() {
+      this.route("show", { path: "/:query_id" });
+    });
+  }
+};
diff --git a/assets/javascripts/discourse/models/query.js.es6 b/assets/javascripts/discourse/models/query.js.es6
index 3575630..501db9a 100644
--- a/assets/javascripts/discourse/models/query.js.es6
+++ b/assets/javascripts/discourse/models/query.js.es6
@@ -23,7 +23,7 @@ const Query = RestModel.extend({
     this.resetParams();
   },
 
-  @observes("name", "description", "sql")
+  @observes("name", "description", "sql", "group_ids")
   markDirty() {
     this.set("dirty", true);
   },
@@ -85,6 +85,7 @@ Query.reopenClass({
     "sql",
     "created_by",
     "created_at",
+    "group_ids",
     "last_run_at"
   ]
 });
diff --git a/assets/javascripts/discourse/routes/admin-plugins-explorer.js.es6 b/assets/javascripts/discourse/routes/admin-plugins-explorer.js.es6
index 6b21191..f6db09c 100644
--- a/assets/javascripts/discourse/routes/admin-plugins-explorer.js.es6
+++ b/assets/javascripts/discourse/routes/admin-plugins-explorer.js.es6
@@ -4,19 +4,36 @@ export default Discourse.Route.extend({
   controllerName: "admin-plugins-explorer",
 
   model() {
-    const p1 = this.store.findAll("query");
-    const p2 = ajax("/admin/plugins/explorer/schema.json", { cache: true });
-    return p1
-      .then(model => {
-        model.forEach(query => query.markNotDirty());
+    const groupPromise = this.store.findAll("group");
+    const schemaPromise = ajax("/admin/plugins/explorer/schema.json", { cache: true });
+    const queryPromise = this.store.findAll("query");
 
-        return p2.then(schema => {
-          return { model, schema };
+    return groupPromise
+      .then(groups => {
+        let groupNames = {};
+        groups.forEach(g => {
+          groupNames[g.id] = g.name;
+        });

[... diff too long, it was truncated ...]

GitHub sha: 30fe9289