@@ -4,11 +4,24 @@ import {
44 InferAttributes ,
55 InferCreationAttributes ,
66 Model ,
7+ NonAttribute ,
78 Options ,
89} from "@sequelize/core" ;
910import { createSequelize7Instance } from "../dev/create-sequelize-instance" ;
1011import { expect } from "chai" ;
1112import { PostgresDialect } from "@sequelize/postgres" ;
13+ import {
14+ Attribute ,
15+ AutoIncrement ,
16+ BelongsToMany ,
17+ ColumnName ,
18+ Default ,
19+ DeletedAt ,
20+ HasMany ,
21+ Index ,
22+ NotNull ,
23+ PrimaryKey ,
24+ } from "@sequelize/core/decorators-legacy" ;
1225
1326// if your issue is dialect specific, remove the dialects you don't need to test on.
1427export const testingOnDialects = new Set ( [ "postgres" ] ) ;
@@ -17,52 +30,115 @@ class Location extends Model<
1730 InferAttributes < Location > ,
1831 InferCreationAttributes < Location >
1932> {
33+ @Attribute ( DataTypes . INTEGER )
34+ @AutoIncrement
35+ @PrimaryKey
2036 declare id : CreationOptional < number > ;
21- declare name : string ;
2237
23- declare customers ?: Customer [ ] ;
24- declare systems ?: System [ ] ;
38+ @Attribute ( DataTypes . TEXT )
39+ declare name ?: string ;
40+
41+ @HasMany ( ( ) => System , {
42+ foreignKey : "locationId" ,
43+ inverse : "location" ,
44+ } )
45+ declare systems ?: NonAttribute < System [ ] > ;
46+
47+ @BelongsToMany ( ( ) => Customer , {
48+ through : {
49+ model : ( ) => CustomerLocation ,
50+ // Only "active" relationships
51+ scope : { endAt : null } ,
52+ } ,
53+ foreignKey : "locationId" ,
54+ otherKey : "customerId" ,
55+ inverse : {
56+ as : "locations" ,
57+ } ,
58+ } )
59+ declare customers ?: NonAttribute < Customer [ ] > ;
2560}
2661
2762class Customer extends Model <
2863 InferAttributes < Customer > ,
2964 InferCreationAttributes < Customer >
3065> {
66+ @Attribute ( DataTypes . INTEGER )
67+ @AutoIncrement
68+ @PrimaryKey
3169 declare id : CreationOptional < number > ;
32- declare name : string ;
3370
34- declare locations ?: Location [ ] ;
71+ @Attribute ( DataTypes . TEXT )
72+ declare name ?: CreationOptional < string | null > ;
73+
74+ declare locations ?: NonAttribute < Location [ ] > ;
75+ declare CustomerLocation ?: NonAttribute < CustomerLocation > ;
3576}
3677
3778class System extends Model <
3879 InferAttributes < System > ,
3980 InferCreationAttributes < System >
4081> {
82+ @Attribute ( DataTypes . INTEGER )
83+ @AutoIncrement
84+ @PrimaryKey
4185 declare id : CreationOptional < number > ;
86+
87+ @Attribute ( DataTypes . TEXT )
4288 declare name : string ;
43- declare locationId : number ;
4489
90+ declare locationId : number ;
4591 declare location ?: Location ;
92+
93+ @HasMany ( ( ) => FuelDelivery , {
94+ foreignKey : "systemId" ,
95+ inverse : "system" ,
96+ } )
4697 declare fuelDeliveries ?: FuelDelivery [ ] ;
4798}
4899
49100class FuelDelivery extends Model <
50101 InferAttributes < FuelDelivery > ,
51102 InferCreationAttributes < FuelDelivery >
52103> {
104+ @Attribute ( DataTypes . INTEGER )
105+ @AutoIncrement
106+ @PrimaryKey
53107 declare id : CreationOptional < number > ;
108+
109+ @Attribute ( DataTypes . TEXT )
54110 declare product : string ;
111+
112+ @Attribute ( DataTypes . INTEGER )
55113 declare systemId : number ;
56114
57115 declare system ?: System ;
58116}
59117
60- class LocationCustomer extends Model <
61- InferAttributes < LocationCustomer > ,
62- InferCreationAttributes < LocationCustomer >
118+ class CustomerLocation extends Model <
119+ InferAttributes < CustomerLocation > ,
120+ InferCreationAttributes < CustomerLocation >
63121> {
64- declare locationId : number ;
122+ @Attribute ( DataTypes . INTEGER )
123+ @PrimaryKey
124+ @NotNull
65125 declare customerId : number ;
126+
127+ @Attribute ( DataTypes . INTEGER )
128+ @PrimaryKey
129+ @NotNull
130+ declare locationId : number ;
131+
132+ @Attribute ( DataTypes . TEXT )
133+ declare relationType : string ;
134+
135+ @Attribute ( DataTypes . DATE ( 6 ) )
136+ @Index ( )
137+ declare endAt ?: Date | null ;
138+
139+ declare customer ?: Customer ;
140+
141+ declare location ?: Location ;
66142}
67143
68144// Your SSCCE goes inside this function.
@@ -76,137 +152,18 @@ export async function run() {
76152 // Keep model definitions lean so the regression focus stays on include resolution.
77153 timestamps : false ,
78154 } ,
155+ models : [ Customer , Location , System , FuelDelivery , CustomerLocation ] ,
79156 } as Options < PostgresDialect > ) ;
80157
81- Location . init (
82- {
83- id : {
84- type : DataTypes . INTEGER ,
85- autoIncrement : true ,
86- primaryKey : true ,
87- } ,
88- name : {
89- type : DataTypes . STRING ,
90- allowNull : false ,
91- } ,
92- } ,
93- {
94- sequelize,
95- tableName : "locations" ,
96- }
97- ) ;
98-
99- Customer . init (
100- {
101- id : {
102- type : DataTypes . INTEGER ,
103- autoIncrement : true ,
104- primaryKey : true ,
105- } ,
106- name : {
107- type : DataTypes . STRING ,
108- allowNull : false ,
109- } ,
110- } ,
111- {
112- sequelize,
113- tableName : "customers" ,
114- }
115- ) ;
116-
117- System . init (
118- {
119- id : {
120- type : DataTypes . INTEGER ,
121- autoIncrement : true ,
122- primaryKey : true ,
123- } ,
124- name : {
125- type : DataTypes . STRING ,
126- allowNull : false ,
127- } ,
128- locationId : {
129- type : DataTypes . INTEGER ,
130- allowNull : false ,
131- } ,
132- } ,
133- {
134- sequelize,
135- tableName : "systems" ,
136- }
137- ) ;
138-
139- FuelDelivery . init (
140- {
141- id : {
142- type : DataTypes . INTEGER ,
143- autoIncrement : true ,
144- primaryKey : true ,
145- } ,
146- product : {
147- type : DataTypes . STRING ,
148- allowNull : false ,
149- } ,
150- systemId : {
151- type : DataTypes . INTEGER ,
152- allowNull : false ,
153- } ,
154- } ,
155- {
156- sequelize,
157- tableName : "fuel_deliveries" ,
158- }
159- ) ;
160-
161- LocationCustomer . init (
162- {
163- locationId : {
164- type : DataTypes . INTEGER ,
165- allowNull : false ,
166- primaryKey : true ,
167- } ,
168- customerId : {
169- type : DataTypes . INTEGER ,
170- allowNull : false ,
171- primaryKey : true ,
172- } ,
173- } ,
174- {
175- sequelize,
176- tableName : "location_customers" ,
177- }
178- ) ;
179-
180- FuelDelivery . belongsTo ( System , { as : "system" , foreignKey : "systemId" } ) ;
181- System . hasMany ( FuelDelivery , {
182- as : "fuelDeliveries" ,
183- foreignKey : "systemId" ,
184- } ) ;
185-
186- System . belongsTo ( Location , { as : "location" , foreignKey : "locationId" } ) ;
187- Location . hasMany ( System , { as : "systems" , foreignKey : "locationId" } ) ;
188-
189- Location . belongsToMany ( Customer , {
190- as : "customers" ,
191- through : LocationCustomer ,
192- foreignKey : "locationId" ,
193- otherKey : "customerId" ,
194- } ) ;
195- Customer . belongsToMany ( Location , {
196- as : "locations" ,
197- through : LocationCustomer ,
198- foreignKey : "customerId" ,
199- otherKey : "locationId" ,
200- } ) ;
201-
202158 try {
203159 await sequelize . sync ( { force : true } ) ;
204160
205161 const customer = await Customer . create ( { name : "Propane Co-op" } ) ;
206162 const location = await Location . create ( { name : "Rural Depot" } ) ;
207- await LocationCustomer . create ( {
163+ await CustomerLocation . create ( {
208164 customerId : customer . id ,
209165 locationId : location . id ,
166+ relationType : "primary" ,
210167 } ) ;
211168
212169 const system = await System . create ( {
@@ -247,6 +204,79 @@ export async function run() {
247204 expect ( customers ) . to . not . be . undefined ;
248205 expect ( customers ) . to . have . length ( 1 ) ;
249206 expect ( customers ! [ 0 ] . id ) . to . equal ( customer . id ) ;
207+
208+ // Test Two
209+
210+ const result2 = await Customer . findOne ( {
211+ include : [
212+ {
213+ association : "locations" ,
214+
215+ include : [
216+ {
217+ association : "customers" ,
218+ } ,
219+ ] ,
220+ } ,
221+ ] ,
222+ } ) ;
223+
224+ expect ( result2 ) . to . not . be . null ;
225+ expect ( result2 ! . locations ) . to . not . be . undefined ;
226+ const locations = result2 ! . locations ! ;
227+ expect ( locations ) . to . have . length . greaterThan ( 0 ) ;
228+ expect ( locations [ 0 ] . customers ) . to . not . be . undefined ;
229+ expect ( locations [ 0 ] . customers ) . to . have . length . greaterThan ( 0 ) ;
230+
231+ /// Test Three
232+
233+ const result3 = await FuelDelivery . findByPk ( delivery . id , {
234+ include : [
235+ {
236+ association : "system" ,
237+ include : [
238+ {
239+ association : "location" ,
240+ include : [
241+ {
242+ association : "customers" ,
243+ required : true ,
244+ include : [
245+ {
246+ association : "locations" ,
247+ required : false ,
248+ include : [
249+ {
250+ association : "systems" ,
251+ where : { name : "Delivery System Alpha" } ,
252+ required : false ,
253+ include : [ ] ,
254+ } ,
255+ ] ,
256+ } ,
257+ ] ,
258+ } ,
259+ ] ,
260+ } ,
261+ ] ,
262+ } ,
263+ ] ,
264+ } ) ;
265+
266+ expect ( result3 ) . to . not . be . null ;
267+ expect ( result3 ! . system ) . to . not . be . undefined ;
268+ expect ( result3 ! . system ! . location ) . to . not . be . undefined ;
269+ const customers3 = result3 ! . system ! . location ! . customers ;
270+ expect ( customers3 ) . to . not . be . undefined ;
271+ expect ( customers3 ) . to . have . length ( 1 ) ;
272+ expect ( customers3 ! [ 0 ] . id ) . to . equal ( customer . id ) ;
273+ expect ( customers3 ! [ 0 ] . locations ) . to . not . be . undefined ;
274+ expect ( customers3 ! [ 0 ] . locations ) . to . have . length . greaterThan ( 0 ) ;
275+ expect ( customers3 ! [ 0 ] . locations ! [ 0 ] . systems ) . to . not . be . undefined ;
276+ expect ( customers3 ! [ 0 ] . locations ! [ 0 ] . systems ) . to . have . length . greaterThan ( 0 ) ;
277+ expect ( customers3 ! [ 0 ] . locations ! [ 0 ] . systems ! [ 0 ] . name ) . to . equal (
278+ "Delivery System Alpha"
279+ ) ;
250280 } finally {
251281 await sequelize . close ( ) ;
252282 }
0 commit comments