Skip to content

Commit 239db67

Browse files
authored
fix: normalization error on nested relationship (#61)
1 parent c319b3a commit 239db67

File tree

2 files changed

+110
-1
lines changed

2 files changed

+110
-1
lines changed

src/schema/Schema.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export class Schema {
2929
model = model || this.model
3030
parent = parent || this.model
3131

32-
const entity = model.$entity()
32+
const entity = `${model.$entity()}${parent.$entity()}`
3333

3434
if (this.schemas[entity]) {
3535
return this.schemas[entity]
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import { createStore, assertState } from 'test/Helpers'
2+
import { Model, Str, Num, BelongsTo, HasMany } from '@/index'
3+
4+
// A model with more than 2 related models related to the same model was
5+
// causing a normalization error. It was due to the Schema class was caching
6+
// the first created schema with its key.
7+
//
8+
// For example, the first relation creates a schema and say its name is
9+
// `proposal_settings`. Now the parent of this model is `proposal_templates`,
10+
// because it's defined first in Opportunity model.
11+
//
12+
// Next, if the `proposals` model also depends on `proposal_settings`, but
13+
// because the schema for `proposal_settings` is already created by
14+
// `proposal_templates`, it uses the same `proposal_settings` schema. Now we
15+
// have problem because the parent model this time should be `proposals` but
16+
// it's using the previous `proposal_templates`.
17+
//
18+
// We've fixed this by caching the schema with `model` AND `parent` entity
19+
// name. So only when the model and the parent name match, we use the cache.
20+
describe('regression/normalizing_nested_relations_missing_parent_model', () => {
21+
class Opportunity extends Model {
22+
static entity = 'opportunities'
23+
24+
@Num(null, { nullable: true }) id!: number | null
25+
26+
@HasMany(() => ProposalTemplate, 'opportunityId')
27+
proposalTemplates!: ProposalTemplate[]
28+
29+
@HasMany(() => Proposal, 'opportunityId')
30+
proposals!: Proposal[]
31+
}
32+
33+
class ProposalTemplate extends Model {
34+
static entity = 'proposal_templates'
35+
36+
static primaryKey = ['opportunityId', 'proposalId']
37+
38+
@Num(null, { nullable: true }) opportunityId!: number | null
39+
@Num(null, { nullable: true }) proposalId!: number | null
40+
41+
@BelongsTo(() => ProposalSetting, 'proposalId')
42+
proposal!: ProposalSetting
43+
}
44+
45+
class Proposal extends Model {
46+
static entity = 'proposals'
47+
48+
@Num(null, { nullable: true }) id!: number | null
49+
@Num(null, { nullable: true }) opportunityId!: number | null
50+
@Num(null, { nullable: true }) templateId!: number | null
51+
52+
@BelongsTo(() => ProposalSetting, 'templateId')
53+
template!: ProposalSetting | null
54+
}
55+
56+
class ProposalSetting extends Model {
57+
static entity = 'proposal_settings'
58+
59+
@Num(null, { nullable: true }) id!: number | null
60+
@Str('') name!: string
61+
}
62+
63+
it('???', async () => {
64+
const store = createStore()
65+
66+
const dealRepo = store.$repo(Opportunity)
67+
68+
await dealRepo.insert({
69+
id: 1,
70+
proposalTemplates: [
71+
{
72+
opportunityId: 2,
73+
proposalId: 1,
74+
proposal: { id: 1 }
75+
},
76+
{
77+
opportunityId: 2,
78+
proposalId: 2,
79+
proposal: { id: 2 }
80+
}
81+
],
82+
proposals: [
83+
{
84+
id: 1,
85+
opportunityId: 1,
86+
templateId: 1,
87+
template: { id: 1, name: 'Hello, world!' }
88+
}
89+
]
90+
})
91+
92+
assertState(store, {
93+
opportunities: {
94+
1: { id: 1 }
95+
},
96+
proposals: {
97+
1: { id: 1, opportunityId: 1, templateId: 1 }
98+
},
99+
proposal_settings: {
100+
1: { id: 1, name: 'Hello, world!' },
101+
2: { id: 2, name: '' }
102+
},
103+
proposal_templates: {
104+
'[1,1]': { opportunityId: 1, proposalId: 1 },
105+
'[1,2]': { opportunityId: 1, proposalId: 2 }
106+
}
107+
})
108+
})
109+
})

0 commit comments

Comments
 (0)