⚠ If you use old View Engine
compilation or Angular 6.x.x - 11.x.x you need to use 2.x.x lib version.
See more about it here.
-
Versions that work with old
View Engine
compilation [2.0.0
-2.x.x
]. -
Versions that work with new
Ivy
compilation [3.0.0
-x.x.x
].
This client can be used to develop Angular 12+
applications working with RESTfull server API.
By RESTfull API
means when the server application implements all the layers of the Richardson Maturity Model
and the server provides HAL/JSON response type.
This client compatible with Java server-side applications based on Spring HATEOAS or Spring Data REST.
This client is a continuation of the @lagoshny/ngx-hal-client. You can find out about the motivation to create a new client here. To migrate from
@lagoshny/ngx-hal-client
to this client you can use the migration guide.
You can found examples of usage this client with task-manager-front application that uses server-side task-manager-back application.
- Decorators
- BaseResource
- Resource
- EmbeddedResource
- ResourceCollection
- PagedResourceCollection
- Subtypes support
- Resource projection support
- GetResource
- GetCollection
- GetPage
- CreateResource
- UpdateResource
- UpdateResourceById
- PatchResource
- PatchResourceById
- DeleteResource
- DeleteResourceById
- SearchResource
- SearchCollection
- SearchPage
- CustomQuery
- CustomSearchQuery
Learn about the latest improvements.
To install the latest version use command:
npm i @lagoshny/ngx-hateoas-client@latest --save
Before start, need to configure NgxHateoasClientModule
and pass configuration through NgxHateoasClientConfigurationService
.
NgxHateoasClientModule
configuration:
import { NgxHateoasClientModule } from '@lagoshny/ngx-hateoas-client';
...
@NgModule({
...
imports: [
...
NgxHateoasClientModule.forRoot()
...
]
...
})
export class AppModule {
...
}
- In constructor app root module inject
NgxHateoasClientConfigurationService
and pass a configuration:
import { ..., NgxHateoasClientConfigurationService } from '@lagoshny/ngx-hateoas-client';
...
export class AppModule {
constructor(hateoasConfig: NgxHateoasClientConfigurationService) {
hateoasConfig.configure({
http: {
rootUrl: 'http://localhost:8080/api/v1'
}
});
}
}
Configuration has only one required param is
rootUrl
mapped to the server API URL. Also, you can configureproxyUrl
when use it in resource links. See more about other a configuration params here.
To represent model class as a resource model extend model class by Resource
class.
Besides you need to decorate the class with @HateoasResource decorator that will register your resource class with passed resourceName
in hateoas-client
.
Suppose you have some Product
model class:
export class Product {
public name: string;
public cost: number;
}
After making it as a Resource
it will look like this:
import { Resource, HateoasResource } from '@lagoshny/ngx-hateoas-client';
/*
Resource name 'products' should be map to server-side resource name that used to build resource self URL.
For example in this case it can be: http://localhost:8080/api/v1/products
*/
@HateoasResource('products')
export class Product extend Resource {
public name: string;
public cost: number;
}
Thereafter, the Product
class will have Resource
methods to work with the product's relations through resource links.
You can create a resource projection class that will map to a server-side projection model. How to do it read in this section.
Also, you can extend model classes with the
EmbeddedResource
class and decorate with @HateasEmbeddedResource decorator when the model class used as an embeddable entity. You can read more aboutEmbeddedResource
here.
It is recommended also to declare the Product
resource class (others Resources
and EmbeddedResources
too) in the hateoas-client
configuration:
...
hateoasConfig.configure({
...
useTypes: {
resources: [Product]
}
}
...
See more about
useTypes
in the configuration section here.
Now you have created a resource class and ready to perform requests for this resource. For this you can use universal on resource built-in HateoasResourceService or a create custom resource service for concrete resource class.
The library has built-in universal on resource HateoasResourceService
.
It is a simple service with methods to get
/create
/update
/delete
/search
resources.
To use it inject HateoasResourceService
to a component or a service class after that you can perform resource requests by passing the resource type.
import { Resource, HateoasResource, HateoasResourceService } from '@lagoshny/ngx-hateoas-client';
@HateoasResource('products')
export class Product extends Resource {
...
}
@Component({
...
})
export class SomeComponent {
constructor(private resourceService: HateoasResourceService) {
}
onSomeAction() {
const product = new Product();
product.cost = 100;
product.name = 'Fruit';
this.resourceService.createResource(Product, product)
.subscribe((createdResource: Product) => {
// TODO something
});
};
}
Each HateoasResourceService
method has the first param is the resource type that extends Resource class and decorated with @HateoasResource.
The resource type uses to build a URL for resource requests and create resources with a concrete class when parsing the server's answer.
More about HateoasResourceService
methods see here.
HateoasResourceService
is a universal service that can work with severalResources
at a time, you need only pass a desired resource type as the first param. If you have not extra logic to work with your resources then you can inject onceHateoasResourceService
and use it to work with your resources.
When you have some logic that should be preparing resource before a request you need to create a custom resource service extends HateoasResourceOperation
to see more about this here.
To create a custom resource service create a new service and extends it with HateoasResourceOperation
and pass resourceType
to the parent constructor.
import { Resource, HateoasResource, HateoasResourceOperation } from '@lagoshny/ngx-hateoas-client';
@HateoasResource('products')
export class Product extends Resource {
...
}
@Injectable({providedIn: 'root'})
export class ProductService extends HateoasResourceOperation<Product> {
constructor() {
super(Product);
}
}
HateoasResourceOperation
has the same methods as HateoasResourceService
without resourceType
as the first param (because you pass resourceType
with service constructor).
To test your services, that are using HateoasResourceOperation
or HateoasResourceService
you need import NgxHateoasClientModule.forRoot()
in the test module.
After that you can inject or mock the next services (if you need it):
NgxHateoasClientConfigurationService
HateoasResourceService
Below you can find simple lib test examples:
Suppose you have the User
and some UserService
like that:
import {
HateoasResourceOperation,
HateoasResourceService,
} from '@lagoshny/ngx-hateoas-client';
export class User {
name: string;
age: number;
}
@Injectable()
export class UserService extends HateoasResourceOperation<User> {
constructor(public resourceService: HateoasResourceService) {
super(User);
}
public create(user: User): Observable<Observable<never> | User> {
return super.createResource({body: user});
}
public getAllUsersByAge(age: number): Observable<PagedResourceCollection<User>> {
return this.resourceService.searchPage(User, 'allByAge', {
params: {
age
}
}
);
}
}
Note, UserService
extends HateoasResourceOperation
and uses HateoasResourceService
to perform requests.
If you prefer to use standard TestBed
for testing, you can do that in the following way:
import {
HateoasResourceService,
NgxHateoasClientModule,
PagedResourceCollection,
ResourceCollection
} from '@lagoshny/ngx-hateoas-client';
import { TestBed, waitForAsync } from '@angular/core/testing';
import { of } from 'rxjs';
describe('UserServiceTest', () => {
let hateoasResourceServiceSpy;
beforeEach(() => {
hateoasResourceServiceSpy = {
createResource: jasmine.createSpy('createResource'),
searchPage: jasmine.createSpy('searchPage')
};
TestBed.configureTestingModule({
imports: [
NgxHateoasClientModule.forRoot()
],
providers: [
UserService,
{provide: HateoasResourceService, useValue: hateoasResourceServiceSpy}
]
});
});
it('should init service', () => {
const userService = TestBed.inject(UserService);
expect(userService).toBeTruthy();
});
it('should create new user', waitForAsync(() => {
const userService = TestBed.inject(UserService);
const newUser = new User();
newUser.id = '1';
hateoasResourceServiceSpy.createResource.and.returnValue(of(newUser));
const user = new User();
user.name = 'Test user';
userService.create(user).subscribe((createdUser: User) => {
expect(createdUser).toBeTruthy();
expect(createdUser.id).toEqual('1');
});
}));
it('should return paged user list', waitForAsync(() => {
const userService = TestBed.inject(UserService);
const returnedUser = new User();
returnedUser.id = '1';
const resourceCollection = new ResourceCollection<User>();
resourceCollection.resources = [returnedUser];
hateoasResourceServiceSpy.searchPage.and.returnValue(of(new PagedResourceCollection(resourceCollection)));
userService.getAllUsersByAge(35).subscribe((users: PagedResourceCollection<User>) => {
expect(users).toBeTruthy();
expect(users.resources).toBeTruthy();
expect(users.resources[0]).toBeTruthy();
expect(users.resources[0].id).toEqual('1');
});
}));
});
If you prefer to use @ngneat/spectator for testing, you can do that in the following way:
import {
HateoasResourceService,
NgxHateoasClientModule,
PagedResourceCollection,
ResourceCollection
} from '@lagoshny/ngx-hateoas-client';
import { waitForAsync } from '@angular/core/testing';
import { of } from 'rxjs';
import { createServiceFactory, SpectatorService } from '@ngneat/spectator';
describe('UserServiceTest', () => {
let spectator: SpectatorService<UserService>;
const createService = createServiceFactory({
imports: [NgxHateoasClientModule.forRoot()],
service: UserService,
mocks: [HateoasResourceService]
});
beforeEach(() => {
spectator = createService();
});
it('should init service', () => {
const userService = spectator.inject(UserService);
expect(userService).toBeTruthy();
});
it('should create new user', waitForAsync(() => {
const userService = spectator.inject(UserService);
const newUser = new User();
newUser.id = '1';
const hateoasResourceServiceMock = spectator.inject(HateoasResourceService);
hateoasResourceServiceMock.createResource.and.returnValue(of(newUser));
const user = new User();
user.name = 'Test user';
userService.create(user).subscribe((createdUser: User) => {
expect(createdUser).toBeTruthy();
expect(createdUser.id).toEqual('1');
});
}));
it('should return paged user list', waitForAsync(() => {
const userService = spectator.inject(UserService);
const returnedUser = new User();
returnedUser.id = '1';
const resourceCollection = new ResourceCollection<User>();
resourceCollection.resources = [returnedUser];
const hateoasResourceServiceMock = spectator.inject(HateoasResourceService);
hateoasResourceServiceMock.searchPage.and.returnValue(of(new PagedResourceCollection(resourceCollection)));
userService.getAllUsersByAge(35).subscribe((users: PagedResourceCollection<User>) => {
expect(users).toBeTruthy();
expect(users.resources).toBeTruthy();
expect(users.resources[0]).toBeTruthy();
expect(users.resources[0].id).toEqual('1');
});
}));
});
There are several types of resources: the main resource type is Resource represents the server-side entity model class. If the server-side model has Embeddable entity type then use EmbeddedResource type instead Resource type.
Both Resource and EmbeddedResource have some the same methods therefore they have common parent BaseResource class implements these methods.
Also, you can create Resource
class to represent resource projection, see more about resource projection here.
Each
Resource
/EmbeddedResource
has decorators @HateoasResource/@HateoasEmbeddedResource respectively. They're used to register info about your resources inhateoas-client
. For example,resourceName
that used to build resource requests.
To work with resource collections uses ResourceCollection type its holds an array of the resources. When you have a paged collection of resources result use an extension of ResourceCollection is PagedResourceCollection that allows you to navigate by pages and perform custom page requests.
In some cases, the server-side can have an entity inheritance model how to work with entity subtypes, you can found here.
@HateoasResource
decorator use to register your Resource
classes in hateoas-client
with passed resourceName
as decorator's param.
resourceName
should be equals to the server-side resource name that uses to represent self resource link.
For example, you need to work with Shop
resource:
import { Resource, HateoasResource } from '@lagoshny/ngx-hateoas-client';
@HateoasResource('shops')
export class Shop extends Resource {
...
}
It means that server-side use shops
as resource name for Shop
entity and it is resource self-link seem like: http://localhost:8080/api/v1/shops
(with the assumption that server's root URL is http://localhost:8080/api/v1
)
It is required to mark your
Resource
classes with this decorator otherwise you will get an error when performing resource request
@HateoasEmbeddedResource
decorator use to register your EmbeddedResource
classes in hateoas-client
with passed relationNames
as decorator's param.
relationNames
is an array of the names where each is the name of relation with whichEmbeddedResource
using inResource
class.
For example, you have Client
resource that using Address
embedded resource:
import { EmbeddedResource, Resource, HateoasEmbeddedResource, HateoasResource } from '@lagoshny/ngx-hateoas-client';
@HateoasEmbeddedResource(['clientAddress'])
export class Address extends EmbeddedResource {
...
}
@HateoasResource('clients')
export class Client extends Resource {
...
public clientAddress: Address;
...
}
In this case, @HateoasEmbeddedResource.relationNames
has one element (clientAddress
) that equals to property Address
name in Client
resource.
If embedded resource Address
will use in several resources then @HateoasEmbeddedResource.relationNames
should have all different property Address
names that used in these resources.
It is required to mark your
EmbeddedResource
classes with this decorator if wou want get concrete embedded resource class (in this exampleAddress
class). Otherwise you will get a warning about creating an embedded resource when parsing server's answer with the defaultEmbeddedResource
class when performing resource request that using an embedded resource.
@HateoasProjection
decorator use to register your projection resource classes in hateoas-client
with passed resourceType
and projectionName
as decorator's params.
resourceType
equals to resource type that use this resource projection.projectionName
should be equals to the server-side resource projection name.
For example, you have Shop
resource projection with the name shopProjection
:
import { Resource, HateoasResource, HateoasProjection } from '@lagoshny/ngx-hateoas-client';
@HateoasResource('shops')
export class Shop extends Resource {
...
}
@HateoasProjection(Shop, 'shopProjection')
export class ShopProjection extends Resource {
...
}
Using @HateoasProjection
you can create separate resource projection classes with desired resource properties and relations.
For resource projection relations to another resource, you need to wrap these relations with ProjectionRelType type and mark these relations with @ProjectionRel decorator with a relation resource type.
- ProjectionRelType will hide
Resource
/EmbeddedResource
methods for projection relation that will lead clear projection relation interface. - @ProjectionRel decorator will be used to create relation with concrete resource type when parsing server-side answer.
See more about resource projection support here.
@ProjectionRel
decorator use to register projection relation resource type in hateoas-client
with passed relationType
as decorator's param.
relationType
equals to resource type that used as a relation property in resource projection.
For example, you have Shop
resource projection with the name shopProjection
and this projection has relation to Cart
resource:
import { Resource, HateoasResource, HateoasProjection, ProjectionRel, ProjectionRelType } from '@lagoshny/ngx-hateoas-client';
import { ProjectionRelType } from './declarations';
@HateoasResource('carts')
export class Cart extends Resource {
...
}
@HateoasProjection(Shop, 'shopProjection')
export class ShopProjection extends Resource {
...
@ProjectionRel(Cart)
public cart: ProjectionRelType<Cart>;
...
}
Using @ProjectionRel
decorator allows knowinghateoas-client
which relation resource type needs to use when creating a resource from the serv-side answer.
Here used
ProjectionRelType
type as a wrapper forCart
resource class, to hideResource
/EmbeddedResource
methods in projection relation. See more here about this type.
Examples of usage resource relation methods rely on presets.
-
Resource classes are
import { Resource, HateoasResource } from '@lagoshny/ngx-hateoas-client'; @HateoasResource('carts') export class Cart extends Resource { public shop: Shop; public products: Array<Product>; public status: string; public client: PhysicalClient; } @HateoasResource('shops') export class Shop extends Resource { public name: string; public rating: number; } @HateoasResource('products') export class Product extends Resource { public name: string; public cost: number; public description: string; } @HateoasResource('clients') export class Client extends Resource { public address: string; } @HateoasResource('physicalClients') export class PhysicalClient extends Client { public fio: string; } @HateoasResource('juridicalClients') export class JuridicalClient extends Client { public inn: string; }
-
HateoasClientConfiguration:
hateoasConfig.configure({ http: { rootUrl: 'http://localhost:8080/api/v1' }, useTypes: { resources: [Cart, Shop, Product, Client, PhysicalClient, JuridicalClient] } })
-
Suppose we have existed resources:
Cart: { "status": "New", "_links": { "self": { "href": "http://localhost:8080/api/v1/carts/1" }, "cart": { "href": "http://localhost:8080/api/v1/carts/1{?projection}", "templated": true }, "shop": { "href": "http://localhost:8080/api/v1/carts/1/shop" }, "products": { "href": "http://localhost:8080/api/v1/carts/1/products" }, "productsPage": { "href": "http://localhost:8080/api/v1/carts/1/productPage?page={page}&size={size}&sort={sort}&projection={projection}", "templated": true }, "client": { "href": "http://localhost:8080/api/v1/carts/1/client" }, "postExample": { "href": "http://localhost:8080/api/v1/cart/1/postExample" }, "putExample": { "href": "http://localhost:8080/api/v1/cart/1/putExample" }, "patchExample": { "href": "http://localhost:8080/api/v1/cart/1/patchExample" }, } } Shop: { "name": "Some Name", "ratings": 5 "_links": { "self": { "href": "http://localhost:8080/api/v1/shops/1" }, "shop": { "href": "http://localhost:8080/api/v1/shops/1" } } } Product: { "name": "Milk", "cost": 2, "description": "Some description" "_links": { "self": { "href": "http://localhost:8080/api/v1/produtcs/1" }, "produtc": { "href": "http://localhost:8080/api/v1/produtcs/1" } } } Client: { "fio": "Some fio", "_links": { "self": { "href": "http://localhost:8080/api/v1/physicalClients/1" }, "physicalClient": { "href": "http://localhost:8080/api/v1/physicalClients/1" } } }
Parent class for Resource and EmbeddedResource classes. Contains common resource methods to work with resource relations through resource links (see below).
Checks that passed relation name contains in relation _link
array of the BaseResource
.
Method signature:
hasRelation(relationName: string): boolean;
relationName
- resource relation name used to check.return value
-true
if resource has that relation,false
otherwise.
Getting resource relation object by relation name.
This method takes GetOption parameter with it you can pass projection
param
Method signature:
getRelation<T extends BaseResource>(relationName: string, options?: GetOption): Observable<T>;
relationName
- resource relation name used to get request URL.options
- GetOption additional options applied to the request.return value
- Resource with typeT
.throws error
- when required params are not valid or link not found by relation name or returned value is not Resource.
Examples of usage (given the presets):
// Performing GET request by the URL: http://localhost:8080/api/v1/carts/1/shop
cart.getRelation<Shop>('shop')
.subscribe((shop: Shop) => {
// some logic
});
With options:
// Performing GET request by the URL: http://localhost:8080/api/v1/carts/1/shop?projection=shopProjection&testParam=test&sort=name,ASC
cart.getRelation<Shop>('shop', {
params: {
testParam: 'test',
projection: 'shopProjection'
},
sort: {
name: 'ASC'
},
// useCache: true | false, by default true
})
.subscribe((shop: Shop) => {
// some logic
});
Getting related resource collection by relation name.
This method takes GetOption parameter with it you can pass projection
param.
Method signature:
getRelatedCollection<T extends ResourceCollection<BaseResource>>(relationName: string, options?: GetOption): Observable<T>;
relationName
- resource relation name used to get request URL.options
- GetOption additional options applied to the request.return value
- ResourceCollection collection of resources with typeT
.throws error
- when required params are not valid or link not found by relation name or returned value is not ResourceCollection.
Examples of usage (given the presets):
// Performing GET request by the URL: http://localhost:8080/api/v1/carts/1/products
cart.getRelatedCollection<ResourceCollection<Product>>('products')
.subscribe((collection: ResourceCollection<Product>) => {
const products: Array<Product> = collection.resources;
// some logic
});
With options:
// Performing GET request by the URL: http://localhost:8080/api/v1/carts/1/products?projection=productProjection&testParam=test&sort=name,ASC
cart.getRelatedCollection<ResourceCollection<Product>>('products', {
params: {
testParam: 'test',
projection: 'productProjection'
},
sort: {
name: 'ASC'
},
// useCache: true | false, by default true
})
.subscribe((collection: ResourceCollection<Product>) => {
const products: Array<Product> = collection.resources;
// some logic
});
Getting related resource collection with pagination by relation name.
This method takes PagedGetOption parameter with it you can pass projection
param (see below).
If do not pass
pageParams
withPagedGetOption
then will be used default page options. Also, you can set up own default page params through configuration.
Method signature:
getRelatedPage<T extends PagedResourceCollection<BaseResource>>(relationName: string, options?: PagedGetOption): Observable<T>;
relationName
- resource relation name used to get request URL.options
- PagedGetOption additional options applied to the request, if not passedpageParams
then used default page params.return value
- PagedResourceCollection paged collection of resources with typeT
.throws error
- when required params are not valid or link not found by relation name or returned value is not PagedResourceCollection.
Examples of usage (given the presets):
// Performing GET request by the URL: http://localhost:8080/api/v1/carts/1/productPage?page=0&size=20
cart.getRelatedPage<PagedResourceCollection<Product>>('productsPage')
.subscribe((page: PagedResourceCollection<Product>) => {
const products: Array<Product> = page.resources;
/* can use page methods
page.first();
page.last();
page.next();
page.prev();
page.customPage();
*/
});
With options:
// Performing GET request by the URL: http://localhost:8080/api/v1/carts/1/productPage?page=1&size=40&projection=productProjection&testParam=test&sort=name,ASC
cart.getRelatedPage<PagedResourceCollection<Product>>('productsPage', {
pageParams: {
page: 1,
size: 40
},
params: {
testParam: 'test',
projection: 'productProjection'
},
sort: {
name: 'ASC'
},
// useCache: true | false, by default true
})
.subscribe((page: PagedResourceCollection<Product>) => {
const products: Array<Product> = page.resources;
/* can use page methods
page.first();
page.last();
page.next();
page.prev();
page.customPage();
*/
});
Performing POST request with request body by relation link URL.
Method signature:
postRelation(relationName: string, requestBody: RequestBody<any>, options?: RequestOption): Observable<HttpResponse<any> | any>;
relationName
- resource relation name used to get request URL.requestBody
- RequestBody contains request body and additional body options.options
- RequestOption additional options applied to the request.return value
- by defaultraw response data
or AngularHttpResponse
whenoptions
param has aobserve: 'response'
value.
Examples of usage (given the presets):
// Performing POST request by the URL: http://localhost:8080/api/v1/cart/1/postExample
cart.postRelation('postExample', {
// In this case null values in someBody will be ignored
body: someBody
})
.subscribe((rawResult: any) => {
// some logic
});
With options:
// Performing POST request by the URL: http://localhost:8080/api/v1/cart/1/postExample?testParam=test
cart.postRelation('postExample', {
// In this case null values in someBody will be NOT ignored
body: someBody,
valuesOption: {
include: Include.NULL_VALUES
}
}, {
params: {
testParam: 'test'
},
observe: 'response'
})
.subscribe((response: HttpResponse<any>) => {
// some logic
});
Performing PATCH request with request body by relation link URL.
Method signature:
patchRelation(relationName: string, requestBody: RequestBody<any>, options?: RequestOption): Observable<HttpResponse<any> | any>;
relationName
- resource relation name used to get request URL.requestBody
- RequestBody contains request body and additional body options.options
- RequestOption additional options applied to the request.return value
- by defaultraw response data
or AngularHttpResponse
whenoptions
param has aobserve: 'response'
value.
Examples of usage (given the presets):
// Performing PATCH request by the URL: http://localhost:8080/api/v1/cart/1/patchExample
cart.patchRelation('patchExample', {
// In this case null values in someBody will be ignored
body: someBody
})
.subscribe((rawResult: any) => {
// some logic
});
With options:
// Performing PATCH request by the URL: http://localhost:8080/api/v1/cart/1/patchExample?testParam=test
cart.patchRelation('patchExample', {
// In this case null values in someBody will be NOT ignored
body: someBody,
valuesOption: {
include: Include.NULL_VALUES
}
}, {
params: {
testParam: 'test'
},
observe: 'response'
})
.subscribe((response: HttpResponse<any>) => {
// some logic
});
Performing PUT request with request body by relation link URL.
Method signature:
putRelation(relationName: string, requestBody: RequestBody<any>, options?: RequestOption): Observable<HttpResponse<any> | any>;
relationName
- resource relation name used to get request URL.requestBody
- RequestBody contains request body and additional body options.options
- RequestOption additional options applied to the request.return value
- by defaultraw response data
or AngularHttpResponse
whenoptions
param has aobserve: 'response'
value.
Examples of usage (given the presets):
// Performing PUT request by the URL: http://localhost:8080/api/v1/cart/1/putExample
cart.putRelation('putExample', {
// In this case null values in someBody will be ignored
body: someBody
})
.subscribe((rawResult: any) => {
// some logic
});
With options:
// Performing PUT request by the URL: http://localhost:8080/api/v1/cart/1/putExample?testParam=test
cart.putRelation('putExample', {
// In this case null values in someBody will be NOT ignored
body: someBody,
valuesOption: {
include: Include.NULL_VALUES
}
}, {
params: {
testParam: 'test'
},
observe: 'response'
})
.subscribe((response: HttpResponse<any>) => {
// some logic
});
Main resource class. Extend model classes with Resource
class to have the ability to use resource methods.
The difference between the Resource
type and EmbeddedResource is Resource
class has a self link therefore it has an id property, EmbeddedResource
has not.
Resource
classes are @Entity
server-side classes. EmbeddedResource classes are @Embeddable
entities.
Resource
class extend BaseResource with additional resource relations methods that used only with Resource
type.
Each
Resource
class should be declared with @HateoasResource decorator.
Adding passed entities to the resource collection behind the relation name.
Used POST
method with 'Content-Type': 'text/uri-list'
.
This method DOES NOT REPLACED existing resources in the collection instead it adds new ones. To replace collection resource with passed entities use bindRelation method.
Method signature:
addCollectionRelation<T extends Resource>(relationName: string, entities: Array<T>): Observable<HttpResponse<any>>;
relationName
- resource relation name used to get request URL mapped to resource collection.entities
- an array of entities that should be added to resource collection.return value
- AngularHttpResponse
result.
Examples of usage (given the presets):
// Suppose product1 already exists with id = 1
const product1 = ...;
// Suppose product2 already exists with id = 2
const product2 = ...;
cart.addCollectionRelation('products', [product1, product2])
/*
Performing POST request by the URL: http://localhost:8080/api/v1/carts/1/products
Content-type: 'text/uri-list'
Body: [http://localhost:8080/api/v1/products/1, http://localhost:8080/api/v1/products/2]
*/
.subscribe((result: HttpResponse<any>) => {
// some logic
});
Bounding the passed entity or collection of entities to this resource by the relation name.
Used PUT
method with 'Content-Type': 'text/uri-list'
.
This method also REPLACED existing resources in the collection by passed entities. To add entities to collection resource use addCollectionRelation method.
Method signature:
bindRelation<T extends Resource>(relationName: string, entities: T | Array<T>): Observable<HttpResponse<any>>;
relationName
- resource relation name used to get request URL.entities
- an array of entities that should be bound to resource.return value
- AngularHttpResponse
result.
Examples of usage (given the presets):
With single resource relation:
// Suppose shopToBind already exists with id = 1
const shopToBind = ...;
cart.bindRelation('shop', shopToBind)
/*
Performing PUT request by the URL: http://localhost:8080/api/v1/carts/1/shop
Content-type: 'text/uri-list'
Body: http://localhost:8080/api/v1/shops/1
*/
.subscribe((result: HttpResponse<any>) => {
// some logic
});
With collection resource relation:
// Suppose product1 already exists with id = 1
const product1 = ...;
// Suppose product2 already exists with id = 2
const product2 = ...;
cart.bindRelation('products', [product1, product2])
/*
Performing PUT request by the URL: http://localhost:8080/api/v1/carts/1/products
Content-type: 'text/uri-list'
Body: [http://localhost:8080/api/v1/products/1, http://localhost:8080/api/v1/products/2]
*/
.subscribe((result: HttpResponse<any>) => {
// some logic
});
Unbinding single resource relation behind resource name.
Used DELETE
method to relation resource link URL.
This method does not work with collection resource relations. To unbind collection resource relation use UnbindCollectionRelation method. To delete one resource from resource collection use deleteRelation method.
Method signature:
unbindRelation<T extends Resource>(relationName: string): Observable<HttpResponse<any>>;
relationName
- resource relation name to unbind.return value
- AngularHttpResponse
result.
Examples of usage (given the presets):
// Suppose cart already bound shop resource by relation name 'shop'
cart.unbindRelation('shop')
/*
Performing DELETE request by the URL: http://localhost:8080/api/v1/carts/1/shop
*/
.subscribe((result: HttpResponse<any>) => {
// some logic
});
Unbinding all resources from resource collection behind resource name.
Used PUT
method with 'Content-Type': 'text/uri-list'
and EMPTY
body to clear relations.
This method does not work with SINGLE resource relations. To delete single resource relations use unbindRelation or deleteRelation methods. To delete one resource from collection use deleteRelation method.
Method signature:
unbindCollectionRelation<T extends Resource>(relationName: string): Observable<HttpResponse<any>>;
relationName
- resource relation name used to get request URL.return value
- AngularHttpResponse
result.
Examples of usage (given the presets):
/*
Performing PUT request by the URL: http://localhost:8080/api/v1/carts/1/products
Content-type: 'text/uri-list'
Body: ''
*/
cart.unbindCollectionRelation('products')
.subscribe((result: HttpResponse<any>) => {
// some logic
});
Deleting resource relation.
For collection, means that only passed entity will be unbind from the collection. For single resource, deleting relation the same as unbindRelation method.
To delete all resource relations from collection use unbindCollectionRelation method.
Method signature:
deleteRelation<T extends Resource>(relationName: string, entity: T): Observable<HttpResponse<any>>;
relationName
- resource relation name used to get request URL.entity
- entity to unbind.return value
- AngularHttpResponse
result.
Examples of usage (given the presets):
// Performing DELETE request by the URL: http://localhost:8080/api/v1/carts/1/shop/1
// Suppose shopToDelete already exists with id = 1
const shopToDelete = ...;
cart.deleteRelation('shop', shopToDelete)
.subscribe((result: HttpResponse<any>) => {
// some logic
});
This resource type uses when a server-side entity is @Embeddable. It means that this entity has not an id property and can't exist standalone.
Because embedded resources have not an id then it can use only BaseResource methods.
Each
EmbeddedResource
class should be declared with @HateoasEmbeddedResource decorator.
This resource type represents collection of resources.
You can get this type as result GetRelatedCollection, GetResourceCollection or perform CustomQuery/CustomSearchQuery with passed return type as ResourceCollection
.
Resource collection holds resources in the public property with the name resources
.
This resource type represents paged collection of resources. You can get this type as result GetRelatedPage, GetPage or perform CustomQuery/CustomSearchQuery with passed return type as PagedResourceCollection.
PagedResourceCollection extends ResourceCollection type and adds methods to work with a page.
When you do not pass page
or size
params in methods with PagedGetOption then used default page values.
Also, you can set up own default page params through configuration.
Checks that PagedResourceCollection
has the link to get the first-page result.
Method signature:
hasFirst(): boolean;
return value
-true
when the link to get the first page exists,false
otherwise.
Checks that PagedResourceCollection
has the link to get the last page result.
Method signature:
hasLast(): boolean;
return value
-true
when the link to get the last page exists,false
otherwise.
Checks that PagedResourceCollection
has the link to get the next page result.
Method signature:
hasNext(): boolean;
return value
-true
when the link to get the next page exists,false
otherwise.
Checks that PagedResourceCollection
has the link to get the previous page result.
Method signature:
hasPrev(): boolean;
return value
-true
when the link to get the prev page exists,false
otherwise.
Performing a request to get the first-page result by the first-page link.
Method signature:
first(options?: {useCache: true;}): Observable<PagedResourceCollection<T>>;
options
- additional options to manipulate the cache when getting a result (by default will be used the cache if it enabled in the configuration).return value
- PagedResourceCollection with resource typesT
.throws error
- when the link to get the first-page result is not exist.
Suppose products have 3 pages with 20 resources per page, and the previous request was to get products with a page number = 1.
To get the first products page, will perform request to page number = 0 with current or default page size (if before page size not passed).
// Performing GET request by the URL: http://localhost:8080/api/v1/products?page=0&size=20
const pagedProductCollection = ...;
pagedProductCollection.first()
.subscribe((firstPageResult: PagedResourceCollection<Product>) => {
// firstPageResult can be fetched from the cache if before was performing the same request
// some logic
});
With options:
// Performing GET request by the URL: http://localhost:8080/api/v1/products?page=0&size=20
const pagedProductCollection = ...;
pagedProductCollection.first({useCache: false})
.subscribe((firstPageResult: PagedResourceCollection<Product>) => {
// firstPageResult always will be fetched from the server because the cache is disabled for this request
// some logic
});
Performing a request to get the last-page result by the last-page link.
Method signature:
last(options?: {useCache: true;}): Observable<PagedResourceCollection<T>>;
options
- additional options to manipulate the cache when getting a result (by default will be used the cache if it enabled in the configuration).return value
- PagedResourceCollection with resource typesT
.throws error
- when the link to get the last-page result is not exist.
Suppose products have 3 pages with 20 resources per page, and the previous request was to get products with a page number = 1.
To get the last products page, will perform request to page number = 2 with current or default page size (if before page size not passed).
// Performing GET request by the URL: http://localhost:8080/api/v1/products?page=2&size=20
const pagedProductCollection = ...;
pagedProductCollection.last()
.subscribe((lastPageResult: PagedResourceCollection<Product>) => {
// lastPageResult can be fetched from the cache if before was performing the same request
// some logic
});
With options:
// Performing GET request by the URL: http://localhost:8080/api/v1/products?page=2&size=20
const pagedProductCollection = ...;
pagedProductCollection.last({useCache: false})
.subscribe((lastPageResult: PagedResourceCollection<Product>) => {
// lastPageResult always will be fetched from the server because the cache is disabled for this request
// some logic
});
Performing a request to get the next-page result by the next-page link.
Method signature:
next(options?: {useCache: true;}): Observable<PagedResourceCollection<T>>;
options
- additional options to manipulate the cache when getting a result (by default will be used the cache if it enabled in the configuration).return value
- PagedResourceCollection with resource typesT
.throws error
- when the link to get the next-page result is not exist.
Suppose products have 3 pages with 20 resources per page, and the previous request was to get products with a page number = 1.
To get the next products page, will perform request to page number = 2 with current or default page size (if before page size not passed).
// Performing GET request by the URL: http://localhost:8080/api/v1/products?page=1&size=20
const pagedProductCollection = ...;
pagedProductCollection.next()
.subscribe((nextPageResult: PagedResourceCollection<Product>) => {
// nextPageResult can be fetched from the cache if before was performing the same request
// some logic
});
With options:
// Performing GET request by the URL: http://localhost:8080/api/v1/products?page=1&size=20
const pagedProductCollection = ...;
pagedProductCollection.next({useCache: false})
.subscribe((nextPageResult: PagedResourceCollection<Product>) => {
// nextPageResult always will be fetched from the server because the cache is disabled for this request
// some logic
});
Performing a request to get the prev-page result by the prev-page link.
Method signature:
prev(options?: {useCache: true;}): Observable<PagedResourceCollection<T>>;
options
- additional options to manipulate the cache when getting a result (by default will be used the cache if it enabled in the configuration).return value
- PagedResourceCollection with resource typesT
.throws error
- when the link to get the prev-page result is not exist.
Suppose products have 3 pages with 20 resources per page, and the previous request was to get products with a page number = 1.
To get the prev products page, will perform request to page number = 0 with current or default page size (if before page size not passed).
// Performing GET request by the URL: http://localhost:8080/api/v1/products?page=0&size=20
const pagedProductCollection = ...;
pagedProductCollection.prev()
.subscribe((prevPageResult: PagedResourceCollection<Product>) => {
// prevPageResult can be fetched from the cache if before was performing the same request
// some logic
});
With options:
// Performing GET request by the URL: http://localhost:8080/api/v1/products?page=0&size=20
const pagedProductCollection = ...;
pagedProductCollection.prev({useCache: false})
.subscribe((prevPageResult: PagedResourceCollection<Product>) => {
// prevPageResult always will be fetched from the server because the cache is disabled for this request
// some logic
});
Performing a request to get the page with passed page number and current or default page size (if before page size not passed).
To pass page number, page size, sort params together use customPage method.
Method signature:
page(pageNumber: number, options?: {useCache: true;}): Observable<PagedResourceCollection<T>>;
pageNumber
- number of the page to get.options
- additional options to manipulate the cache when getting a result (by default will be used the cache if it enabled in the configuration).return value
- PagedResourceCollection with resource typesT
.throws error
- whenpageNumber
greater than total pages.
Suppose products have 5 pages with 20 resources per page, and the previous request was to get products with a page number = 1.
To get the products page = 3, will perform request to page number = 3 with current or default page size (if before page size not passed).
// Performing GET request by the URL: http://localhost:8080/api/v1/products?page=3&size=20
const pagedProductCollection = ...;
pagedProductCollection.page(3)
.subscribe((customPageResult: PagedResourceCollection<Product>) => {
// customPageResult can be fetched from the cache if before was performing the same request
// some logic
});
With options:
// Performing GET request by the URL: http://localhost:8080/api/v1/products?page=3&size=20
const pagedProductCollection = ...;
pagedProductCollection.page(3, {useCache: false})
.subscribe((customPageResult: PagedResourceCollection<Product>) => {
// customPageResult always will be fetched from the server because the cache is disabled for this request
// some logic
});
Performing a request to get the page with passed page size and page number reset to 0
value.
To pass page number, page size, sort params together use customPage method.
Method signature:
size(size: number, options?: {useCache: true;}): Observable<PagedResourceCollection<T>>;
size
- count of resources to page.options
- additional options to manipulate the cache when getting a result (by default will be used the cache if it enabled in the configuration).return value
- PagedResourceCollection with resource typesT
.throws error
- whensize
greater than total count resources.
Suppose products have 5 pages with 20 resources per page, and the previous request was to get products with a page number = 1, size = 20.
To increase the current page size to 50, will perform a request to the page number = 0 and page size = 50.
// Performing GET request by the URL: http://localhost:8080/api/v1/products?page=0&size=50
const pagedProductCollection = ...;
pagedProductCollection.size(50)
.subscribe((customPageResult: PagedResourceCollection<Product>) => {
// customPageResult can be fetched from the cache if before was performing the same request
// some logic
});
With options:
// Performing GET request by the URL: http://localhost:8080/api/v1/products?page=0&size=50
const pagedProductCollection = ...;
pagedProductCollection.size(50, {useCache: false})
.subscribe((customPageResult: PagedResourceCollection<Product>) => {
// customPageResult always will be fetched from the server because the cache is disabled for this request
// some logic
});
Sorting the current page result.
To pass page number, page size, sort params together use customPage method.
Method signature:
sortElements(sortParam: Sort, options?: {useCache: true;}): Observable<PagedResourceCollection<T>>;
sortParam
- Sort params.options
- additional options to manipulate the cache when getting a result (by default will be used the cache if it enabled in the configuration).return value
- PagedResourceCollection with resource typesT
.
Suppose products have 5 pages with 20 resources per page, and the previous request was to get products with a page number = 1, size = 20.
To sort the current page result, will perform a request to the current page number with the current page size and passed sort params.
// Performing GET request by the URL: http://localhost:8080/api/v1/products?page=1&size=20&sort=cost,ASC&sort=name,DESC
const pagedProductCollection = ...;
pagedProductCollection.sortElements({cost: 'ASC', name: 'DESC'})
.subscribe((customPageResult: PagedResourceCollection<Product>) => {
// customPageResult can be fetched from the cache if before was performing the same request
// some logic
});
With options:
// Performing GET request by the URL: http://localhost:8080/api/v1/products?page=1&size=20&sort=cost,ASC&sort=name,DESC
const pagedProductCollection = ...;
pagedProductCollection.sortElements({cost: 'ASC', name: 'DESC'}, {useCache: false})
.subscribe((customPageResult: PagedResourceCollection<Product>) => {
// customPageResult always will be fetched from the server because the cache is disabled for this request
// some logic
});
Performing a request to get the page with custom page number, page size, and sort params.
Method signature:
customPage(params: SortedPageParam, options?: {useCache: true;}): Observable<PagedResourceCollection<T>>;
params
- SortedPageParam page and sort params.options
- additional options to manipulate the cache when getting a result (by default will be used the cache if it enabled in the configuration).return value
- PagedResourceCollection with resource typesT
.throws error
- when the page size, greater than total count resources or page number greater than total pages.
When pass only part of the
params
then used default page params for not passed ones.
// Performing GET request by the URL: http://localhost:8080/api/v1/products?page=2&size=30&sort=name,ASC
const pagedProductCollection = ...;
pagedProductCollection.customPage({
pageParams: {
page: 2,
size: 30
},
sort: {
name: 'ASC'
}
})
.subscribe(value1 => {
// customPageResult can be fetched from the cache if before was performing the same request
// some logic
});
With options:
// Performing GET request by the URL: http://localhost:8080/api/v1/products?page=2&size=30&sort=name,ASC
const pagedProductCollection = ...;
pagedProductCollection.customPage({
pageParams: {
page: 2,
size: 30
},
sort: {
name: 'ASC'
}
},
{useCache: true})
.subscribe(value1 => {
// customPageResult always will be fetched from the server because the cache is disabled for this request
// some logic
});
The library allows work with entities hierarchy.
Suppose exists the next resource's hierarchy:
import { Resource, HateoasResource } from '@lagoshny/ngx-hateoas-client';
@HateoasResource('carts')
export class Cart extends Resource {
public client: Client;
}
@HateoasResource('clients')
export class Client extends Resource {
public address: string;
}
@HateoasResource('physicalClients')
export class PhysicalClient extends Client {
public fio: string;
}
@HateoasResource('juridicalClients')
export class JuridicalClient extends Client {
public inn: string;
}
With hal-json
representation:
Cart:
{
"status": "New",
"_links": {
"self": {
"href": "http://localhost:8080/api/v1/carts/1"
},
"cart": {
"href": "http://localhost:8080/api/v1/carts/1{?projection}",
"templated": true
},
"client": {
"href": "http://localhost:8080/api/v1/carts/1/physicalClient"
}
}
}
PhysicalClient:
{
"fio": "Some fio",
"_links": {
"self": {
"href": "http://localhost:8080/api/v1/physicalClients/1"
},
"physicalClient": {
"href": "http://localhost:8080/api/v1/physicalClients/1"
}
}
}
Of course, resource types are set in the configuration useTypes section:
...
hateoasConfig.configure({
useTypes: {
resources: [Cart, Client, PhysicalClient, JuridicalClient]
}
}
...
From the example, above can note that the Cart
resource has the client
property with type Client
.
In its turn, client
can have one of the types PhysicalClient
or JuridicalClient
.
You can use instanceof
statement to know what client
resource type you got.
// Suppose exists cart resource and after getting client relation need to know what is the client type
const cart = ...
cart.getRelation('client')
.subscribe((client: Client) => {
if (client instanceof PhysicalClient) {
const physicalClient = client as PhysicalClient;
// some logic
} else if (client instanceof JuridicalClient) {
const juridicalClient = client as JuridicalClient;
// some logic
}
});
Spring Data Rest allows creating resource projections. Projection can show resource relations with a resource object as inner objects (not only as links in resource relation links array).
Hateoas-client
has support for these resource projections.
See more about Spring Data Rest projections here.
To create resource projection you need to create a projection class extend it with Resource and mark it with @HateaosProjection decorator passing projection resourceType
and projectionName
params.
resourceType
should be equals to resource type that use this projection.projectionName
should be equals to the server-side resource projection name.
Use HateoasResourceService to perform projection request with first param as projection type for all the methods. You can also create custom resource service to concreate projection type.
If your projection has property relations to resource classes then you should decorate these properties with @ProjectionRel decorator passing resourceType
param.
Besides need to wrap property resource type with ProjectionRelType type to hide Resource
/EmbeddedResource
methods (see example below).
As mentioned earlier that projection relations that resources are plain JSON objects. This means that these objects have not resources relations links array and other signs of the resource. But in your code when you will use existing resources classes as a relation type in projection class you will have all
Resource
/EmbeddedResource
methods that can be applied only to resource type. To prevent this behavior you need all projection resource relation types to wrap with ProjectionRelType type.
Resource projection example:
Suppose, you have a projection with the name cartProjection
for Cart
resource:
import { Resource, HateoasResource, HateoasProjection, ProjectionRel, ProjectionRelType } from '@lagoshny/ngx-hateoas-client';
@HateoasResource('carts')
export class Cart extends Resource {
...
public shop: Shop;
...
}
@HateoasResource('shops')
export class Shop extends Resource {
...
public shopName: string;
public printShopName() {
console.log(this.shopName)
}
...
}
@HateoasProjection(Cart, 'cartProjection')
export class CartProjection extends Resource {
...
@ProjectionRel(Shop)
public shop: ProjectionRelType<Shop>;
...
}
Usages:
import { HateoasResourceService } from '@lagoshny/ngx-hateoas-client';
...
export class CartComponent {
constructor(private reosurceService: HateoasResourceService) {
}
public getCartProjection(): CartProjection {
this.reosurceService.getResource(CartProjection, 1)
.subscribe((cartProjection: CartProjection) => {
// Will print `true` because cartProjection.shop is Shop type as was declared with @ProjectionRel decorator
console.log(cartProjection.shop instanceof Shop);
// Will print the shop name to the console
cartProjection.shop.printShopName();
// Will not compile because ProjectionRelType<Shop> has not Resource/EmbeddedResource methods
cartProjection.shop.getRelation(...);
})
}
}
...
In this case, CartProjection.shop
is a simple object with the type Shop
that has only Shop
class properties and methods without Resource
/EmbeddedResource
methods.
Resource projection classes can be used with
HateoasResourceService
methods, you need to pass resource projection type as the first methods param instead of a simple resource type.
This is a special type that allows wrapping your resource relations properties in resource projection to hide all Reosurce
/EmbeddedResource
methods.
Usages:
import { Resource, HateoasResource, HateoasProjection, ProjectionRel, ProjectionRelType } from '@lagoshny/ngx-hateoas-client';
@HateoasResource('carts')
export class Cart extends Resource {
...
public shop: Shop;
...
}
@HateoasResource('shops')
export class Shop extends Resource {
}
@HateoasProjection(Cart, 'cartProjection')
export class CartProjection extends Resource {
...
@ProjectionRel(Shop)
public shop: ProjectionRelType<Shop>;
...
}
Here CartProjection.shop
will show only Shop
type properties and methods. Methods from Resource
will be hidden.
As described before to work with resources you can use built-in HateoasResourceService or create custom resource service.
Difference in methods signature between built-in HateoasResourceService and custom resource service is built-in service always has a resource type as the first method param.
Examples of usage resource service methods rely on this presets.
-
Resource class is
import { Resource, HateoasResource } from '@lagoshny/ngx-hateoas-client'; @HateoasResource('products') export class Product extends Resource { public name: string; public cost: number; public description: string; public type: ProductType; } @HateoasResource('productTypes') export class ProductType extends Resource { public name: string; }
-
Resource projection class is
import { Resource, HateoasProjection, ProjectionRel, ProjectionRelType } from '@lagoshny/ngx-hateoas-client'; @HateoasProjection(Product, 'productProjection') export class ProuctProjection extends Resource { public name: string; public cost: number; public description: string; @ProjectionRel(ProductType) public type: ProjectionRelType<ProductType>; }
-
HateoasClientConfiguration:
hateoasConfig.configure({ http: { rootUrl: 'http://localhost:8080/api/v1' }, useTypes: { resources: [Product, ProductType, ProuctProjection] } })
-
Resource service as built-in HateoasResourceService is
@Component({ ... }) export class AppComponent { constructor(private resourceHateoasService: HateoasResourceService) { } }
-
Resource service as custom resource service is
import { HateoasResourceOperation, HateoasResourceService, PagedResourceCollection, ResourceCollection} from '@lagoshny/ngx-hateoas-client'; @Injectable({providedIn: 'root'}) export class ProductService extends HateoasResourceOperation<Product> { constructor(private resourceHateoasService: HateoasResourceService) { super(Product); } public getProductProjection(id: number): Observable<ProductProjection> { return this.resourceHateoasService.getResource(ProductProjection, id); } public getProductProjections(): Observable<ResourceCollection<ProductProjection>> { return this.resourceHateoasService.getCollection(ProductProjection); } public getPagedProductProjections(): Observable<PagedResourceCollection<ProductProjection>> { return this.resourceHateoasService.getPage(ProductProjection); } public searchProductProjection(searchQuery: string): Observable<ProductProjection> { return this.resourceHateoasService.searchResource(ProductProjection, searchQuery); } public searchProductProjections(searchQuery: string): Observable<ResourceCollection<ProductProjection>> { return this.resourceHateoasService.searchCollection(ProductProjection, searchQuery); } public searchPagedProductProjections(searchQuery: string): Observable<PagedResourceCollection<ProductProjection>> { return this.resourceHateoasService.searchPage(ProductProjection, searchQuery); } } @Component({ ... }) export class AppComponent { constructor(private productService: ProductService) { } }
No matter which service used (custom or built-in) both have the same resource methods.
Getting one resource Resource. This method takes GetOption parameter type.
To get resource projection instead of a resource type pass resource projection type in first method param when used
resourceHateoasService
. See more about projection support here.
Method signature:
getResource(id: number | string, options?: GetOption): Observable<T>;
id
- resource id to get.options
- GetOption additional options applied to the request.return value
- Resource with typeT
.throws error
when returned value is not Resource.
Example of usage (given the presets):
// Performing GET request by the URL: http://localhost:8080/api/v1/products/1
this.productService.getResource(1)
.subscribe((product: Product) => {
// some logic
})
this.resourceHateoasService.getResource(Product, 1)
.subscribe((product: Product) => {
// some logic
})
With options:
// Performing GET request by the URL: http://localhost:8080/api/v1/products/1?testParam=test&sort=cost,ASC
this.productService.getResource(1, {
params: {
testParam: 'test'
},
sort: {
cost: 'ASC'
},
// useCache: true | false, by default true
}).subscribe((product: Product) => {
// some logic
})
this.resourceHateoasService.getResource(Product, 1, {
params: {
testParam: 'test'
},
sort: {
cost: 'ASC'
},
// useCache: true | false, by default true
}).subscribe((product: Product) => {
// some logic
})
Get projection example:
// Performing GET request by the URL: http://localhost:8080/api/v1/products/1?projection=productProjection
this.productService.getProductProjection(1)
.subscribe((productProjection: ProductProjection) => {
// some logic
})
this.resourceHateoasService.getResource(ProductProjection, 1)
.subscribe((productProjection: ProductProjection) => {
// some logic
})
Getting collection of resources ResourceCollection. This method takes GetOption parameter type.
To get resource projection instead of a resource type pass resource projection type in first method param when used
resourceHateoasService
. See more about projection support here.
Method signature:
getCollection(options?: GetOption): Observable<ResourceCollection<T>>;
options
- GetOption additional options applied to the request.return value
- ResourceCollection collection of resources with typeT
.throws error
when returned value is not ResourceCollection.
Example of usage (given the presets):
// Performing GET request by the URL: http://localhost:8080/api/v1/products
this.productService.getCollection()
.subscribe((collection: ResourceCollection<Product>) => {
const products: Array<Product> = collection.resources;
// some logic
})
this.resourceHateoasService.getCollection(Product)
.subscribe((collection: ResourceCollection<Product>) => {
const products: Array<Product> = collection.resources;
// some logic
})
With options:
// Performing GET request by the URL: http://localhost:8080/api/v1/products?testParam=test&sort=cost,ASC
this.productService.getCollection({
params: {
testParam: 'test'
},
sort: {
cost: 'ASC'
},
// useCache: true | false, by default true
}).subscribe((collection: ResourceCollection<Product>) => {
const products: Array<Product> = collection.resources;
// some logic
})
this.resourceHateoasService.getCollection(Producr, {
params: {
testParam: 'test'
},
sort: {
cost: 'ASC'
},
// useCache: true | false, by default true
}).subscribe((collection: ResourceCollection<Product>) => {
const products: Array<Product> = collection.resources;
// some logic
})
Get projection example:
// Performing GET request by the URL: http://localhost:8080/api/v1/products?projection=productProjection
this.productService.getProductProjections()
.subscribe((productProjections: ResourceCollection<ProductProjection>) => {
// some logic
})
this.resourceHateoasService.getCollection(ProductProjection)
.subscribe((productProjections: ResourceCollection<ProductProjection>) => {
// some logic
})
Getting paged collection of resources PagedResourceCollection. This method takes PagedGetOption parameter type.
To get resource projection instead of a resource type pass resource projection type in first method param when used
resourceHateoasService
. See more about projection support here.
If do not pass
pageParams
withPagedGetOption
then will be used default page options. Also, you can set up own default page params through configuration.
Method signature:
getPage(options?: PagedGetOption): Observable<PagedResourceCollection<T>>;
options
- PagedGetOption additional options applied to the request, if not passedpageParams
then used default page params.return value
- PagedResourceCollection paged collection of resources with typeT
.throws error
when returned value is not PagedResourceCollection
Example of usage (given the presets):
// Performing GET request by the URL: http://localhost:8080/api/v1/products?page=0&size=20
this.productService.getPage()
.subscribe((page: PagedResourceCollection<Product>) => {
const products: Array<Product> = page.resources;
/* can use page methods
page.first();
page.last();
page.next();
page.prev();
page.size(...);
page.page(...);
page.sortElements(...);
page.customPage(...);
*/
});
this.resourceHateoasService.getPage(Product)
.subscribe((page: PagedResourceCollection<Product>) => {
const products: Array<Product> = page.resources;
/* can use page methods
page.first();
page.last();
page.next();
page.prev();
page.size(...);
page.page(...);
page.sortElements(...);
page.customPage(...);
*/
});
With options:
// Performing GET request by the URL: http://localhost:8080/api/v1/products?testParam=test&page=1&size=40&sort=cost,ASC
this.productService.getPage({
pageParams: {
page: 1,
size: 40
},
params: {
testParam: 'test'
},
sort: {
cost: 'ASC'
},
// useCache: true | false, by default true
}).subscribe((page: PagedResourceCollection<Product>) => {
const products: Array<Product> = page.resources;
/* can use page methods
page.first();
page.last();
page.next();
page.prev();
page.size(...);
page.page(...);
page.sortElements(...);
page.customPage(...);
*/
});
this.resourceHateoasService.getPage(Product, {
pageParams: {
page: 1,
size: 40
},
params: {
testParam: 'test'
},
sort: {
cost: 'ASC'
},
// useCache: true | false, by default true
}).subscribe((page: PagedResourceCollection<Product>) => {
const products: Array<Product> = page.resources;
/* can use page methods
page.first();
page.last();
page.next();
page.prev();
page.size(...);
page.page(...);
page.sortElements(...);
page.customPage(...);
*/
});
Get projection example:
// Performing GET request by the URL: http://localhost:8080/api/v1/products?projection=productProjection
this.productService.getPagedProductProjections()
.subscribe((page: PagedResourceCollection<ProductProjection>) => {
const products: Array<ProductProjection> = page.resources;
/* can use page methods
page.first();
page.last();
page.next();
page.prev();
page.size(...);
page.page(...);
page.sortElements(...);
page.customPage(...);
*/
})
this.resourceHateoasService.getPage(ProductProjection)
.subscribe((page: PagedResourceCollection<ProductProjection>) => {
const products: Array<ProductProjection> = page.resources;
/* can use page methods
page.first();
page.last();
page.next();
page.prev();
page.size(...);
page.page(...);
page.sortElements(...);
page.customPage(...);
*/
})
Creating new resource Resource.
Method signature:
createResource(requestBody: RequestBody<T>, options?: RequestOption): Observable<T | any>;
requestBody
- RequestBody contains request body (in this case resource object) and additional body options.options
- (optional) RequestOption that should be applied to the requestreturn value
- Resource with typeT
orraw response data
when returned value is not resource object.
Example of usage (given the presets):
/*
Performing POST request by the URL: http://localhost:8080/api/v1/products
Request body:
{
"name": "Apple",
"cost": 100
}
Note: description is not passed because by default null values ignore. If you want pass description = null value then need to pass additional valuesOption params.
*/
const newProduct = new Product();
newProduct.cost = 100;
newProduct.name = 'Apple';
newProduct.description = null;
this.productService.createResource({
body: newProduct
}).subscribe((createdProduct: Product) => {
// some logic
});
this.resourceHateoasService.createResource(Product, {
body: newProduct
}).subscribe((createdProduct: Product) => {
// some logic
});
With options:
/*
Performing POST request by the URL: http://localhost:8080/api/v1/products
Request body:
{
"name": "Apple",
"cost": 100,
"description": null
}
Note: description is passed with null value because valuesOption = Include.NULL_VALUES was passed.
*/
const newProduct = new Product();
newProduct.cost = 100;
newProduct.name = 'Apple';
newProduct.description = null;
this.productService.createResource({
body: newProduct,
valuesOption: {
include: Include.NULL_VALUES
}
}).subscribe((createdProduct: Product) => {
// some logic
});
this.resourceHateoasService.createResource(Product, {
body: newProduct,
valuesOption: {
include: Include.NULL_VALUES
}
}).subscribe((createdProduct: Product) => {
// some logic
});
Updating all values of an existing resource at once by resource self link URL
.
For not passed values of resource,
null
values will be used.
To update a resource performs PUT
request by the URL equals to resource self link
passed in entity
param.
To update a resource by an id
directly use UpdateResourceById.
To update part of the values of resource use PatchResource method.
Method signature:
updateResource(entity: T, requestBody?: RequestBody<any>, options?: RequestOption): Observable<T | any>;
entity
- resource to update.requestBody
- RequestBody contains request body (in this case new values for resource) and additional body options.options
- (optional) RequestOption that should be applied to the requestreturn value
- Resource with typeT
orraw response data
when returned value is not resource object.
When passed only
entity
param then values ofentity
will be used to update values of resource.
Example of usage (given the presets):
/*
Suppose exitsProduct has a self link = http://localhost:8080/api/v1/products/1
Performing PUT request by the URL: http://localhost:8080/api/v1/products/1
Request body:
{
"name" = exitsProduct.name,
"cost": 500
}
Note:
1) Description is not passed because by default null values ignore. If you want pass description = null value then need to pass additional valuesOption params.
2) Since update resource updating all resource values at once and for description value is not passing then the server-side can overwrite description to null.
*/
const exitsProduct = ...;
exitsProduct.cost = 500;
exitsProduct.description = null;
this.productService.updateResource(exitsProduct)
.subscribe((updatedProduct: Product) => {
// some logic
});
// For resourceHateoasService this snippet is identical
With options:
/*
Suppose exitsProduct has a self link = http://localhost:8080/api/v1/products/1
Performing PUT request by the URL: http://localhost:8080/api/v1/products/1
Request body:
{
"name": null,
"cost": 500
}
Note:
1) Name was passed with null value because valuesOption = Include.NULL_VALUES was passed.
2) Since update resource updating all resource values at once and for description value is not passing then the server-side can overwrite description to null.
*/
const exitsProduct = ...;
this.productService.updateResource(exitsProduct, {
body: {
name: null,
cost: 500
},
valuesOption: {
include: Include.NULL_VALUES
}
}).subscribe((updatedProduct: Product) => {
// some logic
});
// For resourceHateoasService this snippet is identical
Updating all values of an existing resource at once by resource id
.
To update a resource by resource self link URL
use UpdateResource.
To update part of the values of resource use PatchResource method.
Method signature:
updateResourceById(id: number | string, requestBody: RequestBody<any>, options?: RequestOption): Observable<T | any>;
id
- resource id to update.requestBody
- RequestBody contains request body (in this case new values for resource) and additional body options.options
- (optional) RequestOption that should be applied to the requestreturn value
- Resource with typeT
orraw response data
when returned value is not resource object.
Example of usage (given the presets):
/*
Suppose exitsProduct has an id = 1
Performing PUT request by the URL: http://localhost:8080/api/v1/products/1
Request body:
{
"name" = exitsProduct.name,
"cost": 500
}
Note:
1) Description is not passed because by default null values ignore. If you want pass description = null value then need to pass additional valuesOption params.
2) Since update resource updating all resource values at once and for description value is not passing then the server-side can overwrite description to null.
*/
const exitsProduct = ...;
exitsProduct.cost = 500;
exitsProduct.description = null;
this.productService.updateResourceById(1, {
body: {
...exitsProduct
}
})
.subscribe((updatedProduct: Product) => {
// some logic
});
this.resourceHateoasService.updateResourceById(Product, 1, {
body: {
...exitsProduct
}
})
.subscribe((updatedProduct: Product) => {
// some logic
});
With options:
/*
Suppose exitsProduct has an id = 1
Performing PUT request by the URL: http://localhost:8080/api/v1/products/1
Request body:
{
"name": null,
"cost": 500
}
Note:
1) Name was passed with null value because valuesOption = Include.NULL_VALUES was passed.
2) Since update resource updating all resource values at once and for description value is not passing then the server-side can overwrite description to null.
*/
this.productService.updateResourceById(1, {
body: {
name: null,
cost: 500
},
valuesOption: {
include: Include.NULL_VALUES
}
})
.subscribe((updatedProduct: Product) => {
// some logic
});
this.resourceHateoasService.updateResourceById(Product, 1, {
body: {
name: null,
cost: 500
},
valuesOption: {
include: Include.NULL_VALUES
}
})
.subscribe((updatedProduct: Product) => {
// some logic
});
Patching part values of an existing resource by resource self link URL
.
To patch a resource performs PATCH
request by the URL equals to resource self link
passed in entity
param.
To patch a resource by an id
directly use PatchResourceById.
To update all values of the resource at once use UpdateResource method.
Method signature:
patchResource(entity: T, requestBody?: RequestBody<any>, options?: RequestOption): Observable<T | any>;
entity
- resource to patch.requestBody
- RequestBody contains request body (in this case new values for resource) and additional body options.options
- (optional) RequestOption that should be applied to the requestreturn value
- Resource with typeT
orraw response data
when returned value is not resource object.
When passed only
entity
param then values ofentity
will be used to patch values of resource.
Example of usage (given the presets):
/*
Suppose exitsProduct has a self link = http://localhost:8080/api/v1/products/1
Performing PATCH request by the URL: http://localhost:8080/api/v1/products/1
Request body:
{
"name" = exitsProduct.name,
"cost": 500
}
Note:
1) Description is not passed because by default null values ignore. If you want pass description = null value then need to pass additional valuesOption params.
2) Since patch resource updating only part of resource values at once then all not passed values will have the old values.
*/
const exitsProduct = ...;
exitsProduct.cost = 500;
exitsProduct.description = null;
this.productService.patchResource(exitsProduct)
.subscribe((patchedProduct: Product) => {
// some logic
});
// For resourceHateoasService this snippet is identical
With options:
/*
Suppose exitsProduct has a self link = http://localhost:8080/api/v1/products/1
Performing PATCH request by the URL: http://localhost:8080/api/v1/products/1
Request body:
{
"name": null,
"cost": 500
}
Note:
1) Name was passed with null value because valuesOption = Include.NULL_VALUES was passed.
2) Since patch resource updating only part of resource values at once then all not passed values will have the old values.
*/
const exitsProduct = ...;
this.productService.patchResource(exitsProduct, {
body: {
name: null,
cost: 500
},
valuesOption: {
include: Include.NULL_VALUES
}
}).subscribe((patchedProduct: Product) => {
// some logic
});
// For resourceHateoasService this snippet is identical
Patching part values of an existing resource by resource id
.
To patch a resource by resource self link URL
use UpdateResource.
To update all values of the resource at once use UpdateResource method.
Method signature:
patchResourceById(id: number | string, requestBody: RequestBody<any>, options?: RequestOption): Observable<T | any>;
id
- resource id to patch.requestBody
- RequestBody contains request body (in this case new values for resource) and additional body options.options
- (optional) RequestOption that should be applied to the requestreturn value
- Resource with typeT
orraw response data
when returned value is not resource object.
Example of usage (given the presets):
/*
Suppose exitsProduct has an id = 1
Performing PATCH request by the URL: http://localhost:8080/api/v1/products/1
Request body:
{
"name" = existProduct.name,
"cost": 500
}
Note:
1) Description is not passed because by default null values ignore. If you want pass description = null value then need to pass additional valuesOption params.
2) Since patch resource updating only part of resource values at once then all not passed values will have the old values.
*/
const exitsProduct = ...;
exitsProduct.cost = 500;
exitsProduct.description = null;
this.productService.patchResourceById(1, {
body: {
...exitsProduct
}
})
.subscribe((patchedProduct: Product) => {
// some logic
});
this.resourceHateoasService.patchResourceById(Product, 1, {
body: {
...exitsProduct
}
})
.subscribe((patchedProduct: Product) => {
// some logic
});
With options:
/*
Suppose exitsProduct has an id = 1
Performing PUT request by the URL: http://localhost:8080/api/v1/products/1
Request body:
{
"name": null,
"cost": 500
}
Note:
1) Name was passed with null value because valuesOption = Include.NULL_VALUES was passed.
2) Since patch resource updating only part of resource values at once then all not passed values will have the old values.
*/
this.productService.patchResourceById(1, {
body: {
name: null,
cost: 500
},
valuesOption: {
include: Include.NULL_VALUES
}
})
.subscribe((patchedProduct: Product) => {
// some logic
});
this.resourceHateoasService.patchResourceById(Product, 1, {
body: {
name: null,
cost: 500
},
valuesOption: {
include: Include.NULL_VALUES
}
})
.subscribe((patchedProduct: Product) => {
// some logic
});
Deleting resource by resource self link URL
.
To delete a resource performs DELETE
request by the URL equals to resource self link
passed in entity
param.
To delete a resource by an
id
directly use DeleteResourceById.
Method signature:
deleteResource(entity: T, options?: RequestOption): Observable<HttpResponse<any> | any>;
entity
- resource to delete.options
- RequestOption additional options applied to the request.return value
- by defaultraw response data
or AngularHttpResponse
whenoptions
param has aobserve: 'response'
value.
Example of usage (given the presets):
/*
Suppose exitsProduct has a self link = http://localhost:8080/api/v1/products/1
Will be perform DELETE request to the http://localhost:8080/api/v1/products/1
*/
this.productService.deleteResource(exitsProduct)
.subscribe((result: any) => {
// some logic
});
// For resourceHateoasService this snippet is identical
With options:
/*
Suppose exitsProduct has a self link = http://localhost:8080/api/v1/products/1
Will be perform DELETE request to the http://localhost:8080/api/v1/products/1?testParam=test
*/
const exitsProduct = ...;
this.productService.deleteResource(exitsProduct, {
observe: 'response',
params: {
testParam: 'test'
}
})
.subscribe((result: HttpResponse<any>) => {
// some logic
});
// For resourceHateoasService this snippet is identical
Deleting resource by resource id
.
To delete a resource by resource
self link URL
use DeleteResource.
Method signature:
deleteResourceById(id: number | string, options?: RequestOption): Observable<HttpResponse<any> | any>;
id
- resource id to delete.options
- RequestOption additional options applied to the request.return value
- by defaultraw response data
or AngularHttpResponse
whenoptions
param has aobserve: 'response'
value.
Example of usage (given the presets):
/*
Suppose exitsProduct has an id = 1
Will be perform DELETE request to the http://localhost:8080/api/v1/products/1
*/
this.productService.deleteResourceById(1)
.subscribe((result: any) => {
// some logic
});
this.resourceHateoasService.deleteResourceById(Product, 1)
.subscribe((result: any) => {
// some logic
});
With options:
/*
Suppose exitsProduct has an id = 1
Will be perform DELETE request to the http://localhost:8080/api/v1/products/1?testParam=test
*/
const exitsProduct = ...;
this.productService.deleteResourceById(1, {
observe: 'response',
params: {
testParam: 'test'
}
})
.subscribe((result: HttpResponse<any>) => {
// some logic
});
this.resourceHateoasService.deleteResourceById(Product, 1, {
observe: 'response',
params: {
testParam: 'test'
}
})
.subscribe((result: HttpResponse<any>) => {
// some logic
});
Searching for one resource Resource.
This method takes GetOption parameter type.
To search resource projection instead of a resource type pass resource projection type in first method param when used
resourceHateoasService
. See more about projection support here.
Method signature:
searchResource(searchQuery: string, options?: GetOption): Observable<T>;
searchQuery
- additional part of the URL that follow after/search/
resource URL.options
- GetOption additional options applied to the request.return value
- Resource with typeT
.throws error
when returned value is not Resource
When using the method not need to pass
/search/
part of the URL insearchQuery
.
Example of usage (given the presets):
// Performing GET request by the URL: http://localhost:8080/api/v1/products/search/searchQuery
this.productService.searchResource('searchQuery')
.subscribe((product: Product) => {
// some logic
});
this.resourceHateoasService.searchResource(Product, 'searchQuery')
.subscribe((product: Product) => {
// some logic
});
With options:
// Performing GET request by the URL: http://localhost:8080/api/v1/products/search/byName?name=Fruit&sort=name,ASC
this.productService.searchResource('byName', {
params: {
name: 'Fruit'
},
sort: {
name: 'ASC'
},
// useCache: true | false, by default true
})
.subscribe((product: Product) => {
// some logic
});
this.resourceHateoasService.searchResource(Product, 'byName', {
params: {
name: 'Fruit'
},
sort: {
name: 'ASC'
},
// useCache: true | false, by default true
})
.subscribe((product: Product) => {
// some logic
});
Get projection example:
// Performing GET request by the URL: http://localhost:8080/api/v1/products/search/searchQuery?projection=productProjection
this.productService.searchProductProjection()
.subscribe((productProjections: ProductProjection) => {
// some logic
})
this.resourceHateoasService.searchResource(ProductProjection, 'searchQuery')
.subscribe((productProjections: ProductProjection) => {
// some logic
})
Searching for collection of resources ResourceCollection.
This method takes GetOption parameter type.
To search resource projection instead of a resource type pass resource projection type in first method param when used
resourceHateoasService
. See more about projection support here.
Method signature:
searchCollection(searchQuery: string, options?: GetOption): Observable<ResourceCollection<T>>;
searchQuery
- additional part of the URL that follow after/search/
resource URL.options
- GetOption additional options applied to the request.return value
- ResourceCollection collection of resources with typeT
.throws error
when returned value is not ResourceCollection
When using the method not need to pass
/search/
part of the URL insearchQuery
.
Example of usage (given the presets):
// Performing GET request by the URL: http://localhost:8080/api/v1/products/search/searchQuery
this.productService.searchCollection('searchQuery')
.subscribe((collection: ResourceCollection<Product>) => {
const products: Array<Product> = collection.resources;
// some logic
});
this.resourceHateoasService.searchCollection(Product, 'searchQuery')
.subscribe((collection: ResourceCollection<Product>) => {
const products: Array<Product> = collection.resources;
// some logic
});
With options:
// Performing GET request by the URL: http://localhost:8080/api/v1/products/search/byName?name=Fruit&sort=name,ASC
this.productService.searchCollection('byName', {
params: {
name: 'Fruit'
},
sort: {
name: 'ASC'
},
// useCache: true | false, by default true
}).subscribe((collection: ResourceCollection<Product>) => {
const products: Array<Product> = collection.resources;
// some logic
});
this.resourceHateoasService.searchCollection(Product, 'byName', {
params: {
name: 'Fruit'
},
sort: {
name: 'ASC'
},
// useCache: true | false, by default true
}).subscribe((collection: ResourceCollection<Product>) => {
const products: Array<Product> = collection.resources;
// some logic
});
Get projection example:
// Performing GET request by the URL: http://localhost:8080/api/v1/products/search/searchQuery?projection=productProjection
this.productService.searchProductProjections()
.subscribe((productProjections: ResourceCollection<ProductProjection>) => {
// some logic
})
this.resourceHateoasService.searchCollection(Product, 'searchQuery')
.subscribe((productProjections: ResourceCollection<ProductProjection>) => {
// some logic
})
Searching for collection of resources with paginationPagedResourceCollection. This method takes PagedGetOption parameter type.
To search resource projection instead of a resource type pass resource projection type in first method param when used
resourceHateoasService
. See more about projection support here.
If do not pass
pageParams
withPagedGetOption
then will be used default page options. Also, you can set up own default page params through configuration.
Method signature:
searchPage(searchQuery: string, options?: PagedGetOption): Observable<PagedResourceCollection<T>>;
searchQuery
- additional part of the URL that follow after/search/
resource URL.options
- PagedGetOption additional options applied to the request, if not passedpageParams
then used default page params.return value
- PagedResourceCollection paged collection of resources with typeT
.throws error
when returned value is not PagedResourceCollection
When using the method not need to pass
/search/
part of the URL insearchQuery
.
Example of usage (given the presets):
// Performing GET request by the URL: http://localhost:8080/api/v1/products/search/searchQuery?page=0&size=20
this.productService.searchPage('searchQuery')
.subscribe((pagedCollection: PagedResourceCollection<Product>) => {
const products: Array<Product> = pagedCollection.resources;
/* can use page methods
page.first();
page.last();
page.next();
page.prev();
page.size(...);
page.page(...);
page.sortElements(...);
page.customPage(...);
*/
});
this.resourceHateoasService.searchPage(Product, 'searchQuery')
.subscribe((pagedCollection: PagedResourceCollection<Product>) => {
const products: Array<Product> = pagedCollection.resources;
/* can use page methods
page.first();
page.last();
page.next();
page.prev();
page.size(...);
page.page(...);
page.sortElements(...);
page.customPage(...);
*/
});
With options:
// Performing GET request by the URL: http://localhost:8080/api/v1/products/search/byName?name=Fruit&page=1&size=30&sort=name,ASC
this.productService.searchPage('byName', {
pageParams: {
page: 1,
size: 30
},
params: {
name: 'Fruit'
},
sort: {
name: 'ASC'
},
// useCache: true | false, by default true
}).subscribe((pagedCollection: PagedResourceCollection<Product>) => {
const products: Array<Product> = pagedCollection.resources;
/* can use page methods
page.first();
page.last();
page.next();
page.prev();
page.size(...);
page.page(...);
page.sortElements(...);
page.customPage(...);
*/
});
this.resourceHateoasService.searchPage(Product, 'byName', {
pageParams: {
page: 1,
size: 40
},
params: {
testParam: 'test'
},
sort: {
cost: 'ASC'
},
// useCache: true | false, by default true
}).subscribe((pagedCollection: PagedResourceCollection<Product>) => {
const products: Array<Product> = pagedCollection.resources;
/* can use page methods
page.first();
page.last();
page.next();
page.prev();
page.size(...);
page.page(...);
page.sortElements(...);
page.customPage(...);
*/
});
Get projection example:
// Performing GET request by the URL: http://localhost:8080/api/v1/products/search/searchQuery?page=0&size=20&projection=productProjection
this.productService.searchPagedProductProjections()
.subscribe((page: PagedResourceCollection<ProductProjection>) => {
const products: Array<ProductProjection> = page.resources;
/* can use page methods
page.first();
page.last();
page.next();
page.prev();
page.size(...);
page.page(...);
page.sortElements(...);
page.customPage(...);
*/
})
this.resourceHateoasService.searchPage(ProductProjection)
.subscribe((page: PagedResourceCollection<ProductProjection>) => {
const products: Array<ProductProjection> = page.resources;
/* can use page methods
page.first();
page.last();
page.next();
page.prev();
page.size(...);
page.page(...);
page.sortElements(...);
page.customPage(...);
*/
})
Performing custom HTTP request for resource.
For example, use this method to perform a count query (see example below).
Method signature:
customQuery<R>(method: HttpMethod, query: string, requestBody?: RequestBody<any>, options?: PagedGetOption): Observable<R>;
method
- HTTP request method (GET/POST/PUT/PATCH).query
- additional part of the URL, added after root resource URL.requestBody
- RequestBody uses whenmethod
isPOST
,PATCH
,PUT
to pass request body and additional body options.options
- PagedGetOption additional options applied to the request.return value
-any
object that equals to passed generic type<R>
Example of usage (given the presets):
// Performing GET request by the URL: http://localhost:8080/api/v1/products/search/countAll
this.productService.customQuery<number>(HttpMethod.GET, '/search/countAllBy')
.subscribe((count: number) => {
// some logic
});
this.resourceHateoasService.customQuery<number>(Product, HttpMethod.GET, '/search/countAllBy')
.subscribe((count: number) => {
// some logic
});
Performing custom search HTTP request for resource.
For example, use this method to perform a count query (see example below).
Method signature:
customSearchQuery<R>(method: HttpMethod, searchQuery: string, requestBody?: RequestBody<any>, options?: PagedGetOption): Observable<R>;
method
- HTTP request method (GET/POST/PUT/PATCH).searchQuery
- additional part of the URL that follow after/search/
resource URL.requestBody
- RequestBody uses whenmethod
isPOST
,PATCH
,PUT
to pass request body and additional body options.options
- PagedGetOption additional options applied to the request.return value
-any
object that equals to passed generic type<R>
Example of usage (given the presets):
// Performing GET request by the URL: http://localhost:8080/api/v1/products/search/countAll
this.productService.customSearchQuery<number>(HttpMethod.GET, '/countAllBy')
.subscribe((count: number) => {
// some logic
});
this.resourceHateoasService.customSearchQuery<number>(Product, HttpMethod.GET, '/countAllBy')
.subscribe((count: number) => {
// some logic
});
This section describes library configuration params.
The library accepts configuration object:
http: {
rootUrl: string;
proxyUrl?: string;
};
logs?: {
verboseLogs?: boolean;
};
cache?: {
enabled: boolean;
lifeTime?: number;
};
useTypes?: {
resources: Array<new (...args: any[]) => Resource>;
embeddedResources?: Array<new (...args: any[]) => EmbeddedResource>;
};
isProduction?: boolean;
rootUrl
(required) - defines root server URL that will be used to perform resource requests.proxyUrl
(optional) - defines proxy URL that uses to change rootUrl to proxyUrl when getting a relation link.
verboseLogs
- to debug lib works enable logging to the console. With enabled logging, all prepared resource stages will be printed.
See more about logging here.
enabled
-true
to use cache forGET
requests,false
otherwise, default value istrue
.lifeTime
- default cache lifetime is 300 000 seconds (=5 minutes) pass new value to change default one.
See more about caching here.
This configuration section uses to declare resource/embedded resource types that will be used to create resources with concrete resource classes when parsed server's answer.
For example, you use subtypes and you need to know which subtype is received from the server (see more about suptypes in this section).
If you do not declare this subtype type in useTypes.resources
section you get the common Resource
class type without your subtype class methods.
Because @HateoasResource/@HateoasEmbeddedResource decorators are used to registering heirs of Resource
/EmbeddedResource
classes in hateoas-client
, then if you did not use your Resource
class in your code
(i.e. there was no import of this resource in your code, it will be means that the decorator did not run), then you will receive a generic Resource
type instead of a concrete resource type when parsed the server's answer.
When will it happen you will get warnings
in the browser console.
To prevent it you need to declare this resource type in useTypes.resources
section. This will run @HaeoasResource resource decorator and register your Resource
type in hateoas-client
.
The same logic applied to EmbeddedResource's. Use useTypes.embeddedResources
to register your EmbeddedResource
's type in hateoas-client
.
It is recommend if all types of your resources (i.e. your classes that extended Resource/EmbeddedResource classes) will be declared in sections
useTypes.resources
anduseTypes.embeddedResources
respectively.
resources
- an array of the Resource types used to create concrete resource types when parsed server's answer instead of commonResource
type.embeddedResources
- an array of the EmbeddedResource types used to create concrete embedded resource types when parsed server's answer instead of commonEmbeddedResource
type.
Some hateoas-client
features can change their behaviours depends on this param. For example, Logging disable all warning
messages when isProduction
is true.
isProduction
- need set totrue
when running in production environment,false
by default.
Let to change default page params that use when a page request not pass page params.
size
- change default page size, current value you can see here.page
- change default page number, current value you can see here.
The library supports caching GET
request response values.
By default, the cache enabled
.
To enable cache pass cache.enabled = true
to library configuration. Also, you can manage the cache expired time with cache.lifeTime
param.
More about the cache configuration see here.
Also, methods with options types GetOption
or PagedGetOption
has additional param useCache
that allows to manage the cache.
By default useCache
has true
value, but when the cache disabled then it param ignored.
After each successful GET request to get a resource or resource collection or paginated resource collection, response placed in the cache map
.
The cache map
key is CacheKey
object that holds full request url and all request options.
The cache map
value is raw hal-json
response.
When perform the same GET
request twice (to get resource or resource collection or paginated resource collection). First request hits the server to get the result, second request does not hit the server and result got from the cache.
When perform POST
, PATCH
, PUT
, DELETE
requests for some resource than all saved cache values for this resource will be evicted.
You can know when resource result got from the server or got from the cache enable the library verboseLogs param. See more about logging here.
To debug library use library logging.
To enable logging set logs.verboseLogs
to true
value see more here.
There are several logging stages:
BEGIN
- first stage, when method invokedCHECK_PARAMS
- logs errors after validation input paramsPREPARE_URL
- logs parts of generated request URL fromrootUrl
/proxyUrl
,resource name
and passed optionsPREPARE_PARAMS
- logs applied default request paramsINIT_RESOURCE
- logs when on initializing resource from a response errors occursRESOLVE_VALUES
- logs stage when resolving resource relations converting related resource object to a link urlCACHE_PUT
- logs a value saved to the cacheCACHE_GET
- logs a received value from the cacheCACHE_EVICT
- logs an evicted value from the cacheHTTP_REQUEST
- logs HTTP request paramsHTTP_RESPONSE
- logs raw HTTP response dataEND
- last stage, when method invoke was successful
With logging, you can find out was a value fetched from the cache or the server, how resource relationships resolved etc.
Logs can appear in the browser's console with a warning level. These messages can help you to understand what is problem and how you need to fix it.
It, not good practice to show these messages in production therefore warning level logs to disable when hateoas-client
used in production
mode.
See more configuration section how to set production mode.
This section describes public classes available to use in client apps.
Contains common options for all requests, it is allowing to add Angular HttpClient
options to resource request.
Uses directly as option type in methods that create, change or delete resource.
With RequestOption
you can change response type to HttpResponse
passing observe: 'response'
, by default used observe: body
.
Also, you can pass params
that will be added to the HTTP request as HTTP params and other Angular HttpClient
options.
export interface RequestOption {
params?: [paramName: string]: Resource | string | number | boolean;
headers?: HttpHeaders | {
[header: string]: string | string[];
};
observe?: 'body' | 'response';
reportProgress?: boolean;
withCredentials?: boolean;
}
params
iskey: value
values that will be added to the HTTP request as HTTP paramsobserve
is response type paramheaders
is http headers
Uses as option type in methods that retrieve resource or resource collection from the server.
GetOption
extends RequestOption
and adds additional params to GET request.
export interface GetOption extends RequestOption {
sort?: Sort;
useCache?: boolean;
}
sort
is Sort object with response sort optionsuseCache
is property allows to disable the cache for current request
Uses as option type in methods that retrieve paginated resource collection from the server.
PagedGetOption
extends GetOption
and adds the page params.
export interface PagedGetOption extends GetOption {
pageParams?: PageParam;
}
export interface PageParam {
page?: number;
size?: number;
}
page
is page number paramsize
is page size param
Uses as request body type in methods that create or change resource.
RequestBody
is an interface that describes the next options:
export interface RequestBody<T> {
body: T;
valuesOption?: ValuesOption;
}
export interface ValuesOption {
include: Include;
}
export enum Include {
NULL_VALUES = 'NULL_VALUES'
}
body
is a request body objectvaluesOption
are additional options that allows manipulation body object values
For example, passed body as {body: {name: 'Name', age: null}}
by default any properties that have null
values will be ignored and will not pass.
To pass null
values, need to pass valuesOption: {include: Include.NULL_VALUES}
.
When body object has
undefined
property then it will be ignored even you passInclude.NULL_VALUES
.
Uses as param type in methods that applied GetOption
, PagedGetOption
options.
Sort
params are key: value
object where key
is a property name to sort and value
is sort order.
export interface Sort {
[propertyToSort: string]: SortOrder;
}
export type SortOrder = 'DESC' | 'ASC';
Uses as option type in PagedResourceCollection in customPage method.
SortedPageParam
is an interface with page and sort params.
export interface SortedPageParam {
pageParams?: PageParam;
sort?: Sort;
}
export interface PageParam {
page?: number;
size?: number;
}
pageParams
is page number and page size paramssort
is Sort object with response sort options