Merge branch 'tariff' into 'dev'

feat: tariff

See merge request pena-services/hub_admin_backend_service!3
This commit is contained in:
Mikhail 2022-12-21 20:06:04 +00:00
commit bda96dbd14
17 changed files with 261 additions and 164 deletions

@ -1,6 +1,7 @@
import { setAccountRoutes } from "@/routes/account.routes";
import { setPrivilegeRoutes } from "@/routes/privilege.routes";
import { setRoleRoutes } from "@/routes/role.routes";
import { setTariffRoutes } from "@/routes/tariff.routes";
import type { FastifyInstance } from "fastify";
@ -8,4 +9,5 @@ export const combineRoutes = (server: FastifyInstance): void => {
server.register(setRoleRoutes, { prefix: "/role" });
server.register(setAccountRoutes, { prefix: "/account" });
server.register(setPrivilegeRoutes, { prefix: "/privilege" });
server.register(setTariffRoutes, { prefix: "/tariff" });
};

@ -0,0 +1,22 @@
import { validateEmptyFields } from "@/utils/validate-empty-fields";
import type { TariffMessage } from "@/types/messages/tariff-message.type";
import type { ObjectWithRequiredFields } from "@/types/object-with-required-fields";
export const validateTariff = (tariff: TariffMessage): [ObjectWithRequiredFields<TariffMessage>, Error | null] => {
const [validatedTariff, errorEmpty] = validateEmptyFields(
tariff,
["isCustom", "name", "price", "privilegies"],
false
);
if (errorEmpty) {
return [validatedTariff, errorEmpty];
}
if (isNaN(Number(validatedTariff.price))) {
return [validatedTariff, new Error("invalid 'price' value")];
}
return [validatedTariff, null];
};

@ -0,0 +1,125 @@
import { Types } from "mongoose";
import { TariffModel } from "@/models/tariff.model";
import { PrivilegeModel } from "@/models/privilege.model";
import { validateEmptyFields } from "@/utils/validate-empty-fields";
import { validateTariff } from "./helpers";
import type { FastifyReply } from "fastify";
import type { Privilege } from "@/types/models/privilege.type";
import type { CreateTariffRequest, GetTariffRequest, ReplaceTariffRequest } from "./types";
export const getTariffs = async () => TariffModel.find({}).lean();
export const getTariff = async (request: GetTariffRequest, reply: FastifyReply) => {
const [requestParams, error] = validateEmptyFields(request.params || {}, ["id"]);
if (error) {
reply.status(400);
return error;
}
if (!Types.ObjectId.isValid(requestParams.id)) {
reply.status(400);
return new Error("invalid id");
}
const tariff = await TariffModel.findById(requestParams.id).lean();
if (!tariff) {
reply.status(404);
return new Error("tariff not found");
}
return tariff;
};
export const createTariff = async (request: CreateTariffRequest, reply: FastifyReply) => {
const [requestBody, error] = validateTariff(request.body || {});
if (error) {
reply.status(400);
return error;
}
for (const privilege of requestBody.privilegies) {
const findedPrivilege = await PrivilegeModel.findOne({ privilegeId: privilege.privilegeId }).lean();
if (!findedPrivilege) {
reply.status(404);
return new Error(`privilege with id ${privilege.privilegeId} not found`);
}
}
const privilegiesMap = requestBody.privilegies.reduce<Record<string, Privilege[]>>((accamulator, privilege) => {
if (!accamulator[privilege.privilegeId]) {
accamulator[privilege.privilegeId] = [];
}
accamulator[privilege.privilegeId].push(privilege);
return accamulator;
}, {});
const newTariff = new TariffModel({
name: requestBody.name,
price: requestBody.price,
isCustom: requestBody.isCustom,
privilegies: privilegiesMap,
});
await newTariff.save();
return newTariff;
};
export const replaceTariff = async (request: ReplaceTariffRequest, reply: FastifyReply) => {
const [requestBody, error] = validateTariff(request.body || {});
if (error) {
reply.status(400);
return error;
}
if (!Types.ObjectId.isValid(request.params?.id || "")) {
reply.status(400);
return new Error("invalid id");
}
const tariff = await TariffModel.findById(request.params?.id);
if (!tariff) {
reply.status(404);
return new Error("tariff not found");
}
for (const privilege of requestBody.privilegies) {
const findedPrivilege = await PrivilegeModel.findOne({ privilegeId: privilege.privilegeId }).lean();
if (!findedPrivilege) {
reply.status(404);
return new Error(`privilege with id ${privilege.privilegeId} not found`);
}
}
const privilegiesMap = requestBody.privilegies.reduce<Record<string, Privilege[]>>((accamulator, privilege) => {
if (!accamulator[privilege.privilegeId]) {
accamulator[privilege.privilegeId] = [];
}
accamulator[privilege.privilegeId].push(privilege);
return accamulator;
}, {});
await tariff.replaceOne({
name: requestBody.name,
price: requestBody.price,
isCustom: requestBody.isCustom,
privilegies: privilegiesMap,
});
return tariff;
};

@ -0,0 +1,11 @@
import type { FastifyRequest } from "fastify";
import type { CreateTariffRoute, ReplaceTariffRoute } from "@/types/routes/tariff-routes.type";
export type GetTariffRequest = FastifyRequest<{
Params?: {
id?: string;
};
}>;
export type CreateTariffRequest = FastifyRequest<CreateTariffRoute>;
export type ReplaceTariffRequest = FastifyRequest<ReplaceTariffRoute>;

@ -1,6 +1,6 @@
import { Schema, model, SchemaDefinition } from "mongoose";
import { eloquentModelSchema } from "./eloquent-model.schema";
import { EloquentSchema } from "./eloquent-model.schema";
import type { Account } from "@/types/models/account.type";
@ -22,7 +22,7 @@ const schema: SchemaDefinition<Account> = {
type: String,
default: "user",
},
...eloquentModelSchema,
...EloquentSchema,
};
const schemaSettings = {

@ -1,7 +1,8 @@
import type { SchemaDefinition } from "mongoose";
import { Schema, SchemaDefinition } from "mongoose";
import type { EloquentModel } from "@/types/models/eloquent-model.type";
export const eloquentModelSchema: SchemaDefinition<EloquentModel> = {
export const schema: SchemaDefinition<EloquentModel> = {
createdAt: {
type: Date,
required: true,
@ -22,3 +23,10 @@ export const eloquentModelSchema: SchemaDefinition<EloquentModel> = {
default: false,
},
};
const schemaSettings = {
versionKey: false,
collection: "privilegies",
};
export const EloquentSchema = new Schema<EloquentModel>(schema, schemaSettings);

@ -1,6 +1,6 @@
import { Schema, model, SchemaDefinition } from "mongoose";
import { eloquentModelSchema } from "./eloquent-model.schema";
import { EloquentSchema } from "./eloquent-model.schema";
import type { Privilege } from "@/types/models/privilege.type";
@ -37,7 +37,7 @@ const schema: SchemaDefinition<Privilege> = {
type: Number,
required: true,
},
...eloquentModelSchema,
...EloquentSchema,
};
const schemaSettings = {
@ -45,6 +45,6 @@ const schemaSettings = {
collection: "privilegies",
};
const PrivilegeSchema = new Schema<Privilege>(schema, schemaSettings);
export const PrivilegeSchema = new Schema<Privilege>(schema, schemaSettings);
export const PrivilegeModel = model("Privilege", PrivilegeSchema);

@ -1,6 +1,6 @@
import { Schema, model, SchemaDefinition } from "mongoose";
import { eloquentModelSchema } from "./eloquent-model.schema";
import { EloquentSchema } from "./eloquent-model.schema";
import type { Role } from "@/types/models/role.type";
@ -14,7 +14,7 @@ const schema: SchemaDefinition<Role> = {
of: Boolean,
default: {},
},
...eloquentModelSchema,
...EloquentSchema,
};
const schemaSettings = {

@ -0,0 +1,36 @@
import { Schema, model, SchemaDefinition } from "mongoose";
import { EloquentSchema } from "./eloquent-model.schema";
import { PrivilegeSchema } from "./privilege.model";
import type { Tariff } from "@/types/models/tariff.type";
const schema: SchemaDefinition<Tariff> = {
name: {
type: String,
required: true,
},
price: {
type: Number,
required: true,
},
isCustom: {
type: Boolean,
default: false,
},
privilegies: {
type: Map,
of: PrivilegeSchema,
default: {},
},
...EloquentSchema,
};
const schemaSettings = {
versionKey: false,
collection: "tariffs",
};
const TariffSchema = new Schema<Tariff>(schema, schemaSettings);
export const TariffModel = model("Tariff", TariffSchema);

@ -1,5 +1,5 @@
import { createAccount, getAccount } from "@/handlers/account";
import { verifyUser } from "@/handlers/account/middleware";
import { verifyUser } from "@/handlers/auth/middleware";
import type { FastifyInstance, FastifyPluginOptions } from "fastify";
import type { GetAccountRoute, CreateAccountRoute } from "@/types/routes/account-routes.type";

@ -0,0 +1,14 @@
import { createTariff, replaceTariff, getTariff, getTariffs } from "@/handlers/tariff";
import { verifyUser } from "@/handlers/auth/middleware";
import type { FastifyInstance, FastifyPluginOptions } from "fastify";
import type { CreateTariffRoute, ReplaceTariffRoute } from "@/types/routes/tariff-routes.type";
export const setTariffRoutes = <T = FastifyPluginOptions>(server: FastifyInstance, opts: T, done: () => void): void => {
server.get("/:id", getTariff);
server.get("/", getTariffs);
server.post<CreateTariffRoute>("/", { preHandler: [verifyUser] }, createTariff);
server.put<ReplaceTariffRoute>("/", { preHandler: [verifyUser] }, replaceTariff);
done();
};

@ -1,154 +0,0 @@
{
"openapi": "3.0.3",
"info": {
"title": "PenaHub Server",
"version": "1.0.11",
"license": {
"name": "Apache 2.0",
"url": "http://www.apache.org/licenses/LICENSE-2.0.html"
}
},
"externalDocs": {
"description": "Find out more about Swagger",
"url": "http://swagger.io"
},
"servers": [
{
"url": "http://84.201.155.77:8080"
}
],
"paths": {
"/": {
"get": {
"tags": [
"/"
],
"summary": "Список пользователей",
"responses": {
"200": {
"description": "Successful"
}
}
}
},
"/auth": {
"get": {
"tags": [
"auth"
],
"summary": "Страница регистрации",
"responses": {
"200": {
"description": "Successful"
}
}
}
},
"/api/v1/auth": {
"post": {
"tags": [
"auth"
],
"summary": "Регистрация нового пользователя",
"requestBody": {
"content": {
"application/urlencoded": {
"schema": {
"type": "object",
"properties": {
"login": {
"type": "string",
"example": "valera24"
},
"email": {
"type": "string",
"example": "valera24@mail.com"
},
"phoneNumber": {
"type": "string",
"example": 79993337733
},
"password": {
"type": "string",
"example": "secret"
}
}
}
}
}
},
"responses": {
"200": {
"description": "Successful"
}
}
}
},
"/login": {
"get": {
"tags": [
"login"
],
"summary": "Страница авторизации",
"parameters": [
{
"name": "return",
"in": "query",
"description": "URL на который будет перенаправлен пользователь после авторизации",
"required": false
}
],
"responses": {
"200": {
"description": "Successful"
}
}
}
},
"/api/v1/login": {
"post": {
"tags": [
"login"
],
"summary": "Авторизация пользователя",
"requestBody": {
"content": {
"application/urlencoded": {
"schema": {
"type": "object",
"properties": {
"email": {
"type": "string",
"example": "valera24@mail.com"
},
"password": {
"type": "string",
"example": "secret"
},
"goto": {
"type": "string",
"example": "http://penahub.quiz/signedup"
}
}
}
}
}
},
"responses": {
"200": {
"description": "Successful (Если не передан goto)"
},
"304": {
"description": "Successful (Если передан goto)"
},
"401": {
"description": "password incorrect"
},
"404": {
"description": "User not found"
}
}
}
}
}
}

@ -0,0 +1,10 @@
import type { EloquentModel } from "../models/eloquent-model.type";
import type { Privilege } from "../models/privilege.type";
import type { Tariff } from "../models/tariff.type";
import type { ObjectWithPossibleFields } from "../object-with-possible-fields";
export type TariffMessage = ObjectWithPossibleFields<
Omit<Tariff, keyof EloquentModel | "privilegies"> & {
privilegies: Privilege[];
}
>;

@ -0,0 +1,9 @@
import type { Privilege } from "./privilege.type";
import type { EloquentModel } from "./eloquent-model.type";
export type Tariff = EloquentModel & {
name: string;
price: number;
isCustom: boolean;
privilegies: Record<string, Privilege>;
};

@ -0,0 +1,3 @@
export type ObjectWithPossibleFields<KeyValue extends Record<string, unknown>> = {
[Key in keyof KeyValue]?: KeyValue[Key];
};

@ -0,0 +1,11 @@
import type { RequestGenericInterface } from "fastify";
import type { TariffMessage } from "../messages/tariff-message.type";
export type CreateTariffRoute = RequestGenericInterface & {
Body?: TariffMessage;
};
export type ReplaceTariffRoute = RequestGenericInterface & {
Body?: TariffMessage;
Params?: { id?: string };
};