Skip to content

Commit b04b629

Browse files
spaceisntsyntaxgmcharlt
authored andcommitted
LP#2098043,2098407,2098117: Angular Bucket updates
This commit builds on several improvements to the open-ils.pcrud service, allowing for improved performance and removal of some layers of indirection that additionally provide the opportunity for permission and correctness bugs. General changes: * Add "count_only" support directly to the pcrud services, a la idlist. * Improve open-ils.fielder map generation by only causing joins on fields that are used for sorting or filtering; displayed fields come from fleshing. * Use pcrud to add and remove bucket items via the bucket service Bucket UI improvements: * Use pcrud for all grids on all tabs, rather than open-ils.actor methods, leveraging the new permission testing functionality. * Deserialize count fetching, and use pcrud * Remove unnecessary grid and count refreshing * Add user-applied grid filters to the datasource's query for fetching buckets to display. These filters are /not/ included in the count call that populates the tab label, that is always the unfiltered count. The definitions for "Shared with {me|others}" and "Visible to me" have changed such that: * Shared with me - all NON-public buckets that I can see. That is, if I don't own the bucket, I have one of CREATE_BIB_BUCKET, ADMIN_BIB_BUCKET, VIEW_CONTAINER, or UPDATE_CONTAINER permissions at the bucket owning lib or one of the share_maps orgs, or have VIEW_CONTAINER or UPDATE_CONTAINER object permissions on a bucket assigned by the owner. * Shared with others - all buckets that I own that have entries in the share map table (org sharing) or that have entries in the object permission mapping table (user permission grant). * Visible to me - All PUBLIC buckets that I don't own. We actively exclude "temp" btype buckets and add an index to support that exclusion. These buckets are only used for transient purposes and will not accumulate on a normal, production instance. However, if (for instance) A/T event definitions used for printing and emailing records are disabled, the buckets are not cleaned up. This can happen on a test instance where most A/T events are disabled to avoid sending test-data emails to "real" email addresses. In such cases, there may be many orders of magnitude more "temp" buckets than actual user-created ones. Signed-off-by: Mike Rylander <[email protected]> Signed-off-by: Galen Charlton <[email protected]>
1 parent f589fee commit b04b629

File tree

7 files changed

+97
-60
lines changed

7 files changed

+97
-60
lines changed

Open-ILS/examples/fm_IDL.xml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9050,19 +9050,19 @@ SELECT usr,
90509050
</links>
90519051
<permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
90529052
<actions>
9053-
<retrieve permission="CREATE_BIB_BUCKET ADMIN_BIB_BUCKET VIEW_CONTAINER">
9053+
<create permission="CREATE_BIB_BUCKET ADMIN_BIB_BUCKET CREATE_CONTAINER_ITEM UPDATE_CONTAINER">
90549054
<context delegate="bucket" field="owning_lib" owning_user="owner"/>
90559055
<context delegate="bucket" jump="share_maps" field="share_org"/>
9056-
</retrieve>
9057-
<create permission="CREATE_BIB_BUCKET ADMIN_BIB_BUCKET UPDATE_CONTAINER">
9056+
</create>
9057+
<retrieve permission="CREATE_BIB_BUCKET ADMIN_BIB_BUCKET VIEW_CONTAINER UPDATE_CONTAINER">
90589058
<context delegate="bucket" field="owning_lib" owning_user="owner"/>
90599059
<context delegate="bucket" jump="share_maps" field="share_org"/>
9060-
</create>
9060+
</retrieve>
90619061
<update permission="CREATE_BIB_BUCKET ADMIN_BIB_BUCKET UPDATE_CONTAINER">
90629062
<context delegate="bucket" field="owning_lib" owning_user="owner"/>
90639063
<context delegate="bucket" jump="share_maps" field="share_org"/>
90649064
</update>
9065-
<delete permission="CREATE_BIB_BUCKET ADMIN_BIB_BUCKET UPDATE_CONTAINER">
9065+
<delete permission="CREATE_BIB_BUCKET ADMIN_BIB_BUCKET DELETE_CONTAINER_ITEM UPDATE_CONTAINER">
90669066
<context delegate="bucket" field="owning_lib" owning_user="owner"/>
90679067
<context delegate="bucket" jump="share_maps" field="share_org"/>
90689068
</delete>

Open-ILS/src/eg2/src/app/core/pcrud.service.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ interface PcrudReqOps {
1414
authoritative?: boolean;
1515
anonymous?: boolean;
1616
idlist?: boolean;
17+
count_only?: boolean;
1718
atomic?: boolean;
1819
// If true, link-type fields which link to a class that defines a
1920
// selector will be fleshed with the linked value. This affects
@@ -165,7 +166,12 @@ export class PcrudContext {
165166
reqOps = reqOps || {};
166167
this.authoritative = reqOps.authoritative || false;
167168

168-
const returnType = reqOps.idlist ? 'id_list' : 'search';
169+
let returnType = reqOps.idlist ? 'id_list' : 'search';
170+
if (reqOps.count_only) {
171+
returnType = 'count';
172+
reqOps.atomic = reqOps.fleshSelectors = false;
173+
}
174+
169175
let method = `open-ils.pcrud.${returnType}.${fmClass}`;
170176

171177
if (reqOps.atomic) { method += '.atomic'; }

Open-ILS/src/eg2/src/app/share/grid/grid-flat-data.service.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,12 @@ import {GridContext, GridColumnSort} from './grid';
66
import {Pager} from '@eg/share/util/pager';
77

88
interface FlatQueryFields {
9-
[name: string]: string;
9+
[name:string] : {
10+
path: string,
11+
sort?: boolean,
12+
filter?: boolean,
13+
display?: boolean
14+
};
1015
}
1116

1217

@@ -27,12 +32,20 @@ export class GridFlatDataService {
2732
}
2833

2934
const fields = this.compileFields(gridContext);
35+
3036
const flatSort = sort.map(s => {
3137
const obj: any = {};
3238
obj[s.name] = s.dir;
39+
fields[s.name].sort = true;
3340
return obj;
3441
});
3542

43+
if (query && query['-and']) {
44+
query['-and'].forEach(col => {
45+
Object.keys(col).forEach(k => fields[k].filter = true)
46+
});
47+
}
48+
3649
return this.net.request(
3750
'open-ils.fielder',
3851
'open-ils.fielder.flattened_search',
@@ -52,7 +65,14 @@ export class GridFlatDataService {
5265
// Verify the column describes a proper IDL field
5366
const path = col.path || col.name;
5467
const info = gridContext.columnSet.idlInfoFromDotpath(path);
55-
if (info) { fields[col.name] = path; }
68+
if (info) {
69+
fields[col.name] = {
70+
path : path,
71+
display: true,
72+
sort : false,
73+
filter : false
74+
};
75+
}
5676
});
5777

5878
return fields;

Open-ILS/src/eg2/src/app/staff/share/buckets/bucket.service.ts

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -67,28 +67,13 @@ export class BucketService {
6767
item.target_biblio_record_entry(itemId);
6868
items.push(item);
6969
});
70-
const requestObs = this.net.request(
71-
'open-ils.actor',
72-
'open-ils.actor.container.item.create',
73-
this.auth.token(),
74-
'biblio',
75-
items
76-
);
77-
78-
return lastValueFrom(requestObs);
70+
return this.pcrud.create(items).toPromise().then(l => l.map(i => i.id()));
7971
}
8072

8173
async removeBibsFromRecordBucket(bucketId: number, bibIds: number[]): Promise<any> {
82-
const requestObs = this.net.request(
83-
'open-ils.actor',
84-
'open-ils.actor.container.item.delete.batch',
85-
this.auth.token(),
86-
'biblio_record_entry',
87-
bucketId,
88-
bibIds
89-
);
90-
91-
return lastValueFrom(requestObs);
74+
return this.pcrud.search(
75+
'cbrebi', { bucket: bucketId, target_biblio_record_entry: bibIds }, {}, {atomic: true}
76+
).toPromise().then(entries => this.pcrud.remove(entries).toPromise());
9277
}
9378

9479
async retrieveRecordBuckets(bucketIds: number[]): Promise<any[]> {

Open-ILS/src/eg2/src/app/staff/share/buckets/record-bucket.component.ts

Lines changed: 53 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -132,12 +132,15 @@ export class RecordBucketComponent implements OnInit, OnDestroy {
132132
}
133133

134134
search_or_count(justCount, hint, query, pcrudOps, pcrudReqOps): Observable<number | any[]> {
135-
if (justCount) {
136-
return this.net.request('open-ils.actor', 'open-ils.actor.count_with_pcrud.authoritative',
137-
this.auth.token(), hint, query);
138-
} else {
139-
return this.pcrud.search(hint, query, pcrudOps, pcrudReqOps);
135+
if (!justCount) {
136+
let query_filters = this.extractGridFitlers();
137+
138+
if (query_filters.length > 0) {
139+
query['-and'] = query_filters;
140+
}
140141
}
142+
143+
return this.pcrud.search(hint, {...query, btype: { '!=': 'temp' }}, pcrudOps, {...pcrudReqOps, count_only : justCount});
141144
}
142145

143146
initViews() {
@@ -152,7 +155,7 @@ export class RecordBucketComponent implements OnInit, OnDestroy {
152155
let result: BucketQueryResult;
153156
const response = await lastValueFrom(
154157
this.search_or_count(justCount, 'cbreb',
155-
{ id: { '!=' : null } },
158+
{ owner: { '!=' : this.userId || this.auth.user().id() }, pub: 't' },
156159
{
157160
...(pager?.limit && { limit: pager.limit }),
158161
...(pager?.offset && { offset: pager.offset }),
@@ -288,11 +291,21 @@ export class RecordBucketComponent implements OnInit, OnDestroy {
288291
console.debug('translatedSort', translatedSort);
289292
let result: BucketQueryResult;
290293
const response = await lastValueFrom(
291-
justCount
292-
? this.net.request( 'open-ils.actor',
293-
'open-ils.actor.container.retrieve_biblio_record_entry_buckets_shared_with_others.count', this.auth.token())
294-
: this.net.request( 'open-ils.actor',
295-
'open-ils.actor.container.retrieve_biblio_record_entry_buckets_shared_with_others', this.auth.token())
294+
this.search_or_count(justCount, 'cbreb',
295+
{ owner: { '=' : this.userId || this.auth.user().id() }, '-or': [
296+
{'-exists':{from:'cbrebs',where:{bucket:{'=':{'+cbreb':'id'}}}}},
297+
{'-exists':{from:'puopm',where:{
298+
object_type:"cbreb",
299+
"+cbreb":{id:{"=":{transform:"text",value:{"+puopm":"object_id"}}}}}}
300+
}
301+
]},
302+
{
303+
...(pager?.limit && { limit: pager.limit }),
304+
...(pager?.offset && { offset: pager.offset }),
305+
...(translatedSort && translatedSort),
306+
},
307+
{ idlist: true, atomic: true }
308+
)
296309
);
297310
if (justCount) {
298311
result = { bucketIds: [], count: response as number };
@@ -314,11 +327,15 @@ export class RecordBucketComponent implements OnInit, OnDestroy {
314327
console.debug('translatedSort', translatedSort);
315328
let result: BucketQueryResult;
316329
const response = await lastValueFrom(
317-
justCount
318-
? this.net.request('open-ils.actor',
319-
'open-ils.actor.container.retrieve_biblio_record_entry_buckets_shared_with_user.count', this.auth.token())
320-
: this.net.request('open-ils.actor',
321-
'open-ils.actor.container.retrieve_biblio_record_entry_buckets_shared_with_user', this.auth.token())
330+
this.search_or_count(justCount, 'cbreb',
331+
{ owner: { '!=' : this.userId || this.auth.user().id() }, pub: 'f' },
332+
{
333+
...(pager?.limit && { limit: pager.limit }),
334+
...(pager?.offset && { offset: pager.offset }),
335+
...(translatedSort && translatedSort),
336+
},
337+
{ idlist: true, atomic: true }
338+
)
322339
);
323340
if (justCount) {
324341
result = { bucketIds: [], count: response as number };
@@ -353,15 +370,17 @@ export class RecordBucketComponent implements OnInit, OnDestroy {
353370

354371
const viewKeys = this.getViewKeys();
355372

356-
try {
357-
viewKeys.forEach( v => { this.views[v].count = -1; } );
358-
// Wait for all bucketIdQuery operations for counting to complete
359-
await Promise.all(viewKeys.map(v => this.views[v].bucketIdQuery(null, [], true)));
360-
} catch (error) {
361-
console.error('Error updating counts:', error);
362-
}
363-
this.countInProgress = false;
364-
console.warn('countInProgress = false');
373+
viewKeys.forEach( v => { this.views[v].count = -1; } );
374+
375+
// Wait for all bucketIdQuery operations for counting to complete
376+
Promise.all(
377+
viewKeys.map(v => this.views[v].bucketIdQuery(null, [], true))
378+
).catch(
379+
error => console.error('Error updating counts:', error)
380+
).finally( () => {
381+
this.countInProgress = false;
382+
console.warn('countInProgress = false');
383+
});
365384
}
366385

367386
getViewKeys(): string[] {
@@ -458,12 +477,16 @@ export class RecordBucketComponent implements OnInit, OnDestroy {
458477
console.debug('switchTo', source);
459478
this.currentView = source;
460479
this.router.navigate([this.mapDatasourceToUrl(source)], { relativeTo: this.route.parent });
461-
if (!this.initInProgress) {
462-
this.grid.reload();
463-
this.updateCounts();
464-
}
480+
// router navigation causes a grid refresh
465481
}
466482

483+
extractGridFitlers() {
484+
let query_filters = [];
485+
Object.keys(this.dataSource.filters).forEach(key => {
486+
query_filters = query_filters.concat(this.dataSource.filters[key]);
487+
});
488+
return query_filters;
489+
}
467490

468491
buildRetrieveByIdsQuery(bucketIds: number[]) {
469492
console.debug('buildRetrieveByIdsQuery, bucketIds', bucketIds);
@@ -473,10 +496,7 @@ export class RecordBucketComponent implements OnInit, OnDestroy {
473496
// query something even if no ids to avoid exception
474497
query['id'] = bucketIds.length === 0 ? [-1] : bucketIds.map( b => this.idl.pkeyValue(b) );
475498

476-
let query_filters = [];
477-
Object.keys(this.dataSource.filters).forEach(key => {
478-
query_filters = query_filters.concat(this.dataSource.filters[key]);
479-
});
499+
let query_filters = this.extractGridFitlers();
480500

481501
if (query_filters.length > 0) {
482502
query['-and'] = query_filters;

Open-ILS/src/sql/Pg/070.schema.container.sql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ CREATE TABLE container.biblio_record_entry_bucket (
164164
create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
165165
CONSTRAINT breb_name_once_per_owner UNIQUE (owner,name,btype)
166166
);
167+
CREATE INDEX cbreb_pub_owner_not_temp_idx ON container.biblio_record_entry_bucket (pub,owner) WHERE btype != 'temp';
167168

168169
CREATE TABLE container.biblio_record_entry_bucket_shares (
169170
id SERIAL PRIMARY KEY,
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
BEGIN;
2+
3+
CREATE INDEX IF NOT EXISTS cbreb_pub_owner_not_temp_idx ON container.biblio_record_entry_bucket (pub,owner) WHERE btype != 'temp';
4+
5+
COMMIT;

0 commit comments

Comments
 (0)