lagoshny / ngx-hal-client

Spring HAL client for Angular
21 stars 15 forks source link

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

Closed MartinKutz closed 4 years ago

MartinKutz commented 4 years ago

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.

lagoshny commented 4 years ago

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.

MartinKutz commented 4 years ago

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);
    }
lagoshny commented 4 years ago

@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.

MartinKutz commented 4 years ago

@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 33694c76e449133a5da2452dd4f9d9358819937e on 9th April.

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

lagoshny commented 4 years ago

@MartinKutz, ok, no problem)