Code Monkey home page Code Monkey logo

ngx-api-orm's People

Contributors

dependabot[bot] avatar maurei avatar wisepotato avatar

Stargazers

 avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

Forkers

wisepotato

ngx-api-orm's Issues

Allow namespaces + additions to urls

So in a perfect world all models come from the same microservice, this would of course in the real world be a utopia.

In my case, I have a dedicated API Gateway (lets say https://graph.nao.com) and I have multiple microservices containing my model information. Lets say microservice A and B, which their urls being:

  • A : https://graph.nao.com/a/v1/*
  • B : https://graph.nao.com/b/v1/*

Now, I have a model on the first microservice, and one on the second. They share authentication, and are related to each other. Is this something we can support by means of:

@Model({namespace: 'a'})
export class ModelOnA extends Resource {
  @Field()
  public id: number;
  @Field()
  public name: string;
  @ToMany(ModelOnB): 
  public tasks: ToManyRelation<ModelOnA, ModelOnB>;
}
@Model({namespace: 'b'})
export class ModelOnB extends Resource {
  @Field()
  public id: number;
  @Field()
  public name: string;
  @ToMany(ModelOnA): 
  public tasks: ToManyRelation<ModelOnB, ModelOnA>;
}

A few things need to be done here:

  • Support namespaces
  • Support relationships between namespaces

Allow for lazy-getting To-Many relations

Given the following model

@Model()
export class B extends Resource {
	@Field()
	public id: string;
 }
@Model()
export class A extends Resource {
	@Field()
	public id: string;
	@ToMany()
	public toManyB: ToOneRelation<A,B>;
 }

Allow for

a = new A();
await a.toManyB.fetch();

Allow for optional fields

Usage:

	@Model()
	class Dummy extends Resource {
		@Field({optional: true})
		public id: number;
		@OptionalField() // is an alias for above
		public name: any;
		@ToOne({relatedResource: RelatedOne, optional: true)
		public relatedInstances: any;
		@OptionalToOne(RelatedOne)  // is an alias for the version above
		public relatedInstance: any;
	}

Allow for lazy getting of To-One relations

Given the following model

@Model()
export class B extends Resource {
	@Field()
	public id: string;
 }
@Model()
export class A extends Resource {
	@Field()
	public id: string;
	@ToOne()
	public toOneB: ToOneRelation<A,B>;
 }

Allow for

a = new A();
await a.toOneB.fetch();

JsonApi request handlers: allow for non-included relationships

Given the following models

 @Model()
export class B extends Resource {
	@Field()
	public id: string;
 }
@Model()
export class A extends Resource {
	@Field()
	public id: string;
	@ToOne()
	public toOneB: ToOneRelation<A,B>;
 }

Then, given a JSON API response

Allow for

{
  "data": [
    {
      "type": "A",
      "id": "1",
      "attributes": {},
      "relationships": {
        "toOneB": {
          "data": {
            "type": "B",
            "id": "100"
          }
        }
      }
    }
  ],
  "included": [
    {
      "type": "B",
      "id": "100",
      "attributes": {}
    }
  ]
}

will work, but

{
  "data": [
    {
      "type": "A",
      "id": "1",
      "attributes": {},
      "relationships": {
        "toOneB": {
          "data": {
            "type": "B",
            "id": "100"
          }
        }
      }
    }
  ]
}

will throw an error (expected key toOneB). This should not be the case.

Caching and TTL extension

A few feature requests

  • An internal cache
  • A TTL per resource (even per request?)
  • updating, should check if resource has been updated

This can all be condensed into using a repository as an intermediary between the model and any application logic. By using predefined Repository<TModel> we can seperate the model (DBAL) and the Caching functinoality from each other (seperation of concerns).

@Cache({TimeToLive: 500})
class TasksRepository extends Repository<Task> { }

With an implementation in a component being

class class ExampleComponent implements OnInit  {
  public tasks: Task[];
  constructor(private taskRepository: TaskRepository){
    this.tasks = this.taskRepository.all();
  }
  public async getSpecificTaskOnHover(id: number) : Task{
    return this.taskRepository.find(t => t.id == id);
  }
  public async refreshTasksOnClick(){
    this.tasks = this.taskRepository.forceRefresh();
  }
  public async updateSpecifiTaskTitle(id: number, newTitle: string){
    let task = this.taskRepository.findById(id); // alternative to find(expression)
    task.title = newTitle;
    this.taskRepository.update(task);
  }
  public async forceRefresh(){
    this.tasks = this.taskRepository.forceRefresh().all();
    /// or: this.tasks = this.taskRepository.all({forceRefresh: true});
  }
}

This should make apps more snappy using this package. Maybe related to #13

This usage of the repository pattern would allow for a few things

  • Seperation of concerns with regard to saving, updating etc
  • the ability to allow for caching and a TTL per model even per part of the application (repository instance specific config)
  • Unit Of Work
  • Allow syncing with the database every x seconds with a full refresh force available per repository

and a general sense of wellbeing :)

saving an instance creates a new object in memory

  • when creating a new instance of a resource,
 const instance = new MyResource({ ... });

the instance is not saved to the internal instance registery for that resource ( the one you access with MyResource.collection() ). Only saved instances, i.e. with an ID, are registered.

  • the constructor that is called to build the instance from the API response when callinginstance.save() will then not detect that instance in the alreadyExsists check
  • therefore a new instance is made, which is returned from the save method (which for some reason is wrapped in an array, will figure that out.
  • at this point there are two instances of MyResource floating around that represent the same data: the unsaved instance which it probably bound to your template, and the saved instance which is not used unless you explicitly do so.

Circular dependency issue

So every once in a while you need many-to-ony and one-to-many relationships, in angular you can fix this problem with the following fix: (see forwardRef):

externaltarget.ts

@Model({dashedSingularName : 'external-target', dashedPluralName: 'external-targets'})
export class ExternalTarget extends Resource {

  @Field()
  public id: string;

  @Field()
  public percentage: number;

  @Field()
  public startDate: Date;

  @Field()
  public endDate: Date;

  @ToOne(forwardRef(() => ExternalTarget))
  public employee: ToOneRelation<ExternalTarget, Employee>;


  public static async get(): Promise<ExternalTarget[]> {
    const params = new HttpParams().set('include', 'employee');
    return this.fetch();
  }
}

employee.ts

Model()
export class Employee extends Resource {
  @Field()
  public id: number;

  @Field()
  public code: string;

  @Field()
  public firstName: string;

  @Field()
  public middleName: string;

  @Field()
  public lastName: string;

  @ToMany(ExternalTarget, 'external-targets')
  public externalTargets: ToManyRelation<Employee, ExternalTarget>;

  public fullName: string;


  public static async get(): Promise<Employee[]> {
    const params = new HttpParams().set('include', 'external-targets');
    return this.fetch({ params: params });
  }

  onInit() {
    this.fullName = this.firstName + ' ' + this.middleName + ' ' + this.lastName;
  }


}

Now the circular dependency is ok, however when using json:api spec the attribute employee on external target is not an attribute but a relationship when you encounter it nested in a response.

In short: when a resource is being nested in a response in jsonapi, dont parse the relationships, they are not needed at that point. only go one level deep.

Model does not work

I have the following model:

team.resource.ts

import { ResourceModel, Resource, ResourceField } from "@ngx-api-orm/core";

@ResourceModel()
export class Team extends Resource {
  
  @ResourceField()
  public id: number;

  @ResourceField()
  public name: string;
}

For some reason, when importing this in my subcomponent as such it doesnt work:

import { Component, OnInit } from '@angular/core';
import { Team } from '../resources/team.resource';

@Component({
  selector: 'app-teams',
  templateUrl: './teams.component.html',
  styleUrls: ['./teams.component.css']
})
export class TeamsComponent implements OnInit {

  constructor() { }

  teams: Array<Team> ;

  ngOnInit() {
    this.importTeams();
  }

  async importTeams() {
    const teams: Array<Team> = await Team.fetch();
  }

}

I have a feeling this has to do with type saftey as it is returning a Promise of type Promise<Resource[]>.

save() returns an array

When save'ing a model, the newly saved model is returned as an array. Current hack is to use the first index of this list to continue,

But this is of course not the greatest way to implement this

Filter support

I would love to be able to filter the queries sent to the api. A very basic example would include support for the following: JsonApitDotNetCore. Which would include the following:

?filter[attribute]=eq:value
?filter[attribute]=ne:value
?filter[attribute]=lt:value
?filter[attribute]=gt:value
?filter[attribute]=le:value
?filter[attribute]=ge:value
?filter[attribute]=like:value
?filter[attribute]=in:value

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.