Skip to content

Commit 318abef

Browse files
committed
bulk data load methods for space and document views
1 parent bc72db7 commit 318abef

File tree

9 files changed

+313
-27
lines changed

9 files changed

+313
-27
lines changed

README.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
# Documize Community Edition
22

3+
## The mission
4+
5+
To bring software development inspired features to the world of documenting -- refactoring, importing, testing, linting, metrics, PRs, versioning....
6+
37
## What is it?
48

5-
Documize is an intelligent document environment (IDE) for creating, securing and sharing documents -- everything yoyu need in one place.
9+
Documize is an intelligent document environment (IDE) for creating, securing and sharing documents -- everything you need in one place.
610

711
## Why should I care?
812

9-
Because maybe like us you are tired of:
13+
Because maybe like us, you might be tired of:
1014

1115
* juggling WYSIWYG editors, wiki software and various document related solutions
12-
* playing email tennis with document versions, contributions and feedback
16+
* playing document related email tennis with contributions, versions and feedback
1317
* sharing not-so-secure folders with external participants
1418

1519
Sound familiar? Read on.
@@ -18,10 +22,10 @@ Sound familiar? Read on.
1822

1923
Anyone who wants a single place for any kind of document.
2024

21-
Anyone who wants to loop in external participants without leaking information.
25+
Anyone who wants to loop in external participants complete security.
2226

2327
Anyone who wishes documentation and knowledge capture worked like agile software development.
24-
28+
2529
## What's different about Documize?
2630

2731
Sane organization through personal, team and public spaces.
@@ -83,10 +87,6 @@ Documize is compatible with Auth0 identity as a service.
8387

8488
Open Source Identity and Access Management
8589

86-
## The mission
87-
88-
To bring software development inspired features to the world of documenting -- refactoring, testing, linting, metrics, PRs, versioning....
89-
9090
## The legal bit at the end
9191

9292
<https://documize.com>

domain/category/endpoint.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ func (h *Handler) Get(w http.ResponseWriter, r *http.Request) {
112112

113113
cat, err := h.Store.Category.GetBySpace(ctx, spaceID)
114114
if err != nil && err != sql.ErrNoRows {
115+
h.Runtime.Log.Error("get space categories visible to user failed", err)
115116
response.WriteServerError(w, method, err)
116117
return
117118
}
@@ -460,3 +461,66 @@ func (h *Handler) GetSpaceCategoryMembers(w http.ResponseWriter, r *http.Request
460461

461462
response.WriteJSON(w, cat)
462463
}
464+
465+
// FetchSpaceData returns:
466+
// 1. categories that user can see for given space
467+
// 2. summary data for each category
468+
// 3. category viewing membership records
469+
func (h *Handler) FetchSpaceData(w http.ResponseWriter, r *http.Request) {
470+
method := "category.FetchSpaceData"
471+
ctx := domain.GetRequestContext(r)
472+
473+
spaceID := request.Param(r, "spaceID")
474+
if len(spaceID) == 0 {
475+
response.WriteMissingDataError(w, method, "spaceID")
476+
return
477+
}
478+
479+
ok := permission.HasPermission(ctx, *h.Store, spaceID, pm.SpaceManage, pm.SpaceOwner, pm.SpaceView)
480+
if !ok {
481+
response.WriteForbiddenError(w)
482+
return
483+
}
484+
485+
fetch := category.FetchSpaceModel{}
486+
487+
// get space categories visible to user
488+
cat, err := h.Store.Category.GetBySpace(ctx, spaceID)
489+
if err != nil && err != sql.ErrNoRows {
490+
h.Runtime.Log.Error("get space categories visible to user failed", err)
491+
response.WriteServerError(w, method, err)
492+
return
493+
}
494+
if len(cat) == 0 {
495+
cat = []category.Category{}
496+
}
497+
498+
// summary of space category usage
499+
summary, err := h.Store.Category.GetSpaceCategorySummary(ctx, spaceID)
500+
if err != nil {
501+
h.Runtime.Log.Error("get space category summary failed", err)
502+
response.WriteServerError(w, method, err)
503+
return
504+
}
505+
if len(summary) == 0 {
506+
summary = []category.SummaryModel{}
507+
}
508+
509+
// get category membership records
510+
member, err := h.Store.Category.GetSpaceCategoryMembership(ctx, spaceID)
511+
if err != nil && err != sql.ErrNoRows {
512+
h.Runtime.Log.Error("get document category membership for space", err)
513+
response.WriteServerError(w, method, err)
514+
return
515+
}
516+
517+
if len(member) == 0 {
518+
member = []category.Member{}
519+
}
520+
521+
fetch.Category = cat
522+
fetch.Summary = summary
523+
fetch.Membership = member
524+
525+
response.WriteJSON(w, fetch)
526+
}

domain/document/endpoint.go

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ import (
2929
"github.com/documize/community/model/audit"
3030
"github.com/documize/community/model/doc"
3131
"github.com/documize/community/model/link"
32+
pm "github.com/documize/community/model/permission"
3233
"github.com/documize/community/model/search"
34+
"github.com/documize/community/model/space"
3335
)
3436

3537
// Handler contains the runtime information such as logging and database.
@@ -371,3 +373,104 @@ func (h *Handler) SearchDocuments(w http.ResponseWriter, r *http.Request) {
371373

372374
response.WriteJSON(w, results)
373375
}
376+
377+
// FetchDocumentData returns all document data in single API call.
378+
func (h *Handler) FetchDocumentData(w http.ResponseWriter, r *http.Request) {
379+
method := "document.FetchDocumentData"
380+
ctx := domain.GetRequestContext(r)
381+
382+
id := request.Param(r, "documentID")
383+
if len(id) == 0 {
384+
response.WriteMissingDataError(w, method, "documentID")
385+
return
386+
}
387+
388+
// document
389+
document, err := h.Store.Document.Get(ctx, id)
390+
if err == sql.ErrNoRows {
391+
response.WriteNotFoundError(w, method, id)
392+
return
393+
}
394+
if err != nil {
395+
response.WriteServerError(w, method, err)
396+
h.Runtime.Log.Error(method, err)
397+
return
398+
}
399+
400+
if !permission.CanViewSpaceDocument(ctx, *h.Store, document.LabelID) {
401+
response.WriteForbiddenError(w)
402+
return
403+
}
404+
405+
// permissions
406+
perms, err := h.Store.Permission.GetUserSpacePermissions(ctx, document.LabelID)
407+
if err != nil && err != sql.ErrNoRows {
408+
response.WriteServerError(w, method, err)
409+
return
410+
}
411+
if len(perms) == 0 {
412+
perms = []pm.Permission{}
413+
}
414+
415+
record := pm.DecodeUserPermissions(perms)
416+
417+
// links
418+
l, err := h.Store.Link.GetDocumentOutboundLinks(ctx, id)
419+
if len(l) == 0 {
420+
l = []link.Link{}
421+
}
422+
if err != nil && err != sql.ErrNoRows {
423+
response.WriteServerError(w, method, err)
424+
h.Runtime.Log.Error(method, err)
425+
return
426+
}
427+
428+
// spaces
429+
sp, err := h.Store.Space.GetViewable(ctx)
430+
if err != nil && err != sql.ErrNoRows {
431+
response.WriteServerError(w, method, err)
432+
h.Runtime.Log.Error(method, err)
433+
return
434+
}
435+
if len(sp) == 0 {
436+
sp = []space.Space{}
437+
}
438+
439+
data := documentData{}
440+
data.Document = document
441+
data.Permissions = record
442+
data.Links = l
443+
data.Spaces = sp
444+
445+
ctx.Transaction, err = h.Runtime.Db.Beginx()
446+
if err != nil {
447+
response.WriteServerError(w, method, err)
448+
h.Runtime.Log.Error(method, err)
449+
return
450+
}
451+
452+
err = h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
453+
LabelID: document.LabelID,
454+
SourceID: document.RefID,
455+
SourceType: activity.SourceTypeDocument,
456+
ActivityType: activity.TypeRead})
457+
458+
if err != nil {
459+
h.Runtime.Log.Error(method, err)
460+
}
461+
462+
h.Store.Audit.Record(ctx, audit.EventTypeDocumentView)
463+
464+
ctx.Transaction.Commit()
465+
466+
response.WriteJSON(w, data)
467+
}
468+
469+
// documentData represents all data associated for a single document.
470+
// Used by FetchDocumentData() bulk data load call.
471+
type documentData struct {
472+
Document doc.Document `json:"document"`
473+
Permissions pm.Record `json:"permissions"`
474+
Spaces []space.Space `json:"folders"`
475+
Links []link.Link `json:"link"`
476+
}

gui/app/pods/document/route.js

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,22 +24,31 @@ export default Ember.Route.extend(AuthenticatedRouteMixin, {
2424
this.set('documentId', this.paramsFor('document').document_id);
2525

2626
return new Ember.RSVP.Promise((resolve) => {
27-
this.get('documentService').getDocument(this.get('documentId')).then((document) => {
28-
this.set('document', document);
27+
this.get('documentService').fetchDocumentData(this.get('documentId')).then((data) => {
28+
this.set('document', data.document);
29+
this.set('folders', data.folders);
30+
this.set('folder', data.folder);
31+
this.set('permissions', data.permissions);
32+
this.set('links', data.links);
33+
resolve();
34+
});
2935

30-
this.get('folderService').getAll().then((folders) => {
31-
this.set('folders', folders);
36+
// this.get('documentService').getDocument(this.get('documentId')).then((document) => {
37+
// this.set('document', document);
3238

33-
this.get('folderService').getFolder(this.get('folderId')).then((folder) => {
34-
this.set('folder', folder);
39+
// this.get('folderService').getAll().then((folders) => {
40+
// this.set('folders', folders);
3541

36-
this.get('folderService').setCurrentFolder(folder).then(() => {
37-
this.set('permissions', this.get('folderService').get('permissions'));
38-
resolve();
39-
});
40-
});
41-
});
42-
});
42+
// this.get('folderService').getFolder(this.get('folderId')).then((folder) => {
43+
// this.set('folder', folder);
44+
45+
// this.get('folderService').setCurrentFolder(folder).then(() => {
46+
// this.set('permissions', this.get('folderService').get('permissions'));
47+
// resolve();
48+
// });
49+
// });
50+
// });
51+
// });
4352
});
4453
},
4554

@@ -50,7 +59,7 @@ export default Ember.Route.extend(AuthenticatedRouteMixin, {
5059
document: this.get('document'),
5160
page: this.get('pageId'),
5261
permissions: this.get('permissions'),
53-
links: this.get('linkService').getDocumentLinks(this.get('documentId')),
62+
links: this.get('links'),
5463
sections: this.get('sectionService').getAll()
5564
});
5665
},

gui/app/pods/folder/index/route.js

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,18 @@ import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-rout
1515
export default Ember.Route.extend(AuthenticatedRouteMixin, {
1616
categoryService: Ember.inject.service('category'),
1717

18+
beforeModel() {
19+
return new Ember.RSVP.Promise((resolve) => {
20+
this.get('categoryService').fetchSpaceData(this.modelFor('folder').folder.get('id')).then((data) => {
21+
this.set('categories', data.category);
22+
this.set('categorySummary', data.summary);
23+
this.set('categoryMembers', data.membership);
24+
25+
resolve(data);
26+
});
27+
});
28+
},
29+
1830
model() {
1931
this.get('browser').setTitle(this.modelFor('folder').folder.get('name'));
2032

@@ -25,14 +37,25 @@ export default Ember.Route.extend(AuthenticatedRouteMixin, {
2537
documents: this.modelFor('folder').documents,
2638
templates: this.modelFor('folder').templates,
2739
showStartDocument: false,
28-
categories: this.get('categoryService').getUserVisible(this.modelFor('folder').folder.get('id')),
29-
categorySummary: this.get('categoryService').getSummary(this.modelFor('folder').folder.get('id')),
30-
categoryMembers: this.get('categoryService').getSpaceCategoryMembership(this.modelFor('folder').folder.get('id')),
31-
rootDocCount: 0
40+
rootDocCount: 0,
41+
categories: this.get('categories'),
42+
categorySummary: this.get('categorySummary'),
43+
categoryMembers: this.get('categoryMembers'),
44+
// categories: this.get('categoryService').getUserVisible(this.modelFor('folder').folder.get('id')),
45+
// categorySummary: this.get('categoryService').getSummary(this.modelFor('folder').folder.get('id')),
46+
// categoryMembers: this.get('categoryService').getSpaceCategoryMembership(this.modelFor('folder').folder.get('id')),
3247
});
3348
},
3449

3550
afterModel(model, transition) { // eslint-disable-line no-unused-vars
51+
// model.folder = this.modelFor('folder').folder;
52+
// model.permissions = this.modelFor('folder').permissions;
53+
// model.folders = this.modelFor('folder').folders;
54+
// model.documents = this.modelFor('folder').documents;
55+
// model.templates = this.modelFor('folder').templates;
56+
// model.showStartDocument = false;
57+
// model.rootDocCount = 0;
58+
3659
let docs = model.documents;
3760
let categoryMembers = model.categoryMembers;
3861
let rootDocCount = 0;

gui/app/services/category.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,5 +151,34 @@ export default BaseService.extend({
151151
}).then((response) => {
152152
return response;
153153
});
154+
},
155+
156+
// fetchXXX represents UI specific bulk data loading designed to
157+
// reduce network traffic and boost app performance.
158+
// This method that returns:
159+
// 1. getUserVisible()
160+
// 2. getSummary()
161+
// 3. getSpaceCategoryMembership()
162+
fetchSpaceData(spaceId) {
163+
return this.get('ajax').request(`fetch/category/space/${spaceId}`, {
164+
method: 'GET'
165+
}).then((response) => {
166+
let data = {
167+
category: [],
168+
membership: [],
169+
summary: []
170+
};
171+
172+
let cats = response.category.map((obj) => {
173+
let data = this.get('store').normalize('category', obj);
174+
return this.get('store').push(data);
175+
});
176+
177+
data.category = cats;
178+
data.membership = response.membership;
179+
data.summary = response.summary;
180+
181+
return data;
182+
});
154183
}
155184
});

0 commit comments

Comments
 (0)