Skip to content
Merged
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
40 changes: 26 additions & 14 deletions src/GenericResource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export class GenericResource<
private body: GenericBody<R> = { data: {} as any }
private pendingData?: unknown
private pendingDataCollected = false
private pendingAdditional: Record<string, any>[] = []
private res?: Response
public resource: R
public collects?: typeof Resource<T>
Expand Down Expand Up @@ -313,6 +314,7 @@ export class GenericResource<
rootKey
) as GenericBody<R>
this.body = this.applySerializePlugins(this.body) as GenericBody<R>
this.applyPendingAdditional()
}

// if (this.collects) console.log(this.body, this.constructor.name, this.collects.name)
Expand Down Expand Up @@ -384,6 +386,7 @@ export class GenericResource<
rootKey
) as GenericBody<R>
this.body = this.applySerializePlugins(this.body) as GenericBody<R>
this.applyPendingAdditional()
}

return undefined
Expand Down Expand Up @@ -429,25 +432,34 @@ export class GenericResource<
additional<X extends Record<string, any>>(extra: X) {
this.called.additional = true
this.json()
this.pendingAdditional.push({ ...extra })

const extraData = extra.data
if (this.called.json) {
this.applyPendingAdditional()
}

delete extra.data
delete extra.pagination
return this
}

const payloadKey = this.getPayloadKey()
if (extraData && payloadKey && typeof this.body[payloadKey] !== 'undefined') {
this.body[payloadKey] = Array.isArray(this.body[payloadKey])
? [...this.body[payloadKey], ...extraData]
: { ...this.body[payloadKey], ...extraData }
}
private applyPendingAdditional() {
for (const extra of this.pendingAdditional.splice(0)) {
const extraData = extra.data

this.body = {
...this.body,
...extra,
}
delete extra.data
delete extra.pagination

return this
const payloadKey = this.getPayloadKey()
if (extraData && payloadKey && typeof this.body[payloadKey] !== 'undefined') {
this.body[payloadKey] = Array.isArray(this.body[payloadKey])
? [...this.body[payloadKey], ...extraData]
: { ...this.body[payloadKey], ...extraData }
}

this.body = {
...this.body,
...extra,
}
}
}

/**
Expand Down
34 changes: 23 additions & 11 deletions src/Resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export class Resource<R extends ResourceData | NonCollectible = ResourceData> ex
[key: string]: any;
private body: ResourceBody<R> = { data: {} as any }
private pendingData?: unknown
private pendingAdditional: Record<string, any>[] = []
private res?: Response
public resource: R
protected withResponseContext?: {
Expand Down Expand Up @@ -235,6 +236,7 @@ export class Resource<R extends ResourceData | NonCollectible = ResourceData> ex

this.body = appendRootProperties(this.body, customMeta, rootKey) as ResourceBody<R>
this.body = this.applySerializePlugins(this.body) as ResourceBody<R>
this.applyPendingAdditional()
}

return this
Expand Down Expand Up @@ -284,6 +286,7 @@ export class Resource<R extends ResourceData | NonCollectible = ResourceData> ex

this.body = appendRootProperties(this.body, customMeta, rootKey) as ResourceBody<R>
this.body = this.applySerializePlugins(this.body) as ResourceBody<R>
this.applyPendingAdditional()
}

return undefined
Expand Down Expand Up @@ -329,23 +332,32 @@ export class Resource<R extends ResourceData | NonCollectible = ResourceData> ex
additional<X extends Record<string, any>>(extra: X) {
this.called.additional = true
this.json()
this.pendingAdditional.push({ ...extra })

const payloadKey = this.getPayloadKey()

if (extra.data && payloadKey && typeof this.body[payloadKey] !== 'undefined') {
this.body[payloadKey] = Array.isArray(this.body[payloadKey])
? [...this.body[payloadKey], ...extra.data]
: { ...this.body[payloadKey], ...extra.data }
}

this.body = {
...this.body,
...extra,
if (this.called.json) {
this.applyPendingAdditional()
}

return this
}

private applyPendingAdditional() {
for (const extra of this.pendingAdditional.splice(0)) {
const payloadKey = this.getPayloadKey()

if (extra.data && payloadKey && typeof this.body[payloadKey] !== 'undefined') {
this.body[payloadKey] = Array.isArray(this.body[payloadKey])
? [...this.body[payloadKey], ...extra.data]
: { ...this.body[payloadKey], ...extra.data }
}

this.body = {
...this.body,
...extra,
}
}
}

/**
* Build a response object, optionally accepting a raw response to mutate in withResponse.
*/
Expand Down
34 changes: 23 additions & 11 deletions src/ResourceCollection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export class ResourceCollection<
private body: CollectionBody<R> = { data: [] as any }
private pendingData?: unknown
private pendingDataCollected = false
private pendingAdditional: Record<string, any>[] = []
private res?: Response
public resource: R
public collects?: typeof Resource<T>
Expand Down Expand Up @@ -322,6 +323,7 @@ export class ResourceCollection<
rootKey
) as CollectionBody<R>
this.body = this.applySerializePlugins(this.body) as CollectionBody<R>
this.applyPendingAdditional()
}

return this
Expand Down Expand Up @@ -395,6 +397,7 @@ export class ResourceCollection<
rootKey
) as CollectionBody<R>
this.body = this.applySerializePlugins(this.body) as CollectionBody<R>
this.applyPendingAdditional()
}

return undefined
Expand Down Expand Up @@ -481,22 +484,31 @@ export class ResourceCollection<
additional<X extends Record<string, any>>(extra: X) {
this.called.additional = true
this.json()
this.pendingAdditional.push({ ...extra })

delete extra.cursor
delete extra.pagination
if (this.called.json) {
this.applyPendingAdditional()
}

const payloadKey = this.getPayloadKey()
return this
}

if (extra.data && payloadKey && Array.isArray(this.body[payloadKey])) {
this.body[payloadKey] = [...this.body[payloadKey], ...extra.data] as never
}
private applyPendingAdditional() {
for (const extra of this.pendingAdditional.splice(0)) {
delete extra.cursor
delete extra.pagination

this.body = {
...this.body,
...extra,
}
const payloadKey = this.getPayloadKey()

return this
if (extra.data && payloadKey && Array.isArray(this.body[payloadKey])) {
this.body[payloadKey] = [...this.body[payloadKey], ...extra.data] as never
}

this.body = {
...this.body,
...extra,
}
}
}

/**
Expand Down
32 changes: 32 additions & 0 deletions tests/express.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,28 @@ describe('Connect-style Requests (Express)', () => {
return await new ProfileCollection(models, res).response().setStatusCode(202)
})

app.get('/additional/test', async (_, res) => {
return await new UserResource({
id: 2,
name: 'John',
profile: {
id: 20,
displayName: 'John Doe',
},
}, res)
.additional({ data: { fromAdditional: true }, status: 'success' })
.response()
})

app.get('/all/additional/test', async (_, res) => {
return await new ProfileCollection(models, res)
.additional({
data: [{ id: 3, displayName: 'Additional' }],
status: 'success',
})
.response()
})

await request(app).get('/test').expect(202).expect({
data: {
id: 1,
Expand All @@ -366,6 +388,16 @@ describe('Connect-style Requests (Express)', () => {
await request(app).get('/all/test').expect(202).expect({
data: models.all(),
})

await request(app).get('/additional/test').expect({
data: { fromAdditional: true },
status: 'success',
})

await request(app).get('/all/additional/test').expect({
data: [{ id: 3, displayName: 'Additional' }],
status: 'success',
})
})

it('should allow class-level withResponse hook to customize headers/status/body', async () => {
Expand Down
21 changes: 21 additions & 0 deletions tests/generic.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,4 +129,25 @@ describe('Generic Core', () => {
],
})
})

it('should retain additional data after async serialization', async () => {
class AsyncGenericResource extends GenericResource {
data(): any {
return Promise.resolve({ id: 1, name: 'Jane' })
}
}

const body = await new AsyncGenericResource({ id: 1, name: 'Jane' })
.additional({ data: { fromAdditional: true }, status: 'success' })
.response()

expect(body).toEqual({
data: {
id: 1,
name: 'Jane',
fromAdditional: true,
},
status: 'success',
})
})
})
Loading