Ember JSON API Adapter - customise request URL using dynamic param - ember-data

When the action below is called in a route, the default the Ember JSON API Adapter will send a PATCH request to ${HOST}/${NAMESPACE}/${MODEL}/${ID}.
saveChanges: function(record) {
return record.save();
},
I would like to be able to send a single PATCH request to ${HOST}/${NAMESPACE}/${MODEL}/************/${ID} where the value of ************ can be passed to the action as a dynamic parameter when calling record.save().
Is there any way to do this using the JSONAPI adapter, or do I have to just use a vanilla AJAX request?

You could customize your application- or model-adapter
ember generate adapter application
or
ember generate adapter model-name
app/adapters/application.js or app/adapters/model-name.js
import DS from 'ember-data';
export default DS.JSONAPIAdapter.extend({
namespace: 'api',
urlForUpdateRecord(id, modelName, snapshot) {
let originalUpdateURL = this._super(...arguments);
let { adapterOptions } = snapshot;
if (adapterOptions && adapterOptions.customString) {
let modelPath = this.pathForType(modelName);
return originalUpdateURL.replace(`/${modelPath}/`, `/${modelPath}/${adapterOptions.customString}/`);
}
return originalUpdateURL;
}
});
after that you can call save-method of your model with adapterOptions passed:
this.get('model').save({
adapterOptions: { customString: 'hello-world' }
});
after that your patchURL will look like:
/api/your-model/hello-world/1

Related

How can I return UUID using RabbitMQ

I would like to return the UUID(v4) created for each request in nestjs as a response to the request.
I want to return the UUID(v4) that nestjs creates for each request as the response to the request.
However, I am using rabbitmq.
Is there any other way to return the UUID after going through rabbitmq for each request?
With this method, I am creating a connection for each request.
I want to create the connection only once after nestjs is started.
Also, any other method using other libraries would be good.
import { Controller, Get } from '#nestjs/common';
import { v4 as uuidv4 } from 'uuid';
#Controller('cats')
export class CatsController {
#Get()
findAll(): string {
const sequence = uuidv4(); // I want to return this.
return 'This action returns all cats';
}
}
...
await channel.consume(queueName, async (data: any) => {
if (queueName === 'testQueue') {
// do something.
}
});

Ember-simple-auth and ember-fetch no authorization in fetch query

I am using ember with ember-simple-auth(1.7.0) for authentication. Here is the application adapter function :
authorize(xhr) {
let { email, token } = this.get('session.data.authenticated');
let authData = `Token token="${token}", email="${email}"`;
xhr.setRequestHeader('Authorization', authData);
}
When I use ember-fetch(5.1.3) there is no header for authentication :
fetch('/appname/v1/user/count'+count_params).then((response)=>{return response.json()})
The same model do successful emberDS query with the auth info in the header.
How can I add the information to the fetch headers ?
EDIT :
This is the service I created to wrap fetch :
import Service from '#ember/service';
import fetch from 'fetch';
import { inject as service} from "#ember/service"
export default Service.extend({
fetch(url){
let { email, token } = this.get('session.data.authenticated')
let authData = `Token token="${token}", email="${email}"`
return fetch(url,{headers: {'Authorization': authData}}).then(
(response)=>{return response.json()}
)
},
session: service()
});
You need to create fetch wrapper service and use it, instead of "raw" fetch and boilerplating.
Raw usage possible with headers - https://github.com/github/fetch#post-json

React-Admin: Implementing a Custom Request Type

Is it possible to implement a custom request type in a custom provider in the react-admin framework?
My Use Case
In my case I have 2 separate cases of reference fields.
1. Reference ID field (normal)
api -> users/1
api -> comments/1
2. Sub Entity Reference
api -> users/1/comments/1
So I was planning to implement another request type, like this:
switch (type) {
case GET_LIST:
return apiGetList(resourceName, params);
case GET_MANY:
return apiGetMany(resourceName, params);
case GET_MANY_REFERENCE:
return apiGetManyReference(resourceName, params);
case GET_MANY_REFERENCE_CUSTOM:
return apiGetManyReferenceCustom(resourceName, params);
}
But I can't figure out how to trigger the type from the custom field?
Update for react-admin 3.x
So with React Admin 3.x the data provider now uses method calls instead of a switch case.
For example you can create your own dataprovider method, and the consumer can check if it exists by calling it.
try {
const response = await dataProvider.createMany(resource, { data: values });
return response;
} catch (error) {
const shouldTryFallback = error.toString().includes("Unknown dataProvider");
const apiError = !shouldTryFallback;
if (apiError) {
// handle api error
}
if (shouldTryFallback) {
console.log(
"createInDataProvider",
"createMany not found on data provider (you may need to implement it)"
);
try {
// try and use fallback dataprovider methods
} catch (error) {
// handle fallback error
}
}
}
return reportItems;
Full example of how this is used: https://github.com/benwinding/react-admin-import-csv/blob/0868ca554501c3545dac28a5101ee60a20736aa2/src/uploader.ts#L78

Http post and get request in angular 6

In angular 5.2.x for http get and post I had this code:
post(url: string, model: any): Observable<boolean> {
return this.http.post(url, model)
.map(response => response)
.do(data => console.log(url + ': ' + JSON.stringify(data)))
.catch(err => this.handleError(err));
}
get(url: string): Observable<any> {
return this.http.get(url)
.map(response => response)
.do(data =>
console.log(url + ': ' + JSON.stringify(data))
)
.catch((error: any) => Observable.throw(this.handleError(error)));
}
In angular 6 it doesn't work.
How can we make an HTTP post or get request?
Update :
In angular 7, they are the same as 6
In angular 6
the complete answer found in live example
/** POST: add a new hero to the database */
addHero (hero: Hero): Observable<Hero> {
return this.http.post<Hero>(this.heroesUrl, hero, httpOptions)
.pipe(
catchError(this.handleError('addHero', hero))
);
}
/** GET heroes from the server */
getHeroes (): Observable<Hero[]> {
return this.http.get<Hero[]>(this.heroesUrl)
.pipe(
catchError(this.handleError('getHeroes', []))
);
}
it's because of pipeable/lettable operators which now angular is able to use tree-shakable and remove unused imports and optimize the app
some rxjs functions are changed
do -> tap
catch -> catchError
switch -> switchAll
finally -> finalize
more in MIGRATION
and Import paths
For JavaScript developers, the general rule is as follows:
rxjs: Creation methods, types, schedulers and utilities
import { Observable, Subject, asapScheduler, pipe, of, from, interval, merge, fromEvent } from 'rxjs';
rxjs/operators: All pipeable operators:
import { map, filter, scan } from 'rxjs/operators';
rxjs/webSocket: The web socket subject implementation
import { webSocket } from 'rxjs/webSocket';
rxjs/ajax: The Rx ajax implementation
import { ajax } from 'rxjs/ajax';
rxjs/testing: The testing utilities
import { TestScheduler } from 'rxjs/testing';
and for backward compatability you can use rxjs-compat
You can do a post/get using a library which allows you to use HttpClient with strongly-typed callbacks.
The data and the error are available directly via these callbacks.
The library is called angular-extended-http-client.
angular-extended-http-client library on GitHub
angular-extended-http-client library on NPM
Very easy to use.
Traditional approach
In the traditional approach you return Observable<HttpResponse<T>> from Service API. This is tied to HttpResponse.
With this approach you have to use .subscribe(x => ...) in the rest of your code.
This creates a tight coupling between the http layer and the rest of your code.
Strongly-typed callback approach
You only deal with your Models in these strongly-typed callbacks.
Hence, The rest of your code only knows about your Models.
Sample usage
The strongly-typed callbacks are
Success:
IObservable<T>
IObservableHttpResponse
IObservableHttpCustomResponse<T>
Failure:
IObservableError<TError>
IObservableHttpError
IObservableHttpCustomError<TError>
Add package to your project and in your app module
import { HttpClientExtModule } from 'angular-extended-http-client';
and in the #NgModule imports
imports: [
.
.
.
HttpClientExtModule
],
Your Models
export class SearchModel {
code: string;
}
//Normal response returned by the API.
export class RacingResponse {
result: RacingItem[];
}
//Custom exception thrown by the API.
export class APIException {
className: string;
}
Your Service
In your Service, you just create params with these callback types.
Then, pass them on to the HttpClientExt's get method.
import { Injectable, Inject } from '#angular/core'
import { SearchModel, RacingResponse, APIException } from '../models/models'
import { HttpClientExt, IObservable, IObservableError, ResponseType, ErrorType } from 'angular-extended-http-client';
.
.
#Injectable()
export class RacingService {
//Inject HttpClientExt component.
constructor(private client: HttpClientExt, #Inject(APP_CONFIG) private config: AppConfig) {
}
//Declare params of type IObservable<T> and IObservableError<TError>.
//These are the success and failure callbacks.
//The success callback will return the response objects returned by the underlying HttpClient call.
//The failure callback will return the error objects returned by the underlying HttpClient call.
searchRaceInfo(model: SearchModel, success: IObservable<RacingResponse>, failure?: IObservableError<APIException>) {
let url = this.config.apiEndpoint;
this.client.post<SearchModel, RacingResponse>(url, model,
ResponseType.IObservable, success,
ErrorType.IObservableError, failure);
}
}
Your Component
In your Component, your Service is injected and the searchRaceInfo API called as shown below.
search() {
this.service.searchRaceInfo(this.searchModel, response => this.result = response.result,
error => this.errorMsg = error.className);
}
Both, response and error returned in the callbacks are strongly typed. Eg. response is type RacingResponse and error is APIException.
For reading full response in Angular you should add the observe option:
{ observe: 'response' }
return this.http.get(`${environment.serverUrl}/api/posts/${postId}/comments/?page=${page}&size=${size}`, { observe: 'response' });

Can I set authorization headers with RequireJS?

We want to have 2 sets of resources for our AngularJS app (public/private) which uses RequireJS for dependency management. Basically everything on the login page would be public and once logged in, another angularjs app would be loaded (new requirejs config) that would load resources that require authentication to access.
Is there a way to configure requirejs to set an authorization header when loading resources?
It depends on what you mean by "resources" and how your server is configured. But in general - yes, since you are using AngularJS you can use the $httpProvider to inject an interceptor service.
For example, in a service:
var dependencies = ['$rootScope', 'userService'];
var service = function ($rootScope, userService) {
return {
request: function(config) {
var currentUser = userService.getCurrentUser();
var access_token = currentUser ? currentUser.access_token : null;
if(access_token) {
config.headers.authorization = access_token;
}
return config;
},
responseError: function (response) {
if(response.status === 401) {
$rootScope.$broadcast('unauthorized');
}
return response;
}
};
};
module.factory(name, dependencies.concat(service));
Then, after you configure your routes, you can use:
$httpProvider.interceptors.push( 'someService');
You can find some more information on interceptors here: https://docs.angularjs.org/api/ng/service/$http#interceptors
UPDATE
You might be able to use the text plugin to try and receive it, but I don't see the point in protecting client side code. Plus, if you want to use optimization the resources will just come in one file anyway...
config: {
text: {
onXhr: function (xhr, url) {
xhr.setRequestHeader('Authorization','Basic ' + token);
}
}
}
Refer to: custom-xhr-hooks
Another UPDATE
You could also use urlArgs (mainly used for cache invalidation) without using the text plugin:
require.config({
urlArgs: 'token='+token,
...
)}