finally: fix calculate error

(caused by empty cookies in bonus validation user request)
This commit is contained in:
vchikalkin 2023-03-24 20:21:10 +03:00
parent dded238358
commit a3f98c1e91
19 changed files with 138 additions and 124 deletions

6
.env
View File

@ -10,8 +10,4 @@ URL_GET_USER_DIRECT=
URL_CRM_GRAPHQL_DIRECT=
URL_CORE_FINGAP_DIRECT=
URL_CORE_CALCULATE_DIRECT=
URL_1C_TRANSTAX_DIRECT=
####### SERVER ########
# DEFAULT: 'token'
# COOKIE_TOKEN_NAME=
URL_1C_TRANSTAX_DIRECT=

View File

@ -1,4 +1,5 @@
import { getUser } from '@/api/user/query';
import { STALE_TIME } from '@/constants/request';
import { min } from '@/styles/mq';
import { useQuery } from '@tanstack/react-query';
import styled from 'styled-components';
@ -19,8 +20,8 @@ const UserText = styled.span`
`;
function User() {
const { data: user } = useQuery(['user'], () => getUser(), {
enabled: false,
const { data: user } = useQuery(['user'], ({ signal }) => getUser({ signal }), {
staleTime: STALE_TIME,
});
return <UserText>{user?.displayName}</UserText>;

View File

@ -14,9 +14,10 @@ export async function calculateFinGAP(payload: RequestFinGAP, { signal }: QueryF
}
export async function calculate(payload: RequestCalculate, { signal }: QueryFunctionContext) {
const { data } = await axios.post<ResponseCalculate>(URL_CORE_CALCULATE, payload, {
signal,
});
return data;
return await axios
.post<ResponseCalculate>(URL_CORE_CALCULATE, payload, {
signal,
})
.then((response) => response.data)
.catch((error) => error.response.data);
}

View File

@ -2,7 +2,6 @@ const { z } = require('zod');
const envSchema = z.object({
BASE_PATH: z.string().optional().default(''),
COOKIE_TOKEN_NAME: z.string().default('token'),
PORT: z.string().optional(),
URL_1C_TRANSTAX_DIRECT: z.string(),
URL_CORE_CALCULATE_DIRECT: z.string(),

View File

@ -8,7 +8,6 @@ const publicRuntimeConfigSchema = envSchema.pick({
const serverRuntimeConfigSchema = envSchema.pick({
BASE_PATH: true,
COOKIE_TOKEN_NAME: true,
PORT: true,
URL_1C_TRANSTAX_DIRECT: true,
URL_CORE_CALCULATE_DIRECT: true,

View File

@ -1,12 +0,0 @@
import { serverRuntimeConfigSchema } from './schema/runtime-config';
import getConfig from 'next/config';
const { serverRuntimeConfig } = getConfig();
function getServerConfig() {
const { COOKIE_TOKEN_NAME } = serverRuntimeConfigSchema.parse(serverRuntimeConfig);
return { COOKIE_TOKEN_NAME };
}
export default getServerConfig;

View File

@ -1 +1,10 @@
export const STALE_TIME = Number.POSITIVE_INFINITY;
import dayjs from 'dayjs';
import duration from 'dayjs/plugin/duration';
dayjs.extend(duration);
export const STALE_TIME = dayjs
.duration({
hours: 1,
})
.asMilliseconds();

View File

@ -21,9 +21,7 @@
"@trpc/react-query": "^10.13.0",
"@trpc/server": "^10.13.0",
"axios": "^1.3.4",
"cookies-next": "^2.1.1",
"dayjs": "^1.11.7",
"jwt-decode": "^3.1.2",
"less": "^4.1.3",
"less-loader": "^11.1.0",
"mobx": "^6.8.0",

View File

@ -4,6 +4,7 @@ import * as Calculation from '@/Components/Calculation';
import { CRMError } from '@/Components/Common/Error';
import Output from '@/Components/Output';
import { useDefaultReactions } from '@/config/process';
import { STALE_TIME } from '@/constants/request';
import * as init from '@/process/init';
import { min } from '@/styles/mq';
import { dehydrate, QueryClient } from '@tanstack/react-query';
@ -56,15 +57,20 @@ function Home({ error }) {
/** @type {import('next').GetServerSideProps} */
export const getServerSideProps = async ({ req }) => {
const { cookie = '' } = req.headers;
const queryGetUser = () =>
getUser({
headers: {
cookie,
},
});
const queryClient = new QueryClient();
const user = await queryClient.fetchQuery(['user'], queryGetUser);
const user = await queryClient.fetchQuery(
['user'],
({ signal }) =>
getUser({
headers: {
cookie,
},
signal,
}),
{ staleTime: STALE_TIME }
);
const apolloClient = initializeApollo();

View File

@ -1,23 +1,17 @@
import type { ValidationContext } from '../../types';
import { getUser } from '@/api/user/query';
import type { ElementsTypes } from '@/Components/Calculation/config/map/values';
import { STALE_TIME } from '@/constants/request';
import * as CRMTypes from '@/graphql/crm.types';
import dayjs from 'dayjs';
export type ProductId = ElementsTypes['selectProduct'];
export default function helper({ apolloClient, queryClient }: ValidationContext) {
export default function helper({ apolloClient, user }: ValidationContext) {
return {
async getCoefficient(productId: ProductId) {
if (!productId) {
if (!productId || !user) {
return null;
}
const user = await queryClient.fetchQuery(['user'], () => getUser(), {
staleTime: STALE_TIME,
});
const {
data: { systemuser },
} = await apolloClient.query({

View File

@ -25,24 +25,34 @@ export async function action({ store, trpcClient }) {
const payments = toJS($tables.payments.values);
const res = await trpcClient.calculate.calculate.query({
insurance: { values: insurance },
payments: { values: payments },
values,
});
if (res.success === false) {
notification.error({
description: res.error,
key,
message,
trpcClient.calculate.calculate
.mutate({
insurance: { values: insurance },
payments: { values: payments },
values,
})
.then((res) => {
if (res.success === false) {
notification.error({
description: res.error,
key,
message,
});
} else {
$results.setPayments(res.data.resultPayments);
$results.setValues(res.data.resultValues);
$calculation.$values.setValues(res.data.values);
}
})
.catch((error) => {
notification.error({
description: JSON.stringify(error),
key,
message,
});
})
.finally(() => {
$calculation.$status.setStatus('btnCalculate', 'Default');
$calculation.$status.setStatus('btnCreateKP', 'Default');
});
} else {
$results.setPayments(res.data.resultPayments);
$results.setValues(res.data.resultValues);
$calculation.$values.setValues(res.data.values);
}
$calculation.$status.setStatus('btnCalculate', 'Default');
$calculation.$status.setStatus('btnCreateKP', 'Default');
}

View File

@ -1,8 +1,10 @@
import { getUser } from '@/api/user/query';
import { STALE_TIME } from '@/constants/request';
import type { Process, ProcessContext } from '@/process/types';
import { useStore } from '@/stores/hooks';
import { trpcPureClient } from '@/trpc/client';
import { useApolloClient } from '@apollo/client';
import { useQueryClient } from '@tanstack/react-query';
import { useQuery, useQueryClient } from '@tanstack/react-query';
export function useProcess({ reactions }: Process) {
const context = useProcessContext();
@ -18,10 +20,15 @@ export function useProcessContext(): ProcessContext {
const apolloClient = useApolloClient();
const queryClient = useQueryClient();
const { data: user } = useQuery(['user'], ({ signal }) => getUser({ signal }), {
staleTime: STALE_TIME,
});
return {
apolloClient,
queryClient,
store,
trpcClient: trpcPureClient,
user,
};
}

View File

@ -1,3 +1,4 @@
import type { User } from '@/api/user/types';
import type RootStore from '@/stores/root';
import type { TRPCPureClient } from '@/trpc/types';
import type { ApolloClient } from '@apollo/client';
@ -8,6 +9,7 @@ export type ProcessContext = {
queryClient: QueryClient;
store: RootStore;
trpcClient: TRPCPureClient;
user: User | undefined;
};
export type Process = {

View File

@ -16,17 +16,30 @@ function getBaseUrl() {
const url = `${getBaseUrl()}/api/trpc`;
export const trpcClient = createTRPCNext<AppRouter>({
config() {
config({ ctx }) {
return {
links: [
httpBatchLink({
headers() {
return {
cookie: ctx?.req?.headers.cookie,
};
},
url,
}),
],
queryClientConfig: {
defaultOptions: {
queries: {
refetchOnMount: false,
refetchOnWindowFocus: false,
},
},
},
transformer: SuperJSON,
};
},
ssr: true,
ssr: false,
});
export const trpcPureClient = createTRPCProxyClient<AppRouter>({

View File

@ -1,15 +1,18 @@
import getServerConfig from '@/config/server';
import { getUser } from '@/api/user/query';
import type { inferAsyncReturnType } from '@trpc/server';
import type { CreateNextContextOptions } from '@trpc/server/adapters/next';
import { getCookie } from 'cookies-next';
const { COOKIE_TOKEN_NAME } = getServerConfig();
export async function createContext({ req }: CreateNextContextOptions) {
const { cookie = '' } = req.headers;
export async function createContext({ req, res }: CreateNextContextOptions) {
const token = getCookie(COOKIE_TOKEN_NAME, { req, res });
const user = await getUser({
headers: {
cookie,
},
});
return {
token,
user,
};
}

View File

@ -1,12 +1,8 @@
import { t } from './server';
import { getUser } from '@/api/user/query';
import getServerConfig from '@/config/server';
import { TRPCError } from '@trpc/server';
const { COOKIE_TOKEN_NAME } = getServerConfig();
export const user = t.middleware(async ({ ctx, next }) => {
if (process.env.NODE_ENV !== 'development' && !ctx.token) {
export const userMiddleware = t.middleware(({ ctx, next }) => {
if (process.env.NODE_ENV !== 'development' && !ctx.user) {
throw new TRPCError({
code: 'UNAUTHORIZED',
});
@ -14,11 +10,7 @@ export const user = t.middleware(async ({ ctx, next }) => {
return next({
ctx: {
user: await getUser({
headers: {
Cookie: `${COOKIE_TOKEN_NAME}=${ctx.token}`,
},
}),
isAdmin: false,
},
});
});

View File

@ -1,20 +1,22 @@
import { user } from '../../middleware';
import { userMiddleware } from '../../middleware';
import { t } from '../../server';
import { convertCalculateResults } from './convert';
import { getRequestData } from './request';
import { CalculateInputSchema, CalculateOutputSchema } from './types';
import { validate } from './validation';
import { calculate } from '@/api/core/query';
import type { ResponseCalculate } from '@/api/core/types/calculate';
import initializeApollo from '@/apollo/client';
import { STALE_TIME } from '@/constants/request';
import type { QueryFunctionContext } from '@tanstack/react-query';
import { QueryClient } from '@tanstack/react-query';
const calculateRouter = t.router({
calculate: t.procedure
.use(user)
.use(userMiddleware)
.input(CalculateInputSchema)
.output(CalculateOutputSchema)
.query(async ({ input, ctx }) => {
.mutation(async ({ input, ctx }) => {
const apolloClient = initializeApollo();
const queryClient = new QueryClient();
@ -22,6 +24,7 @@ const calculateRouter = t.router({
context: {
apolloClient,
queryClient,
user: ctx.user,
},
input,
});
@ -37,34 +40,46 @@ const calculateRouter = t.router({
context: {
apolloClient,
queryClient,
user: ctx.user,
},
input,
user: ctx.user,
});
const request = (context: QueryFunctionContext) => calculate(payload, context);
try {
const calculateResult = await queryClient.fetchQuery(
['calculate'],
(context: QueryFunctionContext) => calculate(payload, context),
{
staleTime: STALE_TIME,
}
);
const calculateResult = await queryClient.fetchQuery(['calculate'], request, {
staleTime: 15_000,
});
if (calculateResult.errors?.length > 0) {
return {
error: calculateResult.errors[0],
success: false,
};
}
const result = convertCalculateResults({
calculateInput: input,
requestCalculate: payload,
responseCalculate: calculateResult,
});
if (calculateResult.errors?.length > 0) {
return {
error: calculateResult.errors[0],
data: result,
success: true,
};
} catch (error) {
const { errors } = error as Pick<ResponseCalculate, 'errors'>;
return {
error: errors[0],
success: false,
};
}
const result = convertCalculateResults({
calculateInput: input,
requestCalculate: payload,
responseCalculate: calculateResult,
});
return {
data: result,
success: true,
};
}),
});

View File

@ -5,7 +5,7 @@ import ValuesSchema from '@/config/schema/values';
import type { ProcessContext } from '@/process/types';
import { z } from 'zod';
export type Context = Pick<ProcessContext, 'apolloClient' | 'queryClient'>;
export type Context = Pick<ProcessContext, 'apolloClient' | 'queryClient' | 'user'>;
export const CalculateInputSchema = z
.object({

View File

@ -2112,11 +2112,6 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.14.2.tgz#c076ed1d7b6095078ad3cf21dfeea951842778b1"
integrity sha512-1uEQxww3DaghA0RxqHx0O0ppVlo43pJhepY51OxuQIKHpjbnYLA7vcdwioNPzIqmC2u3I/dmylcqjlh0e7AyUA==
"@types/node@^16.10.2":
version "16.18.16"
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.16.tgz#09ff98b144abae2d7cce3e9fe9040ab2bf73222c"
integrity sha512-ZOzvDRWp8dCVBmgnkIqYCArgdFOO9YzocZp8Ra25N/RStKiWvMOXHMz+GjSeVNe5TstaTmTWPucGJkDw0XXJWA==
"@types/normalize-package-data@^2.4.0":
version "2.4.1"
resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301"
@ -3374,20 +3369,11 @@ convert-source-map@^2.0.0:
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a"
integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==
cookie@^0.4.0, cookie@^0.4.2:
cookie@^0.4.2:
version "0.4.2"
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432"
integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==
cookies-next@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/cookies-next/-/cookies-next-2.1.1.tgz#8d82f1b78fccfb19d9d7c26766fa5707a3ec4695"
integrity sha512-AZGZPdL1hU3jCjN2UMJTGhLOYzNUN9Gm+v8BdptYIHUdwz397Et1p+sZRfvAl8pKnnmMdX2Pk9xDRKCGBum6GA==
dependencies:
"@types/cookie" "^0.4.1"
"@types/node" "^16.10.2"
cookie "^0.4.0"
copy-anything@^2.0.1:
version "2.0.6"
resolved "https://registry.yarnpkg.com/copy-anything/-/copy-anything-2.0.6.tgz#092454ea9584a7b7ad5573062b2a87f5900fc480"
@ -6508,11 +6494,6 @@ jws@^3.2.2:
jwa "^1.4.1"
safe-buffer "^5.0.1"
jwt-decode@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-3.1.2.tgz#3fb319f3675a2df0c2895c8f5e9fa4b67b04ed59"
integrity sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A==
keyv@^4.5.2:
version "4.5.2"
resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.2.tgz#0e310ce73bf7851ec702f2eaf46ec4e3805cce56"