swagger security, update transaction
This commit is contained in:
parent
f342995e89
commit
43b9dd941b
@ -13,6 +13,7 @@ import {
|
|||||||
UseInterceptors,
|
UseInterceptors,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import {
|
import {
|
||||||
|
ApiBearerAuth,
|
||||||
ApiBody,
|
ApiBody,
|
||||||
ApiOkResponse,
|
ApiOkResponse,
|
||||||
ApiOperation,
|
ApiOperation,
|
||||||
@ -39,6 +40,7 @@ import {
|
|||||||
StorageAddItemRequestDto,
|
StorageAddItemRequestDto,
|
||||||
StorageItemUpdateRequestDto,
|
StorageItemUpdateRequestDto,
|
||||||
StorageStoredItemTransactionRequestDto,
|
StorageStoredItemTransactionRequestDto,
|
||||||
|
StorageStoredItemTransactionUpdateRequestDto,
|
||||||
StorageStoredItemUpdateRequestDto,
|
StorageStoredItemUpdateRequestDto,
|
||||||
} from './dto/storage-add-item-request.dto';
|
} from './dto/storage-add-item-request.dto';
|
||||||
import { StorageItemRequestQueryDto } from './dto/storage-item-request.dto';
|
import { StorageItemRequestQueryDto } from './dto/storage-item-request.dto';
|
||||||
@ -47,6 +49,7 @@ import {
|
|||||||
StorageItemSearchResponseDto,
|
StorageItemSearchResponseDto,
|
||||||
StorageStoredItemResponseDto,
|
StorageStoredItemResponseDto,
|
||||||
StorageTransactionResponseDto,
|
StorageTransactionResponseDto,
|
||||||
|
StorageTransactionWithActorResponseDto,
|
||||||
} from './dto/storage-item-response.dto';
|
} from './dto/storage-item-response.dto';
|
||||||
import {
|
import {
|
||||||
StorageCreateRequestDto,
|
StorageCreateRequestDto,
|
||||||
@ -64,6 +67,7 @@ import { StorageSetResponseDto } from './dto/storage-set-response.dto';
|
|||||||
})
|
})
|
||||||
@ApiTags('storage')
|
@ApiTags('storage')
|
||||||
@ApiSecurity('Bearer token')
|
@ApiSecurity('Bearer token')
|
||||||
|
@ApiBearerAuth('Bearer token')
|
||||||
@UseInterceptors(ClassSerializerInterceptor)
|
@UseInterceptors(ClassSerializerInterceptor)
|
||||||
@UseGuards(AuthGuard, BuildingGuard, RoomGuard, StorageGuard)
|
@UseGuards(AuthGuard, BuildingGuard, RoomGuard, StorageGuard)
|
||||||
export class AppStorageController {
|
export class AppStorageController {
|
||||||
@ -232,7 +236,7 @@ export class AppStorageController {
|
|||||||
return this.service.updateStoredItemDetails(storage, storedItemId, body);
|
return this.service.updateStoredItemDetails(storage, storedItemId, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post('item/:storageId/:storedItemId/transaction')
|
@Post('item/:storageId/:storedItemId/transactions')
|
||||||
@ApiParam({ name: 'storedItemId', description: 'Stored Item ID' })
|
@ApiParam({ name: 'storedItemId', description: 'Stored Item ID' })
|
||||||
@ApiOperation({ summary: 'Create a new stored item transaction' })
|
@ApiOperation({ summary: 'Create a new stored item transaction' })
|
||||||
@ApiBody({ type: StorageStoredItemTransactionRequestDto })
|
@ApiBody({ type: StorageStoredItemTransactionRequestDto })
|
||||||
@ -251,6 +255,39 @@ export class AppStorageController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Get('item/:storageId/:storedItemId/transactions')
|
||||||
|
@ApiParam({ name: 'storedItemId', description: 'Stored Item ID' })
|
||||||
|
@ApiOperation({ summary: 'Get a stored items transactions' })
|
||||||
|
@ApiOkResponse({
|
||||||
|
type: StorageTransactionWithActorResponseDto,
|
||||||
|
isArray: true,
|
||||||
|
})
|
||||||
|
async getStoredItemTransactions(
|
||||||
|
@Param('storedItemId', ParseIntPipe) storedItemId: number,
|
||||||
|
@CurrentStorage() storage: Storage,
|
||||||
|
) {
|
||||||
|
return this.service.getStoredItemTransaction(storage, storedItemId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Patch('item/:storageId/:storedItemId/transactions/:transactionId')
|
||||||
|
@ApiParam({ name: 'storedItemId', description: 'Stored Item ID' })
|
||||||
|
@ApiOperation({ summary: 'Update a stored item transaction' })
|
||||||
|
@ApiBody({ type: StorageStoredItemTransactionUpdateRequestDto })
|
||||||
|
@ApiOkResponse({ type: StorageTransactionResponseDto })
|
||||||
|
async updateStoredItemTransaction(
|
||||||
|
@LoggedInUser() user: User,
|
||||||
|
@Param('storedItemId', ParseIntPipe) storedItemId: number,
|
||||||
|
@Param('transactionId', ParseIntPipe) transactionId: number,
|
||||||
|
@Body() body: StorageStoredItemTransactionUpdateRequestDto,
|
||||||
|
) {
|
||||||
|
return this.service.updateStoredItemTransaction(
|
||||||
|
user,
|
||||||
|
storedItemId,
|
||||||
|
transactionId,
|
||||||
|
body,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@Get('item/:storageId/:storedItemId')
|
@Get('item/:storageId/:storedItemId')
|
||||||
@ApiParam({ name: 'storedItemId', description: 'Stored Item ID' })
|
@ApiParam({ name: 'storedItemId', description: 'Stored Item ID' })
|
||||||
@ApiOperation({ summary: 'Get a stored items details' })
|
@ApiOperation({ summary: 'Get a stored items details' })
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
import {
|
||||||
|
Injectable,
|
||||||
|
NotFoundException,
|
||||||
|
UnauthorizedException,
|
||||||
|
} from '@nestjs/common';
|
||||||
import omit from 'lodash.omit';
|
import omit from 'lodash.omit';
|
||||||
import pick from 'lodash.pick';
|
import pick from 'lodash.pick';
|
||||||
import { BuildingService } from 'src/objects/building/building.service';
|
import { BuildingService } from 'src/objects/building/building.service';
|
||||||
@ -17,6 +21,7 @@ import {
|
|||||||
StorageItemUpdateRequestDto,
|
StorageItemUpdateRequestDto,
|
||||||
StorageStoredItemRequestDto,
|
StorageStoredItemRequestDto,
|
||||||
StorageStoredItemTransactionRequestDto,
|
StorageStoredItemTransactionRequestDto,
|
||||||
|
StorageStoredItemTransactionUpdateRequestDto,
|
||||||
StorageStoredItemUpdateRequestDto,
|
StorageStoredItemUpdateRequestDto,
|
||||||
} from './dto/storage-add-item-request.dto';
|
} from './dto/storage-add-item-request.dto';
|
||||||
import { StorageItemRequestQueryDto } from './dto/storage-item-request.dto';
|
import { StorageItemRequestQueryDto } from './dto/storage-item-request.dto';
|
||||||
@ -25,6 +30,7 @@ import {
|
|||||||
StorageItemSearchResponseDto,
|
StorageItemSearchResponseDto,
|
||||||
StorageStoredItemResponseDto,
|
StorageStoredItemResponseDto,
|
||||||
StorageTransactionResponseDto,
|
StorageTransactionResponseDto,
|
||||||
|
StorageTransactionWithActorResponseDto,
|
||||||
} from './dto/storage-item-response.dto';
|
} from './dto/storage-item-response.dto';
|
||||||
import {
|
import {
|
||||||
StorageCreateRequestDto,
|
StorageCreateRequestDto,
|
||||||
@ -268,6 +274,7 @@ export class AppStorageService {
|
|||||||
storage,
|
storage,
|
||||||
storedItemId,
|
storedItemId,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!storedItem) {
|
if (!storedItem) {
|
||||||
throw new NotFoundException('Stored item not found');
|
throw new NotFoundException('Stored item not found');
|
||||||
}
|
}
|
||||||
@ -289,14 +296,59 @@ export class AppStorageService {
|
|||||||
await this.storageService.saveStoredItem(storedItem);
|
await this.storageService.saveStoredItem(storedItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.formatTransactionNoDetails(transaction);
|
return this.formatTransaction(transaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateStoredItemTransaction(
|
||||||
|
user: User,
|
||||||
|
storedItemId: number,
|
||||||
|
transactionId: number,
|
||||||
|
body: StorageStoredItemTransactionUpdateRequestDto,
|
||||||
|
) {
|
||||||
|
const storedItemTransaction =
|
||||||
|
await this.storageService.getStoredItemTransactionById(
|
||||||
|
storedItemId,
|
||||||
|
transactionId,
|
||||||
|
['actor'],
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!storedItemTransaction) {
|
||||||
|
throw new NotFoundException('Stored item not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (storedItemTransaction.actor.sub === user.sub) {
|
||||||
|
throw new UnauthorizedException(
|
||||||
|
'You can only update your own transactions',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(storedItemTransaction, body);
|
||||||
|
await this.storageService.saveStoredItemTransaction(storedItemTransaction);
|
||||||
|
|
||||||
|
return this.formatTransactionWithActor(storedItemTransaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getStoredItemTransaction(storage: Storage, storedItemId: number) {
|
||||||
|
const storedItem = await this.storageService.getStoredItemByStorageAndId(
|
||||||
|
storage,
|
||||||
|
storedItemId,
|
||||||
|
['transactions', 'transactions.actor'],
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!storedItem) {
|
||||||
|
throw new NotFoundException('Stored item not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
return storedItem.transactions.map((transaction) =>
|
||||||
|
this.formatTransactionWithActor(transaction),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getStoredItemDetails(storage: Storage, storedItemId: number) {
|
async getStoredItemDetails(storage: Storage, storedItemId: number) {
|
||||||
const storedItem = await this.storageService.getStoredItemByStorageAndId(
|
const storedItem = await this.storageService.getStoredItemByStorageAndId(
|
||||||
storage,
|
storage,
|
||||||
storedItemId,
|
storedItemId,
|
||||||
['transactions', 'item', 'addedBy'],
|
['item', 'addedBy'],
|
||||||
);
|
);
|
||||||
if (!storedItem) {
|
if (!storedItem) {
|
||||||
throw new NotFoundException('Stored item not found');
|
throw new NotFoundException('Stored item not found');
|
||||||
@ -315,18 +367,18 @@ export class AppStorageService {
|
|||||||
return this.formatStorageWithItems(storage);
|
return this.formatStorageWithItems(storage);
|
||||||
}
|
}
|
||||||
|
|
||||||
formatActor(input: User): StorageActorResponse {
|
private formatActor(input: User): StorageActorResponse {
|
||||||
return pick(input, ['name', 'sub', 'color']);
|
return pick(input, ['name', 'sub', 'picture', 'color']);
|
||||||
}
|
}
|
||||||
|
|
||||||
formatStorageNoItems(storage: Storage): StorageResponseDto {
|
private formatStorageNoItems(storage: Storage): StorageResponseDto {
|
||||||
return {
|
return {
|
||||||
...omit(storage, ['room', 'set', 'items']),
|
...omit(storage, ['room', 'set', 'items']),
|
||||||
addedBy: storage.addedBy && this.formatActor(storage.addedBy),
|
addedBy: storage.addedBy && this.formatActor(storage.addedBy),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
formatStorageWithItems(storage: Storage): StorageResponseDto {
|
private formatStorageWithItems(storage: Storage): StorageResponseDto {
|
||||||
return {
|
return {
|
||||||
...omit(storage, ['room', 'set']),
|
...omit(storage, ['room', 'set']),
|
||||||
items: !!storage.items?.length
|
items: !!storage.items?.length
|
||||||
@ -336,25 +388,36 @@ export class AppStorageService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
formatItem(item: Item): StorageItemResponseDto {
|
private formatItem(item: Item): StorageItemResponseDto {
|
||||||
return {
|
return {
|
||||||
...omit(item, ['instances', 'addedBy']),
|
...omit(item, ['instances', 'addedBy']),
|
||||||
addedBy: item.addedBy && this.formatActor(item.addedBy),
|
addedBy: item.addedBy && this.formatActor(item.addedBy),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
formatTransactionNoDetails(
|
private formatTransaction(
|
||||||
transaction: StoredItemTransaction,
|
transaction: StoredItemTransaction,
|
||||||
): StorageTransactionResponseDto {
|
): StorageTransactionResponseDto {
|
||||||
return omit(transaction, ['storedItem', 'actor']);
|
return omit(transaction, ['storedItem', 'actor']);
|
||||||
}
|
}
|
||||||
|
|
||||||
formatStoredItem(storedItem: StoredItem): StorageStoredItemResponseDto {
|
private formatTransactionWithActor(
|
||||||
|
transaction: StoredItemTransaction,
|
||||||
|
): StorageTransactionWithActorResponseDto {
|
||||||
|
return {
|
||||||
|
...omit(transaction, ['storedItem']),
|
||||||
|
actor: transaction.actor && this.formatActor(transaction.actor),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private formatStoredItem(
|
||||||
|
storedItem: StoredItem,
|
||||||
|
): StorageStoredItemResponseDto {
|
||||||
return {
|
return {
|
||||||
...omit(storedItem, ['storage']),
|
...omit(storedItem, ['storage']),
|
||||||
transactions: !!storedItem.transactions?.length
|
transactions: !!storedItem.transactions?.length
|
||||||
? storedItem.transactions.map((transaction) =>
|
? storedItem.transactions.map((transaction) =>
|
||||||
this.formatTransactionNoDetails(transaction),
|
this.formatTransaction(transaction),
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
item: storedItem.item ? this.formatItem(storedItem.item) : null,
|
item: storedItem.item ? this.formatItem(storedItem.item) : null,
|
||||||
|
@ -120,6 +120,10 @@ export class StorageStoredItemTransactionRequestDto {
|
|||||||
actionAt?: Date;
|
actionAt?: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class StorageStoredItemTransactionUpdateRequestDto extends PartialType(
|
||||||
|
StorageStoredItemTransactionRequestDto,
|
||||||
|
) {}
|
||||||
|
|
||||||
export class StorageAddExistingItemRequestDto {
|
export class StorageAddExistingItemRequestDto {
|
||||||
@ApiPropertyOptional({ type: StorageStoredItemRequestDto })
|
@ApiPropertyOptional({ type: StorageStoredItemRequestDto })
|
||||||
@Type(() => StorageStoredItemRequestDto)
|
@Type(() => StorageStoredItemRequestDto)
|
||||||
|
@ -27,6 +27,11 @@ export class StorageTransactionResponseDto extends OmitType(
|
|||||||
['storedItem', 'actor'],
|
['storedItem', 'actor'],
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
export class StorageTransactionWithActorResponseDto extends StorageTransactionResponseDto {
|
||||||
|
@ApiProperty({ type: StorageActorResponse })
|
||||||
|
actor: StorageActorResponse;
|
||||||
|
}
|
||||||
|
|
||||||
export class StorageStoredItemResponseDto extends OmitType(StoredItem, [
|
export class StorageStoredItemResponseDto extends OmitType(StoredItem, [
|
||||||
'addedBy',
|
'addedBy',
|
||||||
'transactions',
|
'transactions',
|
||||||
|
@ -6,6 +6,7 @@ import { StorageStoredItemResponseDto } from './storage-item-response.dto';
|
|||||||
export class StorageActorResponse extends PickType(User, [
|
export class StorageActorResponse extends PickType(User, [
|
||||||
'sub',
|
'sub',
|
||||||
'name',
|
'name',
|
||||||
|
'picture',
|
||||||
'color',
|
'color',
|
||||||
]) {}
|
]) {}
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ export class AppUserController {
|
|||||||
@Get()
|
@Get()
|
||||||
@UseGuards(AuthGuard)
|
@UseGuards(AuthGuard)
|
||||||
@ApiOperation({ summary: 'Get logged in user' })
|
@ApiOperation({ summary: 'Get logged in user' })
|
||||||
@ApiBearerAuth('Bearer auth')
|
@ApiBearerAuth('Bearer token')
|
||||||
async userGetInfo(@LoggedInUser() user: User) {
|
async userGetInfo(@LoggedInUser() user: User) {
|
||||||
return {
|
return {
|
||||||
name: user.name,
|
name: user.name,
|
||||||
|
17
src/main.ts
17
src/main.ts
@ -15,14 +15,17 @@ async function bootstrap() {
|
|||||||
.addTag('groups')
|
.addTag('groups')
|
||||||
.addTag('storage')
|
.addTag('storage')
|
||||||
.addTag('buildings')
|
.addTag('buildings')
|
||||||
.addBearerAuth({
|
.addSecurity('Bearer token', {
|
||||||
name: 'Bearer token',
|
name: 'Authorization',
|
||||||
type: 'apiKey',
|
|
||||||
})
|
|
||||||
.addBasicAuth({
|
|
||||||
name: 'Email and Password',
|
|
||||||
description: 'For acquiring a Bearer token',
|
|
||||||
type: 'http',
|
type: 'http',
|
||||||
|
scheme: 'bearer',
|
||||||
|
bearerFormat: 'JWT',
|
||||||
|
in: 'header',
|
||||||
|
})
|
||||||
|
.addSecurity('Email and Password', {
|
||||||
|
type: 'http',
|
||||||
|
description: 'For acquiring the access token',
|
||||||
|
scheme: 'basic',
|
||||||
})
|
})
|
||||||
.build();
|
.build();
|
||||||
const document = SwaggerModule.createDocument(app, config);
|
const document = SwaggerModule.createDocument(app, config);
|
||||||
|
@ -272,6 +272,20 @@ export class StorageService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getStoredItemTransactionById(
|
||||||
|
storedItemId: number,
|
||||||
|
storedItemTransactionId: number,
|
||||||
|
relations = [],
|
||||||
|
) {
|
||||||
|
return this.transactionRepository.findOne({
|
||||||
|
where: {
|
||||||
|
id: storedItemTransactionId,
|
||||||
|
storedItem: { id: storedItemId },
|
||||||
|
},
|
||||||
|
relations,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async saveStorage(data: Partial<Storage>) {
|
async saveStorage(data: Partial<Storage>) {
|
||||||
const newStorage = new Storage();
|
const newStorage = new Storage();
|
||||||
Object.assign(newStorage, data);
|
Object.assign(newStorage, data);
|
||||||
|
Loading…
Reference in New Issue
Block a user