mathematic-inc / ts-japi

A highly-modular (typescript-friendly)-framework agnostic library for serializing data to the JSON:API specification
Apache License 2.0
205 stars 16 forks source link

[BUG] Not serializing relationship to include #59

Closed basz closed 1 year ago

basz commented 1 year ago

Describe the bug*

Serializing an array of entities from TupeORM with some nested includes only works if I JSON.parse(JSON.stringify(entities)) first.

To Reproduce*

const serializer = new Serializer('user', {
  pluralizeType: false,
  attributes: ['name', 'email', 'role', 'hasBeenActivated', 'createdAt', 'updatedAt', 'organisationMembers'],
  organisationMembers: {
    ref: 'id',
    attributes: ['role', 'active', 'organisation'],
    organisation: {
      ref: 'id',
      attributes: ['name', 'accountNumber', 'active'],
    },
  },
})

this does not include the organisation

const serializedData = serializer.serialize(data);

but this one does

const serializedData = serializer.serialize(JSON.parse(JSON.stringify(data));

Expected behavior*

I would expect the include property would include the organisation regardless.

Platform*

"@nestjs/typeorm": "^9.0.1", "typeorm": "^0.3.11",

"ts-japi": "^1.8.0",

Additional context

It could be that I should do something to get TypeORM to play along.

My entities contain the following;

User

@PrimaryGeneratedColumn('uuid')
  id: string;

@OneToMany(() => OrganisationMember, (organisationMember) => organisationMember.user)
  organisationMembers!: Array<OrganisationMember>;

OrganisationMember

@PrimaryGeneratedColumn('uuid')
  id: string;

@ManyToOne(() => Organisation, (organisation) => organisation.organisationMembers)
  @JoinColumn({ name: 'organisationId', referencedColumnName: 'id' })
  public organisation: Organisation;

Organisation

@PrimaryGeneratedColumn('uuid')
  id: string;

@OneToMany(() => OrganisationMember, (organisationMember) => organisationMember.organisation)
  organisationMembers: Array<OrganisationMember>;

log without JSON.stringify

{
[1]   "included": [
[1]     {
[1]       "type": "organisationMembers",
[1]       "id": "9a983fe5-65c1-4d45-8aca-62835ba6dd81",
[1]       "attributes": {
[1]         "role": "team-admin"
[1]       }
[1]     }
[1]   ],
[1]   "data": {
[1]     "type": "user",
[1]     "id": "8f8c9e81-130a-47b0-8128-8fa590f0b339",
[1]     "attributes": {
[1]       "name": null,
[1]       "email": "test@example.nl",
[1]       "role": "admin",
[1]       "has-been-activated": true,
[1]       "created-at": "2023-03-10T18:55:50.909Z",
[1]       "updated-at": "2023-03-10T18:56:16.609Z"
[1]     },
[1]     "relationships": {
[1]       "organisation-members": {
[1]         "data": [
[1]           {
[1]             "type": "organisationMembers",
[1]             "id": "9a983fe5-65c1-4d45-8aca-62835ba6dd81"
[1]           }
[1]         ]
[1]       }
[1]     }
[1]   }
[1] }

log with JSON.stringify

"included": [
[1]     {
[1]       "type": "organisation",
[1]       "id": "808a1c9b-4b3e-47a2-830a-ff3cdee42d6d",
[1]       "attributes": {
[1]         "name": "Podolab Hoekschewaard",
[1]         "account-number": "1",
[1]         "active": true
[1]       }
[1]     },
[1]     {
[1]       "type": "organisationMembers",
[1]       "id": "9a983fe5-65c1-4d45-8aca-62835ba6dd81",
[1]       "attributes": {
[1]         "role": "team-admin"
[1]       },
[1]       "relationships": {
[1]         "organisation": {
[1]           "data": {
[1]             "type": "organisation",
[1]             "id": "808a1c9b-4b3e-47a2-830a-ff3cdee42d6d"
[1]           }
[1]         }
[1]       }
[1]     }
[1]   ],
[1]   "data": {
[1]     "type": "user",
[1]     "id": "8f8c9e81-130a-47b0-8128-8fa590f0b339",
[1]     "attributes": {
[1]       "name": null,
[1]       "email": "test@example.nl",
[1]       "role": "admin",
[1]       "has-been-activated": true,
[1]       "created-at": "2023-03-10T18:55:50.909Z",
[1]       "updated-at": "2023-03-10T18:56:16.609Z"
[1]     },
[1]     "relationships": {
[1]       "organisation-members": {
[1]         "data": [
[1]           {
[1]             "type": "organisationMembers",
[1]             "id": "9a983fe5-65c1-4d45-8aca-62835ba6dd81"
[1]           }
[1]         ]
[1]       }
[1]     }
[1]   }
[1] }
jrandolf commented 1 year ago

Usually this happens if some of the objects are cyclicly referenced. Can you verify they aren't?

basz commented 1 year ago

ai, i don't know. I think TypeORM return simple pojo's for entities? The data structure itself isn't for sure.

A user has many org-member rows, and an org-member row can reference the same org for different users, but not in this example...

jrandolf commented 1 year ago

Looking at the missing objects, you can see they use the same ID, so it's definitely a cycle problem. TypeORM is probably intelligent enough to cache based on identity which is causing this problem.

@egmacke Do you know a workaround?

egmacke commented 1 year ago

@basz apologies for not seeing this sooner - I had my first kid at the start of April so been a bit busy, and finally catching up with things!

I'll have a look into this for you - however it would be really useful if you could provide a repo with a simple replication of the issue (I've not worked with TypeORM before)

basz commented 1 year ago

weird, that is code from jsonapi-serializer i was playing with... sorry...