Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor business rules #2965

Merged
merged 58 commits into from
May 19, 2023
Merged
Changes from 1 commit
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
135b342
Create static uniqueness rules file
melton-jason Feb 10, 2023
3bc8718
Refactor non-interaction business rules on the frontend
melton-jason Feb 10, 2023
2fc31dc
Refactor interaction business rules
melton-jason Feb 13, 2023
c4772c1
Fix remaining bugs
melton-jason Feb 16, 2023
86dfeba
Merge branch 'production' into refactor-busrules
melton-jason Feb 16, 2023
ad3f75d
Fix dockerize errors
melton-jason Feb 16, 2023
047b17a
Add missing UniquenessRules
melton-jason Feb 22, 2023
c810ea8
Remove _ dependency from businessRule files
melton-jason Feb 24, 2023
1d09705
Remove unnecessary _this statements
melton-jason Feb 24, 2023
7017a3c
Refactor out pluck function
melton-jason Feb 24, 2023
5611147
Remove unnecessary return statements in arrow functions
melton-jason Feb 24, 2023
ee19feb
Add type for Uniqueness Rules
melton-jason Feb 27, 2023
01a7275
Fix most typescript errors
melton-jason Feb 27, 2023
b08cf8e
Add type to BusinessRuleResult
melton-jason Feb 27, 2023
be8197f
Add count support for DNASequence geneSequence
melton-jason Feb 28, 2023
9713e12
Add UniquenessRule for TaxonTreeDef and TaxonTreeDefItem
melton-jason Feb 28, 2023
0068372
Merge branch 'production' into refactor-busrules
melton-jason Feb 28, 2023
070bce5
Fix typescript errors in interactionBusinessRules.ts
melton-jason Mar 1, 2023
7c4bb25
Address most code comments
melton-jason Mar 2, 2023
029d2da
Fix other misc. Typescript errors
melton-jason Mar 3, 2023
410fcf9
Merge branch 'production' into refactor-busrules
melton-jason Mar 6, 2023
6990364
Remove globalEvents in businessRules.ts
melton-jason Mar 6, 2023
684548a
Create a new a new object for assigning uniqueness rules
melton-jason Mar 7, 2023
cfed4da
Merge branch 'production' into refactor-busrules
melton-jason Mar 10, 2023
eea0b3c
Set Taxon.isAccepted on save to whether it is synonimized or not
melton-jason Mar 10, 2023
ced0397
Begin writing business rule tests
melton-jason Mar 10, 2023
3881441
Fix LoanReturnPreparation bugs
melton-jason Mar 14, 2023
73bbdc0
Merge branch 'production' into refactor-busrules
melton-jason Mar 14, 2023
f413f69
Narrow type of fieldName in checkField(...) to SCHEMA['fields']
melton-jason Mar 14, 2023
7252156
Add uniqueness rule tests
melton-jason Mar 16, 2023
c18fd80
Make field names lowercase in rule definitions
melton-jason Mar 16, 2023
d9513f6
Check prep availabiliy for Disposal when DisposalPrep quantity changes
melton-jason Mar 31, 2023
8bc316f
Merge branch 'production' into refactor-busrules
melton-jason Mar 31, 2023
f8083d0
Revert fieldnames to lowercase in business rule definitions
melton-jason Apr 4, 2023
1048348
Merge branch 'production' into refactor-busrules
melton-jason Apr 4, 2023
3fd7aad
Merge branch 'production' into refactor-busrules
melton-jason Apr 5, 2023
25997e2
Fix remaining Typescript errors
melton-jason Apr 12, 2023
4fdd0e1
Add static division for tests
melton-jason Apr 14, 2023
a056fec
Add overrideAjax calls to fix failing unit tests
melton-jason Apr 14, 2023
243ee0b
Modify getUniqueFields to follow new UniquenessRules format
melton-jason Apr 17, 2023
2245448
Add static files to address failing tests
melton-jason Apr 17, 2023
e846ab1
Change resource tests to accomadate new uniqueness rules
melton-jason Apr 17, 2023
2498fca
Add unique fields in fields to not clone test
melton-jason Apr 17, 2023
e23a148
Fix CollectionObject not being defined for schema.models in business …
melton-jason Apr 18, 2023
d130369
Add overrideAjax to all resources in business rule tests
melton-jason Apr 18, 2023
fb3389f
Remove redundant text-left classNames
maxpatiiuk Apr 19, 2023
86de81a
Cleanup manual overrdieAjax resources in business rule tests
melton-jason Apr 19, 2023
11c329e
Move resource creation into test block from describe block
melton-jason Apr 19, 2023
313914e
Move determination resource creation into test() blocks
melton-jason Apr 19, 2023
b5ddfe4
Move all overrideAjax calls to describe() scope
melton-jason Apr 19, 2023
8c71c70
Add missing getResourceApiUrl in overrideAjax call
melton-jason Apr 19, 2023
05bd279
Save original permit before checking for duplicate in businessRule tests
melton-jason Apr 19, 2023
f7ecb0e
Add fields to resources for uniqueness rule tests
melton-jason Apr 19, 2023
a5a3c2e
Move overrideAjax calls outside if describe() blocks
melton-jason Apr 19, 2023
672f283
Rename toOneField to scope
melton-jason Apr 19, 2023
73c0b84
Manually specify resource api url
melton-jason Apr 19, 2023
60da04b
Add static file ajax tests
melton-jason Apr 19, 2023
b9eeea0
Address code comments
melton-jason May 2, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Fix dockerize errors
  • Loading branch information
melton-jason committed Feb 16, 2023

Verified

This commit was signed with the committer’s verified signature.
alexsapran Alexandros Sapranidis
commit ad3f75d9eda8f4f8bd2b601b17c1c95f56f268f3
210 changes: 53 additions & 157 deletions specifyweb/frontend/js_src/lib/components/DataModel/collectionApi.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import _ from 'underscore';
import { Backbone } from './backbone';

import { assert } from '../Errors/assert';
import { Backbone } from './backbone';
import { hasHierarchyField } from './schema';

var Base = Backbone.Collection.extend({
const Base = Backbone.Collection.extend({
__name__: 'CollectionBase',
getTotalCount() {
return Promise.resolve(this.length);
async getTotalCount() {
return this.length;
},
});

@@ -42,25 +43,29 @@ export const DependentCollection = Base.extend({
this.on(
'add remove',
function () {
// Warning: changing a collection record does not trigger a
// change event in the parent (though it probably should)
/*
* Warning: changing a collection record does not trigger a
* change event in the parent (though it probably should)
*/
this.trigger('saverequired');
},
this
);

setupToOne(this, options);

// If the id of the related resource changes, we go through and update
// all the objects that point to it with the new pointer.
// This is to support having collections of objects attached to
// newly created resources that don't have ids yet. When the
// resource is saved, the related objects can have their foreign keys
// set correctly.
/*
* If the id of the related resource changes, we go through and update
* all the objects that point to it with the new pointer.
* This is to support having collections of objects attached to
* newly created resources that don't have ids yet. When the
* resource is saved, the related objects can have their foreign keys
* set correctly.
*/
this.related.on(
'change:id',
function () {
var relatedUrl = this.related.url();
const relatedUrl = this.related.url();
_.chain(this.models)
.compact()
.invoke('set', this.field.name, relatedUrl);
@@ -80,22 +85,22 @@ export const LazyCollection = Base.extend({
__name__: 'LazyCollectionBase',
_neverFetched: true,
constructor(options) {
options || (options = {});
options ||= {};
Base.call(this, null, options);
this.filters = options.filters || {};
this.domainfilter =
!!options.domainfilter &&
Boolean(options.domainfilter) &&
(typeof this.model?.specifyModel !== 'object' ||
hasHierarchyField(this.model.specifyModel));
},
url() {
return '/api/specify/' + this.model.specifyModel.name.toLowerCase() + '/';
return `/api/specify/${this.model.specifyModel.name.toLowerCase()}/`;
},
isComplete() {
return this.length === this._totalCount;
},
parse(resp) {
var objects;
let objects;
if (resp.meta) {
this._totalCount = resp.meta.total_count;
objects = resp.objects;
@@ -110,17 +115,13 @@ export const LazyCollection = Base.extend({
async fetch(options) {
this._neverFetched = false;

const Base = Backbone.Collection.extend({
__name__: 'CollectionBase',
async getTotalCount() {
return this.length;
},
});
if (this._fetch) return this._fetch;
else if (this.isComplete() || this.related?.isNew()) return this;

if (this.isComplete())
console.error('fetching for already filled collection');

options || (options = {});
options ||= {};

options.update = true;
options.remove = false;
@@ -132,140 +133,35 @@ export const LazyCollection = Base.extend({
_.extend({ domainfilter: this.domainfilter }, this.filters);
options.data.offset = this.length;

export const DependentCollection = Base.extend({
__name__: 'DependentCollectionBase',
constructor(options, models = []) {
assert(_.isArray(models));
Base.call(this, models, options);
},
initialize(_models, options) {
this.on(
'add remove',
function () {
/*
* Warning: changing a collection record does not trigger a
* change event in the parent (though it probably should)
*/
this.trigger('saverequired');
},
this
);

setupToOne(this, options);

/*
* If the id of the related resource changes, we go through and update
* all the objects that point to it with the new pointer.
* This is to support having collections of objects attached to
* newly created resources that don't have ids yet. When the
* resource is saved, the related objects can have their foreign keys
* set correctly.
*/
this.related.on(
'change:id',
function () {
const relatedUrl = this.related.url();
_.chain(this.models)
.compact()
.invoke('set', this.field.name, relatedUrl);
},
this
);
},
isComplete() {
return true;
},
fetch: fakeFetch,
sync: notSupported,
create: notSupported,
});

export const LazyCollection = Base.extend({
__name__: 'LazyCollectionBase',
_neverFetched: true,
constructor(options) {
options ||= {};
Base.call(this, null, options);
this.filters = options.filters || {};
this.domainfilter =
Boolean(options.domainfilter) &&
(typeof this.model?.specifyModel !== 'object' ||
hasHierarchyField(this.model.specifyModel));
},
url() {
return `/api/specify/${this.model.specifyModel.name.toLowerCase()}/`;
},
isComplete() {
return this.length === this._totalCount;
},
parse(resp) {
let objects;
if (resp.meta) {
this._totalCount = resp.meta.total_count;
objects = resp.objects;
} else {
console.warn("expected 'meta' in response");
this._totalCount = resp.length;
objects = resp;
}

return objects;
},
async fetch(options) {
this._neverFetched = false;

if (this._fetch) return this._fetch;
else if (this.isComplete() || this.related?.isNew()) return this;

if (this.isComplete())
console.error('fetching for already filled collection');

options ||= {};

options.update = true;
options.remove = false;
options.silent = true;
assert(options.at == null);

options.data =
options.data ||
_.extend({ domainfilter: this.domainfilter }, this.filters);
options.data.offset = this.length;

_(options).has('limit') && (options.data.limit = options.limit);
this._fetch = Backbone.Collection.prototype.fetch.call(this, options);
return this._fetch.then(() => {
this._fetch = null;
return this;
});
},
async fetchIfNotPopulated() {
return this._neverFetched && this.related?.isNew() !== true
? this.fetch()
: this;
},
getTotalCount() {
if (_.isNumber(this._totalCount))
return Promise.resolve(this._totalCount);
return this.fetchIfNotPopulated().then((_this) => _this._totalCount);
},
_(options).has('limit') && (options.data.limit = options.limit);
this._fetch = Backbone.Collection.prototype.fetch.call(this, options);
return this._fetch.then(() => {
this._fetch = null;
return this;
});
},
async fetchIfNotPopulated() {
return this._neverFetched && this.related?.isNew() !== true
? this.fetch()
: this;
},
getTotalCount() {
if (_.isNumber(this._totalCount)) return Promise.resolve(this._totalCount);
return this.fetchIfNotPopulated().then((_this) => _this._totalCount);
},
});

export const ToOneCollection = LazyCollection.extend({
__name__: 'LazyToOneCollectionBase',
initialize(_models, options) {
setupToOne(this, options);
},
async fetch() {
if (this.related.isNew()) {
console.error(
"can't fetch collection related to unpersisted resource"
);
return this;
}
this.filters[this.field.name.toLowerCase()] = this.related.id;
return Reflect.apply(LazyCollection.prototype.fetch, this, arguments);
},
});
export const ToOneCollection = LazyCollection.extend({
__name__: 'LazyToOneCollectionBase',
initialize(_models, options) {
setupToOne(this, options);
},
async fetch() {
if (this.related.isNew()) {
console.error("can't fetch collection related to unpersisted resource");
return this;
}
this.filters[this.field.name.toLowerCase()] = this.related.id;
return Reflect.apply(LazyCollection.prototype.fetch, this, arguments);
},
});
Original file line number Diff line number Diff line change
@@ -1,26 +1,18 @@
import _ from 'underscore';
import { Backbone } from './backbone';

import { assert } from '../Errors/assert';
import { globalEvents } from '../../utils/ajax/specifyApi';

import { hijackBackboneAjax } from '../../utils/ajax/backboneAjax';
import { Http } from '../../utils/ajax/definitions';
import { globalEvents } from '../../utils/ajax/specifyApi';
import { removeKey } from '../../utils/utils';
import { assert } from '../Errors/assert';
import { softFail } from '../Errors/Crash';
import { Backbone } from './backbone';
import {
getFieldsToNotClone,
getResourceApiUrl,
getResourceViewUrl,
resourceFromUrl,
} from './resource';
import { getResourceAndField } from '../../hooks/resource';
import { hijackBackboneAjax } from '../../utils/ajax/backboneAjax';
import { Http } from '../../utils/ajax/definitions';
import { removeKey } from '../../utils/utils';

function eventHandlerForToOne(related, field) {
return function (event) {