Skip to content

Commit

Permalink
refactor(query): get rid of queryBasedCompiler
Browse files Browse the repository at this point in the history
  • Loading branch information
KutsenkoA committed Aug 2, 2018
1 parent 4a65cce commit 1405b83
Show file tree
Hide file tree
Showing 18 changed files with 317 additions and 459 deletions.
12 changes: 12 additions & 0 deletions src/api/dataops/dataset.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@ import { SelectQuery } from "./queries/selectQuery";
import { UpdateQuery } from "./queries/updateQuery";

describe("Dataset class", () => {
describe("after initiating", () => {
it("dataset name should be set", () => {
const dataset = new Dataset("test", createMockFor(RequestExecuter));
expect(dataset.name).toEqual("test");
});

it("requestExecuter should be set", () => {
const requestExecuter = createMockFor(RequestExecuter);
const dataset = new Dataset("test", requestExecuter);
expect((dataset as any).requestExecuter).toEqual(requestExecuter);
});
});

it("should be able start a select query", () => {
const dataset = new Dataset("test", createMockFor(RequestExecuter));
Expand Down
4 changes: 2 additions & 2 deletions src/api/dataops/dataset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ export class Dataset<T extends object = any, D extends DatasetInterface<T> = Dat
* @returns Query object specialized for update statements.
* Don't forget to apply a filter to specify the fields that will be modified.
*/
public update(data: D): UpdateQuery<D> {
return new UpdateQuery<D>(this.requestExecuter, data, this.datasetName);
public update(data: T): UpdateQuery<T> {
return new UpdateQuery<T>(this.requestExecuter, data, this.datasetName);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/api/dataops/filteringApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export class FieldFilter<U> {
/**
* @internal
*/
class FilteringCriterion implements IFilteringCriterion {
export class FilteringCriterion implements IFilteringCriterion {
private lowLevelCondition: ICondition;

constructor(lowLevelCondition?: ICondition, highLevelCriteria?: FilteringCriterion) {
Expand Down
27 changes: 20 additions & 7 deletions src/api/dataops/queries/baseQuery.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,12 @@ let createSubject = ({
datasetName = "dataset",
requestExecuterMock = createMockFor(RequestExecuter),
} = {}) => {
// Declare child class as long as QueryRequest is abstract
class BaseQueryChild<T> extends BaseQuery<T> {};
// Declare child class as long as BaseQuery class is abstract
class BaseQueryChild<T> extends BaseQuery<T> {
constructor(r: RequestExecuter, a: QueryAction, d: string) {
super(r, a, d);
}
}

const subject = new BaseQueryChild<IUser>(requestExecuterMock, action, datasetName);

Expand Down Expand Up @@ -44,33 +48,42 @@ describe("QueryRequest class", () => {
});

it("Query dataset should be set to 'dataset'", () => {
expect((subject as any).query.dataSet).toEqual("dataset");
expect((subject as any).query.dataset).toEqual("dataset");
});
});

describe("fields method", () => {
it("should accept one field as string", () => {
const { subject } = createSubject();
subject.fields("id");
expect((subject as any).query.Fields).toEqual(["id"]);
expect((subject as any).query.fields).toEqual(["id"]);
});

it("should accept severeal fields as strings", () => {
const { subject } = createSubject();
subject.fields("id", "name");
expect((subject as any).query.Fields).toEqual(["id", "name"]);
expect((subject as any).query.fields).toEqual(["id", "name"]);
});

it("should accept one field as an array", () => {
const { subject } = createSubject();
subject.fields(["id"]);
expect((subject as any).query.Fields).toEqual(["id"]);
expect((subject as any).query.fields).toEqual(["id"]);
});

it("should accept several fields in an array", () => {
const { subject } = createSubject();
subject.fields(["id", "name"]);
expect((subject as any).query.Fields).toEqual(["id", "name"]);
expect((subject as any).query.fields).toEqual(["id", "name"]);
});
});

describe("compiledRequest method", () => {
let { subject } = createSubject();
it("should return compiled object", () => {
expect((subject as any).compiledRequest).toEqual({
action: QueryAction.select,
});
});
});

Expand Down
43 changes: 31 additions & 12 deletions src/api/dataops/queries/baseQuery.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { RequestExecuter } from "../../../internal/executer";
import { Query } from "../../../internal/query";
import { compileDataRequest } from "../../../internal/queryBasedCompiler";
import { ICompiledQuery, Query } from "../../../internal/query";

interface ICompiledRequest<T> {
action: QueryAction;
params?: Partial<ICompiledQuery<T>>;
records?: T[];
}

/**
* @internal
Expand All @@ -16,21 +21,24 @@ export enum QueryAction {
* Base class for SELECT, INSERT, UPDATE and DELETE queries. Implements fields to be returned
* and execute method (things that shared for all query types)
*
* @template T Generic type of dataset, inhereted from dataset object
* Can't be instantiated directly, must be extended by
* the all kinds of queries
*
* @template T Generic type of dataset, inherited from dataset object
*/
export abstract class BaseQuery<T> {
/**
* Used for INSERT query. Should be in base class by the reason of
* execute() method takes it here
* compiledRequest getter takes it here
*/
protected records: T[];

/**
* @internal
*/
protected query: Query;
protected query: Query<T>;

constructor(
protected constructor(
private queryExecuter: RequestExecuter,
private readonly action: QueryAction,
readonly dataset: string,
Expand All @@ -45,19 +53,30 @@ export abstract class BaseQuery<T> {
public fields<K extends Extract<keyof T, string>>(fields: K[]): this;
public fields<K extends Extract<keyof T, string>>(...fields: K[]): this;
public fields<K extends Extract<keyof T, string>>(field: K, ...fields: K[]): this {
this.query.Fields = Array.isArray(field) ? field : [field, ...fields];
this.query.fields = Array.isArray(field) ? field : [field, ...fields];
return this;
}

/**
* Prepare compiled request before execute it
* @template <T> Generic type of dataset model
* @returns {ICompiledRequest<T>}
*/
private get compiledRequest(): ICompiledRequest<T> {
const compiledQuery = this.query.compile();

return Object.assign(
{ action: this.action },
Object.keys(compiledQuery).length && { params: compiledQuery },
this.records && { records: this.records },
);
}

/**
* Execute this query
* @returns Result of this operation with the affected data
*/
public execute(): Promise<T[]> {
return this.queryExecuter.executeRequest(compileDataRequest({
action: this.action,
query: this.query,
records: this.records,
}));
return this.queryExecuter.executeRequest(this.compiledRequest);
}
}
9 changes: 1 addition & 8 deletions src/api/dataops/queries/deleteQuery.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// tslint:disable:no-string-literal
import { createMockFor, createRequestExecuterMock } from "../../../../spec/testUtils";
import { RequestExecuter } from "../../../internal/executer";
import { compileDataRequest } from "../../../internal/queryBasedCompiler";
import { QueryAction } from "./baseQuery";
import { DeleteQuery } from "./deleteQuery";

let createSubject = ({
Expand Down Expand Up @@ -40,13 +38,8 @@ describe("QueryRequest class", () => {
it("should correct execute the query", () => {
let qe = createRequestExecuterMock(projectID, dataset);
let subject: any = new DeleteQuery(qe, dataset);
let compiledRequest = compileDataRequest({
action: QueryAction.delete,
query: subject["query"],
records: subject["records"],
});
spyOn(subject["queryExecuter"], "executeRequest");
subject.execute();
expect(subject["queryExecuter"].executeRequest).toHaveBeenLastCalledWith(compiledRequest);
expect(subject["queryExecuter"].executeRequest).toHaveBeenLastCalledWith({action: "delete"});
});
});
8 changes: 6 additions & 2 deletions src/api/dataops/queries/filterableQuery.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ let createSubject = ({
createMockForQuery = true,
} = {}) => {
// Declare child class as long as FilterableQuery is abstract
class FilterableQueryChild<T> extends FilterableQuery<T> {};
class FilterableQueryChild<T> extends FilterableQuery<T> {
constructor(r: RequestExecuter, a: QueryAction, d: string) {
super(r, a, d);
}
}

const subject = new FilterableQueryChild<IUser>(requestExecuterMock, action, datasetName);
let queryMock = createMockForQuery ? createMockFor(Query) : new Query(datasetName);
Expand Down Expand Up @@ -75,7 +79,7 @@ describe("when instantiating a select query object", () => {
const datasetMock = createMockFor(Dataset, { returnValue: { query: testQuery } });
const queryObj = subject.relation(datasetMock);
// tslint:disable-next-line:no-string-literal
expect(queryObj["query"]["Relations"]).toEqual([testQuery]);
expect(queryObj["query"]["relations"]).toEqual([testQuery]);
});

it("should have the correct query for relation with configured query", () => {
Expand Down
4 changes: 2 additions & 2 deletions src/api/dataops/queries/filterableQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { SelectQuery } from "./selectQuery";
* dataModule.dataset("posts")
* .select()
* .where(filter => filter("date").isLessThan(lastDate))
* .relataion("authors", query => query.where(filter => filter("age").isGreaterThan(minAge)))
* .relation("authors", query => query.where(filter => filter("age").isGreaterThan(minAge)))
* .execute();
* ```
*/
Expand Down Expand Up @@ -40,7 +40,7 @@ export abstract class FilterableQuery<T> extends BaseQuery<T> {
callback: (query: SelectQuery<DatasetInterface<R>>) => SelectQuery<DatasetInterface<R>> = (q) => q,
): this {
let relation = callback(dataSet.select()) as any;
this.query.AddRelation(relation.query);
this.query.addRelation(relation.query);
return this;
}
}
12 changes: 4 additions & 8 deletions src/api/dataops/queries/insertQuery.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
// tslint:disable:no-string-literal
import { createRequestExecuterMock } from "../../../../spec/testUtils";
import { compileDataRequest } from "../../../internal/queryBasedCompiler";
import { QueryAction } from "./baseQuery";
import { InsertQuery } from "./insertQuery";

describe("InsertQuery class", () => {
Expand Down Expand Up @@ -44,13 +42,11 @@ describe("InsertQuery class", () => {
it("should correct execute the query", () => {
let qe = createRequestExecuterMock(projectID, dataset);
let subject: any = new InsertQuery(qe, [{ title: "Another first post", user_id: 1 }], dataset);
let compiledRequest = compileDataRequest({
action: QueryAction.insert,
query: subject["query"],
records: subject["records"],
});
spyOn(subject["queryExecuter"], "executeRequest");
subject.execute();
expect(subject["queryExecuter"].executeRequest).toHaveBeenLastCalledWith(compiledRequest);
expect(subject["queryExecuter"].executeRequest).toHaveBeenLastCalledWith({
action: "insert",
records: [{ title: "Another first post", user_id: 1 }],
});
});
});
9 changes: 1 addition & 8 deletions src/api/dataops/queries/selectQuery.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import { createMockFor, createRequestExecuterMock } from "../../../../spec/testU
import { MESSAGE } from "../../../config/message";
import { RequestExecuter } from "../../../internal/executer";
import { Query } from "../../../internal/query";
import { compileDataRequest } from "../../../internal/queryBasedCompiler";
import { QueryAction } from "./baseQuery";
import { SelectQuery } from "./selectQuery";

interface ITestUser {
Expand Down Expand Up @@ -120,14 +118,9 @@ describe("SelectQuery class", () => {
it("should correct execute the query", () => {
let qe = createRequestExecuterMock(projectID, dataset);
let subject: any = new SelectQuery(qe, dataset);
let compiledRequest = compileDataRequest({
action: QueryAction.select,
query: subject["query"],
records: subject["records"],
});
spyOn(subject["queryExecuter"], "executeRequest");
subject.execute();
expect(subject["queryExecuter"].executeRequest).toHaveBeenLastCalledWith(compiledRequest);
expect(subject["queryExecuter"].executeRequest).toHaveBeenLastCalledWith({action: "select"});
});

});
8 changes: 4 additions & 4 deletions src/api/dataops/queries/selectQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export class SelectQuery<T> extends FilterableQuery<T> {
* @param limit number limit records amount
*/
public limit(limit: number): this {
this.query.Limit = limit;
this.query.limit = limit;
return this;
}

Expand All @@ -40,7 +40,7 @@ export class SelectQuery<T> extends FilterableQuery<T> {
* @param offset number offset amount
*/
public offset(offset: number): this {
this.query.Offset = offset;
this.query.offset = offset;
return this;
}

Expand All @@ -54,7 +54,7 @@ export class SelectQuery<T> extends FilterableQuery<T> {
if (!field || field.length === 0) {
throw new Error(MESSAGE.QUERY.MUST_PROVIDE_SORTING_FIELD);
}
this.query.AddSortCondition("asc", ...(Array.isArray(field) ? field : field && [field, ...fields]));
this.query.addSortCondition("asc", ...(Array.isArray(field) ? field : field && [field, ...fields]));
return this;
}

Expand All @@ -68,7 +68,7 @@ export class SelectQuery<T> extends FilterableQuery<T> {
if (!field || field.length === 0) {
throw new Error(MESSAGE.QUERY.MUST_PROVIDE_SORTING_FIELD);
}
this.query.AddSortCondition("desc", ...(Array.isArray(field) ? field : field && [field, ...fields]));
this.query.addSortCondition("desc", ...(Array.isArray(field) ? field : field && [field, ...fields]));
return this;
}
}
8 changes: 3 additions & 5 deletions src/api/dataops/queries/updateQuery.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// tslint:disable:no-string-literal
import { createMockFor, createRequestExecuterMock } from "../../../../spec/testUtils";
import { RequestExecuter } from "../../../internal/executer";
import { compileDataRequest } from "../../../internal/queryBasedCompiler";
import { QueryAction } from "./baseQuery";
import { UpdateQuery } from "./updateQuery";

Expand Down Expand Up @@ -40,11 +39,10 @@ describe("QueryRequest class", () => {
it("should correct execute the query", () => {
let qe = createRequestExecuterMock(projectID, dataset);
let subject: any = new UpdateQuery(qe, [{ title: "Another first post", user_id: 1 }], dataset);
let compiledRequest = compileDataRequest({
let compiledRequest = {
action: QueryAction.update,
query: subject["query"],
records: subject["records"],
});
params: subject["query"].compile(),
};
spyOn(subject["queryExecuter"], "executeRequest");
subject.execute();
expect(subject["queryExecuter"].executeRequest).toHaveBeenLastCalledWith(compiledRequest);
Expand Down
2 changes: 1 addition & 1 deletion src/api/dataops/queries/updateQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,6 @@ export class UpdateQuery<T> extends FilterableQuery<T> {
*/
public constructor(queryExecuter: RequestExecuter, data: T, dataset: string) {
super(queryExecuter, QueryAction.update, dataset);
this.query.Data = data;
this.query.data = data;
}
}
3 changes: 1 addition & 2 deletions src/internal/executer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { ClientInit } from "../api/core/client";
import { AuthOptions, IAuthOptions, TokenManager } from "../api/core/tokenManager";
import { DataSetName } from "../api/dataops/dataops.tokens";
import { API } from "../config/config";
import { ICompiledRequest } from "./queryBasedCompiler";
import { Methods, RequestAdapter } from "./requestAdapter";

@Injectable()
Expand All @@ -16,7 +15,7 @@ export class RequestExecuter {
private tokenManager: TokenManager,
) { }

public async executeRequest(options: ICompiledRequest): Promise<any> {
public async executeRequest(options: any): Promise<any> {
await this.systemInit;
const token = await this.tokenManager.token;
return this.requestAdapter.execute(
Expand Down

0 comments on commit 1405b83

Please sign in to comment.