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

Feature: Implementing pagination strategy and data source driver interface #12

Merged
merged 1 commit into from
Jul 4, 2022

Conversation

kokokuo
Copy link
Contributor

@kokokuo kokokuo commented Jun 9, 2022

Description

This PR includes some components for two parts:

1. Support setting pagination parameter to provide mapping query parameter in API

Implementing pagination strategy abstract class and its sub-classes

Add PaginationStragtegy abstract class and support OffsetBasedStragtegy, CursorBasedStragtegy, KeysetBasedStragtegy sub-clasesses for transforming the koa ctx request to pagination format.

image

2. Add data source driver interface

Add IDataSource interface for implementation in the future, the return format currently returns objects temporarily.

How To Test / Expected Results

For the test result, please see the below test cases that passed the unit test:
image

Commit Message

  • faf6f08 - feat(serve): add interface of data source, pagination, and pass to the data query builder
    • add pagination field in APISchema.
    • add PaginationStrategy interface and offset, cursor, keyset based strategy class.
    • add PaginationTransformer to do transfrom from koa ctx .
    • add data source interface IDataSource.
    • update test cases of each SQL clause method of DataQueryBuilder.

@kokokuo kokokuo requested review from oscar60310 and wwwy3y3 June 9, 2022 09:51
@kokokuo kokokuo self-assigned this Jun 9, 2022
@kokokuo kokokuo force-pushed the feature/data-query-builder branch from f5ffb0e to 184e2aa Compare June 14, 2022 02:49
@kokokuo kokokuo force-pushed the feature/pagination-stragtegy branch from 59b8b30 to faf6f08 Compare June 14, 2022 02:56
@kokokuo kokokuo force-pushed the feature/data-query-builder branch from 184e2aa to 7bf9ed3 Compare June 16, 2022 07:40
Copy link
Contributor

@oscar60310 oscar60310 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other part LGTM

) {
// TODO: implement query by dataset
ctx.response.body = reqParams;
ctx.response.body = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just want to confirm: Would we call templateEngine.execute(name, req, pagination) here?

Copy link
Contributor Author

@kokokuo kokokuo Jun 16, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for reviewing, yes, the original is called templateEngine.render, right?

export class OffsetBasedStrategy extends PaginationStrategy<OffsetPagination> {
public async transform(ctx: KoaRouterContext) {
const checkFelidInHeader = ['limit', 'offset'].every((field) =>
Object.keys(ctx.request.header).includes(field)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall we move "paginate transforms" to "route-component" level? I guess you use header here in order because of using the same transformer in both restful and graphql routes, but indicating pagination in headers is strange.

We use in params or arguments more often:
/users?offset=100&limit=10

users(offset: 100, limit: 10) {
  id
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for reviewing and finding the weird part, it should pass by query string would be correct.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It has been fixed and move the "paginate transforms" to "route-component" level, please check it.


export class KeysetBasedStrategy extends PaginationStrategy<KeysetPagination> {
public async transform(ctx: KoaRouterContext) {
const checkFelidInHeader = ['limit', 'createAt'].every((field) =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. We won't always use the key "createAt" to paginate, I think we can let users definition their keys in schema file.
  2. I don't know the difference between keyset and cursor strategy. IMO, they both use a "key" to select limited data below or above the row and return a page. Can we merge these two strategies?

Copy link
Contributor Author

@kokokuo kokokuo Jun 16, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for reviewing and suggesting.

After discussing with Ivan, we will keep the keyset strategy now for the second part, because not sure how the Driver section handles pagination.

For the first part - define the key name in the schema to define what key used in keyset pagination has been added in KeysetBaseStrategy, please check it.

);
if (!checkFelidInHeader)
throw new Error(
`The ${PaginationMode.KEYSET} must provide limit and offset in header.`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrong message here~

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for reviewing and finding wrong message, it has been fixed.

@kokokuo kokokuo force-pushed the feature/pagination-stragtegy branch 5 times, most recently from 3d5a65e to 1c16da8 Compare June 16, 2022 10:12

export class CursorBasedStrategy extends PaginationStrategy<CursorPagination> {
public async transform(ctx: KoaRouterContext) {
const checkFelidInHeader = ['limit', 'cursor'].every((field) =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're still checking the "header", it should be query too :D

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, thanks founding the missing part, it has been fix~

@@ -0,0 +1,38 @@
import { KoaRouterContext } from '@route/route-component';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It has been fixed and move the "paginate transforms" to "route-component" level, please check it.

Nice, but I think we should do some further action.

Shall we move "paginate transforms" to "route-component" level? I guess you use header here in order because of using the same transformer in both restful and graphql routes, but indicating pagination in headers is strange.

We use in params or arguments more often: /users?offset=100&limit=10

users(offset: 100, limit: 10) {
  id
}

I meant we might need to move the transform code into routers, because the pagination arguments come from different ways.

restfulRoute.ts

 protected async handleRequest(
    ctx: KoaRouterContext,
    reqParams: RequestParameters,
  ) {
   // handle user?offset=10&limit=10
    const pagination = someFunctionToGetPaginationViaQueryParameters(ctx);
    // TODO: implement query by dataset
    ctx.response.body = {
      reqParams,
      pagination,
    };
    return;
  }

graphQLRoute.ts

 protected async handleRequest(
    ctx: KoaRouterContext,
    reqParams: RequestParameters,
  ) { 
    // handle user(offset: 10, limit; 10) { }
    const pagination = someFunctionToGetPaginationViaRequestBody(ctx);
    // TODO: implement query by dataset
    ctx.response.body = {
      reqParams,
      pagination,
    };
    return;
  }

Copy link
Contributor Author

@kokokuo kokokuo Jun 20, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, @oscar60310, thanks for catching the graphql and restful handle request content, not a consistent part!

Currently, I have refactored for separating the request and pagination transformer to a prepare method, and made graphql* and restful route implemented prepare method to handle request and pagination transformer

However, currently, the graphql route is not implemented the prepare method because we have not yet developed graphql part ).

restfulRoute.ts:

public async respond(ctx: KoaRouterContext) {
    const transformed = await this.prepare(ctx);
    await this.handle(transformed);
   // TODO: get template engine handled result and return response by checking API schema
    ctx.response.body = {
      ...transformed,
    };
  }

  protected async prepare(ctx: KoaRouterContext) {
    const reqParams = await this.reqTransformer.transform(ctx, this.apiSchema);
    await this.reqValidator.validate(reqParams, this.apiSchema);
    const pagination = await this.paginationTransformer.transform(
      ctx,
      this.apiSchema
    );
    return {
      reqParams,
      pagination,
    };
  }

grapqlRoute.ts:

 protected async prepare(ctx: KoaRouterContext) {
    /**
     * TODO: the graphql need to transform from body.
     * Therefore, current request and pagination transformer not suitable (need to provide another graphql transform method or class)
     */

    return {
      reqParams: {},
    };
  }

  public async respond(ctx: KoaRouterContext) {
    const transformed = await this.prepare(ctx);
    await this.handle(transformed);
   // TODO: get template engine handled result and return response by checking API schema
    return transformed;
  }

@kokokuo kokokuo force-pushed the feature/pagination-stragtegy branch 2 times, most recently from b51e0c8 to 7dabe61 Compare June 20, 2022 04:09
@kokokuo kokokuo force-pushed the feature/data-query-builder branch from 7bf9ed3 to c36c855 Compare July 4, 2022 02:07
Base automatically changed from feature/data-query-builder to feature/serve-ioc-container July 4, 2022 02:09
…a query builder

- add pagination field in "APISchema".
- add "PaginationStrategy" interface and offset, cursor, keyset based strategy class.
- add "PaginationTransformer" to do transfrom from koa ctx .
- add data source inerface "IDataSource".
- refactor "BaseRoute" for splting request and pagination transformer to implmented restful & graphql class, because of graphql get request from body.
- update test cases of each sql clause method of "DataQueryBuilder".
@kokokuo kokokuo force-pushed the feature/pagination-stragtegy branch from 7dabe61 to f9cec9b Compare July 4, 2022 02:17
@kokokuo kokokuo merged commit bac2982 into feature/serve-ioc-container Jul 4, 2022
@kokokuo kokokuo deleted the feature/pagination-stragtegy branch July 4, 2022 02:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants