Akita: NgEntityService with OData pattern

Created on 31 Jan 2020  路  9Comments  路  Source: datorama/akita

I'm submitting a...


[ ] Regression (a behavior that used to work and stopped working in a new release)
[ ] Bug report  
[ ] Performance issue
[ ] Feature request
[ ] Documentation issue or request
[x] Support request
[ ] Other... Please describe:

Support request


I have a backend with OData pattern and I wish to use NgEntityService, but it needs to perform the url with some minimum differences like:

  • GET api/heroes/5 should be GET api/heroes(5);
  • Odata returns a wrapper object containing @odata.context and data array inside value property, so I need to map every http call.

Is it possible to do just extending the NgEntityService class? I'm taking a look here at the code and the url formating is very hard coded, there is no helper for it that I could override and do all other steps as it is in NgEntityService.

cheers!

enhancement

Most helpful comment

Great! Will do it

All 9 comments

Just to add what I'm currently doing.

I'm extending NgEntityService and overloading some methods:

import { NgEntityService, HttpConfig, Msg, isID } from '@datorama/akita-ng-entity-service';
import { EntityState, isDefined, getEntityType } from '@datorama/akita';
import { ODataQuery } from 'odata-fluent-query';
import * as _ from 'lodash';

/**
 * Parses query string into Angular HttpParams object
 * @param query ODataQuery Object
 */
export function queryToObject(query: ODataQuery<any>) {
  return _(query.toString())
    .split('&')
    .flatMap(q => {
      const [key, value] = q.split('=');
      return {key, value};
    })
    .keyBy('key')
    .mapValues('value')
    .valueOf();
}

// @ts-ignore
export class ODataEntityService<S extends EntityState> extends NgEntityService<S> {
  /**
   *
   * Get all or one entity - Creates a GET request
   *
   * service.get().subscribe()
   * service.get({ headers, params, url })
   *
   * service.get(id)
   * service.get(id, { headers, params, url })
   *
   */
  get<T = getEntityType<S>>(config?: HttpConfig & { append?: boolean } & Msg & { query?: ODataQuery<any> }): Observable<T>;
  get<T = getEntityType<S>>(id?: getEntityType<S>['id'], config?: HttpConfig & { append?: boolean } & Msg & { query?: ODataQuery<any> }): Observable<T>;
  get<T = getEntityType<S>>(
    idOrConfig?: getEntityType<S>['id'] | HttpConfig,
    config?: HttpConfig & { append?: boolean; upsert?: boolean } & Msg & { query?: ODataQuery<any> }
  ): Observable<T> {
    const isSingle = isID(idOrConfig);
    const _config: HttpConfig & { append?: boolean; upsert?: boolean } & Msg & { query?: ODataQuery<any> } = (isSingle ? config : idOrConfig) || {};

    if (isSingle) {
      _config.mapResponseFn = _config.mapResponseFn || (res => {
        delete res['@odata.context'];
        return res;
      });
    } else {
      _config.mapResponseFn = _config.mapResponseFn || (res => res.value);
    }

    if (isSingle && !_config.url) {
      _config.url = isSingle ? `${this.api}(${idOrConfig})` : this.api;
    }

    if (_config.query) {
      _config.params = queryToObject(_config.query);
    }

    return super.get(
      (isSingle ? idOrConfig : _config) as any,
      _config
    );
  }

  private resolveUrl(config: HttpConfig, id?: any) {
    const customUrl = (config || {}).url;
    if (isDefined(id)) {
      return customUrl || `${this.api}(${id})`;
    }

    return customUrl || this.api;
  }
}

I needed to add a new config property that is a custom helper for OData query params. It's working, but I still think it's too verbose, maybe there's a simpler way to do it.

And another problem is that I needed to override resolveUrl but it's a private method, so I needed to add @ts-ignore to prevent ts errors.

We can add a URLBuilder provider that you can override. Do you want to create a PR?

@NetanelBasal well, I can have a try... I've been thinking, what if akita had an OData module, just like akita firebase. Do you think it's worthwhile? I can help to make it solid with my experience.

Sure, you can provide it as an external package that implements EntityService, and I will add it to the official docs.

Great! Will do it

@NetanelBasal

Hi... I've been working since then to create this lib and test it. I've developed as I was working on one of my projects and I think now it's ready to be released.

The project is under https://github.com/rosostolato/akita-ng-odata-service

How can we proceed?

That's great! Publish the library, and I will link it from our official docs.

Fine! It's already published on npm. Thank you!

[email protected]

Added a link in our offical docs. Thanks!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

hoisel picture hoisel  路  5Comments

DanielNetzer picture DanielNetzer  路  5Comments

twittwer picture twittwer  路  7Comments

stephencarr picture stephencarr  路  4Comments

RonnieRocket147 picture RonnieRocket147  路  5Comments