Angular2 multiple components using one REST call - rest

I am part of a Angular2 application (we use beta3) and the issue is the following:
Usually we have a component that uses some service that uses some rest call and the component displays the data. Great.
However we do have a page with more then 6 components all of them using the same REST call...(the backend returns data for ALL of them) and it doesn't make sense to call 6 times the REST for each component, also it will be weird if we do some client side caching.
Is there something available out of the box ? Or a Pattern to handle such case?
Thanks.

Just do it in a shared service. If you add it only in bootstrap(..., [OtherProviders, HTTP_PROVIDERS, MyService]) each component will get injected the same instance. Store the data in the service and every component can access it
export class MyComponent {
constructor(private dataService:MyService) {
dataService.getData().subscribe(data => { this.data = data; });
}
}
export class MyService {
getData() {
if(!this.data) {
return http.get(...).map(...).subscribe(data => { this.data = data;});
}
return this.data;
}
}

The #Günter's answer really makes sense!
I don't know your code is organized but observable can also be subscribed several times. To do that you need to make them "hot" using the share operator:
export class MyService {
dataObservable:Observable;
initDataObservable() {
this.dataObservable = http.get(...).map(...).share();
}
}
Without using the share operator, corresponding request will executed several times (one per subscribe).
You can notice that the request will be executed once one subscribe method is called on the observable.

Related

Denormalization practices in reactive application

I am creating a reactive application with Meteor (with MongoDB as a backend).
I initially created a non-reactive-aware collection and denormalizers, eg.:
class DocCollection extends Mongo.Collection {
insert(doc, callback) {
const docId = super.insert(doc, callback);
doc = docMongo.findOne(docId); // for illustration, A
console.log(doc);
return docId;
}
}
docMongo = new DocCollection();
Now, I'd like to wrap it into MongoObservable, which will facilitate listening to the changes to the collection:
export const Doc = new MongoObservable.Collection(docMongo);
Then, I define a Method:
Meteor.methods({
add_me() {
Doc.insert(myDoc);
}
});
in server/main.js and call it in app.component.ts's constructor:
#Component(...)
export class AppComponent {
constructor() {
Meteor.call('add_me');
}
}
I get undefined printed to console (unless I sleep a little before findOne), so I suppose when I was looking for the doc after insertion in my Mongo.Collection, the document wasn't yet ready to be searched for.
Why does it happen, even though I overwrote the non-reactive class and only then wrapped it in MongoObservable?
How do I typically do denormalization with a reactive collection? Should I pass observables to my denormalizers and there create new ones, or is it possible to nicely wrap the non-reactive code afterwards (like I tried and failed above)? Note that I don't want to directly pass doc inside, as in more complex scenarios it will cause other inserts/updates elsewhere for which I'd also want to wait.
How do people typically test these things? If I run a test, the code above may succeed locally, as db insertion time is small, but fail when the delay is higher.

Multiple Slim routes with the same signature

We are looking at using Slim 3 as the framework for our API. I have searched SO and the Slim docs, but cannot find an answer to the issue. If we have different route files (e.g. v1, v2, etc.) and if two routes have the same signature, an error is thrown. Is there any way to cascade the routes so that the last loaded route for a particular signature is used?
For example, say v1.php has a route for GET ("/test") and v2.php also contains this route, can we use the latest version? Even simpler would be if a file of routes contains two routes with the same signature, is there a way of the latter method being used (and no error being thrown)?
A similar question is asked here but this uses hooks (which have been removed from Slim 3 as per here)
I looked at the Slim code and I didn't find a simple way of allowing duplicated routes (preventing the exception).
The new Slim uses FastRoute as dependency. It calls FastRoute\simpleDispatcher and doesn't offer any configuration possiblity. Even if it did allow some configuration, FastRoute doesn't have any built-in option to allow duplicated routes. A custom implementation of a DataGenerator would be needed.
But following the instructions above, we can get a custom DataGenerator by passing to Slim App a custom Router which instantiates some FastRoute::Dispatcher implementation which then uses the custom DataGenerator.
First the CustomDataGenerator (let's go the easy way and do some copy and pasting from \FastRoute\RegexBasedAbstract and \FastRoute\GroupCountBased)
<?php
class CustomDataGenerator implements \FastRoute\DataGenerator {
/*
* 1. Copy over everything from the RegexBasedAbstract
* 2. Replace abstract methods with implementations from GroupCountBased
* 3. change the addStaticRoute and addVariableRoute
* to the following implementations
*/
private function addStaticRoute($httpMethod, $routeData, $handler) {
$routeStr = $routeData[0];
if (isset($this->methodToRegexToRoutesMap[$httpMethod])) {
foreach ($this->methodToRegexToRoutesMap[$httpMethod] as $route) {
if ($route->matches($routeStr)) {
throw new BadRouteException(sprintf(
'Static route "%s" is shadowed by previously defined variable route "%s" for method "%s"',
$routeStr, $route->regex, $httpMethod
));
}
}
}
if (isset($this->staticRoutes[$httpMethod][$routeStr])) {
unset($this->staticRoutes[$httpMethod][$routeStr]);
}
$this->staticRoutes[$httpMethod][$routeStr] = $handler;
}
private function addVariableRoute($httpMethod, $routeData, $handler) {
list($regex, $variables) = $this->buildRegexForRoute($routeData);
if (isset($this->methodToRegexToRoutesMap[$httpMethod][$regex])) {
unset($this->methodToRegexToRoutesMap[$httpMethod][$regex]);
}
$this->methodToRegexToRoutesMap[$httpMethod][$regex] = new \FastRoute\Route(
$httpMethod, $handler, $regex, $variables
);
}
}
Then the custom Router
<?php
class CustomRouter extends \Slim\Router {
protected function createDispatcher() {
return $this->dispatcher ?: \FastRoute\simpleDispatcher(function (\FastRoute\RouteCollector $r) {
foreach ($this->getRoutes() as $route) {
$r->addRoute($route->getMethods(), $route->getPattern(), $route->getIdentifier());
}
}, [
'routeParser' => $this->routeParser,
'dataGenerator' => new CustomDataGenerator()
]);
}
}
and finally instantiate the Slim app with the custom router
<?php
$app = new \Slim\App(array(
'router' => new CustomRouter()
));
The code above, if a duplicated route is detected, removes the previous route and stores the new one.
I hope I didn't miss any simpler way of achieving this result.

How do I correctly map versioned resources in Grails?

I have grails running a REST API and using version numbers in the URL like so: https://api.mycompany.com/v2/metadata. I need to change the parameters of one of the endpoints, so I'm bumping the version up to v3. Only one controller is affected, so I would like to delegate the remaining calls back to the controllers for v2 without having to copy/paste everything again. Here's the relevant section of my UrlMappings.groovy:
class UrlMappings {
static mappings = {
"/v3/widget"(controller: "v3widget")
"/v3/$otherResource" {
// does not work, but illustrates what I want to happen
uri = { "/v2/" + params.otherResource }
}
// rest of my file...
"/v2/metadata"(controller: 'metadata')
...
What's the correct way to do this? I'm using grails 2.2.5
I would use a variable in the uri path, and instead of your example you would have the following mappings:
class UrlMappings {
static mappings = {
"/$apiVersion/widget"(controller: "v3widget")
"/$apiVersion/otherResource"(controller: "otherResource")
// rest of my file...
"/$apiVersion/metadata"(controller: 'metadata')
...
And then you could check for the value in controller:
class OtherResourceController {
def index(){
if(params.apiVersion == 'v2') {
...
} else {
...
}
}
}
The example here is checking for the string value, but you could go a bit deeper and actually convert the string value into internal api version enum representation which might be easier to manage. You could also do that in filter.
This way you can increment the logic changes and api will have a nice fallback, will delegate to default version.
But it gets really curly when you have couple of api versions layered one on the other.
The solution I found that works relies on the fact that wildcard mappings can also accept other regular expressions:
class UrlMappings {
static mappings = {
// v3 specific
"/v3/widget"(controller: "v3widget")
// v3/v2 common
"/v[23]/$otherResource" {
// normal mappings go here
}
// v2 specific
"/v2/metadata"(controller: 'v2metadata')
...
This solution works well since I don't have to repeat any mappings, and it's clear what's different between v2 and v3 resources.

Does Luracast Restler support multi-part URIs?

I've recently started a project using Luracast Restler. It seems a very simple and effective way to set up a REST API. With very little code, I was able to provide CRUD services for my Category and Product resources.
My GET methods look like this:
class Categories
{
function get($id=NULL) {
if (isset($id))
{
// return category details for $id.
}
else
{
// return all categories.
}
}
}
class Products
{
function get($id=NULL) {
if (isset($id))
{
// return product details for $id.
}
else
{
// return all products.
}
}
}
Clients can get the details of the "books" category using:
http:api/categories/books
or all categories using:
http:api/categories
Same for products. One product:
http:api/products/123
All products:
http:api/products
So far so good.
Now I want to progress to something slightly more involved. I want to give my clients access to the products in a category.
I want my URI to be:
http:api/categories//products
E.g.
http:api/categories/books/products
and from there, I want to offer:
http:api/categories//products/
E.g.
http:api/categories/books/products/123
This gives my client the ability to transfer from one resource to another using a progressive series of links, which I see as a core principle of REST.
But I can't see a way of achieving this with Restler. I've seen some mention of JavaDoc comments being used to specify URI mapping, so I tried this:
class Products
{
/**
* url GET /categories/:catId/products/:prodId
*/
function get($catId=NULL, $prodId=NULL) {
// Get product($prodId) of category($catId)
}
}
But this doesn’t work. Restler doesn’t seem to take any information from the comment; it implicitly creates the URI route based on class name and function name.
Can anyone help? Am I missing something? Any advice would be much appreciated.
Everything is fine in the example above and what you are trying to achieve except one simple mistake that stopped it from working!
Your PHPDoc comment is missing #
Change your code as follows
<?php
class Products
{
/**
* #url GET /categories/:catId/products/:prodId
*/
function get($catId=NULL, $prodId=NULL) {
// Get product($prodId) of category($catId)
}
}
Also take a look at the related question
How do you organize Luracast Restler classes to create related route endpoints?

Replace registration in Autofac

I have an application which does data processing. There is
class Pipeline {
IEnumerable<IFilter> Filters {get; set;}
I register filters implementations as
builder.RegisterType<DiversityFilter>().As<IFilter>();
builder.RegisterType<OverflowFilter>().As<IFilter>();
...
So far so good. Now, for experimentation and fine-tuning I want to be able to override any filter implementation in config file with a program(script) which would read data from stdin, process it and send data to stdout. I've implemented a module with "fileName", "args" and "insteadOf" custom properties, described module in xml and got it called.
In the module I register my "ExecutableFilter" but how do I make it run "instead of" desired service? If I try do it like this:
builder.RegisterType<ExecutableFilter>().As<DiversityFilter>()
then I get an exception " The type 'ExecutableFilter' is not assignable to service 'DiversityFilter'.". Ok, this is logical. But what are my options then?
Once you've overridden the registration for IFilter "After" with your wire-tap, you won't be able to resolve it from the container, as the new registration will be activated instead, hence the circular lookup.
Instead, create and register a module that hooks into the filter's creation, and replaces the instance with the 'wire tapped' one:
class WiretapModule : Module
{
override void AttachToComponentRegistration(
IComponentRegistration registration,
IComponentRegistry registry)
{
if (registration.Services.OfType<KeyedService>().Any(
s => s.ServiceKey == After && s.ServiceType == typeof(IFilter)))
{
registration.Activating += (s, e) => {
e.Instance = new WireTap((IFilter)e.Instance, new ExecuteProvider(fileName, args))
};
}
}
}
(Cross-posted to the Autofac group: https://groups.google.com/forum/#!topic/autofac/yLbTeuCObrU)
What you describe is part container work, part business logic. The challenge is to keep separation of concerns here. IMO, the container should do what it is supposed to do, that is building and serving up instances or collections thereof. It should not do the "instead of" in this case. I would rather "enrich" the services with enough information so that the pipeline make the decision.
The "enrichment" can be accomplished by making the ExecutableFilter implement a more distinct interface.
interface IInsteadOfFilter : IFilter { }
...
builder.RegisterType<ExecutableFilter>().As<IFilter>();
...
class Pipeline
{
IEnumerable<IFilter> Filters {get;set;}
public void DoTheFiltering()
{
var filters = Filters.OfType<IInsteadOfFilter>();
if (!insteadof.Any())
filters = Filters;
foreach(var filter in filters)
{
...
}
}
You could also solve this using the metadata infrastructure, which gives us an even more expressive way of differentiating services.