Skip to content

Commit

Permalink
feat(queries): migrate data queries to the observables (#134)
Browse files Browse the repository at this point in the history
  • Loading branch information
KutsenkoA committed Dec 20, 2019
1 parent c94d788 commit bd8143f
Show file tree
Hide file tree
Showing 9 changed files with 317 additions and 309 deletions.
495 changes: 220 additions & 275 deletions extra_docs/dataset/README.md

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions extra_docs/jfs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,10 +187,10 @@ These fields, as well as any custom fields, can be used for select queries (but
#### Select query
```typescript
const files = await jfs.fileset("fileset_name")
jfs.fileset("fileset_name")
.select("name", "url")
.where(field => field("size").isGreaterThan(1024000))
.execute();
.subscribe();

// array of files that fit to the condition will be returned
// files === [{ name: "file1.jpj", url: "https://..." }, {...}, ...]
Expand All @@ -200,15 +200,15 @@ const files = await jfs.fileset("fileset_name")
Update fileset in the same way as dataset. Updating a fileset record with new file is not supported.
```typescript
await jfs.fileset("fileset_name")
jfs.fileset("fileset_name")
.update({ "isDefaultImage": false })
.where(field => field("name").isEqualTo("companyLogo.png"))
.execute();
.subscribe();
```
#### Delete query
```typescript
await jfs.fileset("fileset_name")
jfs.fileset("fileset_name")
.delete()
.where(field => field("size").isGreaterThan(1024000))
.execute();
.subscribe();
```
6 changes: 3 additions & 3 deletions src/api/core/queries/actionQuery.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ describe("ActionQuery class", () => {
filter,
);

subject.execute();
subject.subscribe();

expect(requestExecuterMock.executeRequest).toHaveBeenLastCalledWith(
expect.objectContaining({
Expand All @@ -95,7 +95,7 @@ describe("ActionQuery class", () => {
field("id").isInArray(fakeIdsList),
);

subject.execute();
subject.subscribe();

expect(requestExecuterMock.executeRequest).toHaveBeenLastCalledWith(
expect.objectContaining({
Expand Down Expand Up @@ -124,7 +124,7 @@ describe("ActionQuery class", () => {
field("id").isInArray(ids),
);

subject.execute();
subject.subscribe();

expect(requestExecuterMock.executeRequest).toHaveBeenLastCalledWith(
expect.objectContaining({
Expand Down
70 changes: 65 additions & 5 deletions src/api/core/queries/baseQuery.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,15 @@ const createSubject = ({
method = DEFAULT_REQUEST_METHOD,
resourceName = RESOURCE_NAME,
resourceType = RESOURCE_TYPE,
requestExecuterMock = createMockFor(RequestExecuter),
requestExecutorResult = [{ field: faker.random.word() }],
requestExecutorError = null,
requestExecuterMock = createMockFor(RequestExecuter, {
returnValue: new Promise((res, rej) => {
requestExecutorError
? rej(requestExecutorError)
: res(requestExecutorResult);
}),
}),
} = {}) => {
// Declare child class as long as BaseQuery class is abstract
class BaseQueryChild<T> extends BaseQuery<T> {
Expand All @@ -37,6 +45,8 @@ const createSubject = ({
resourceType,
subject,
requestExecuterMock,
requestExecutorResult,
requestExecutorError,
};
};

Expand Down Expand Up @@ -167,10 +177,60 @@ describe("compiledRequest method", () => {
});
});

describe("execute method", () => {
it("should call queryExecuter.executeRestRequest()", () => {
let { subject, requestExecuterMock } = createSubject();
subject.execute();
describe("query as observable", () => {
it("should not execute request if there is no subscriber", () => {
const { requestExecuterMock } = createSubject();

expect(requestExecuterMock.executeRequest).not.toHaveBeenCalled();
});

it("should execute request once there is a subscriber", () => {
const { subject, requestExecuterMock } = createSubject();

subject.subscribe();

expect(requestExecuterMock.executeRequest).toHaveBeenCalled();
});

it("should emit query execution result to the subscriber", (done) => {
const { subject, requestExecutorResult } = createSubject();

subject.subscribe((result) => {
expect(result).toEqual(requestExecutorResult);
done();
});
});

it("should complete an observable once result is obtained", (done) => {
const { subject } = createSubject();

const completeSpy = jasmine.createSpy("completeSpy");

subject.subscribe({ complete: () => {
completeSpy();
expect(completeSpy).toHaveBeenCalled();
done();
}});
});

it("should throw an error to the subscriber", (done) => {
const { subject, requestExecutorError } = createSubject({
requestExecutorError: new Error(faker.lorem.sentence()) as any });

subject.subscribe({ error: (error) => {
expect(error).toEqual(requestExecutorError);
done();
}});
});

it("should execute requests as many times as many subscribers are", () => {
const { subject, requestExecuterMock } = createSubject();
const times = faker.random.number({ min: 3, max: 10 });

for (let i = 0; i < times; i++) {
subject.subscribe();
}

expect(requestExecuterMock.executeRequest).toHaveBeenCalledTimes(times);
});
});
19 changes: 17 additions & 2 deletions src/api/core/queries/baseQuery.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Observable } from "rxjs";
import { RequestExecuter } from "../../../internal/executer";
import { IRequestExecuterData } from "../../../internal/executer.interfaces";
import { IAggField, Query } from "../../../internal/query";
Expand All @@ -12,8 +13,9 @@ import { ResourceType } from "../resource";
* the all kinds of queries
*
* @template T Generic type of dataset, inherited from dataset object
* @template D Extended dataset type with default fields (i.e id, created_at, updated_at)
*/
export abstract class BaseQuery<T> {
export abstract class BaseQuery<T, D extends T = any> extends Observable<T[]> {
/**
* @internal
*/
Expand All @@ -22,14 +24,23 @@ export abstract class BaseQuery<T> {
* Body of request
* @returns T | T[]
*/
protected abstract get body(): T | T[] | null;
protected abstract get body(): T | T[] | D | D[] | null;

protected constructor(
protected queryExecuter: RequestExecuter,
protected readonly method: RequestMethod,
protected readonly resourceType: ResourceType,
protected readonly resourceName: string,
) {
super((subscriber) => {
this.queryExecuter.executeRequest(this.compiledRequest)
.then((result: T[]) => {
subscriber.next(result);
subscriber.complete();
})
.catch((reason) => subscriber.error(reason));
});

this.query = new Query<T>();
}

Expand Down Expand Up @@ -64,8 +75,12 @@ export abstract class BaseQuery<T> {
/**
* Execute this query
* @returns Result of this operation with the affected data
* @deprecated Use subscribe() instead
*/
public execute(): Promise<T[]> {
console.warn("=============================================");
console.warn(" execute() method is DEPRECATED.\n Please, consider using .subscribe() instead");
console.warn("=============================================");
return this.queryExecuter.executeRequest(this.compiledRequest);
}
}
5 changes: 3 additions & 2 deletions src/api/core/queries/insertQuery.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,13 @@ describe("InsertQuery class", () => {

it("should execute the query with the correct parameters", () => {
spyOn(qe, "executeRequest");
subject.execute();
subject.subscribe();
expect(qe.executeRequest).toHaveBeenLastCalledWith({
method: RequestMethod.POST,
body: [fakeRecord],
resourceType,
resourceName
resourceName,
queryParams: []
});
});
});
15 changes: 1 addition & 14 deletions src/api/core/queries/insertQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { BaseQuery } from "./baseQuery";
* @template T Generic type of your dataset, default to any
* @template D Extended dataset type with default fields (i.e id, created_at, updated_at)
*/
export class InsertQuery<T, D extends T> extends BaseQuery<T> {
export class InsertQuery<T, D extends T> extends BaseQuery<D> {
/**
* @inheritdoc
*/
Expand All @@ -33,17 +33,4 @@ export class InsertQuery<T, D extends T> extends BaseQuery<T> {
super(queryExecuter, RequestMethod.POST, resourceType, resourceName);
this.body = records;
}

/**
* Overload parent execute request to call rest API
* @returns {Promise<D[]>}
*/
public execute(): Promise<D[]> {
return this.queryExecuter.executeRequest({
resourceType: this.resourceType,
resourceName: this.resourceName,
method: this.method,
body: this.body,
});
}
}
2 changes: 1 addition & 1 deletion src/api/core/queries/selectQuery.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ describe("SelectQuery class", () => {
const { subject } = createSubject();
subject.fields("id");
spyOn(subject["queryExecuter"], "executeRequest");
subject.execute();
subject.subscribe();
expect(subject["queryExecuter"].executeRequest).toHaveBeenLastCalledWith((subject as any).compiledRequest);
});

Expand Down
2 changes: 1 addition & 1 deletion src/api/core/queries/updateQuery.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ describe("QueryRequest class", () => {
requestExecuterMock: createRequestExecuterMock() as SpyObj<RequestExecuter>,
});
spyOn(requestExecuterMock, "executeRequest");
subject.execute();
subject.subscribe();
expect(requestExecuterMock.executeRequest).toHaveBeenLastCalledWith(subject["compiledRequest"]);
});
});

0 comments on commit bd8143f

Please sign in to comment.