Skip to content

Commit

Permalink
Merge pull request #765 from tchak/add-query-expression-options
Browse files Browse the repository at this point in the history
Add options to QueryExpression and Operation
  • Loading branch information
dgeb authored Jul 27, 2020
2 parents 6fdece1 + b31113a commit 0f3c538
Show file tree
Hide file tree
Showing 19 changed files with 914 additions and 267 deletions.
1 change: 1 addition & 0 deletions packages/@orbit/data/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export { default } from '@orbit/core';
export * from './exception';
export * from './key-map';
export * from './operation';
export * from './operation-term';
export * from './options';
export * from './query-builder';
export * from './query-expression';
Expand Down
173 changes: 173 additions & 0 deletions packages/@orbit/data/src/operation-term.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
import { deepMerge } from '@orbit/utils';
import {
Operation,
AddRecordOperation,
UpdateRecordOperation,
RemoveRecordOperation,
ReplaceAttributeOperation,
ReplaceKeyOperation,
AddToRelatedRecordsOperation,
RemoveFromRelatedRecordsOperation,
ReplaceRelatedRecordsOperation,
ReplaceRelatedRecordOperation
} from './operation';
import { RequestOptions } from './request';
import { RecordIdentity } from './record';

/**
* Operation terms are used by transform builders to allow for the construction of
* operations in composable patterns.
*/
export class OperationTerm<T extends Operation = Operation> {
operation: T;

constructor(operation?: T) {
this.operation = operation;
}

toOperation(): T {
return this.operation;
}

options(options: RequestOptions): this {
this.operation.options = deepMerge(this.operation.options || {}, options);
return this;
}
}

export class AddRecordTerm extends OperationTerm<AddRecordOperation> {
constructor(record: RecordIdentity) {
const operation: AddRecordOperation = {
op: 'addRecord',
record
};

super(operation);
}
}

export class UpdateRecordTerm extends OperationTerm<UpdateRecordOperation> {
constructor(record: RecordIdentity) {
const operation: UpdateRecordOperation = {
op: 'updateRecord',
record
};

super(operation);
}
}

export class RemoveRecordTerm extends OperationTerm<RemoveRecordOperation> {
constructor(record: RecordIdentity) {
const operation: RemoveRecordOperation = {
op: 'removeRecord',
record
};

super(operation);
}
}

export class ReplaceAttributeTerm extends OperationTerm<
ReplaceAttributeOperation
> {
constructor(record: RecordIdentity, attribute: string, value: unknown) {
const operation: ReplaceAttributeOperation = {
op: 'replaceAttribute',
record,
attribute,
value
};

super(operation);
}
}

export class ReplaceKeyTerm extends OperationTerm<ReplaceKeyOperation> {
constructor(record: RecordIdentity, key: string, value: string) {
const operation: ReplaceKeyOperation = {
op: 'replaceKey',
record,
key,
value
};

super(operation);
}
}

export class AddToRelatedRecordsTerm extends OperationTerm<
AddToRelatedRecordsOperation
> {
constructor(
record: RecordIdentity,
relationship: string,
relatedRecord: RecordIdentity
) {
const operation: AddToRelatedRecordsOperation = {
op: 'addToRelatedRecords',
record,
relationship,
relatedRecord
};

super(operation);
}
}

export class RemoveFromRelatedRecordsTerm extends OperationTerm<
RemoveFromRelatedRecordsOperation
> {
constructor(
record: RecordIdentity,
relationship: string,
relatedRecord: RecordIdentity
) {
const operation: RemoveFromRelatedRecordsOperation = {
op: 'removeFromRelatedRecords',
record,
relationship,
relatedRecord
};

super(operation);
}
}

export class ReplaceRelatedRecordsTerm extends OperationTerm<
ReplaceRelatedRecordsOperation
> {
constructor(
record: RecordIdentity,
relationship: string,
relatedRecords: RecordIdentity[]
) {
const operation: ReplaceRelatedRecordsOperation = {
op: 'replaceRelatedRecords',
record,
relationship,
relatedRecords
};

super(operation);
}
}

export class ReplaceRelatedRecordTerm extends OperationTerm<
ReplaceRelatedRecordOperation
> {
constructor(
record: RecordIdentity,
relationship: string,
relatedRecord: RecordIdentity | null
) {
const operation: ReplaceRelatedRecordOperation = {
op: 'replaceRelatedRecord',
record,
relationship,
relatedRecord
};

super(operation);
}
}
11 changes: 8 additions & 3 deletions packages/@orbit/data/src/operation.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import { eq, deepGet, deepSet } from '@orbit/utils';
import {
Record,
RecordIdentity,
cloneRecordIdentity,
equalRecordIdentities
} from './record';
import { eq, deepGet, deepSet } from '@orbit/utils';
import { RequestOptions } from './request';

/**
* Base Operation interface, which requires just an `op` string.
*/
export interface Operation {
op: string;
options?: RequestOptions;
}

/**
Expand Down Expand Up @@ -54,7 +56,7 @@ export interface ReplaceAttributeOperation extends Operation {
op: 'replaceAttribute';
record: RecordIdentity;
attribute: string;
value: any;
value: unknown;
}

/**
Expand Down Expand Up @@ -126,7 +128,10 @@ function mergeOperations(
superceding: RecordOperation,
consecutiveOps: boolean
): void {
if (equalRecordIdentities(superceded.record, superceding.record)) {
if (superceded.options || superceding.options) {
// do not merge if one of the operations have options
return;
} else if (equalRecordIdentities(superceded.record, superceding.record)) {
if (superceding.op === 'removeRecord') {
markOperationToDelete(superceded);
if (superceded.op === 'addRecord') {
Expand Down
2 changes: 2 additions & 0 deletions packages/@orbit/data/src/query-expression.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { RecordIdentity } from './record';
import { RequestOptions } from './request';

export type SortOrder = 'ascending' | 'descending';

Expand Down Expand Up @@ -69,6 +70,7 @@ export type PageSpecifier = BasePageSpecifier | OffsetLimitPageSpecifier;
*/
export interface QueryExpression {
op: string;
options?: RequestOptions;
}

export interface FindRecord extends QueryExpression {
Expand Down
44 changes: 21 additions & 23 deletions packages/@orbit/data/src/query-term.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isObject } from '@orbit/utils';
import { isObject, deepMerge } from '@orbit/utils';
import {
QueryExpression,
FindRecord,
Expand All @@ -17,6 +17,7 @@ import {
RelatedRecordsFilterSpecifier
} from './query-expression';
import { RecordIdentity } from './record';
import { RequestOptions } from './request';

export interface AttributeSortQBParam {
attribute: string;
Expand Down Expand Up @@ -58,24 +59,27 @@ export type FilterQBParam =
* Query terms are used by query builders to allow for the construction of
* query expressions in composable patterns.
*/
export class QueryTerm {
expression: QueryExpression;
export class QueryTerm<T extends QueryExpression = QueryExpression> {
expression: T;

constructor(expression?: QueryExpression) {
constructor(expression?: T) {
this.expression = expression;
}

toQueryExpression(): QueryExpression {
toQueryExpression(): T {
return this.expression;
}

options(options: RequestOptions): this {
this.expression.options = deepMerge(this.expression.options || {}, options);
return this;
}
}

/**
* A query term representing a single record.
*/
export class FindRecordTerm extends QueryTerm {
expression: FindRecord;

export class FindRecordTerm extends QueryTerm<FindRecord> {
constructor(record: RecordIdentity) {
let expression: FindRecord = {
op: 'findRecord',
Expand All @@ -86,9 +90,7 @@ export class FindRecordTerm extends QueryTerm {
}
}

export class FindRelatedRecordTerm extends QueryTerm {
expression: FindRelatedRecord;

export class FindRelatedRecordTerm extends QueryTerm<FindRelatedRecord> {
constructor(record: RecordIdentity, relationship: string) {
let expression: FindRelatedRecord = {
op: 'findRelatedRecord',
Expand All @@ -100,9 +102,7 @@ export class FindRelatedRecordTerm extends QueryTerm {
}
}

export class FindRelatedRecordsTerm extends QueryTerm {
expression: FindRelatedRecords;

export class FindRelatedRecordsTerm extends QueryTerm<FindRelatedRecords> {
constructor(record: RecordIdentity, relationship: string) {
let expression: FindRelatedRecords = {
op: 'findRelatedRecords',
Expand Down Expand Up @@ -130,7 +130,7 @@ export class FindRelatedRecordsTerm extends QueryTerm {
* 'name' // ascending order
* ```
*/
sort(...params: SortQBParam[]): FindRelatedRecordsTerm {
sort(...params: SortQBParam[]): this {
const specifiers = params.map(sortParamToSpecifier);
this.expression.sort = (this.expression.sort || []).concat(specifiers);
return this;
Expand All @@ -139,7 +139,7 @@ export class FindRelatedRecordsTerm extends QueryTerm {
/**
* Applies pagination to a collection query.
*/
page(param: PageQBParam): FindRelatedRecordsTerm {
page(param: PageQBParam): this {
this.expression.page = pageParamToSpecifier(param);
return this;
}
Expand All @@ -156,16 +156,14 @@ export class FindRelatedRecordsTerm extends QueryTerm {
* { attribute: 'classification', value: 'terrestrial' });
* ```
*/
filter(...params: FilterQBParam[]): FindRelatedRecordsTerm {
filter(...params: FilterQBParam[]): this {
const specifiers = params.map(filterParamToSpecifier);
this.expression.filter = (this.expression.filter || []).concat(specifiers);
return this;
}
}

export class FindRecordsTerm extends QueryTerm {
expression: FindRecords;

export class FindRecordsTerm extends QueryTerm<FindRecords> {
constructor(typeOrIdentities?: string | RecordIdentity[]) {
let expression: FindRecords = {
op: 'findRecords'
Expand Down Expand Up @@ -197,7 +195,7 @@ export class FindRecordsTerm extends QueryTerm {
* 'name' // ascending order
* ```
*/
sort(...params: SortQBParam[]): FindRecordsTerm {
sort(...params: SortQBParam[]): this {
const specifiers = params.map(sortParamToSpecifier);
this.expression.sort = (this.expression.sort || []).concat(specifiers);
return this;
Expand All @@ -206,7 +204,7 @@ export class FindRecordsTerm extends QueryTerm {
/**
* Applies pagination to a collection query.
*/
page(param: PageQBParam): FindRecordsTerm {
page(param: PageQBParam): this {
this.expression.page = pageParamToSpecifier(param);
return this;
}
Expand All @@ -223,7 +221,7 @@ export class FindRecordsTerm extends QueryTerm {
* { attribute: 'classification', value: 'terrestrial' });
* ```
*/
filter(...params: FilterQBParam[]): FindRecordsTerm {
filter(...params: FilterQBParam[]): this {
const specifiers = params.map(filterParamToSpecifier);
this.expression.filter = (this.expression.filter || []).concat(specifiers);
return this;
Expand Down
Loading

0 comments on commit 0f3c538

Please sign in to comment.