1- import { JsonApiBody , JsonApiResource } from "../types/jsonapi.type" ;
2- import { fromJsonApi , Model , ModelConstructor , ModelInstance } from "./model" ;
1+ import { JsonApiBody , JsonApiQueryParams , JsonApiResource } from "../types/jsonapi.type" ;
2+ import { fromJsonApi , Model , ModelConstructor , ModelInstance , models } from "./model" ;
33import Schema from "./schema" ;
44
55type RawResultType < T > = {
@@ -54,6 +54,8 @@ class Query<ResultType, DocType> {
5454 this . init ( model ) ;
5555 }
5656
57+ buildParams ! : ( ) => JsonApiQueryParams ;
58+
5759 catch ! : Promise < ResultType > [ 'catch' ] ;
5860
5961 exec ! : ( ) => Promise < ResultType > ;
@@ -129,62 +131,133 @@ Query.prototype.init = function (model) {
129131 this . options = { } ;
130132} ;
131133
132- Query . prototype . catch = function ( reject ) {
133- return this . exec ( ) . then ( null , reject ) ;
134- } ;
134+ Query . prototype . buildParams = function ( ) {
135+ const options = this . getOptions ( ) ;
135136
136- Query . prototype . exec = async function exec ( ) {
137- const client = this . model . client ;
137+ const getJsonApiName = (
138+ key : string ,
139+ schema ?: Schema < any > ,
140+ ) : string => {
141+ if ( schema ?. attributes [ key ] ) {
142+ return schema . attributes [ key ] . name ?? key ;
143+ } else if ( schema ?. relationships [ key ] ) {
144+ return schema . relationships [ key ] . name ?? key ;
145+ }
146+ return key ;
147+ } ;
138148
139- const flattenIncludeQuery = ( obj : IncludeQuery < any > , prefix = '' ) : string [ ] => {
140- return Object . entries ( obj ) . reduce ( ( acc , [ key , value ] ) => {
141- const path = prefix ? `${ prefix } .${ key } ` : key ;
142-
143- if ( typeof value === 'boolean' ) {
144- return value
145- ? acc . concat ( path )
146- : acc ;
147- } else {
148- const childPaths = flattenIncludeQuery ( value ?? { } , path ) ;
149- return childPaths . length > 0
150- ? acc . concat ( childPaths )
151- : acc . concat ( path ) ;
152- }
153- } , [ ] as string [ ] ) ;
149+ const buildFilterParams = (
150+ filter : FilterQuery < any > ,
151+ schema ?: Schema < any > ,
152+ ) : JsonApiQueryParams [ 'filter' ] => {
153+ return Object . fromEntries (
154+ Object . entries ( filter ) . map ( ( [ key , value ] ) => [ getJsonApiName ( key , schema ) , value ] )
155+ ) ;
154156 } ;
155157
156- const options = this . getOptions ( ) ;
157- const params = {
158- filter : options . filter ,
159- include : options . include
160- ? flattenIncludeQuery ( options . include ) . join ( ',' )
161- : undefined ,
162- fields : options . fields
163- ? Object . fromEntries (
164- Object . entries ( options . fields ) . map ( ( [ type , fields ] ) => [ type , fields . join ( ',' ) ] )
165- )
166- : undefined ,
167- sort : options . sort
168- ? Object . entries ( options . sort )
169- . map ( ( [ field , order ] ) => {
170- if ( order === - 1 || order === 'desc' || order === 'descending' ) {
171- return `-${ field } ` ;
172- }
173- return field ;
174- } )
175- . join ( ',' )
176- : undefined ,
158+ const buildIncludeParams = (
159+ include : IncludeQuery < any > ,
160+ schema ?: Schema < any > ,
161+ ) : JsonApiQueryParams [ 'include' ] => {
162+ const flattenInclude = (
163+ obj : IncludeQuery < any > ,
164+ schema ?: Schema < any > ,
165+ prefix = '' ,
166+ ) : string [ ] => {
167+ return Object . entries ( obj ) . flatMap ( ( [ key , value ] ) => {
168+ let name = schema ?. relationships [ key ] ?. name ?? key ;
169+ const path = prefix ? `${ prefix } .${ name } ` : name ;
170+
171+ if ( typeof value === 'boolean' ) {
172+ return value ? [ path ] : [ ] ;
173+ }
174+
175+ const subType = schema ?. relationships [ key ] ?. type ;
176+ const subSchema = Array . isArray ( subType )
177+ ? models [ subType [ 0 ] ] . schema
178+ : subType
179+ ? models [ subType ] . schema
180+ : undefined ;
181+
182+ const subPaths = flattenInclude ( value ?? { } , subSchema , path ) ;
183+ return subPaths . length ? subPaths : [ path ] ;
184+ } ) ;
185+ } ;
186+
187+ return flattenInclude ( include , schema ) . join ( ',' ) ;
188+ } ;
189+
190+ const buildFieldsParams = (
191+ fields : FieldsQuery ,
192+ ) : JsonApiQueryParams [ 'fields' ] => {
193+ return Object . fromEntries (
194+ Object . entries ( fields ) . map ( ( [ type , list ] ) => {
195+ const schema = models [ type ] ?. schema ;
196+
197+ const names = list
198+ . map ( ( field ) => getJsonApiName ( field , schema ) )
199+ . join ( ',' ) ;
200+
201+ return [ type , names ] ;
202+ } )
203+ ) ;
204+ } ;
205+
206+ const buildSortParams = (
207+ sort : SortQuery < any > ,
208+ schema ?: Schema < any > ,
209+ ) : JsonApiQueryParams [ 'sort' ] => {
210+ return Object . entries ( sort )
211+ . map ( ( [ key , order ] ) => {
212+ let name = getJsonApiName ( key , schema )
213+
214+ if ( order === - 1 || order === 'desc' || order === 'descending' ) {
215+ return `-${ name } ` ;
216+ }
217+ return name ;
218+ } )
219+ . join ( ',' ) ;
220+ } ;
221+
222+ const getSchema = ( ) => {
223+ if ( options . op === 'findRelationship' && options . related ) {
224+ const property = this . model . schema . relationships [ options . related ] ;
225+ return Array . isArray ( property ?. type )
226+ ? models [ property . type [ 0 ] ] . schema
227+ : property ?. type
228+ ? models [ property . type ] . schema
229+ : undefined ;
230+ }
231+ return this . model . schema ;
232+ } ;
233+ const schema = getSchema ( ) ;
234+
235+ return {
236+ filter : options . filter ? buildFilterParams ( options . filter , schema ) : undefined ,
237+ include : options . include ? buildIncludeParams ( options . include , schema ) : undefined ,
238+ fields : options . fields ? buildFieldsParams ( options . fields ) : undefined ,
239+ sort : options . sort ? buildSortParams ( options . sort , schema ) : undefined ,
177240 page : {
178241 limit : options . limit ,
179242 offset : options . offset ,
180243 } ,
181244 } ;
245+ } ;
246+
247+ Query . prototype . catch = function ( reject ) {
248+ return this . exec ( ) . then ( null , reject ) ;
249+ } ;
250+
251+ Query . prototype . exec = async function exec ( ) {
252+ const client = this . model . client ;
253+
254+ const options = this . getOptions ( ) ;
182255
183256 if ( options . op === 'find' ) {
184257 const response = await client . client . get < JsonApiBody < JsonApiResource [ ] > > (
185258 `/${ this . model . type } ` ,
186259 {
187- params : params ,
260+ params : this . buildParams ( ) ,
188261 }
189262 ) ;
190263
@@ -200,7 +273,7 @@ Query.prototype.exec = async function exec() {
200273 const response = await client . client . get < JsonApiBody < JsonApiResource | null > > (
201274 `/${ this . model . type } /${ options . id } ` ,
202275 {
203- params : params ,
276+ params : this . buildParams ( ) ,
204277 }
205278 ) ;
206279
@@ -216,7 +289,7 @@ Query.prototype.exec = async function exec() {
216289 const response = await client . client . get < JsonApiBody < JsonApiResource | null > > (
217290 `/${ this . model . type } /${ options . id } /${ options . related } ` ,
218291 {
219- params : params ,
292+ params : this . buildParams ( ) ,
220293 }
221294 ) ;
222295
0 commit comments