Skip to content

Instantly share code, notes, and snippets.

@soyuka
Created July 31, 2017 14:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save soyuka/c2e89ebf3c7a33f8d059c567aefd471c to your computer and use it in GitHub Desktop.
Save soyuka/c2e89ebf3c7a33f8d059c567aefd471c to your computer and use it in GitHub Desktop.
RxRest in an angular / api-platform context using jsonld and hydra
import { NgModule, Component, OnInit } from '@angular/core'
import { AngularRxRest, AngularRxRestConfiguration } from './rxrest'
@Component({
selector: 'prefix-main',
template: '<prefix-foo></prefix-foo>'
})
export class MainComponent implements OnInit {
loading: boolean = false
loggedIn: boolean = false
constructor(
public user: User,
private rxrestConfiguration: RxRestConfiguration,
) {
this.loggedIn = !!this.user.token
}
ngOnInit() {
let pending = 0
const pendingRequest = (add = true) => {
pending = add ? pending + 1 : pending - 1
if (pending > 0 && this.loading === false) {
this.loading = true
} else if (pending === 0) {
this.loading = false
}
}
const user = this.user
this.rxrestConfiguration.abortCallback = function(req: Request) {
pendingRequest(false)
}
function addHeaders(request: Request) {
const headers = (request.headers as Headers)
headers.set('Content-Type', 'application/ld+json')
headers.set('Accept', 'application/ld+json')
headers.set('Authorization', 'Bearer ' + user.token)
pendingRequest()
return request
}
this.rxrestConfiguration.requestInterceptors.push(addHeaders)
this.rxrestConfiguration.errorInterceptors.push((response: Response) => {
this.logger.error(`Request error on ${response.url}, got status ${response.status}`, response)
if (response.status < 500) {
return
}
this.messages.add('Une erreur critique est survenue !', 'alert')
return response
})
}
}
@NgModule({
bootstrap: [RootComponent],
declarations: [
MainComponent,
UsageComponent
],
providers: [
User,
{provide: RxRest, useClass: AngularRxRest},
{provide: RxRestConfiguration, useClass: AngularRxRestConfiguration},
],
imports: [
BrowserModule,
FormsModule,
// ...
]
})
export class RootModule {
//DO NOT REMOVE BECAUSE IT CALLS THE CONSTRUCTOR WHICH SETS UP RXREST
constructor(private rxrest: RxRest) {
console.log('RxRest up', this.rxrest)
}
}
import {RxRestItem} from 'rxrest'
import * as moment from 'moment-timezone'
import {environment} from '../environments/environment'
function unpopulateHref(e: any) {
if (Array.isArray(e)) {
for (let i = 0; i < e.length; i++) {
unpopulateHref(e[i])
}
return
}
if (typeof e !== 'object' || !e) {
return
}
delete e.id
delete e.href
for (const i in e) {
if (i in e) {
if (e[i] instanceof Date) {
e[i] = moment(e[i]).format(environment.phpDateTimeFormat)
} else if (e[i] && moment.isMoment(e[i])) {
e[i] = e[i].format(environment.phpDateTimeFormat)
}
unpopulateHref(e[i])
}
}
}
export function requestBodyHandler(body: any): string|FormData|URLSearchParams {
if (!body) {
return undefined
}
if (body instanceof FormData || body instanceof URLSearchParams) {
return body
}
if (!(body instanceof RxRestItem)) {
return JSON.stringify(body)
}
body = body.plain()
unpopulateHref(body)
return JSON.stringify(body)
}
import * as moment from 'moment-timezone'
/**
* Transform data
*/
function transformData(data: any) {
if (!data) {
return data
}
if (data['@id']) {
//@TODO through configuration for prefix /api
data.href = data['@id'].replace('/api', '')
const id = data['@id'].split('/')
data.id = id[id.length - 1]
}
//data format
for (const i in data) {
if (~['@id', 'href'].indexOf(i)) {
continue
}
// tslint:disable-next-line:triple-equals
if (parseFloat(data[i]) == data[i] || i.indexOf('@') === 0) {
continue
}
if (typeof data[i] === 'boolean') {
continue
}
if (typeof data[i] === 'string') {
try {
const date = moment(data[i], moment.ISO_8601, true)
if (date.isValid()) {
data[i] = date
continue
}
} catch (e) {}
}
if (data[i] instanceof Object && !Array.isArray(data[i])) {
transformData(data[i])
} else if (Array.isArray(data[i])) {
data[i].forEach((e: any) => transformData(e))
}
}
return data
}
export function responseBodyHandler(body: any): Promise<any> {
return body.text()
.then(text => {
if (!text) {
return null
}
text = JSON.parse(text)
const metadata = text['hydra:view']
if (text['hydra:member']) {
text = text['hydra:member'].map(e => transformData(e))
} else {
text = transformData(text)
}
return {body: text, metadata: metadata}
})
}
import { RxRest, RxRestConfiguration } from 'rxrest'
import { Injectable } from '@angular/core'
import { requestBodyHandler } from './requestBodyHandler'
import { responseBodyHandler } from './responseBodyHandler'
import { environment } from '../../../environments/environment'
@Injectable()
export class AngularRxRestConfiguration extends RxRestConfiguration {
constructor() {
super()
//@TODO through configuration for prefix /api
this.baseURL = environment.baseURL + '/api'
this.requestBodyHandler = requestBodyHandler
this.responseBodyHandler = responseBodyHandler
}
}
@Injectable()
export class AngularRxRest extends RxRest {
constructor(config: RxRestConfiguration) {
super(config)
}
}
import { Component, OnInit } from '@angular/core'
import { RxRest } from 'rxrest'
@Component({
selector: 'prefix-foo',
template: '<ul><li *ngFor="let foo of foos | async">{{foo.id}}</li></ul>'
})
export class UsageComponent implements OnInit {
constructor(private rxrest: RxRest) {}
ngOnInit() {
this.foos = Observable.from(this.rxrest<Foo>.all('foos', true).get())
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment