lagoshny/ngx-hal-client

ResolveRelations not working if using an own base class which implements "Resource"

Closed this issue · 5 comments

If the application needs it's own abstract implementation of Resource, e.g. 'MyBaseResource', the replacement of objects to links is not working.

export abstract class MyBaseResource extends Resource {
    constructor() {
        super();
    }
}

I debugged into the library and found the function where the Resources gets replaced by the links. In the function "static resolveRelations(resource)" the problem is in this code snippet:

if (ResourceHelper.className(resource[key])
    .find((className) => className === 'Resource') || resource[key]._links) {
        if (resource[key]._links) {
             result[key] = resource[key]._links.self.href;
        }
}

It checks via string to 'Resource'. If the snippet/function would you the instanceof function, the implementation should work also for Resource derived base classes.

Hi @MartinKutz ,

You are right that it is not correct compare className with Resource string.

But I think you problem is not in code ResourceHelper.className(resource[key]).find((className) => className === 'Resource').
If resource[key] is MyBaseResource it should has _links object because it extend Resource class and condition will be passed by the second part || resource[key]._links.

Can you provide more information about you code? I will try to reproduce this behavior and say what is the problem.

Hi @lagoshny,

Thank you for your very fast reply! :-)
You're right, the Resource is not the problem.

I debugged a bit deeper in the code, and I hope I have found the problem.

Simplified example

First, here is a simplified example after entering this method:

const payload = ResourceHelper.resolveRelations(entity);

entity (before and after entering the method):

{
   "name":"MySampleProduct",
   "availableLanguages":[
      {
         "name":"en-US",
         "key":"en-US",
         "_links":{
            "self":{
               "href":"http://localhost:4200/rke/api/languages/7"
            },
            "language":{
               "href":"http://localhost:4200/rke/api/languages/7"
            },
            "i18n":{
               "href":"http://localhost:4200/rke/api/workspaces/1/i18n?language=en-US"
            },
            "workspace":{
               "href":"http://localhost:4200/rke/api/languages/7/workspace"
            }
         }
      },
      {
         "name":"en",
         "key":"en",
         "_links":{
            "self":{
               "href":"http://localhost:4200/rke/api/languages/8"
            },
            "language":{
               "href":"http://localhost:4200/rke/api/languages/8"
            },
            "i18n":{
               "href":"http://localhost:4200/rke/api/workspaces/1/i18n?language=en"
            },
            "workspace":{
               "href":"http://localhost:4200/rke/api/languages/8/workspace"
            }
         }
      }
   ],
   "defaultLanguage":"http://localhost:4200/rke/api/languages/8",
   "_links":{
      "self":{
         "href":"http://localhost:4200/rke/api/sampleProducts/5911"
      },
      "sampleProduct":{
         "href":"http://localhost:4200/rke/api/sampleProducts/5911"
      },
      "validate":{
         "href":"http://localhost:4200/rke/api/sampleProducts/validate?id=5911"
      },
      "save":{
         "href":"http://localhost:4200/rke/api/sampleProducts/5911"
      },
      "delete":{
         "href":"http://localhost:4200/rke/api/sampleProducts/5911"
      },
      "defaultLanguage":{
         "href":"http://localhost:4200/rke/api/sampleProducts/5911/defaultLanguage"
      },
      "availableLanguages":{
         "href":"http://localhost:4200/rke/api/sampleProducts/5911/availableLanguages"
      }
   }
}

payload return value:

{
   "name":"MySampleProduct",
   "availableLanguages":[
      {
         "name":"en-US",
         "key":"en-US",
         "_links":{
            "self":{
               "href":"http://localhost:4200/rke/api/languages/7"
            },
            "language":{
               "href":"http://localhost:4200/rke/api/languages/7"
            },
            "i18n":{
               "href":"http://localhost:4200/rke/api/workspaces/1/i18n?language=en-US"
            },
            "workspace":{
               "href":"http://localhost:4200/rke/api/languages/7/workspace"
            }
         }
      },
      {
         "name":"en",
         "key":"en",
         "_links":{
            "self":{
               "href":"http://localhost:4200/rke/api/languages/8"
            },
            "language":{
               "href":"http://localhost:4200/rke/api/languages/8"
            },
            "i18n":{
               "href":"http://localhost:4200/rke/api/workspaces/1/i18n?language=en"
            },
            "workspace":{
               "href":"http://localhost:4200/rke/api/languages/8/workspace"
            }
         }
      }
   ],
   "defaultLanguage":"http://localhost:4200/rke/api/languages/8",
   "_links":{
      "self":{
         "href":"http://localhost:4200/rke/api/sampleProducts/5911"
      },
      "sampleProduct":{
         "href":"http://localhost:4200/rke/api/sampleProducts/5911"
      },
      "validate":{
         "href":"http://localhost:4200/rke/api/sampleProducts/validate?id=5911"
      },
      "save":{
         "href":"http://localhost:4200/rke/api/sampleProducts/5911"
      },
      "delete":{
         "href":"http://localhost:4200/rke/api/sampleProducts/5911"
      },
      "defaultLanguage":{
         "href":"http://localhost:4200/rke/api/sampleProducts/5911/defaultLanguage"
      },
      "availableLanguages":{
         "href":"http://localhost:4200/rke/api/sampleProducts/5911/availableLanguages"
      }
   }
}

Analysis of the problem

I think the problem is in the array part of the code. If the resource[key] is an array, it enters the part

result[key].push(this.resolveRelations(element));

foreach element in the array.
So in the above example for defaultLanguages, the resolveRelations will be called with:

{
         "name":"en-US",
         "key":"en-US",
         "_links":{
            "self":{
               "href":"http://localhost:4200/rke/api/languages/7"
            },
            "language":{
               "href":"http://localhost:4200/rke/api/languages/7"
            },
            "i18n":{
               "href":"http://localhost:4200/rke/api/workspaces/1/i18n?language=en-US"
            },
            "workspace":{
               "href":"http://localhost:4200/rke/api/languages/7/workspace"
            }
         }
      }

I think here begins the problem. Because, now every key of the object is iterated, e.g. name, key, _links.
So resource[key]._links will never be true.

Possible Solution?

I think with this change it should work:
Instead of

result[key].push(this.resolveRelations(element));

use

result[key].push(this.resolveArrayEntity(element));

and resolveArrayEntity is:

static resolveArrayEntity(resource) {
        if (resource._links) {
            return resource._links.self.href;
        }
        return resolveRelations(resource);
    }

@MartinKutz , thanks for your explanation, but I can't still reproduce it.

I pushed a new branch issue-26 where I created the resource-helper.spec.ts to simulate your above example. But test passed successfully.

Can you look at this test and say what need to change to reproduce your behavior?

For run the test you need switch to the issue-26 branch and type in terminal the ng test command.

@lagoshny , thanks again for the fast reply and creating the unit test :-)

Oh no, it's fixed with the newest version. I'm sorry that you had work with it :-(
It was fixed with commit 33694c7 on 9th April.

My last update was end of march.
With the newest version it's now working - thanks for your help!

@MartinKutz, ok, no problem)