Search code examples
rxjsangular2-serviceshateoas

How to use RxJS in Angular2 to interact with HATEOAS API?


I have a (Django) API that implements HATEOAS, so typically foreign keyed objects come through as URLs to other API endpoints. The following is the result from http://localhost:8000/brew-monitor/api/fermentations/1/ and is a Fermentation object with associated Dataset objects:

{
    "id": 1,
    "name": "My Beer",
    "datasets": [
        "http://localhost:8000/brew-monitor/api/datasets/1/",
        "http://localhost:8000/brew-monitor/api/datasets/2/"
    ]
}

I need to write a service that will GET the above object and then iterate through the datasets URLs and GET those as well (I know, maybe it can be more efficient but I'm teaching myself about HATEOAS).

A Dataset object looks like:

{
    "id": 1,
    "unit": "DEGF",
    "variable_measured": "T",
    "fermentation": "http://localhost:8000/brew-monitor/api/fermentations/1/",
    "logging_device": "http://localhost:8000/brew-monitor/api/devices/1/",
    "controls": [],
    "active": true,
    "datapoints": [
        "http://localhost:8000/brew-monitor/api/datapoints/1/",
        "http://localhost:8000/brew-monitor/api/datapoints/2/",
        "http://localhost:8000/brew-monitor/api/datapoints/3/"
    ]
}

So I want the final result from my service to look like:

{
    "id": 1,
    "name": "My Beer",
    "datasets": [
        {
            "id": 1,
            "unit": "DEGF",
            "variable_measured": "T",
            "fermentation": "http://localhost:8000/brew-monitor/api/fermentations/1/",
            "logging_device": "http://localhost:8000/brew-monitor/api/devices/1/",
            "controls": [],
            "active": true,
            "datapoints": [
                "http://localhost:8000/brew-monitor/api/datapoints/1/",
                "http://localhost:8000/brew-monitor/api/datapoints/2/",
                "http://localhost:8000/brew-monitor/api/datapoints/3/"
            ]
        },
        {
            "id": 2,
            "unit": "UNITLESS",
            "variable_measured": "SG",
            "fermentation": "http://localhost:8000/brew-monitor/api/fermentations/1/",
            "logging_device": "http://localhost:8000/brew-monitor/api/devices/1/",
            "controls": [],
            "active": true,
            "datapoints": [
                "http://localhost:8000/brew-monitor/api/datapoints/4/",
                "http://localhost:8000/brew-monitor/api/datapoints/5/",
                "http://localhost:8000/brew-monitor/api/datapoints/6/"
            ]
        }
    ]
}

I just know RxJS can do this. How?

EDIT: I have a solution that uses nested subscriptions but I think it's common knowledge that you should avoid that. So no nested subscriptions please.


Solution

  • I'd do something like this

    const source$ = of({
        "id": 1,
        "name": "My Beer",
        "datasets": [
            "http://localhost:8000/brew-monitor/api/datasets/1/",
            "http://localhost:8000/brew-monitor/api/datasets/2/"
        ]
    });
    const data$ = source$.pipe(
      switchMap(source => this.getDatasets(source))
    );
    
    data$.subscribe(x => console.log(x));
    
    function getDatasets(source) {
      return forkJoin(source.datasets.map(endpoint => fakeHttpRequest(endpoint))).pipe(
        map(results => ({...source, datasets: results}))
      );
    }
    
    function fakeHttpRequest(endpoint) {
      return of({
        test: '123'
      });
    }
    

    RXJS is awesome.