Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions src/util/deserialize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,14 @@ class Deserializer {
record: SpraypaintBase
): void {
const modelIdx = model as any
const associationRecords = modelIdx[associationName]
const associationRecords = modelIdx[associationName] || []
const existingInstance = this.lookupAssociated(associationRecords, record)

if (!existingInstance) {
if (existingInstance) return
if (Array.isArray(modelIdx[associationName])) {
modelIdx[associationName].push(record)
} else {
modelIdx[associationName] = record
}
}

Expand Down Expand Up @@ -199,7 +202,7 @@ class Deserializer {
if (Array.isArray(relationData)) {
for (const datum of relationData) {
const hydratedDatum = this.findResource(datum)
const associationRecords = instanceIdx[relationName]
const associationRecords = instanceIdx[relationName] || []
let relatedInstance = this.relationshipInstanceFor(
hydratedDatum,
associationRecords
Expand Down
2 changes: 2 additions & 0 deletions test/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ export class Genre extends ApplicationRecord {

@Attr name!: string
@HasMany("authors") authors: any

@BelongsTo({ type: "genres" }) parentGenre?: Genre
}

@Model()
Expand Down
67 changes: 66 additions & 1 deletion test/integration/relations.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { expect, fetchMock } from "../test-helper"
import { Author, NonFictionAuthor } from "../fixtures"
import { Author, Book, Genre, NonFictionAuthor } from "../fixtures"
import { IResultProxy } from "../../src/proxies/index"
import { SpraypaintBase } from "../../src/index"

Expand Down Expand Up @@ -55,6 +55,54 @@ const generateMockResponse = (type: string) => {
} as any
}

const generateMockGenreResponse = () => {
return {
data: {
id: "1",
type: "books",
attributes: {
title: "A Song of Ice and Fire"
},
relationships: {
genre: {
data: [
{
id: "genre1",
type: "genres"
}
]
}
}
},
included: [
{
id: "genre1",
type: "genres",
attributes: {
title: "Sword and Sourcery"
},
relationships: {
parentGenre: {
data: [
{
id: "genre2",
type: "genres"
}
]
}
}
},
{
id: "genre2",
type: "genres",
attributes: {
title: "Fantasy"
}
}
]
} as any
}

describe("Relations", () => {
describe("#find()", () => {
beforeEach(() => {
Expand Down Expand Up @@ -98,6 +146,23 @@ describe("Relations", () => {
expect(data.genre).to.eq(undefined)
})
})

describe("when there's a self-referential relationship", () => {
beforeEach(() => {
const response = generateMockGenreResponse()
fetchMock.get(
"http://example.com/api/books/1?include=genre.parent_genre",
response
)
})

it("does not blow up", async () => {
const { data } = await Book.includes({ genre: "parentGenre" }).find(1)
expect(data.klass).to.eq(Book)
expect(data.genre).to.be.instanceOf(Genre)
expect(data.genre.parentGenre).to.be.instanceOf(Genre)
})
})
})

describe("when keyCase is snake_case", () => {
Expand Down
12 changes: 12 additions & 0 deletions test/unit/model-relationships.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,16 @@ describe("Model relationships", () => {

expect(keys).to.include("genre")
})

it("supports self-referential relationships", () => {
const genre = new Genre({ name: "Fantasy" })
const subgenre = new Genre({
name: "Sword and Sourcery",
parentGenre: genre
})

expect(genre).to.be.instanceof(Genre)
expect(subgenre).to.be.instanceof(Genre)
expect(subgenre.parentGenre).to.be.instanceof(Genre)
})
})