Skip to content

Commit

Permalink
feat(nestjs-json-rpc-sdk): Skip empty element in response
Browse files Browse the repository at this point in the history
Skip item of "DeleteOne" in result in atomic operation
  • Loading branch information
klerick committed Dec 5, 2024
1 parent c3b9322 commit 28d3efc
Show file tree
Hide file tree
Showing 5 changed files with 202 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -168,5 +168,121 @@ describe('atomicOperationService', () => {
patchUser.addresses.id,
]);
});
it('Should be return factory with skip false', async () => {
const postAddress = new Addresses();
postAddress.id = 1;
const patchUser = new Users();
patchUser.id = 1;

const deleteUser = new Users();
deleteUser.id = 2;
const patchRelationshipsBookList = new BookList();
patchRelationshipsBookList.id = 'id';
patchRelationshipsBookList.users = [patchUser];

const deleteRelationshipsAddress = new Addresses();
deleteRelationshipsAddress.id = 2;
deleteRelationshipsAddress.user = deleteUser;

patchUser.addresses = postAddress;

const spyHttpInnerClient = jest
.spyOn(httpInnerClient, 'post')
.mockImplementation(() =>
of({
[KEY_MAIN_OUTPUT_SCHEMA]: [
{
meta: {},
data: {
type: 'users',
id: patchUser.id,
attributes: {},
},
},
{
meta: {},
data: {
type: 'addresses',
id: postAddress.id,
attributes: {},
},
},
{
meta: {},
data: [
{
type: 'users',
id: patchUser.id,
attributes: {},
},
],
},
{
meta: {},
data: {
type: 'addresses',
id: postAddress.id,
attributes: {},
},
},
],
})
);

const result$ = atomicOperationsService
.patchOne(patchUser)
.deleteOne(deleteUser, false)
.postOne(postAddress)
.patchRelationships(patchRelationshipsBookList, 'users')
.postRelationships(patchUser, 'addresses')
.deleteRelationships(deleteRelationshipsAddress, 'user')
.run();

const result = await lastValueFrom(result$);
expect(spyHttpInnerClient).toBeCalledTimes(1);
const pathUserResult = new Users();
pathUserResult.id = patchUser.id;
expect(result).toEqual([
pathUserResult,
'EMPTY',
postAddress,
[patchRelationshipsBookList.users[0].id],
patchUser.addresses.id,
]);
});
});
it('Should be return factory with skip false only delete', async () => {
const postAddress = new Addresses();
postAddress.id = 1;
const patchUser = new Users();
patchUser.id = 1;

const deleteUser = new Users();
deleteUser.id = 2;
const patchRelationshipsBookList = new BookList();
patchRelationshipsBookList.id = 'id';
patchRelationshipsBookList.users = [patchUser];

const deleteRelationshipsAddress = new Addresses();
deleteRelationshipsAddress.id = 2;
deleteRelationshipsAddress.user = deleteUser;

patchUser.addresses = postAddress;

const spyHttpInnerClient = jest
.spyOn(httpInnerClient, 'post')
.mockImplementation(() =>
of({
[KEY_MAIN_OUTPUT_SCHEMA]: [],
})
);

const result$ = atomicOperationsService.deleteOne(deleteUser, false).run();

const result = await lastValueFrom(result$);
expect(spyHttpInnerClient).toBeCalledTimes(1);
const pathUserResult = new Users();
pathUserResult.id = patchUser.id;
expect(result).toEqual(['EMPTY']);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,17 @@ export class AtomicOperationsService<T extends unknown[]>
this.jsonApiSdkConfig.operationUrl
);

const indexDeleteIfNotSkipEmpty = atomicBody
.reduce<number[]>((acum, item, index) => {
if (
item.op === 'remove' &&
!this.generateAtomicBody[index].isSkipEmpty
) {
acum.push(index);
}
return acum;
}, [])
.sort((a, b) => a - b);
return this.httpInnerClient.post<T>(operationUrl, body).pipe(
map((r) => r[KEY_MAIN_OUTPUT_SCHEMA]),
map(
Expand All @@ -56,14 +67,39 @@ export class AtomicOperationsService<T extends unknown[]>
? this.jsonApiUtilsService.getResultForRelation(item)
: this.jsonApiUtilsService.convertResponseData(item)
) as T
),
map((r) =>
indexDeleteIfNotSkipEmpty.reduce(
(acc, index, currentIndex) => {
acc.splice(index + currentIndex, 0, 'EMPTY');
return acc;
},
[...r] as T
)
)
);
}

public deleteOne<Entity extends EntityObject>(
deleteOne<Entity extends EntityObject>(
entity: Entity
): AtomicOperations<T> {
return this.setToBody('deleteOne', entity);
): AtomicOperations<[...T]>;
deleteOne<Entity extends EntityObject>(
entity: Entity,
skipEmpty: true
): AtomicOperations<[...T]>;
deleteOne<Entity extends EntityObject>(
entity: Entity,
skipEmpty: false
): AtomicOperations<[...T, 'EMPTY']>;
deleteOne<Entity extends EntityObject>(
entity: Entity,
skipEmpty?: boolean
): AtomicOperations<[...T, 'EMPTY'] | [...T]> {
return this.setToBody(
'deleteOne',
entity,
skipEmpty === undefined ? true : skipEmpty
);
}

public patchOne<Entity extends EntityObject>(
Expand Down Expand Up @@ -109,6 +145,11 @@ export class AtomicOperationsService<T extends unknown[]>
operationType: Extract<keyof AtomicVoidOperation, 'deleteOne'>,
entity: Entity
): AtomicOperations<T>;
private setToBody<Entity extends EntityObject>(
operationType: Extract<keyof AtomicVoidOperation, 'deleteOne'>,
entity: Entity,
skipEmpty: boolean
): AtomicOperations<[...T, 'EMPTY']>;
private setToBody<Entity extends EntityObject>(
operationType: Exclude<keyof AtomicVoidOperation, 'deleteOne'>,
entity: Entity
Expand All @@ -135,30 +176,40 @@ export class AtomicOperationsService<T extends unknown[]>
>(
operationType: keyof AtomicVoidOperation,
entity: Entity,
relationType?: Rel
relationType?: Rel | boolean
):
| AtomicOperations<[...T, Entity]>
| AtomicOperations<[...T, ReturnIfArray<Entity[Rel], string>]>
| AtomicOperations<[...T]> {
| AtomicOperations<[...T]>
| AtomicOperations<[...T, 'EMPTY']> {
const atomicBody = new GenerateAtomicBody<Entity, Rel>(
this.jsonApiUtilsService,
this.jsonApiSdkConfig
);
switch (operationType) {
case 'postRelationships':
case 'patchRelationships':
case 'deleteRelationships':
if (relationType) atomicBody[operationType](entity, relationType);
break;
default:
atomicBody[operationType](entity);
break;

if (typeof relationType === 'boolean') {
if (operationType === 'deleteOne') {
atomicBody[operationType](entity, relationType);
}
} else {
switch (operationType) {
case 'postRelationships':
case 'patchRelationships':
case 'deleteRelationships':
if (relationType) atomicBody[operationType](entity, relationType);
break;
default:
atomicBody[operationType](entity, true);
break;
}
}

this.addBody(atomicBody);

return this as unknown as
| AtomicOperations<[...T, Entity]>
| AtomicOperations<[...T, ReturnIfArray<Entity[Rel], string>]>
| AtomicOperations<T>;
| AtomicOperations<T>
| AtomicOperations<[...T, 'EMPTY']>;
}
}
10 changes: 10 additions & 0 deletions libs/json-api/json-api-nestjs-sdk/src/lib/types/atomic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,19 @@ export interface AtomicMainOperations<T extends unknown[]> {
patchOne<Entity extends EntityObject>(
entity: Entity
): AtomicOperations<[...T, Entity]>;

deleteOne<Entity extends EntityObject>(
entity: Entity
): AtomicOperations<[...T]>;
deleteOne<Entity extends EntityObject>(
entity: Entity,
skipEmpty: true
): AtomicOperations<[...T]>;
deleteOne<Entity extends EntityObject>(
entity: Entity,
skipEmpty: false
): AtomicOperations<[...T, 'EMPTY']>;

patchRelationships<
Entity extends EntityObject,
Rel extends EntityRelation<Entity>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ describe('GenerateAtomicBody', () => {
entity.text = 'text';
entity.users = [user];

expect(() => generateAtomicBody.deleteOne(entity)).toThrowError();
expect(() => generateAtomicBody.deleteOne(entity, true)).toThrowError();
});

it('should not throw error if entity contains id', () => {
Expand All @@ -188,7 +188,7 @@ describe('GenerateAtomicBody', () => {
ref: { type: 'book-list', id: entity.id },
};

generateAtomicBody.deleteOne(entity);
generateAtomicBody.deleteOne(entity, true);
const result = generateAtomicBody.getBody();
expect(result).toEqual(expectedBodyData);
});
Expand All @@ -202,7 +202,7 @@ describe('GenerateAtomicBody', () => {
entity.text = 'text';
entity.users = [user];

expect(() => generateAtomicBody.deleteOne(entity)).toThrowError();
expect(() => generateAtomicBody.deleteOne(entity, true)).toThrowError();
});

it('should not throw error if entity contains id', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ export class GenerateAtomicBody<
private jsonApiSdkConfig: JsonApiSdkConfig
) {}
private bodyData!: BodyType;
private skipEmpty = true;

get isSkipEmpty(): boolean {
return this.skipEmpty;
}

private setToBody(op: Operation, entity: Entity, relationType?: Rel) {
const type = getTypeForReq(entity.constructor.name);
Expand Down Expand Up @@ -88,13 +93,13 @@ export class GenerateAtomicBody<

this.setToBody(Operation.update, entity);
}
deleteOne(entity: Entity): void {
deleteOne(entity: Entity, skipEmpty: boolean): void {
if (!entity[this.jsonApiSdkConfig.idKey]) {
new Error(
'Resource params should be instance of resource with id params'
);
}

this.skipEmpty = skipEmpty;
this.setToBody(Operation.remove, entity);
}
patchRelationships(entity: Entity, relationType: Rel): void {
Expand Down

0 comments on commit 28d3efc

Please sign in to comment.