diff --git a/frontend/package.json b/frontend/package.json index ee74ec79..22652405 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -5,6 +5,7 @@ "dependencies": { "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.0", + "@hookform/resolvers": "^3.6.0", "@mui/icons-material": "^5.15.11", "@mui/material": "^5.15.11", "@types/node": "^20.11.24", @@ -15,16 +16,19 @@ "dayjs": "^1.11.10", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-hook-form": "^7.51.5", "react-router-dom": "^6.22.2", "typescript": "^5.3.3", - "vite": "^5.1.7", - "web-vitals": "^3.5.2" + "vite": "^5.1.4", + "web-vitals": "^3.5.2", + "zod": "^3.23.8" }, "scripts": { "start": "vite", "build": "vite build", "preview": "vite preview", - "format": "prettier --write 'src/**/*.{js,jsx,tsx,css,md,json}' --config ./.prettierrc" + "format": "prettier --write 'src/**/*.{js,jsx,tsx,css,md,json}' --config ./.prettierrc", + "test": "vitest" }, "eslintConfig": { "extends": [ @@ -54,7 +58,8 @@ "eslint-plugin-import": "^2.29.1", "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-react": "^7.34.0", - "prettier": "^3.2.5" + "prettier": "^3.2.5", + "vitest": "^2.0.2" }, "packageManager": "yarn@4.1.0" } diff --git a/frontend/src/App.test.tsx b/frontend/src/App.test.tsx deleted file mode 100644 index d76787ed..00000000 --- a/frontend/src/App.test.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import React from "react"; -import { render, screen } from "@testing-library/react"; -import App from "./App"; - -test("renders learn react link", () => { - render(); - const linkElement = screen.getByText(/learn react/i); - expect(linkElement).toBeInTheDocument(); -}); diff --git a/frontend/src/hooks/subscription-form.tsx b/frontend/src/hooks/subscription-form.tsx new file mode 100644 index 00000000..8203fea2 --- /dev/null +++ b/frontend/src/hooks/subscription-form.tsx @@ -0,0 +1,43 @@ +import { ReactNode } from "react"; +import { Subscription } from "../api"; +import { FormProvider, UseFormProps, useForm, useFormContext } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { defaultSubscriptionDTO, subscriptionDTOSchema, type SubscriptionDTO } from "../lib/dtos/subscription"; + +const SubscriberFormOptions = { + mode: "onBlur", + reValidateMode: "onChange", + resolver: zodResolver(subscriptionDTOSchema), + defaultValues: defaultSubscriptionDTO(), +} satisfies UseFormProps; + +export const SubscriberFormProvider = ({ children }: { children: ReactNode }) => { + const method = useForm(SubscriberFormOptions); + return {children}; +}; + +export const useSubscriptionForm = () => { + const { + register, + handleSubmit, + watch, + getValues, + setValue, + setFocus, + reset, + control, + formState: { errors: validationErrors }, + } = useFormContext(); + + return { + register, + validationErrors, + handleSubmit, + watch, + getValues, + control, + setValue, + setFocus, + reset, + }; +}; diff --git a/frontend/src/lib/const.ts b/frontend/src/lib/const.ts new file mode 100644 index 00000000..8a419a9a --- /dev/null +++ b/frontend/src/lib/const.ts @@ -0,0 +1 @@ +export const DEFAULT_5QI = 9; diff --git a/frontend/src/lib/dtos/subscription.test.ts b/frontend/src/lib/dtos/subscription.test.ts new file mode 100644 index 00000000..ebef264e --- /dev/null +++ b/frontend/src/lib/dtos/subscription.test.ts @@ -0,0 +1,351 @@ +import { test, describe } from "vitest"; +import { Subscription } from "../../api"; +import { + FlowsMapperImpl, + SubscriptionMapperImpl, + SubscriptionDTO, + defaultSubscriptionDTO, +} from "./subscription"; +import assert from "node:assert"; + +const defaultSubscription = (): Subscription => ({ + userNumber: 1, + plmnID: "20893", + ueId: "imsi-208930000000001", + AuthenticationSubscription: { + authenticationMethod: "5G_AKA", + sequenceNumber: "000000000023", + authenticationManagementField: "8000", + permanentKey: { + permanentKeyValue: "8baf473f2f8fd09487cccbd7097c6862", + encryptionKey: 0, + encryptionAlgorithm: 0, + }, + milenage: { + op: { + opValue: "", + encryptionKey: 0, + encryptionAlgorithm: 0, + }, + }, + opc: { + opcValue: "8e27b6af0e692e750f32667a3b14605d", + encryptionKey: 0, + encryptionAlgorithm: 0, + }, + }, + AccessAndMobilitySubscriptionData: { + gpsis: ["msisdn-"], + subscribedUeAmbr: { + uplink: "1 Gbps", + downlink: "2 Gbps", + }, + nssai: { + defaultSingleNssais: [ + { + sst: 1, + sd: "010203", + }, + ], + singleNssais: [ + { + sst: 1, + sd: "112233", + }, + ], + }, + }, + SessionManagementSubscriptionData: [ + { + singleNssai: { + sst: 1, + sd: "010203", + }, + dnnConfigurations: { + internet: { + pduSessionTypes: { + defaultSessionType: "IPV4", + allowedSessionTypes: ["IPV4"], + }, + sscModes: { + defaultSscMode: "SSC_MODE_1", + allowedSscModes: ["SSC_MODE_2", "SSC_MODE_3"], + }, + "5gQosProfile": { + "5qi": 9, + arp: { + priorityLevel: 8, + preemptCap: "", + preemptVuln: "", + }, + priorityLevel: 8, + }, + sessionAmbr: { + uplink: "1000 Mbps", + downlink: "1000 Mbps", + }, + staticIpAddress: [], + }, + }, + }, + { + singleNssai: { + sst: 1, + sd: "112233", + }, + dnnConfigurations: { + internet: { + pduSessionTypes: { + defaultSessionType: "IPV4", + allowedSessionTypes: ["IPV4"], + }, + sscModes: { + defaultSscMode: "SSC_MODE_1", + allowedSscModes: ["SSC_MODE_2", "SSC_MODE_3"], + }, + "5gQosProfile": { + "5qi": 8, + arp: { + priorityLevel: 8, + preemptCap: "", + preemptVuln: "", + }, + priorityLevel: 8, + }, + sessionAmbr: { + uplink: "1000 Mbps", + downlink: "1000 Mbps", + }, + staticIpAddress: [], + }, + }, + }, + ], + SmfSelectionSubscriptionData: { + subscribedSnssaiInfos: { + "01010203": { + dnnInfos: [ + { + dnn: "internet", + }, + ], + }, + "01112233": { + dnnInfos: [ + { + dnn: "internet", + }, + ], + }, + }, + }, + AmPolicyData: { + subscCats: ["free5gc"], + }, + SmPolicyData: { + smPolicySnssaiData: { + "01010203": { + snssai: { + sst: 1, + sd: "010203", + }, + smPolicyDnnData: { + internet: { + dnn: "internet", + }, + }, + }, + "01112233": { + snssai: { + sst: 1, + sd: "112233", + }, + smPolicyDnnData: { + internet: { + dnn: "internet", + }, + }, + }, + }, + }, + FlowRules: [ + { + filter: "1.1.1.1/32", + precedence: 128, + snssai: "01010203", + dnn: "internet", + qosRef: 1, + }, + { + filter: "1.1.1.1/32", + precedence: 127, + snssai: "01112233", + dnn: "internet", + qosRef: 2, + }, + ], + QosFlows: [ + { + snssai: "01010203", + dnn: "internet", + qosRef: 1, + "5qi": 8, + mbrUL: "208 Mbps", + mbrDL: "208 Mbps", + gbrUL: "108 Mbps", + gbrDL: "108 Mbps", + }, + { + snssai: "01112233", + dnn: "internet", + qosRef: 2, + "5qi": 7, + mbrUL: "407 Mbps", + mbrDL: "407 Mbps", + gbrUL: "207 Mbps", + gbrDL: "207 Mbps", + }, + ], + ChargingDatas: [ + { + snssai: "01010203", + dnn: "", + filter: "", + chargingMethod: "Offline", + quota: "100000", + unitCost: "1", + }, + { + snssai: "01010203", + dnn: "internet", + qosRef: 1, + filter: "1.1.1.1/32", + chargingMethod: "Offline", + quota: "100000", + unitCost: "1", + }, + { + snssai: "01112233", + dnn: "", + filter: "", + chargingMethod: "Online", + quota: "100000", + unitCost: "1", + }, + { + snssai: "01112233", + dnn: "internet", + qosRef: 2, + filter: "1.1.1.1/32", + chargingMethod: "Online", + quota: "5000", + unitCost: "1", + }, + ], +}); + +describe("SubscriptionDTO", ({ extend }) => { + describe("default subscription", () => { + test("build", ({ expect }) => { + const subscriptionBuilder = new SubscriptionMapperImpl(new FlowsMapperImpl()); + const subscription = subscriptionBuilder.mapFromDto(defaultSubscriptionDTO()); + assert.deepEqual(JSON.parse(JSON.stringify(subscription)), defaultSubscription()); + }); + + test("parse", ({ expect }) => { + const subscriptionBuilder = new SubscriptionMapperImpl(new FlowsMapperImpl()); + const subscription = subscriptionBuilder.mapFromSubscription(defaultSubscription()); + assert.deepEqual(JSON.parse(JSON.stringify(subscription)), defaultSubscriptionDTO()); + }); + }); + + describe("subscription with up security", () => { + const subscriptionWithUpSecurity: Subscription = defaultSubscription(); + subscriptionWithUpSecurity.SessionManagementSubscriptionData[0].dnnConfigurations![ + "internet" + ].upSecurity = { + upIntegr: "1 Gbps", + upConfid: "2 Gbps", + }; + + const subscriptionWithUpSecurityDTO: SubscriptionDTO = defaultSubscriptionDTO(); + subscriptionWithUpSecurityDTO.SnssaiConfigurations[0].dnnConfigurations["internet"].upSecurity = + { + upIntegr: "1 Gbps", + upConfid: "2 Gbps", + }; + + test("build", ({ expect }) => { + const subscriptionBuilder = new SubscriptionMapperImpl(new FlowsMapperImpl()); + const subscription = subscriptionBuilder.mapFromDto(subscriptionWithUpSecurityDTO); + expect(subscription).toEqual(subscriptionWithUpSecurity); + }); + + test("parse", () => { + const subscriptionBuilder = new SubscriptionMapperImpl(new FlowsMapperImpl()); + const subscription = subscriptionBuilder.mapFromSubscription(subscriptionWithUpSecurity); + assert.deepEqual( + JSON.parse(JSON.stringify(subscription)), + JSON.parse(JSON.stringify(subscriptionWithUpSecurityDTO)), + ); + }); + }); + + describe("OPc subscription", () => { + const subscriptionWithUpSecurity: Subscription = defaultSubscription(); + subscriptionWithUpSecurity.AuthenticationSubscription.milenage!.op!.opValue = ""; + subscriptionWithUpSecurity.AuthenticationSubscription!.opc!.opcValue = + "8e27b6af0e692e750f32667a3b14605d"; + + const subscriptionWithUpSecurityDTO: SubscriptionDTO = defaultSubscriptionDTO(); + subscriptionWithUpSecurityDTO.auth.operatorCodeType = "OPc"; + subscriptionWithUpSecurityDTO.auth.operatorCode = "8e27b6af0e692e750f32667a3b14605d"; + + test("build", () => { + const subscriptionBuilder = new SubscriptionMapperImpl(new FlowsMapperImpl()); + const subscription = subscriptionBuilder.mapFromDto(subscriptionWithUpSecurityDTO); + assert.deepEqual( + JSON.parse(JSON.stringify(subscription)), + JSON.parse(JSON.stringify(subscriptionWithUpSecurity)), + ); + }); + + test("parse", () => { + const subscriptionBuilder = new SubscriptionMapperImpl(new FlowsMapperImpl()); + const subscription = subscriptionBuilder.mapFromSubscription(subscriptionWithUpSecurity); + assert.deepEqual( + JSON.parse(JSON.stringify(subscription)), + JSON.parse(JSON.stringify(subscriptionWithUpSecurityDTO)), + ); + }); + }); + + describe("OP subscription", () => { + const subscriptionWithUpSecurity: Subscription = defaultSubscription(); + subscriptionWithUpSecurity.AuthenticationSubscription.milenage!.op!.opValue = + "8baf473f2f8fd09487cccbd7097c6862"; + subscriptionWithUpSecurity.AuthenticationSubscription!.opc!.opcValue = ""; + + const subscriptionWithUpSecurityDTO: SubscriptionDTO = defaultSubscriptionDTO(); + subscriptionWithUpSecurityDTO.auth.operatorCodeType = "OP"; + subscriptionWithUpSecurityDTO.auth.operatorCode = "8baf473f2f8fd09487cccbd7097c6862"; + + test("build", () => { + const subscriptionBuilder = new SubscriptionMapperImpl(new FlowsMapperImpl()); + const subscription = subscriptionBuilder.mapFromDto(subscriptionWithUpSecurityDTO); + assert.deepEqual( + JSON.parse(JSON.stringify(subscription)), + JSON.parse(JSON.stringify(subscriptionWithUpSecurity)), + ); + }); + + test("parse", () => { + const subscriptionBuilder = new SubscriptionMapperImpl(new FlowsMapperImpl()); + const subscription = subscriptionBuilder.mapFromSubscription(subscriptionWithUpSecurity); + assert.deepEqual( + JSON.parse(JSON.stringify(subscription)), + JSON.parse(JSON.stringify(subscriptionWithUpSecurityDTO)), + ); + }); + }); +}); diff --git a/frontend/src/lib/dtos/subscription.ts b/frontend/src/lib/dtos/subscription.ts new file mode 100644 index 00000000..397e0009 --- /dev/null +++ b/frontend/src/lib/dtos/subscription.ts @@ -0,0 +1,650 @@ +import { z } from "zod"; +import { + AuthenticationSubscription, + ChargingData, + DnnConfiguration, + FlowRules, + Nssai, + QosFlows, + SessionManagementSubscriptionData, + SubscribedUeAmbr, + Subscription, + UpSecurity, +} from "../../api"; +import { DEFAULT_5QI } from "../const"; + +export const subscriberAuthDTOSchema = z.object({ + authenticationManagementField: z.string().regex(/^[A-Fa-f0-9]{4}$/), // 16 bit hex string + authenticationMethod: z.enum(["5G_AKA", "EAP_AKA_PRIME"]), + sequenceNumber: z.string().regex(/^[A-Fa-f0-9]{12}$/), // 48 bit hex string + permanentKey: z.string(), + operatorCodeType: z.enum(["OP", "OPc"]), + operatorCode: z.string(), +}) + +interface SubscriberAuthDTO { + authenticationManagementField: string; + authenticationMethod: string; + sequenceNumber: string; + permanentKey: string; + operatorCodeType: "OP" | "OPc"; + operatorCode: string; +} + +export const ambrDTOSchema = z.object({ + uplink: z.string(), + downlink: z.string(), +}) + +interface AmbrDTO { + uplink: string; + downlink: string; +} + +export const chargingDataDTOSchema = z.object({ + chargingMethod: z.enum(["Online", "Offline"]), + quota: z.string(), + unitCost: z.string(), +}) + +interface ChargingDataDTO { + chargingMethod: "Online" | "Offline"; + quota: string; + unitCost: string; +} + +export const flowRulesDTOSchema = z.object({ + filter: z.string(), + precedence: z.number(), + "5qi": z.number(), + gbrUL: z.string(), + gbrDL: z.string(), + mbrUL: z.string(), + mbrDL: z.string(), + chargingData: chargingDataDTOSchema, +}) + +interface FlowRulesDTO { + filter: string; + precedence: number; + "5qi": number; + gbrUL: string; + gbrDL: string; + mbrUL: string; + mbrDL: string; + chargingData: ChargingDataDTO; +} + +export const upSecurityDTOSchema = z.object({ + upIntegr: z.string(), + upConfid: z.string(), +}) + +interface UpSecurityDTO { + upIntegr: string; + upConfid: string; +} + +export const DnnConfigurationDTOSchema = z.object({ + default5qi: z.number(), + sessionAmbr: ambrDTOSchema, + enableStaticIpv4Address: z.boolean(), + staticIpv4Address: z.string().optional(), + flowRules: z.array(flowRulesDTOSchema), + upSecurity: upSecurityDTOSchema.optional(), +}) + +interface DnnConfigurationDTO { + default5qi: number; + sessionAmbr: AmbrDTO; + enableStaticIpv4Address: boolean; + staticIpv4Address?: string; + flowRules: FlowRulesDTO[]; + upSecurity?: UpSecurityDTO; +} + + +export const SnssaiConfigurationDTOSchema = z.object({ + sst: z.number(), + sd: z.string().optional(), + isDefault: z.boolean(), + chargingData: chargingDataDTOSchema, + dnnConfigurations: z.record(DnnConfigurationDTOSchema), +}); + +interface SnssaiConfiurationDTO { + sst: number; + sd: string; + isDefault: boolean; + chargingData: ChargingDataDTO; + dnnConfigurations: { [key: string]: DnnConfigurationDTO }; +} + + +export const subscriptionDTOSchema = z.object({ + userNumber: z.number().positive(), + ueId: z.string().length(20).startsWith("imsi-"), + plmnID: z.string().length(5), + gpsi: z.string().optional(), + auth: subscriberAuthDTOSchema, + subscribedUeAmbr: ambrDTOSchema, + SnssaiConfigurations: z.array(SnssaiConfigurationDTOSchema), +}) + +interface SubscriptionDTO { + userNumber: number; + ueId: string; + plmnID: string; + gpsi?: string; + auth: SubscriberAuthDTO; + subscribedUeAmbr: AmbrDTO; + SnssaiConfigurations: SnssaiConfiurationDTO[]; +} + +interface FlowsDTO { + flowRules: FlowRules[]; + qosFlows: QosFlows[]; + chargingDatas: ChargingData[]; +} + +interface FlowsMapper { + map(subscription: SubscriptionDTO): FlowsDTO; +} + +class FlowsMapperImpl implements FlowsMapper { + refNumber: number = 1; + flowRules: FlowRules[] = []; + qosFlows: QosFlows[] = []; + chargingDatas: ChargingData[] = []; + + private buildDnns(subscription: SubscriptionDTO): { + snssai: string; + dnn: string; + sliceCharingData: ChargingDataDTO; + flowRules: FlowRulesDTO[]; + }[] { + return subscription.SnssaiConfigurations.reduce( + (acc, s) => { + const snssai = s.sst.toString().padStart(2, "0") + s.sd; + const dnns = Object.entries(s.dnnConfigurations).map(([dnn, dnnConfig]) => ({ + snssai: snssai, + dnn: dnn, + sliceCharingData: s.chargingData, + flowRules: dnnConfig.flowRules, + })); + dnns.forEach((dnn) => acc.push(dnn)); + return acc; + }, + [] as { + snssai: string; + dnn: string; + sliceCharingData: ChargingDataDTO; + flowRules: FlowRulesDTO[]; + }[], + ); + } + + map(subscription: SubscriptionDTO): FlowsDTO { + const dnns = this.buildDnns(subscription); + + dnns.forEach((dnn) => { + const snssai = dnn.snssai; + + this.chargingDatas.push({ + ...dnn.sliceCharingData, + snssai: snssai, + dnn: "", + filter: "", + }); + + dnn.flowRules.forEach((flow) => { + const qosRef = this.refNumber++; + + this.flowRules.push({ + filter: flow.filter, + precedence: flow.precedence, + snssai: snssai, + dnn: dnn.dnn, + qosRef, + }); + + this.qosFlows.push({ + snssai: snssai, + dnn: dnn.dnn, + qosRef, + "5qi": flow["5qi"], + mbrUL: flow.mbrUL, + mbrDL: flow.mbrDL, + gbrUL: flow.gbrUL, + gbrDL: flow.gbrDL, + }); + + this.chargingDatas.push({ + ...flow.chargingData, + snssai: snssai, + dnn: dnn.dnn, + filter: flow.filter, + qosRef, + }); + }); + }); + + return { + flowRules: this.flowRules, + qosFlows: this.qosFlows, + chargingDatas: this.chargingDatas, + }; + } +} + +interface SubscriptionMapper { + mapFromDto(subscription: SubscriptionDTO): Subscription; + mapFromSubscription(subscription: Subscription): SubscriptionDTO; +} + +class SubscriptionMapperImpl implements SubscriptionMapper { + constructor(private readonly flowsBuilder: FlowsMapper) {} + + mapFromDto(subscription: SubscriptionDTO): Subscription { + const flows = this.flowsBuilder.map(subscription); + + return { + userNumber: subscription.userNumber, + ueId: subscription.ueId, + plmnID: subscription.plmnID, + AuthenticationSubscription: this.buildSubscriberAuth(subscription.auth), + AccessAndMobilitySubscriptionData: { + gpsis: [`msisdn-${subscription.gpsi ?? ""}`], + subscribedUeAmbr: this.buildSubscriberAmbr(subscription.subscribedUeAmbr), + nssai: { + defaultSingleNssais: subscription.SnssaiConfigurations.filter((s) => s.isDefault).map( + (s) => this.buildNssai(s), + ), + singleNssais: subscription.SnssaiConfigurations.filter((s) => !s.isDefault).map((s) => + this.buildNssai(s), + ), + }, + }, + + SessionManagementSubscriptionData: subscription.SnssaiConfigurations.map((s) => + this.buildSessionManagementSubscriptionData(s), + ), + + SmfSelectionSubscriptionData: { + subscribedSnssaiInfos: Object.fromEntries( + subscription.SnssaiConfigurations.map((s) => [ + s.sst.toString().padStart(2, "0") + s.sd, + { + dnnInfos: Object.keys(s.dnnConfigurations).map((dnn) => ({ + dnn: dnn, + })), + }, + ]), + ), + }, + + AmPolicyData: { + subscCats: ["free5gc"], + }, + + SmPolicyData: { + smPolicySnssaiData: Object.fromEntries( + subscription.SnssaiConfigurations.map((s) => [ + s.sst.toString().padStart(2, "0") + s.sd, + { + snssai: this.buildNssai(s), + smPolicyDnnData: Object.fromEntries( + Object.keys(s.dnnConfigurations).map((dnn) => [dnn, { dnn: dnn }]), + ), + }, + ]), + ), + }, + + FlowRules: flows.flowRules, + QosFlows: flows.qosFlows, + ChargingDatas: flows.chargingDatas, + }; + } + + mapFromSubscription(subscription: Subscription): SubscriptionDTO { + return { + userNumber: 1, + ueId: subscription.ueId, + plmnID: subscription.plmnID, + gpsi: subscription.AccessAndMobilitySubscriptionData.gpsis?.[0].slice(7) ?? "", + auth: { + authenticationManagementField: + subscription.AuthenticationSubscription.authenticationManagementField ?? "", + authenticationMethod: subscription.AuthenticationSubscription.authenticationMethod, + sequenceNumber: subscription.AuthenticationSubscription.sequenceNumber, + permanentKey: subscription.AuthenticationSubscription.permanentKey.permanentKeyValue, + operatorCodeType: subscription.AuthenticationSubscription.milenage?.op?.opValue + ? "OP" + : "OPc", + operatorCode: subscription.AuthenticationSubscription.milenage?.op?.opValue + ? subscription.AuthenticationSubscription.milenage.op.opValue + : subscription.AuthenticationSubscription.opc?.opcValue ?? "", + }, + subscribedUeAmbr: { + uplink: subscription.AccessAndMobilitySubscriptionData.subscribedUeAmbr?.uplink ?? "", + downlink: subscription.AccessAndMobilitySubscriptionData.subscribedUeAmbr?.downlink ?? "", + }, + SnssaiConfigurations: subscription.SessionManagementSubscriptionData.map((s) => ({ + sst: s.singleNssai.sst, + sd: s.singleNssai.sd ?? "", + isDefault: this.snssaiIsDefault(s.singleNssai, subscription), + chargingData: this.findSliceChargingData(s.singleNssai, subscription), + dnnConfigurations: Object.fromEntries( + Object.entries(s.dnnConfigurations ?? {}).map(([key, value]) => [ + key, + { + default5qi: value["5gQosProfile"]?.["5qi"] ?? DEFAULT_5QI, + sessionAmbr: { + uplink: value.sessionAmbr?.uplink ?? "", + downlink: value.sessionAmbr?.downlink ?? "", + }, + enableStaticIpv4Address: value.staticIpAddress?.length !== 0, + flowRules: this.parseDnnFlowRules(s.singleNssai, key, subscription), + upSecurity: value.upSecurity, + } satisfies DnnConfigurationDTO, + ]), + ), + })), + }; + } + + private snssaiIsDefault(nssai: Nssai, subscription: Subscription): boolean { + return ( + subscription.AccessAndMobilitySubscriptionData.nssai?.defaultSingleNssais?.some( + (n) => n.sst === nssai.sst && n.sd === nssai.sd, + ) ?? false + ); + } + + private findSliceChargingData(nssai: Nssai, subscription: Subscription): ChargingDataDTO { + const charingData = subscription.ChargingDatas.find((c) => { + if (c.dnn !== "" || c.filter !== "") { + return false; + } + + return c.snssai === nssai.sst.toString().padStart(2, "0") + nssai.sd; + }); + + return { + chargingMethod: charingData?.chargingMethod === "Online" ? "Online" : "Offline", + quota: charingData?.quota ?? "", + unitCost: charingData?.unitCost ?? "", + }; + } + + private parseDnnFlowRules( + snssai: Nssai, + dnn: string, + subscription: Subscription, + ): FlowRulesDTO[] { + const qosFlows = subscription.QosFlows.filter( + (f) => f.dnn === dnn && f.snssai === snssai.sst.toString().padStart(2, "0") + snssai.sd, + ); + + return qosFlows.map((f) => { + const flowRule = subscription.FlowRules.find((r) => r.qosRef === f.qosRef); + const chargingData = subscription.ChargingDatas.find((c) => c.qosRef === f.qosRef); + + return { + filter: flowRule?.filter ?? "", + precedence: flowRule?.precedence ?? 0, + "5qi": f["5qi"] ?? DEFAULT_5QI, + gbrUL: f.gbrUL ?? "", + gbrDL: f.gbrDL ?? "", + mbrUL: f.mbrUL ?? "", + mbrDL: f.mbrDL ?? "", + chargingData: { + chargingMethod: chargingData?.chargingMethod === "Online" ? "Online" : "Offline", + quota: chargingData?.quota ?? "", + unitCost: chargingData?.unitCost ?? "", + }, + } + }); + } + + private buildSubscriberAuth(data: SubscriberAuthDTO): AuthenticationSubscription { + return { + authenticationMethod: data.authenticationMethod, + permanentKey: { + permanentKeyValue: data.permanentKey, + encryptionKey: 0, + encryptionAlgorithm: 0, + }, + sequenceNumber: data.sequenceNumber, + authenticationManagementField: data.authenticationManagementField, + milenage: + data.operatorCodeType === "OP" + ? { + op: { + opValue: data.operatorCode, + encryptionKey: 0, + encryptionAlgorithm: 0, + }, + } + : { op: { opValue: "", encryptionKey: 0, encryptionAlgorithm: 0 } }, + opc: + data.operatorCodeType === "OPc" + ? { opcValue: data.operatorCode, encryptionKey: 0, encryptionAlgorithm: 0 } + : { opcValue: "", encryptionKey: 0, encryptionAlgorithm: 0 }, + }; + } + + buildSubscriberAmbr(data: AmbrDTO): SubscribedUeAmbr { + return { + uplink: data.uplink, + downlink: data.downlink, + }; + } + + buildNssai(data: SnssaiConfiurationDTO): Nssai { + return { + sst: data.sst, + sd: data.sd, + }; + } + + buildSessionManagementSubscriptionData( + data: SnssaiConfiurationDTO, + ): SessionManagementSubscriptionData { + return { + singleNssai: { + sst: data.sst, + sd: data.sd, + }, + dnnConfigurations: Object.fromEntries( + Object.entries(data.dnnConfigurations).map(([key, value]) => [ + key, + this.buildDnnConfiguration(value), + ]), + ), + }; + } + + buildDnnConfiguration(data: DnnConfigurationDTO): DnnConfiguration { + return { + pduSessionTypes: { + defaultSessionType: "IPV4", + allowedSessionTypes: ["IPV4"], + }, + sscModes: { + defaultSscMode: "SSC_MODE_1", + allowedSscModes: ["SSC_MODE_2", "SSC_MODE_3"], + }, + "5gQosProfile": { + "5qi": data.default5qi, + arp: { + priorityLevel: 8, + preemptCap: "", + preemptVuln: "", + }, + priorityLevel: 8, + }, + sessionAmbr: this.buildSubscriberAmbr(data.sessionAmbr), + staticIpAddress: data.enableStaticIpv4Address ? [{ ipv4Addr: data.staticIpv4Address }] : [], + upSecurity: this.buildUpSecurity(data.upSecurity), + }; + } + + buildUpSecurity(data: UpSecurityDTO | undefined): UpSecurity | undefined { + if (!data) { + return undefined; + } + + return { + upIntegr: data.upIntegr, + upConfid: data.upConfid, + }; + } +} + +export const defaultSubscriptionDTO = (): SubscriptionDTO => ({ + userNumber: 1, + ueId: "imsi-208930000000001", + plmnID: "20893", + gpsi: "", + auth: { + authenticationManagementField: "8000", + authenticationMethod: "5G_AKA", + sequenceNumber: "000000000023", + permanentKey: "8baf473f2f8fd09487cccbd7097c6862", + operatorCodeType: "OPc", + operatorCode: "8e27b6af0e692e750f32667a3b14605d", + }, + subscribedUeAmbr: { + uplink: "1 Gbps", + downlink: "2 Gbps", + }, + SnssaiConfigurations: [ + { + sst: 1, + sd: "010203", + isDefault: true, + chargingData: { + chargingMethod: "Offline", + quota: "100000", + unitCost: "1", + }, + dnnConfigurations: { + internet: { + enableStaticIpv4Address: false, + default5qi: 9, + sessionAmbr: { + uplink: "1000 Mbps", + downlink: "1000 Mbps", + }, + flowRules: [ + { + filter: "1.1.1.1/32", + precedence: 128, + "5qi": 8, + gbrUL: "108 Mbps", + gbrDL: "108 Mbps", + mbrUL: "208 Mbps", + mbrDL: "208 Mbps", + chargingData: { + chargingMethod: "Offline", + quota: "100000", + unitCost: "1", + }, + }, + ], + }, + }, + }, + { + sst: 1, + sd: "112233", + isDefault: false, + chargingData: { + chargingMethod: "Online", + quota: "100000", + unitCost: "1", + }, + dnnConfigurations: { + internet: { + enableStaticIpv4Address: false, + default5qi: 8, + sessionAmbr: { + uplink: "1000 Mbps", + downlink: "1000 Mbps", + }, + flowRules: [ + { + filter: "1.1.1.1/32", + precedence: 127, + "5qi": 7, + gbrUL: "207 Mbps", + gbrDL: "207 Mbps", + mbrUL: "407 Mbps", + mbrDL: "407 Mbps", + chargingData: { + chargingMethod: "Online", + quota: "5000", + unitCost: "1", + }, + }, + ], + }, + }, + }, + ], +}); + +export const defaultSnssaiConfiguration = (): SnssaiConfiurationDTO => ({ + sst: 1, + sd: "", + isDefault: false, + chargingData: { + chargingMethod: "Offline", + quota: "100000", + unitCost: "1", + }, + dnnConfigurations: { internet: defaultDnnConfig() }, +}); + +export const defaultDnnConfig = (): DnnConfigurationDTO => ({ + default5qi: DEFAULT_5QI, + sessionAmbr: { + uplink: "1000 Mbps", + downlink: "1000 Mbps", + }, + enableStaticIpv4Address: false, + staticIpv4Address: "", + flowRules: [defaultFlowRule()], + upSecurity: undefined, +}); + +export const defaultFlowRule = (): FlowRulesDTO => ({ + filter: "1.1.1.1/32", + precedence: 128, + "5qi": 9, + gbrUL: "208 Mbps", + gbrDL: "208 Mbps", + mbrUL: "108 Mbps", + mbrDL: "108 Mbps", + chargingData: { + chargingMethod: "Online", + quota: "10000", + unitCost: "1", + }, +}); + +export const defaultUpSecurity = (): UpSecurityDTO => ({ + upIntegr: "NOT_NEEDED", + upConfid: "NOT_NEEDED", +}); + +export { + type SubscriptionDTO, + type FlowRulesDTO, + type SnssaiConfiurationDTO, + type DnnConfigurationDTO, + FlowsMapperImpl, + SubscriptionMapperImpl, +}; diff --git a/frontend/src/lib/schemas/subscription.ts b/frontend/src/lib/schemas/subscription.ts new file mode 100644 index 00000000..3f1bec2f --- /dev/null +++ b/frontend/src/lib/schemas/subscription.ts @@ -0,0 +1,265 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* Auto generated by https://transform.tools/typescript-to-zod */ +/* NOTE: Contraints are manually added */ + +import { z } from "zod"; + +export const aNInformationSchema = z.object({ + IPAddress: z.string(), + TEID: z.number(), +}); + +export const amPolicyDataSchema = z.object({ + subscCats: z.array(z.string()).optional(), +}); + +export const arpSchema = z.object({ + priorityLevel: z.number(), + preemptCap: z.string(), + preemptVuln: z.string(), +}); + +export const chargingDataSchema = z.object({ + snssai: z.string().optional(), + dnn: z.string(), + qosRef: z.number().optional(), + filter: z.string(), + chargingMethod: z.string().optional(), + quota: z.string().optional(), + unitCost: z.string().optional(), + ueId: z.string().optional(), +}); + +export const dnnSchema = z.object({ + dnn: z.string(), +}); + +export const flowChargingRecordSchema = z.object({ + Supi: z.string(), + Snssai: z.string(), + Dnn: z.string(), + Filter: z.string(), + QuotaLeft: z.string(), + Usage: z.string(), + TotalVol: z.string(), + UlVol: z.string(), + DlVol: z.string(), +}); + +export const flowRulesSchema = z.object({ + filter: z.string().optional(), + precedence: z.number().optional(), + snssai: z.string().optional(), + dnn: z.string().optional(), + qosRef: z.number().optional(), +}); + +export const ipAddressSchema = z.object({ + ipv4Addr: z.string().optional(), + ipv6Addr: z.string().optional(), + ipv6Prefix: z.string().optional(), +}); + +export const metaSchema = z.object({ + next: z.string().optional(), + prev: z.string().optional(), + limit: z.number().optional(), + total: z.number().optional(), +}); + +export const milenageOpSchema = z.object({ + opValue: z.string(), + encryptionKey: z.number(), + encryptionAlgorithm: z.number(), +}); + +export const model5gQosProfileSchema = z.object({ + "5qi": z.number(), + arp: arpSchema, + priorityLevel: z.number().optional(), +}); + +export const nssaiSchema = z.object({ + sst: z.number(), + sd: z.string().optional(), +}); + +export const opcSchema = z.object({ + opcValue: z.string(), + encryptionKey: z.number(), + encryptionAlgorithm: z.number(), +}); + +export const pduSessionSchema = z.object({ + Dnn: z.string(), + PduSessionId: z.string(), + Sd: z.string(), + SmContextRef: z.string(), + Sst: z.string(), +}); + +export const pduSessionTypesSchema = z.object({ + defaultSessionType: z.string(), + allowedSessionTypes: z.array(z.string()).optional(), +}); + +export const permanentKeySchema = z.object({ + permanentKeyValue: z.string().regex(/^[A-Fa-f0-9]+$/), // hex string + encryptionKey: z.number(), + encryptionAlgorithm: z.number(), +}); + +export const qosFlowsSchema = z.object({ + snssai: z.string(), + dnn: z.string(), + qosRef: z.number(), + "5qi": z.number(), + mbrUL: z.string().optional(), + mbrDL: z.string().optional(), + gbrUL: z.string().optional(), + gbrDL: z.string().optional(), +}); + +export const sessionAmbrSchema = z.object({ + uplink: z.string(), + downlink: z.string(), +}); + +export const sessionRuleSchema = z.object({ + sessRuleId: z.string(), +}); + +export const smPolicySnssaiSchema = z.object({ + snssai: nssaiSchema, + smPolicyDnnData: z.record(dnnSchema).optional(), +}); + +export const sscModesSchema = z.object({ + defaultSscMode: z.string(), + allowedSscModes: z.array(z.string()).optional(), +}); + +export const subscribedSnssaiInfoSchema = z.object({ + dnnInfos: z.array(dnnSchema), +}); + +export const subscribedUeAmbrSchema = z.object({ + uplink: z.string(), + downlink: z.string(), +}); + +export const subscriberSchema = z.object({ + plmnID: z.string(), + ueId: z.string(), + gpsi: z.string(), +}); + +export const tenantSchema = z.object({ + tenantId: z.string().optional(), + tenantName: z.string(), +}); + +export const tunnelSchema = z.object({ + ANInformation: aNInformationSchema, + DataPathPool: z.any(), + PathIDGenerator: z.any(), +}); + +export const ueContextSchema = z.object({ + AccessType: z.string(), + CmState: z.string(), + Guti: z.string(), + Mcc: z.string(), + Mnc: z.string(), + PduSessions: z.array(pduSessionSchema), + Supi: z.string(), + Tac: z.string(), +}); + +export const upSecuritySchema = z.object({ + upIntegr: z.string(), + upConfid: z.string(), +}); + +export const userSchema = z.object({ + userId: z.string().optional(), + tenantId: z.string().optional(), + email: z.string(), + encryptedPassword: z.string(), +}); + +export const defaultSingleNssaisSchema = z.object({ + defaultSingleNssais: z.array(nssaiSchema), + singleNssais: z.array(nssaiSchema).optional(), +}); + +export const dnnConfigurationSchema = z.object({ + pduSessionTypes: pduSessionTypesSchema, + sscModes: sscModesSchema, + "5gQosProfile": model5gQosProfileSchema.optional(), + sessionAmbr: sessionAmbrSchema.optional(), + staticIpAddress: z.array(ipAddressSchema).optional(), + upSecurity: upSecuritySchema.optional(), +}); + +export const milenageSchema = z.object({ + op: milenageOpSchema.optional(), +}); + +export const pduSessionInfoSchema = z.object({ + AnType: z.string(), + Dnn: z.string(), + LocalSEID: z.string().optional(), + PDUAddress: z.string(), + PDUSessionID: z.string(), + RemoteSEID: z.string().optional(), + Sd: z.string(), + SessionRule: sessionRuleSchema, + Sst: z.string(), + Supi: z.string(), + Tunnel: tunnelSchema, + UpCnxState: z.string(), +}); + +export const sessionManagementSubscriptionDataSchema = z.object({ + singleNssai: nssaiSchema, + dnnConfigurations: z.record(dnnConfigurationSchema).optional(), +}); + +export const smPolicyDataSchema = z.object({ + smPolicySnssaiData: z.record(smPolicySnssaiSchema), +}); + +export const smfSelectionSubscriptionDataSchema = z.object({ + subscribedSnssaiInfos: z.record(subscribedSnssaiInfoSchema).optional(), +}); + +export const accessAndMobilitySubscriptionDataSchema = z.object({ + gpsis: z.array(z.string().startsWith("msisdn-")).optional(), + subscribedUeAmbr: subscribedUeAmbrSchema.optional(), + nssai: defaultSingleNssaisSchema.optional(), +}); + +export const authenticationSubscriptionSchema = z.object({ + authenticationMethod: z.enum(["5G_AKA", "EAP_AKA_PRIME"]), + permanentKey: permanentKeySchema, + sequenceNumber: z.string().regex(/^[A-Fa-f0-9]{12}$/), // 48 bit hex string + authenticationManagementField: z.string().regex(/^[A-Fa-f0-9]{4}$/), // 16 bit hex string + milenage: milenageSchema.optional(), + opc: opcSchema.optional(), +}); + +export const subscriptionSchema = z.object({ + userNumber: z.number().positive().int().optional(), + plmnID: z.string().length(5), + ueId: z.string().length(20).startsWith("imsi-"), + AuthenticationSubscription: authenticationSubscriptionSchema, + AccessAndMobilitySubscriptionData: accessAndMobilitySubscriptionDataSchema, + SessionManagementSubscriptionData: z.array(sessionManagementSubscriptionDataSchema), + SmfSelectionSubscriptionData: smfSelectionSubscriptionDataSchema, + AmPolicyData: amPolicyDataSchema, + SmPolicyData: smPolicyDataSchema, + FlowRules: z.array(flowRulesSchema), + QosFlows: z.array(qosFlowsSchema), + ChargingDatas: z.array(chargingDataSchema), +}); diff --git a/frontend/src/lib/utils.ts b/frontend/src/lib/utils.ts new file mode 100644 index 00000000..32278fbd --- /dev/null +++ b/frontend/src/lib/utils.ts @@ -0,0 +1,3 @@ +export function toHex(v: number | undefined): string { + return ("00" + v?.toString(16).toUpperCase()).substr(-2); +} diff --git a/frontend/src/pages/SubscriberCreate.tsx b/frontend/src/pages/SubscriberCreate.tsx deleted file mode 100644 index 96ac29e6..00000000 --- a/frontend/src/pages/SubscriberCreate.tsx +++ /dev/null @@ -1,2069 +0,0 @@ -import React from "react"; -import { useState, useEffect } from "react"; -import { useNavigate, useParams } from "react-router-dom"; - -import axios from "../axios"; -import { - Subscription, - Nssai, - DnnConfiguration, - AccessAndMobilitySubscriptionData, - QosFlows, - IpAddress, -} from "../api/api"; - -import Dashboard from "../Dashboard"; -import { - Button, - Box, - Card, - Checkbox, - FormControl, - Grid, - InputLabel, - MenuItem, - Select, - SelectChangeEvent, - Table, - TableBody, - TableCell, - TableRow, - TextField, - FormControlLabel, - Switch, -} from "@mui/material"; - -interface VerifyScope { - supi: string; - sd: string; - sst: number; - dnn: string; - ipaddr: string; -} - -interface VerifyResult { - ipaddr: string; - valid: boolean; - cause: string; -} - -import { RawOff } from "@mui/icons-material"; - -let isNewSubscriber = false; - -export default function SubscriberCreate() { - const { id, plmn } = useParams<{ - id: string; - plmn: string; - }>(); - - isNewSubscriber = id === undefined && plmn === undefined; - const navigation = useNavigate(); - - const [data, setData] = useState({ - userNumber: 1, - plmnID: "20893", - ueId: "imsi-208930000000001", - AuthenticationSubscription: { - authenticationMethod: "5G_AKA", - sequenceNumber: "000000000023", - authenticationManagementField: "8000", - permanentKey: { - permanentKeyValue: "8baf473f2f8fd09487cccbd7097c6862", - encryptionKey: 0, - encryptionAlgorithm: 0, - }, - milenage: { - op: { - opValue: "", - encryptionKey: 0, - encryptionAlgorithm: 0, - }, - }, - opc: { - opcValue: "8e27b6af0e692e750f32667a3b14605d", - encryptionKey: 0, - encryptionAlgorithm: 0, - }, - }, - AccessAndMobilitySubscriptionData: { - gpsis: ["msisdn-"], - subscribedUeAmbr: { - uplink: "1 Gbps", - downlink: "2 Gbps", - }, - nssai: { - defaultSingleNssais: [ - { - sst: 1, - sd: "010203", - }, - ], - singleNssais: [ - { - sst: 1, - sd: "112233", - }, - ], - }, - }, - SessionManagementSubscriptionData: [ - { - singleNssai: { - sst: 1, - sd: "010203", - }, - dnnConfigurations: { - internet: { - pduSessionTypes: { - defaultSessionType: "IPV4", - allowedSessionTypes: ["IPV4"], - }, - sscModes: { - defaultSscMode: "SSC_MODE_1", - allowedSscModes: ["SSC_MODE_2", "SSC_MODE_3"], - }, - "5gQosProfile": { - "5qi": 9, - arp: { - priorityLevel: 8, - preemptCap: "", - preemptVuln: "", - }, - priorityLevel: 8, - }, - sessionAmbr: { - uplink: "1000 Mbps", - downlink: "1000 Mbps", - }, - staticIpAddress: [], - }, - }, - }, - { - singleNssai: { - sst: 1, - sd: "112233", - }, - dnnConfigurations: { - internet: { - pduSessionTypes: { - defaultSessionType: "IPV4", - allowedSessionTypes: ["IPV4"], - }, - sscModes: { - defaultSscMode: "SSC_MODE_1", - allowedSscModes: ["SSC_MODE_2", "SSC_MODE_3"], - }, - "5gQosProfile": { - "5qi": 8, - arp: { - priorityLevel: 8, - preemptCap: "", - preemptVuln: "", - }, - priorityLevel: 8, - }, - sessionAmbr: { - uplink: "1000 Mbps", - downlink: "1000 Mbps", - }, - staticIpAddress: [], - }, - }, - }, - ], - SmfSelectionSubscriptionData: { - subscribedSnssaiInfos: { - "01010203": { - dnnInfos: [ - { - dnn: "internet", - }, - ], - }, - "01112233": { - dnnInfos: [ - { - dnn: "internet", - }, - ], - }, - }, - }, - AmPolicyData: { - subscCats: ["free5gc"], - }, - SmPolicyData: { - smPolicySnssaiData: { - "01010203": { - snssai: { - sst: 1, - sd: "010203", - }, - smPolicyDnnData: { - internet: { - dnn: "internet", - }, - }, - }, - "01112233": { - snssai: { - sst: 1, - sd: "112233", - }, - smPolicyDnnData: { - internet: { - dnn: "internet", - }, - }, - }, - }, - }, - FlowRules: [ - { - filter: "1.1.1.1/32", - precedence: 128, - snssai: "01010203", - dnn: "internet", - qosRef: 1, - }, - { - filter: "1.1.1.1/32", - precedence: 127, - snssai: "01112233", - dnn: "internet", - qosRef: 2, - }, - ], - QosFlows: [ - { - snssai: "01010203", - dnn: "internet", - qosRef: 1, - "5qi": 8, - mbrUL: "208 Mbps", - mbrDL: "208 Mbps", - gbrUL: "108 Mbps", - gbrDL: "108 Mbps", - }, - { - snssai: "01112233", - dnn: "internet", - qosRef: 2, - "5qi": 7, - mbrUL: "407 Mbps", - mbrDL: "407 Mbps", - gbrUL: "207 Mbps", - gbrDL: "207 Mbps", - }, - ], - ChargingDatas: [ - { - snssai: "01010203", - dnn: "", - filter: "", - chargingMethod: "Offline", - quota: "100000", - unitCost: "1", - }, - { - snssai: "01010203", - dnn: "internet", - qosRef: 1, - filter: "1.1.1.1/32", - chargingMethod: "Offline", - quota: "100000", - unitCost: "1", - }, - { - snssai: "01112233", - dnn: "", - filter: "", - chargingMethod: "Online", - quota: "100000", - unitCost: "1", - }, - { - snssai: "01112233", - dnn: "internet", - qosRef: 2, - filter: "1.1.1.1/32", - chargingMethod: "Online", - quota: "5000", - unitCost: "1", - }, - ], - }); - const [opcType, setOpcType] = useState("OPc"); - const [opcValue, setOpcValue] = useState("8e27b6af0e692e750f32667a3b14605d"); - const [dnnName, setDnnName] = useState([]); - - if (!isNewSubscriber) { - useEffect(() => { - axios.get("/api/subscriber/" + id + "/" + plmn).then((res) => { - console.log('loaded existing subscriber', res.data); - setData(res.data); - }); - }, [id]); - } - function toHex(v: number | undefined): string { - return ("00" + v?.toString(16).toUpperCase()).substr(-2); - } - - const nssai2KeyString = (nssai: Nssai) => { - return toHex(nssai.sst) + nssai.sd; - }; - - const supiIncrement = (supi: string): string => { - const imsi = supi.split("-", 2); - if (imsi.length !== 2) { - return supi; - } - let number = Number(imsi[1]); - number += 1; - return "imsi-" + number; - }; - - const onCreate = () => { - if (data.SessionManagementSubscriptionData === undefined) { - alert("Please add at least one S-NSSAI"); - return; - } - for (let i = 0; i < data.SessionManagementSubscriptionData!.length; i++) { - const nssai = data.SessionManagementSubscriptionData![i]; - const key = nssai2KeyString(nssai.singleNssai!); - Object.keys(nssai.dnnConfigurations!).map((dnn) => { - if (data.SmfSelectionSubscriptionData!.subscribedSnssaiInfos![key] === undefined) { - data.SmfSelectionSubscriptionData!.subscribedSnssaiInfos![key] = { - dnnInfos: [{ dnn: dnn }], - }; - } else { - data.SmfSelectionSubscriptionData!.subscribedSnssaiInfos![key].dnnInfos!.push({ - dnn: dnn, - }); - } - if (data.SmPolicyData!.smPolicySnssaiData![key] === undefined) { - data.SmPolicyData!.smPolicySnssaiData![key] = { - snssai: nssai.singleNssai, - smPolicyDnnData: {}, - }; - } - data.SmPolicyData!.smPolicySnssaiData![key].smPolicyDnnData![dnn] = { - dnn: dnn, - }; - }); - } - // Iterate subscriber data number. - let supi = data.ueId!; - for (let i = 0; i < data.userNumber!; i++) { - data.ueId = supi; - axios - .post("/api/subscriber/" + data.ueId + "/" + data.plmnID, data) - .then(() => { - navigation("/subscriber"); - }) - .catch((err) => { - if (err.response) { - if (err.response.data.cause) { - alert(err.response.data.cause); - } else { - alert(err.response.data); - } - } else { - alert(err.message); - } - return; - }); - supi = supiIncrement(supi); - } - }; - - const onUpdate = () => { - data.SmfSelectionSubscriptionData = { - subscribedSnssaiInfos: {}, - }; - data.SmPolicyData = { - smPolicySnssaiData: {}, - }; - for (let i = 0; i < data.SessionManagementSubscriptionData!.length; i++) { - const nssai = data.SessionManagementSubscriptionData![i]; - const key = nssai2KeyString(nssai.singleNssai!); - if (nssai.dnnConfigurations !== undefined) { - Object.keys(nssai.dnnConfigurations!).map((dnn) => { - if (data.SmfSelectionSubscriptionData!.subscribedSnssaiInfos![key] === undefined) { - data.SmfSelectionSubscriptionData!.subscribedSnssaiInfos![key] = { - dnnInfos: [{ dnn: dnn }], - }; - } else { - data.SmfSelectionSubscriptionData!.subscribedSnssaiInfos![key].dnnInfos!.push({ - dnn: dnn, - }); - } - if (data.SmPolicyData!.smPolicySnssaiData![key] === undefined) { - data.SmPolicyData!.smPolicySnssaiData![key] = { - snssai: nssai.singleNssai, - smPolicyDnnData: {}, - }; - } - data.SmPolicyData!.smPolicySnssaiData![key].smPolicyDnnData![dnn] = { - dnn: dnn, - }; - }); - } - } - axios - .put("/api/subscriber/" + data.ueId + "/" + data.plmnID, data) - .then(() => { - navigation("/subscriber/" + data.ueId + "/" + data.plmnID); - }) - .catch((err) => { - if (err.response) { - if (err.response.data.cause) { - alert(err.response.data.cause); - } else { - alert(err.response.data); - } - } else { - alert(err.message); - } - }); - }; - - const onSnssai = () => { - if ( - data.SessionManagementSubscriptionData === undefined || - data.SessionManagementSubscriptionData!.length === 0 - ) { - data.SessionManagementSubscriptionData = [ - { - singleNssai: { - sst: 1, - sd: "010203", - }, - dnnConfigurations: {}, - }, - ]; - setData({ ...data }); - } else { - data.SessionManagementSubscriptionData.push({ - singleNssai: { - sst: 1, - }, - dnnConfigurations: {}, - }); - setData({ ...data }); - } - }; - - const onSnssaiDelete = (index: number, snssaiToDelete: string) => { - if (data.SessionManagementSubscriptionData !== undefined) { - console.log("delete", snssaiToDelete); - - // Remove charging data if found - data.ChargingDatas = data!.ChargingDatas!.filter( - (chargingData) => chargingData.snssai !== snssaiToDelete, - ); - - data.SessionManagementSubscriptionData.splice(index, 1); - setData({ ...data }); - } - }; - - const onDnnAdd = (index: number) => { - if (data.SessionManagementSubscriptionData !== undefined) { - const name = dnnName[index]; - if (name === undefined || name === "") { - return; - } - // TODO: add charging rule for this DNN - const session = data.SessionManagementSubscriptionData![index]; - session.dnnConfigurations![name] = { - pduSessionTypes: { - defaultSessionType: "IPV4", - allowedSessionTypes: ["IPV4"], - }, - sscModes: { - defaultSscMode: "SSC_MODE_1", - allowedSscModes: ["SSC_MODE_2", "SSC_MODE_3"], - }, - "5gQosProfile": { - "5qi": 9, - arp: { - priorityLevel: 8, - preemptCap: "", - preemptVuln: "", - }, - priorityLevel: 8, - }, - sessionAmbr: { - uplink: "", - downlink: "", - }, - }; - setData({ ...data }); - dnnName[index] = ""; - setDnnName({ ...dnnName }); - } - }; - - const onDnnDelete = (index: number, dnn: string, slice: string) => { - if (data.SessionManagementSubscriptionData !== undefined) { - delete data.SessionManagementSubscriptionData![index].dnnConfigurations![dnn]; - setData({ ...data }); - } - // Remove all flow-based charging rule in this DNN - if (data.ChargingDatas !== undefined) { - for (let i = 0; i < data.ChargingDatas!.length; i++) { - if (data.ChargingDatas![i].dnn === dnn && data.ChargingDatas![i].snssai === slice) { - data.ChargingDatas!.splice(i, 1); - i--; - } - } - } - setData({ ...data }); - }; - - const onFlowRulesDelete = (dnn: string, flowKey: string, qosRef: number | undefined) => { - if (data.FlowRules !== undefined) { - for (let i = 0; i < data.FlowRules!.length; i++) { - if ( - data.FlowRules![i].dnn === dnn && - data.FlowRules![i].snssai === flowKey && - data.FlowRules![i].qosRef === qosRef - ) { - data.FlowRules!.splice(i, 1); - i--; - } - } - } - if (data.QosFlows !== undefined) { - for (let i = 0; i < data.QosFlows!.length; i++) { - if ( - data.QosFlows![i].dnn === dnn && - data.QosFlows![i].snssai === flowKey && - data.QosFlows![i].qosRef === qosRef - ) { - data.QosFlows!.splice(i, 1); - i--; - } - } - } - if (data.ChargingDatas !== undefined) { - for (let i = 0; i < data.ChargingDatas!.length; i++) { - if (data.ChargingDatas![i].qosRef === qosRef) { - data.ChargingDatas!.splice(i, 1); - i--; - } - } - } - setData({ ...data }); - }; - - const onUpSecurity = (dnn: DnnConfiguration | undefined) => { - if (dnn !== undefined) { - dnn.upSecurity = { - upIntegr: "NOT_NEEDED", - upConfid: "NOT_NEEDED", - }; - } - setData({ ...data }); - }; - - function selectQosRef(): number { - const UsedQosRef = []; - for (let i = 0; i < data.QosFlows!.length; i++) { - UsedQosRef.push(data.QosFlows![i]!.qosRef); - } - for (let i = 1; i < 256; i++) { - if (!UsedQosRef.includes(i)) { - return i; - } - } - - window.alert("Cannot select qosRef in 1~128."); - return -1; - } - - function select5Qi(dnn: string, snssai: Nssai): number { - const sstsd = toHex(snssai.sst) + snssai.sd!; - const filteredQosFlows = data.QosFlows!.filter( - (qos) => qos.dnn === dnn && qos.snssai === sstsd, - ); - const Used5Qi = []; - for (let i = 0; i < filteredQosFlows.length; i++) { - Used5Qi.push(filteredQosFlows[i]["5qi"]); - } - Used5Qi.sort((a, b) => a! - b!); - if (Used5Qi[Used5Qi.length - 1]! < 255) { - return Used5Qi[Used5Qi.length - 1]! + 1; - } - return 255; - } - - const onFlowRulesAdd = (dnn: string, snssai: Nssai) => { - const sstSd = toHex(snssai.sst) + snssai.sd!; - let filter = "8.8.8.8/32"; - for (;;) { - let flag = false; - for (let i = 0; i < data.FlowRules!.length; i++) { - if (filter === data.FlowRules![i]!.filter) { - const c = Math.floor(Math.random() * 256); - const d = Math.floor(Math.random() * 256); - filter = "10.10." + c.toString() + "." + d.toString() + "/32"; - flag = true; - break; - } - } - - if (!flag) break; - } - - const selected5Qi = select5Qi(dnn, snssai); - const selectedQosRef = selectQosRef(); - data.FlowRules!.push({ - filter: filter, - precedence: 127, - snssai: sstSd, - dnn: dnn, - qosRef: selectedQosRef, - }); - - data.QosFlows!.push({ - snssai: sstSd, - dnn: dnn, - qosRef: selectedQosRef, - "5qi": selected5Qi, - mbrUL: "200 Mbps", - mbrDL: "200 Mbps", - gbrUL: "100 Mbps", - gbrDL: "100 Mbps", - }); - - data.ChargingDatas!.push({ - snssai: sstSd, - dnn: dnn, - qosRef: selectedQosRef, - filter: filter, - chargingMethod: "Online", - quota: "10000", - unitCost: "1", - }); - - setData({ ...data }); - }; - - const onUpSecurityDelete = (dnn: DnnConfiguration) => { - dnn.upSecurity = undefined; - setData({ ...data }); - }; - - const isDefaultNssai = (nssai: Nssai) => { - if (nssai === undefined || data.AccessAndMobilitySubscriptionData === undefined) { - return false; - } else { - for ( - let i = 0; - i < data.AccessAndMobilitySubscriptionData!.nssai!.defaultSingleNssais!.length; - i++ - ) { - const defaultNssai = data.AccessAndMobilitySubscriptionData!.nssai!.defaultSingleNssais![i]; - if (defaultNssai.sd === nssai.sd && defaultNssai.sst === nssai.sst) { - return true; - } - } - return false; - } - }; - - const imsiValue = (imsi: string | undefined) => { - if (imsi === undefined) { - return ""; - } else { - return imsi.replace("imsi-", ""); - } - }; - - const msisdnValue = (subData: AccessAndMobilitySubscriptionData | undefined) => { - if (subData === undefined) { - return ""; - } else { - if (subData.gpsis !== undefined && subData.gpsis!.length !== 0) { - return subData.gpsis[0].replace("msisdn-", ""); - } else { - return ""; - } - } - }; - - const handleChangeUserNumber = ( - event: React.ChangeEvent, - ): void => { - if (event.target.value === undefined) { - setData({ ...data, userNumber: undefined }); - } else { - const userNumber = Number(event.target.value); - if (userNumber >= 1) { - setData({ ...data, userNumber: Number(event.target.value) }); - } - } - }; - - const handleChangePlmnId = ( - event: React.ChangeEvent, - ): void => { - setData({ ...data, plmnID: event.target.value }); - }; - - const handleChangeUeId = ( - event: React.ChangeEvent, - ): void => { - setData({ ...data, ueId: "imsi-" + event.target.value }); - }; - - const handleChangeMsisdn = ( - event: React.ChangeEvent, - ): void => { - setData({ - ...data, - AccessAndMobilitySubscriptionData: { - ...data.AccessAndMobilitySubscriptionData, - gpsis: ["msisdn-" + event.target.value], - }, - }); - }; - - const handleChangeAuthenticationManagementField = ( - event: React.ChangeEvent, - ): void => { - setData({ - ...data, - AuthenticationSubscription: { - ...data.AuthenticationSubscription, - authenticationManagementField: event.target.value, - }, - }); - }; - - const handleChangeAuthenticationMethod = (event: SelectChangeEvent): void => { - setData({ - ...data, - AuthenticationSubscription: { - ...data.AuthenticationSubscription, - authenticationMethod: event.target.value, - }, - }); - }; - - const handleChangeK = ( - event: React.ChangeEvent, - ): void => { - setData({ - ...data, - AuthenticationSubscription: { - ...data.AuthenticationSubscription, - permanentKey: { - permanentKeyValue: event.target.value, - encryptionKey: 0, - encryptionAlgorithm: 0, - }, - }, - }); - }; - - const handleChangeOperatorCodeType = (event: SelectChangeEvent): void => { - if (event.target.value === "OP") { - setOpcType("OP"); - const tmp = { - ...data, - AuthenticationSubscription: { - ...data.AuthenticationSubscription, - milenage: { - op: { - opValue: opcValue, - encryptionKey: 0, - encryptionAlgorithm: 0, - }, - }, - opc: { - opcValue: "", - encryptionKey: 0, - encryptionAlgorithm: 0, - }, - }, - }; - setData(tmp); - } else { - setOpcType("OPc"); - const tmp = { - ...data, - AuthenticationSubscription: { - ...data.AuthenticationSubscription, - milenage: { - op: { - opValue: "", - encryptionKey: 0, - encryptionAlgorithm: 0, - }, - }, - opc: { - opcValue: opcValue, - encryptionKey: 0, - encryptionAlgorithm: 0, - }, - }, - }; - setData(tmp); - } - }; - - const handleChangeOperatorCodeValue = ( - event: React.ChangeEvent, - ): void => { - setOpcValue(event.target.value); - const auth = data.AuthenticationSubscription; - if (auth !== undefined) { - if (opcType === "OP") { - const tmp = { - ...data, - AuthenticationSubscription: { - ...data.AuthenticationSubscription, - milenage: { - op: { - opValue: event.target.value, - encryptionKey: 0, - encryptionAlgorithm: 0, - }, - }, - opc: { - opcValue: "", - encryptionKey: 0, - encryptionAlgorithm: 0, - }, - }, - }; - setData(tmp); - } else { - const tmp = { - ...data, - AuthenticationSubscription: { - ...data.AuthenticationSubscription, - milenage: { - op: { - opValue: "", - encryptionKey: 0, - encryptionAlgorithm: 0, - }, - }, - opc: { - opcValue: event.target.value, - encryptionKey: 0, - encryptionAlgorithm: 0, - }, - }, - }; - setData(tmp); - } - } - }; - - const handleChangeSQN = ( - event: React.ChangeEvent, - ): void => { - setData({ - ...data, - AuthenticationSubscription: { - ...data.AuthenticationSubscription, - sequenceNumber: event.target.value, - }, - }); - }; - - const handleChangeSubAmbrUplink = ( - event: React.ChangeEvent, - ): void => { - setData({ - ...data, - AccessAndMobilitySubscriptionData: { - ...data.AccessAndMobilitySubscriptionData, - subscribedUeAmbr: { - uplink: event.target.value, - downlink: data.AccessAndMobilitySubscriptionData.subscribedUeAmbr?.downlink ?? "", - }, - }, - }); - }; - - const handleChangeSubAmbrDownlink = ( - event: React.ChangeEvent, - ): void => { - setData({ - ...data, - AccessAndMobilitySubscriptionData: { - ...data.AccessAndMobilitySubscriptionData, - subscribedUeAmbr: { - uplink: data.AccessAndMobilitySubscriptionData.subscribedUeAmbr?.uplink ?? "", - downlink: event.target.value, - }, - }, - }); - }; - - const handleChangeSST = ( - event: React.ChangeEvent, - index: number, - ): void => { - if (event.target.value === "") { - data.SessionManagementSubscriptionData![index].singleNssai!.sst = 0; - } else { - data.SessionManagementSubscriptionData![index].singleNssai!.sst! = Number(event.target.value); - } - setData({ ...data }); - }; - - const handleChangeSD = ( - event: React.ChangeEvent, - index: number, - ): void => { - data.SessionManagementSubscriptionData![index].singleNssai!.sd! = event.target.value; - setData({ ...data }); - }; - - const handleChangeDefaultSnssai = ( - _event: React.ChangeEvent, - nssai: Nssai | undefined, - ) => { - if (nssai === undefined) { - return; - } - let isDefault = false; - let def = undefined; - if (data.AccessAndMobilitySubscriptionData!.nssai!.defaultSingleNssais !== undefined) { - def = data.AccessAndMobilitySubscriptionData!.nssai!.defaultSingleNssais!; - for (let i = 0; i < def.length; i++) { - if (def[i].sd === nssai.sd && def[i].sst === nssai.sst) { - def.splice(i, 1); - isDefault = true; - } - } - } - let single = undefined; - if (data.AccessAndMobilitySubscriptionData!.nssai!.singleNssais !== undefined) { - single = data.AccessAndMobilitySubscriptionData!.nssai!.singleNssais!; - for (let i = 0; i < single.length; i++) { - if (single[i].sd === nssai.sd && single[i].sst === nssai.sst) { - single.splice(i, 1); - } - } - } - if (isDefault) { - if (single !== undefined) { - single.push(nssai); - } - } else { - if (def !== undefined) { - def.push(nssai); - } - } - data.AccessAndMobilitySubscriptionData!.nssai!.defaultSingleNssais = def ?? []; - data.AccessAndMobilitySubscriptionData!.nssai!.singleNssais = single; - setData({ ...data }); - }; - - const dnnValue = (index: number) => { - return dnnName[index]; - }; - - const handleChangeDNN = ( - event: React.ChangeEvent, - index: number, - ): void => { - dnnName[index] = event.target.value; - setDnnName({ ...dnnName }); - }; - - const handleChangeUplinkAMBR = ( - event: React.ChangeEvent, - index: number, - dnn: string, - ): void => { - data.SessionManagementSubscriptionData![index].dnnConfigurations![dnn].sessionAmbr!.uplink = - event.target.value; - setData({ ...data }); - }; - - const handleChangeDownlinkAMBR = ( - event: React.ChangeEvent, - index: number, - dnn: string, - ): void => { - data.SessionManagementSubscriptionData![index].dnnConfigurations![dnn].sessionAmbr!.downlink = - event.target.value; - setData({ ...data }); - }; - - const handleChangeDefault5QI = ( - event: React.ChangeEvent, - index: number, - dnn: string, - ): void => { - if (event.target.value === "") { - data.SessionManagementSubscriptionData![index].dnnConfigurations![dnn]["5gQosProfile"]![ - "5qi" - ] = 8; - } else { - data.SessionManagementSubscriptionData![index].dnnConfigurations![dnn]["5gQosProfile"]![ - "5qi" - ] = Number(event.target.value); - } - setData({ ...data }); - }; - - const handleChangeStaticIp = ( - event: React.ChangeEvent, - index: number, - dnn: string, - ): void => { - data.SessionManagementSubscriptionData![index].dnnConfigurations![dnn][ - "staticIpAddress" - ]![0].ipv4Addr = event.target.value; - setData({ ...data }); - }; - - const handleChangeFilter = ( - event: React.ChangeEvent, - dnn: string, - flowKey: string, - qosRef: number, - ): void => { - if (data.FlowRules !== undefined) { - for (let i = 0; i < data.FlowRules!.length; i++) { - if ( - data.FlowRules![i].snssai === flowKey && - data.FlowRules![i].dnn === dnn && - data.QosFlows![i].qosRef === qosRef - ) { - data.FlowRules![i].filter = event.target.value; - setData({ ...data }); - } - } - } - if (data.ChargingDatas !== undefined) { - for (let i = 0; i < data.ChargingDatas!.length; i++) { - if ( - data.ChargingDatas![i].snssai === flowKey && - data.ChargingDatas![i].dnn === dnn && - data.ChargingDatas![i].qosRef === qosRef - ) { - data.ChargingDatas![i].filter = event.target.value; - setData({ ...data }); - } - } - } - }; - - const handleChangePrecedence = ( - event: React.ChangeEvent, - dnn: string, - flowKey: string, - qosRef: number, - ): void => { - if (data.FlowRules !== undefined) { - for (let i = 0; i < data.FlowRules!.length; i++) { - if ( - data.FlowRules![i].snssai === flowKey && - data.FlowRules![i].dnn === dnn && - data.QosFlows![i].qosRef === qosRef - ) { - if (event.target.value == "") { - data.FlowRules![i].precedence = undefined; - } else { - data.FlowRules![i].precedence = Number(event.target.value); - } - setData({ ...data }); - } - } - } - }; - - const handleChange5QI = ( - event: React.ChangeEvent, - dnn: string, - flowKey: string, - qosRef: number, - ): void => { - if (data.QosFlows !== undefined) { - for (let i = 0; i < data.QosFlows!.length; i++) { - if ( - data.QosFlows![i].snssai === flowKey && - data.QosFlows![i].dnn === dnn && - data.QosFlows![i].qosRef === qosRef - ) { - if (event.target.value == "") { - data.QosFlows![i]["5qi"] = 8; - } else { - data.QosFlows![i]["5qi"] = Number(event.target.value); - } - setData({ ...data }); - } - } - } - }; - - const handleChangeUplinkGBR = ( - event: React.ChangeEvent, - dnn: string, - flowKey: string, - qosRef: number, - ): void => { - if (data.QosFlows !== undefined) { - for (let i = 0; i < data.QosFlows!.length; i++) { - if ( - data.QosFlows![i].snssai === flowKey && - data.QosFlows![i].dnn === dnn && - data.QosFlows![i].qosRef === qosRef - ) { - data.QosFlows![i].gbrUL = event.target.value; - setData({ ...data }); - } - } - } - }; - - const handleChangeDownlinkGBR = ( - event: React.ChangeEvent, - dnn: string, - flowKey: string, - qosRef: number, - ): void => { - if (data.QosFlows !== undefined) { - for (let i = 0; i < data.QosFlows!.length; i++) { - if ( - data.QosFlows![i].snssai === flowKey && - data.QosFlows![i].dnn === dnn && - data.QosFlows![i].qosRef === qosRef - ) { - data.QosFlows![i].gbrDL = event.target.value; - setData({ ...data }); - } - } - } - }; - - const handleChangeUplinkMBR = ( - event: React.ChangeEvent, - dnn: string, - flowKey: string, - qosRef: number, - ): void => { - if (data.QosFlows !== undefined) { - for (let i = 0; i < data.QosFlows!.length; i++) { - if ( - data.QosFlows![i].snssai === flowKey && - data.QosFlows![i].dnn === dnn && - data.QosFlows![i].qosRef === qosRef - ) { - data.QosFlows![i].mbrUL = event.target.value; - setData({ ...data }); - } - } - } - }; - - const handleChangeDownlinkMBR = ( - event: React.ChangeEvent, - dnn: string, - flowKey: string, - qosRef: number, - ): void => { - if (data.QosFlows !== undefined) { - for (let i = 0; i < data.QosFlows!.length; i++) { - if ( - data.QosFlows![i].snssai === flowKey && - data.QosFlows![i].dnn === dnn && - data.QosFlows![i].qosRef === qosRef - ) { - data.QosFlows![i].mbrDL = event.target.value; - setData({ ...data }); - } - } - } - }; - - const handleChangeChargingMethod = ( - event: SelectChangeEvent, - dnn: string | undefined, - flowKey: string, - filter: string | undefined, - ): void => { - for (let i = 0; i < data.ChargingDatas!.length; i++) { - for (let j = 0; j < data.QosFlows!.length; j++) { - if ( - data.ChargingDatas![i].snssai === flowKey && - data.ChargingDatas![i].dnn === dnn && - data.ChargingDatas![i].filter === filter - ) { - data.ChargingDatas![i]!.chargingMethod = event.target.value; - setData({ ...data }); - } - } - } - }; - - const handleChangeChargingQuota = ( - event: React.ChangeEvent, - dnn: string | undefined, - flowKey: string, - filter: string | undefined, - ): void => { - for (let i = 0; i < data.ChargingDatas!.length; i++) { - for (let j = 0; j < data.QosFlows!.length; j++) { - if ( - data.ChargingDatas![i].snssai === flowKey && - data.ChargingDatas![i].dnn === dnn && - data.ChargingDatas![i].filter === filter - ) { - data.ChargingDatas![i]!.quota = event.target.value; - setData({ ...data }); - } - } - } - }; - - const handleChangeChargingUnitCost = ( - event: React.ChangeEvent, - dnn: string | undefined, - flowKey: string, - filter: string | undefined, - ): void => { - for (let i = 0; i < data.ChargingDatas!.length; i++) { - for (let j = 0; j < data.QosFlows!.length; j++) { - if ( - data.ChargingDatas![i].snssai === flowKey && - data.ChargingDatas![i].dnn === dnn && - data.ChargingDatas![i].filter === filter - ) { - data.ChargingDatas![i]!.unitCost = event.target.value; - setData({ ...data }); - } - } - } - }; - - const handleChangeUpIntegrity = ( - event: SelectChangeEvent, - dnn: DnnConfiguration, - ): void => { - if (dnn.upSecurity !== undefined) { - dnn.upSecurity!.upIntegr = event.target.value; - } - setData({ ...data }); - }; - - const handleChangeUpConfidentiality = ( - event: SelectChangeEvent, - dnn: DnnConfiguration, - ): void => { - if (dnn.upSecurity !== undefined) { - dnn.upSecurity!.upConfid = event.target.value; - } - setData({ ...data }); - }; - - const qosFlow = ( - sstSd: string, - dnn: string, - qosRef: number | undefined, - ): QosFlows | undefined => { - if (data.QosFlows !== undefined) { - for (let i = 0; i < data.QosFlows?.length; i++) { - const qos = data.QosFlows![i]; - if (qos.snssai === sstSd && qos.dnn === dnn && qos.qosRef == qosRef) { - return qos; - } - } - } - }; - - const chargingConfig = (dnn: string | undefined, snssai: Nssai, filter: string | undefined) => { - const flowKey = toHex(snssai.sst) + snssai.sd; - for (let i = 0; i < data.ChargingDatas!.length; i++) { - const chargingData = data.ChargingDatas![i]; - const idPrefix = snssai + "-" + dnn + "-" + chargingData.qosRef + "-"; - const isOnlineCharging = data.ChargingDatas![i].chargingMethod === "Online"; - if ( - chargingData.snssai === flowKey && - chargingData.dnn === dnn && - chargingData.filter === filter - ) { - return ( - <> - - - - - Charging Method - - - - - handleChangeChargingQuota(ev, dnn, flowKey, filter)} - /> - - - handleChangeChargingUnitCost(ev, dnn, flowKey, filter)} - /> - - -
- - ); - } - } - }; - - const flowRule = (dnn: string, snssai: Nssai) => { - const flowKey = toHex(snssai.sst) + snssai.sd; - const idPrefix = flowKey + "-" + dnn + "-"; - if (data.FlowRules !== undefined) { - return data.FlowRules.filter((flow) => flow.dnn === dnn && flow.snssai === flowKey).map( - (flow) => ( -
- - - -

Flow Rules {flow.qosRef}

-
- - - - - -
- - - - - - handleChangeFilter(ev, dnn, flowKey, flow.qosRef!)} - /> - - - handleChangePrecedence(ev, dnn, flowKey, flow.qosRef!)} - /> - - - handleChange5QI(ev, dnn, flowKey, flow.qosRef!)} - /> - - - - - - - handleChangeUplinkGBR(ev, dnn, flowKey, flow.qosRef!)} - /> - - - handleChangeDownlinkGBR(ev, dnn, flowKey, flow.qosRef!)} - /> - - - handleChangeUplinkMBR(ev, dnn, flowKey, flow.qosRef!)} - /> - - - handleChangeDownlinkMBR(ev, dnn, flowKey, flow.qosRef!)} - /> - - - -
- {chargingConfig(dnn, snssai, flow.filter!)} -
-
-
- ), - ); - } - }; - - const upSecurity = (dnn: DnnConfiguration | undefined) => { - if (dnn !== undefined && dnn!.upSecurity !== undefined) { - const security = dnn!.upSecurity!; - return ( -
- - - -

UP Security

-
- - - - - -
- - - - - - - Integrity of UP Security - - - - - - - - - - Confidentiality of UP Security - - - - - -
-
-
-
- ); - } else { - return ( -
- - - - - - - - -
-
- ); - } - }; - - const handleVerifyStaticIp = (sd: string, sst: number, dnn: string, ipaddr: string) => { - const scope: VerifyScope = { - supi: "", - sd: sd, - sst: sst, - dnn: dnn, - ipaddr: ipaddr, - }; - axios.post("/api/verify-staticip", scope).then((res) => { - const result = res.data as VerifyResult; - console.log(result); - if (result["valid"] === true) { - alert("OK\n" + result.ipaddr); - } else { - alert("NO!\nCause: " + result["cause"]); - } - }); - }; - - return ( - {}}> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Authentication Method - - - - - - - - - - Operator Code Type - - - - - - - - - - - - - - - - - - -
-
-

Subscribed UE AMBR

- - - - - - - - - - - - -
-
- {data.SessionManagementSubscriptionData?.map((row, index) => ( -
- - -

S-NSSAI Configuration ({toHex(row.singleNssai!.sst) + row.singleNssai!.sd!})

-
- - - - - -
- - - - - - handleChangeSST(ev, index)} - /> - - - handleChangeSD(ev, index)} - /> - - - - - - Default S-NSSAI - - handleChangeDefaultSnssai(ev, row.singleNssai)} - /> - - - -
- {chargingConfig("", row.singleNssai!, "")} - {row.dnnConfigurations && - Object.keys(row.dnnConfigurations!).map((dnn) => ( -
- - - -

DNN Configurations

-
- - - - - -
- - - - - - {dnn} - - - - - - - handleChangeUplinkAMBR(ev, index, dnn)} - /> - - - handleChangeDownlinkAMBR(ev, index, dnn)} - /> - - - handleChangeDefault5QI(ev, index, dnn)} - /> - - - -
- - - - - { - if (event.target.checked) { - var ipaddr: IpAddress = { ipv4Addr: "10.60.100.1" }; - data.SessionManagementSubscriptionData![ - index - ].dnnConfigurations![dnn]["staticIpAddress"] = [ipaddr]; - } else { - data.SessionManagementSubscriptionData![ - index - ].dnnConfigurations![dnn]["staticIpAddress"] = []; - } - setData({ ...data }); - }} - /> - label="Static IPv4 Address" - /> - - - handleChangeStaticIp(ev, index, dnn)} - /> - - - - - - -
- - {chargingConfig(dnn, row.singleNssai!, undefined)} - - {flowRule(dnn, row.singleNssai!)} - -
- - - - - - - - -
-
- {upSecurity(row.dnnConfigurations![dnn])} -
-
-
- ))} - - - - handleChangeDNN(ev, index)} - /> - - - - - - - - -
-
- ))} -
- - - -
- - {isNewSubscriber ? ( - - ) : ( - - )} - -
- ); -} diff --git a/frontend/src/pages/SubscriberCreate/FormCharingConfig.tsx b/frontend/src/pages/SubscriberCreate/FormCharingConfig.tsx new file mode 100644 index 00000000..de8d98dd --- /dev/null +++ b/frontend/src/pages/SubscriberCreate/FormCharingConfig.tsx @@ -0,0 +1,201 @@ +import { + FormControl, + InputLabel, + MenuItem, + Select, + Table, + TableBody, + TableCell, + TableRow, + TextField, +} from "@mui/material"; +import { useSubscriptionForm } from "../../hooks/subscription-form"; +import { Controller } from "react-hook-form"; + +interface FormCharginConfigProps { + snssaiIndex: number; + dnn?: string; + filterIndex?: number; +} + +function FormSliceChargingConfig({ snssaiIndex }: FormCharginConfigProps) { + const { register, validationErrors, watch, control } = useSubscriptionForm(); + + const isOnlineCharging = + watch(`SnssaiConfigurations.${snssaiIndex}.chargingData.chargingMethod`) === "Online"; + + return ( + + + + + + Charging Method + ( + + )} + /> + + + + + + + + + + + + +
+ ); +} + +function FormFlowChargingConfig({ snssaiIndex, dnn, filterIndex }: FormCharginConfigProps) { + const { register, validationErrors, watch, control } = useSubscriptionForm(); + + if (dnn === undefined) { + throw new Error("dnn is undefined"); + } + if (filterIndex === undefined) { + throw new Error("filterIndex is undefined"); + } + + const isOnlineCharging = + watch( + `SnssaiConfigurations.${snssaiIndex}.dnnConfigurations.${dnn}.flowRules.${filterIndex}.chargingData.chargingMethod`, + ) === "Online"; + + return ( + + + + + + Charging Method + ( + + )} + /> + + + + + + + + + + +
+ ); +} + +export default function FormChargingConfig(props: FormCharginConfigProps) { + if (props.dnn === undefined) { + return ; + } + + return ; +} diff --git a/frontend/src/pages/SubscriberCreate/FormFlowRule.tsx b/frontend/src/pages/SubscriberCreate/FormFlowRule.tsx new file mode 100644 index 00000000..c6c4b8eb --- /dev/null +++ b/frontend/src/pages/SubscriberCreate/FormFlowRule.tsx @@ -0,0 +1,267 @@ +import { + Button, + Box, + Card, + Grid, + Table, + TableBody, + TableCell, + TableRow, + TextField, +} from "@mui/material"; +import type { Nssai } from "../../api"; +import { useSubscriptionForm } from "../../hooks/subscription-form"; +import { toHex } from "../../lib/utils"; +import FormChargingConfig from "./FormCharingConfig"; +import { useFieldArray } from "react-hook-form"; +import { defaultFlowRule } from "../../lib/dtos/subscription"; + +interface FormFlowRuleProps { + snssaiIndex: number; + dnn: string; + snssai: Nssai; +} + +export default function FormFlowRule({ snssaiIndex, dnn, snssai }: FormFlowRuleProps) { + const { register, validationErrors, control } = useSubscriptionForm(); + const { + fields: flowRules, + append: appendFlowRule, + remove: removeFlowRule, + } = useFieldArray({ + control, + name: `SnssaiConfigurations.${snssaiIndex}.dnnConfigurations.${dnn}.flowRules`, + }); + + const flowKey = toHex(snssai.sst) + snssai.sd; + const idPrefix = flowKey + "-" + dnn + "-"; + + return ( + <> + {flowRules.map((flow, index) => ( +
+ + + +

Flow Rules {index + 1}

+
+ + + + + +
+ + + + + + + + + + + + + + + {/* Keep layout aligned*/} + + + + + + + + + + + + + + + + +
+ +
+
+
+ ))} + + + + + + + + +
+ + ); +} diff --git a/frontend/src/pages/SubscriberCreate/FormUpSecurity.tsx b/frontend/src/pages/SubscriberCreate/FormUpSecurity.tsx new file mode 100644 index 00000000..83b9b798 --- /dev/null +++ b/frontend/src/pages/SubscriberCreate/FormUpSecurity.tsx @@ -0,0 +1,161 @@ +import { + Button, + Box, + Card, + FormControl, + Grid, + InputLabel, + MenuItem, + Select, + Table, + TableBody, + TableCell, + TableRow, + SelectChangeEvent, +} from "@mui/material"; +import { Controller } from "react-hook-form"; +import { useSubscriptionForm } from "../../hooks/subscription-form"; +import { defaultUpSecurity } from "../../lib/dtos/subscription"; + +interface FormUpSecurityProps { + sessionIndex: number; + dnnKey: string; +} + +function NoUpSecurity(props: FormUpSecurityProps) { + const { watch, setValue } = useSubscriptionForm(); + + const dnnConfig = watch( + `SnssaiConfigurations.${props.sessionIndex}.dnnConfigurations.${props.dnnKey}`, + ); + + const onUpSecurity = () => { + dnnConfig.upSecurity = defaultUpSecurity(); + + setValue( + `SnssaiConfigurations.${props.sessionIndex}.dnnConfigurations.${props.dnnKey}`, + dnnConfig, + ); + }; + + return ( +
+ + + + + + + + +
+
+ ); +} + +export default function FormUpSecurity(props: FormUpSecurityProps) { + const { register, validationErrors, watch, control, getValues, setValue } = useSubscriptionForm(); + + const dnnConfig = watch( + `SnssaiConfigurations.${props.sessionIndex}.dnnConfigurations.${props.dnnKey}`, + ); + + if (!(dnnConfig.upSecurity !== undefined)) { + return ; + } + + const onUpSecurityDelete = () => { + setValue(`SnssaiConfigurations.${props.sessionIndex}.dnnConfigurations.${props.dnnKey}`, { + ...dnnConfig, + upSecurity: undefined, + }); + }; + + return ( +
+ + + +

UP Security

+
+ + + + + +
+ + + + + + + + Integrity of UP Security + ( + + )} + /> + + + + + + + + + + Confidentiality of UP Security + ( + + )} + /> + + + + +
+
+
+
+ ); +} diff --git a/frontend/src/pages/SubscriberCreate/SubscriberFormBasic.tsx b/frontend/src/pages/SubscriberCreate/SubscriberFormBasic.tsx new file mode 100644 index 00000000..03e7c2de --- /dev/null +++ b/frontend/src/pages/SubscriberCreate/SubscriberFormBasic.tsx @@ -0,0 +1,183 @@ +import { Controller } from "react-hook-form"; +import { useSubscriptionForm } from "../../hooks/subscription-form"; +import { + Card, + FormControl, + InputLabel, + MenuItem, + Select, + Table, + TableBody, + TableCell, + TableRow, + TextField, +} from "@mui/material"; + +export default function SubscriberFormBasic() { + const { register, validationErrors, control } = useSubscriptionForm(); + + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Authentication Method + ( + + )} + /> + + + + + + + + + Operator Code Type + ( + + )} + /> + + + + + + + + + + + + + + + + + +
+
+ ); +} diff --git a/frontend/src/pages/SubscriberCreate/SubscriberFormSessions.tsx b/frontend/src/pages/SubscriberCreate/SubscriberFormSessions.tsx new file mode 100644 index 00000000..644c5940 --- /dev/null +++ b/frontend/src/pages/SubscriberCreate/SubscriberFormSessions.tsx @@ -0,0 +1,373 @@ +import { + Button, + Box, + Card, + Checkbox, + Grid, + Table, + TableBody, + TableCell, + TableRow, + TextField, + Switch, +} from "@mui/material"; +import { useSubscriptionForm } from "../../hooks/subscription-form"; +import { toHex } from "../../lib/utils"; +import FormChargingConfig from "./FormCharingConfig"; +import FormFlowRule from "./FormFlowRule"; +import FormUpSecurity from "./FormUpSecurity"; +import axios from "../../axios"; +import { Controller, useFieldArray } from "react-hook-form"; +import { defaultDnnConfig, defaultSnssaiConfiguration } from "../../lib/dtos/subscription"; +import { useState } from "react"; + +interface VerifyScope { + supi: string; + sd: string; + sst: number; + dnn: string; + ipaddr: string; +} + +interface VerifyResult { + ipaddr: string; + valid: boolean; + cause: string; +} + +const handleVerifyStaticIp = (sd: string, sst: number, dnn: string, ipaddr: string) => { + const scope: VerifyScope = { + supi: "", + sd: sd, + sst: sst, + dnn: dnn, + ipaddr: ipaddr, + }; + axios.post("/api/verify-staticip", scope).then((res) => { + const result = res.data as VerifyResult; + console.log(result); + if (result["valid"] === true) { + alert("OK\n" + result.ipaddr); + } else { + alert("NO!\nCause: " + result["cause"]); + } + }); +}; + +export default function SubscriberFormSessions() { + const { register, validationErrors, watch, control, setFocus } = useSubscriptionForm(); + + const { + fields: snssaiConfigurations, + append: appendSnssaiConfiguration, + remove: removeSnssaiConfiguration, + update: updateSnssaiConfiguration, + } = useFieldArray({ + control, + name: "SnssaiConfigurations", + }); + + const [dnnName, setDnnName] = useState(Array(snssaiConfigurations.length).fill("")); + + const handleChangeDNN = ( + event: React.ChangeEvent, + index: number, + ): void => { + setDnnName((dnnName) => dnnName.map((name, i) => (index === i ? event.target.value : name))); + }; + + const onDnnAdd = (index: number) => { + const name = dnnName[index]; + if (name === undefined || name === "") { + return; + } + + const snssaiConfig = watch(`SnssaiConfigurations.${index}`); + updateSnssaiConfiguration(index, { + ...snssaiConfig, + dnnConfigurations: { + ...snssaiConfig.dnnConfigurations, + [name]: defaultDnnConfig(), + }, + }); + + setTimeout(() => { + /* IMPORTANT: setFocus after rerender */ + setFocus(`SnssaiConfigurations.${index}.dnnConfigurations.${name}.sessionAmbr.uplink`); + }); + + // restore input field + setDnnName((dnnName) => dnnName.map((name, i) => (index === i ? "" : name))); + }; + + const onDnnDelete = (index: number, dnn: string, slice: string) => { + const snssaiConfig = watch(`SnssaiConfigurations.${index}`); + const newDnnConfigurations = { ...snssaiConfig.dnnConfigurations }; + delete newDnnConfigurations[dnn]; + + updateSnssaiConfiguration(index, { + ...snssaiConfig, + dnnConfigurations: newDnnConfigurations, + }); + }; + + return ( + <> + {snssaiConfigurations?.map((row, index) => ( +
+ + +

S-NSSAI Configuragtion ({toHex(row.sst) + row.sd})

+
+ + + + + +
+ + + + + + + + + + + + + + + Default S-NSSAI + + } + /> + + + +
+ + + + {Object.keys(row.dnnConfigurations).map((dnn) => ( +
+ + + +

DNN Configurations

+
+ + + + + +
+ + + + + + {dnn} + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + +
+ + + + +
+
+
+ ))} + + + + handleChangeDNN(ev, index)} + /> + + + + + + + + +
+
+ ))} + +
+ + + + + ); +} diff --git a/frontend/src/pages/SubscriberCreate/SubscriberFormUeAmbr.tsx b/frontend/src/pages/SubscriberCreate/SubscriberFormUeAmbr.tsx new file mode 100644 index 00000000..655acb94 --- /dev/null +++ b/frontend/src/pages/SubscriberCreate/SubscriberFormUeAmbr.tsx @@ -0,0 +1,43 @@ +import { Card, Table, TableBody, TableCell, TableRow, TextField } from "@mui/material"; +import { useSubscriptionForm } from "../../hooks/subscription-form"; + +export default function SubscriberFormUeAmbr() { + const { register, validationErrors } = useSubscriptionForm(); + + return ( + + + + + + + + + + + + +
+
+ ); +} diff --git a/frontend/src/pages/SubscriberCreate/index.tsx b/frontend/src/pages/SubscriberCreate/index.tsx new file mode 100644 index 00000000..fecae841 --- /dev/null +++ b/frontend/src/pages/SubscriberCreate/index.tsx @@ -0,0 +1,166 @@ +import React, { useState } from "react"; +import { useEffect } from "react"; +import { useNavigate, useParams } from "react-router-dom"; + +import axios from "../../axios"; + +import Dashboard from "../../Dashboard"; +import { Button, Grid } from "@mui/material"; +import { SubscriberFormProvider, useSubscriptionForm } from "../../hooks/subscription-form"; +import SubscriberFormBasic from "./SubscriberFormBasic"; +import SubscriberFormUeAmbr from "./SubscriberFormUeAmbr"; +import SubscriberFormSessions from "./SubscriberFormSessions"; +import { FlowsMapperImpl, SubscriptionMapperImpl } from "../../lib/dtos/subscription"; + +function FormHOC(Component: React.ComponentType) { + return function (props: any) { + return ( + + + + ); + }; +} + +export default FormHOC(SubscriberCreate); + +function SubscriberCreate() { + const { id, plmn } = useParams<{ + id: string; + plmn: string; + }>(); + + const isNewSubscriber = id === undefined && plmn === undefined; + const navigation = useNavigate(); + const [loading, setLoading] = useState(false); + + const { handleSubmit, getValues, reset } = useSubscriptionForm(); + + if (!isNewSubscriber) { + useEffect(() => { + setLoading(true); + + axios + .get("/api/subscriber/" + id + "/" + plmn) + .then((res) => { + const subscriberMapper = new SubscriptionMapperImpl(new FlowsMapperImpl()); + const subscription = subscriberMapper.mapFromSubscription(res.data); + reset(subscription); + }) + .finally(() => { + setLoading(false); + }); + }, [id]); + } + + if (loading) { + return
Loading...
; + } + + const supiIncrement = (supi: string): string => { + const imsi = supi.split("-", 2); + if (imsi.length !== 2) { + return supi; + } + let number = Number(imsi[1]); + number += 1; + return "imsi-" + number; + }; + + const onCreate = () => { + console.log("trace: onCreate"); + + const data = getValues(); + + if (data.SnssaiConfigurations.length === 0) { + alert("Please add at least one S-NSSAI"); + return; + } + + const subscriberMapper = new SubscriptionMapperImpl(new FlowsMapperImpl()); + const subscription = subscriberMapper.mapFromDto(data); + + // Iterate subscriber data number. + let supi = subscription.ueId; + for (let i = 0; i < subscription.userNumber!; i++) { + subscription.ueId = supi; + axios + .post("/api/subscriber/" + subscription.ueId + "/" + subscription.plmnID, subscription) + .then(() => { + navigation("/subscriber"); + }) + .catch((err) => { + if (err.response) { + const msg = "Status: " + err.response.status; + if (err.response.data.cause) { + alert(msg + ", cause: " + err.response.data.cause); + } else if (err.response.data) { + alert(msg + ", data:" + err.response.data); + } else { + alert(msg); + } + } else { + alert(err.message); + } + console.log(err); + return; + }); + supi = supiIncrement(supi); + } + }; + + const onUpdate = () => { + console.log("trace: onUpdate"); + + const data = getValues(); + const subscriberMapper = new SubscriptionMapperImpl(new FlowsMapperImpl()); + const subscription = subscriberMapper.mapFromDto(data); + + axios + .put("/api/subscriber/" + subscription.ueId + "/" + subscription.plmnID, subscription) + .then(() => { + navigation("/subscriber/" + subscription.ueId + "/" + subscription.plmnID); + }) + .catch((err) => { + if (err.response) { + const msg = "Status: " + err.response.status; + if (err.response.data.cause) { + alert(msg + ", cause: " + err.response.data.cause); + } else if (err.response.data) { + alert(msg + ", data:" + err.response.data); + } else { + alert(msg); + } + } else { + alert(err.message); + } + }); + }; + + const formSubmitFn = isNewSubscriber ? onCreate : onUpdate; + const formSubmitText = isNewSubscriber ? "CREATE" : "UPDATE"; + + return ( + {}}> +
{ + console.log("form error: ", err); + })} + > + + +

Subscribed UE AMBR

+ + + + +
+ + + + +
+ ); +} diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 8ed0ab72..0774377a 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -12,6 +12,16 @@ __metadata: languageName: node linkType: hard +"@ampproject/remapping@npm:^2.3.0": + version: 2.3.0 + resolution: "@ampproject/remapping@npm:2.3.0" + dependencies: + "@jridgewell/gen-mapping": "npm:^0.3.5" + "@jridgewell/trace-mapping": "npm:^0.3.24" + checksum: 10c0/81d63cca5443e0f0c72ae18b544cc28c7c0ec2cea46e7cb888bb0e0f411a1191d0d6b7af798d54e30777d8d1488b2ec0732aac2be342d3d7d3ffd271c6f489ed + languageName: node + linkType: hard + "@babel/code-frame@npm:^7.0.0": version: 7.23.5 resolution: "@babel/code-frame@npm:7.23.5" @@ -229,6 +239,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/aix-ppc64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/aix-ppc64@npm:0.21.5" + conditions: os=aix & cpu=ppc64 + languageName: node + linkType: hard + "@esbuild/android-arm64@npm:0.20.2": version: 0.20.2 resolution: "@esbuild/android-arm64@npm:0.20.2" @@ -236,6 +253,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-arm64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/android-arm64@npm:0.21.5" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/android-arm@npm:0.20.2": version: 0.20.2 resolution: "@esbuild/android-arm@npm:0.20.2" @@ -243,6 +267,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-arm@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/android-arm@npm:0.21.5" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + "@esbuild/android-x64@npm:0.20.2": version: 0.20.2 resolution: "@esbuild/android-x64@npm:0.20.2" @@ -250,6 +281,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-x64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/android-x64@npm:0.21.5" + conditions: os=android & cpu=x64 + languageName: node + linkType: hard + "@esbuild/darwin-arm64@npm:0.20.2": version: 0.20.2 resolution: "@esbuild/darwin-arm64@npm:0.20.2" @@ -257,6 +295,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/darwin-arm64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/darwin-arm64@npm:0.21.5" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/darwin-x64@npm:0.20.2": version: 0.20.2 resolution: "@esbuild/darwin-x64@npm:0.20.2" @@ -264,6 +309,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/darwin-x64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/darwin-x64@npm:0.21.5" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + "@esbuild/freebsd-arm64@npm:0.20.2": version: 0.20.2 resolution: "@esbuild/freebsd-arm64@npm:0.20.2" @@ -271,6 +323,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/freebsd-arm64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/freebsd-arm64@npm:0.21.5" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/freebsd-x64@npm:0.20.2": version: 0.20.2 resolution: "@esbuild/freebsd-x64@npm:0.20.2" @@ -278,6 +337,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/freebsd-x64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/freebsd-x64@npm:0.21.5" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/linux-arm64@npm:0.20.2": version: 0.20.2 resolution: "@esbuild/linux-arm64@npm:0.20.2" @@ -285,6 +351,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-arm64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/linux-arm64@npm:0.21.5" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/linux-arm@npm:0.20.2": version: 0.20.2 resolution: "@esbuild/linux-arm@npm:0.20.2" @@ -292,6 +365,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-arm@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/linux-arm@npm:0.21.5" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + "@esbuild/linux-ia32@npm:0.20.2": version: 0.20.2 resolution: "@esbuild/linux-ia32@npm:0.20.2" @@ -299,6 +379,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-ia32@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/linux-ia32@npm:0.21.5" + conditions: os=linux & cpu=ia32 + languageName: node + linkType: hard + "@esbuild/linux-loong64@npm:0.20.2": version: 0.20.2 resolution: "@esbuild/linux-loong64@npm:0.20.2" @@ -306,6 +393,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-loong64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/linux-loong64@npm:0.21.5" + conditions: os=linux & cpu=loong64 + languageName: node + linkType: hard + "@esbuild/linux-mips64el@npm:0.20.2": version: 0.20.2 resolution: "@esbuild/linux-mips64el@npm:0.20.2" @@ -313,6 +407,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-mips64el@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/linux-mips64el@npm:0.21.5" + conditions: os=linux & cpu=mips64el + languageName: node + linkType: hard + "@esbuild/linux-ppc64@npm:0.20.2": version: 0.20.2 resolution: "@esbuild/linux-ppc64@npm:0.20.2" @@ -320,6 +421,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-ppc64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/linux-ppc64@npm:0.21.5" + conditions: os=linux & cpu=ppc64 + languageName: node + linkType: hard + "@esbuild/linux-riscv64@npm:0.20.2": version: 0.20.2 resolution: "@esbuild/linux-riscv64@npm:0.20.2" @@ -327,6 +435,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-riscv64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/linux-riscv64@npm:0.21.5" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + "@esbuild/linux-s390x@npm:0.20.2": version: 0.20.2 resolution: "@esbuild/linux-s390x@npm:0.20.2" @@ -334,6 +449,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-s390x@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/linux-s390x@npm:0.21.5" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + "@esbuild/linux-x64@npm:0.20.2": version: 0.20.2 resolution: "@esbuild/linux-x64@npm:0.20.2" @@ -341,6 +463,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-x64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/linux-x64@npm:0.21.5" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + "@esbuild/netbsd-x64@npm:0.20.2": version: 0.20.2 resolution: "@esbuild/netbsd-x64@npm:0.20.2" @@ -348,6 +477,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/netbsd-x64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/netbsd-x64@npm:0.21.5" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/openbsd-x64@npm:0.20.2": version: 0.20.2 resolution: "@esbuild/openbsd-x64@npm:0.20.2" @@ -355,6 +491,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/openbsd-x64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/openbsd-x64@npm:0.21.5" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/sunos-x64@npm:0.20.2": version: 0.20.2 resolution: "@esbuild/sunos-x64@npm:0.20.2" @@ -362,6 +505,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/sunos-x64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/sunos-x64@npm:0.21.5" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + "@esbuild/win32-arm64@npm:0.20.2": version: 0.20.2 resolution: "@esbuild/win32-arm64@npm:0.20.2" @@ -369,6 +519,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-arm64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/win32-arm64@npm:0.21.5" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/win32-ia32@npm:0.20.2": version: 0.20.2 resolution: "@esbuild/win32-ia32@npm:0.20.2" @@ -376,6 +533,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-ia32@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/win32-ia32@npm:0.21.5" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + "@esbuild/win32-x64@npm:0.20.2": version: 0.20.2 resolution: "@esbuild/win32-x64@npm:0.20.2" @@ -383,6 +547,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-x64@npm:0.21.5": + version: 0.21.5 + resolution: "@esbuild/win32-x64@npm:0.21.5" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@eslint-community/eslint-utils@npm:^4.2.0, @eslint-community/eslint-utils@npm:^4.4.0": version: 4.4.0 resolution: "@eslint-community/eslint-utils@npm:4.4.0" @@ -463,6 +634,15 @@ __metadata: languageName: node linkType: hard +"@hookform/resolvers@npm:^3.6.0": + version: 3.6.0 + resolution: "@hookform/resolvers@npm:3.6.0" + peerDependencies: + react-hook-form: ^7.0.0 + checksum: 10c0/3577949a82b02b823b81592ec44630d28dc8748e9c42e57d8a19046670bb6d270d5e2c4780d65193c206bc18abb5575a0a5e261fa7aac17198faee1823208be6 + languageName: node + linkType: hard + "@humanwhocodes/config-array@npm:^0.11.14": version: 0.11.14 resolution: "@humanwhocodes/config-array@npm:0.11.14" @@ -502,6 +682,48 @@ __metadata: languageName: node linkType: hard +"@jridgewell/gen-mapping@npm:^0.3.5": + version: 0.3.5 + resolution: "@jridgewell/gen-mapping@npm:0.3.5" + dependencies: + "@jridgewell/set-array": "npm:^1.2.1" + "@jridgewell/sourcemap-codec": "npm:^1.4.10" + "@jridgewell/trace-mapping": "npm:^0.3.24" + checksum: 10c0/1be4fd4a6b0f41337c4f5fdf4afc3bd19e39c3691924817108b82ffcb9c9e609c273f936932b9fba4b3a298ce2eb06d9bff4eb1cc3bd81c4f4ee1b4917e25feb + languageName: node + linkType: hard + +"@jridgewell/resolve-uri@npm:^3.1.0": + version: 3.1.2 + resolution: "@jridgewell/resolve-uri@npm:3.1.2" + checksum: 10c0/d502e6fb516b35032331406d4e962c21fe77cdf1cbdb49c6142bcbd9e30507094b18972778a6e27cbad756209cfe34b1a27729e6fa08a2eb92b33943f680cf1e + languageName: node + linkType: hard + +"@jridgewell/set-array@npm:^1.2.1": + version: 1.2.1 + resolution: "@jridgewell/set-array@npm:1.2.1" + checksum: 10c0/2a5aa7b4b5c3464c895c802d8ae3f3d2b92fcbe84ad12f8d0bfbb1f5ad006717e7577ee1fd2eac00c088abe486c7adb27976f45d2941ff6b0b92b2c3302c60f4 + languageName: node + linkType: hard + +"@jridgewell/sourcemap-codec@npm:^1.4.10, @jridgewell/sourcemap-codec@npm:^1.4.14, @jridgewell/sourcemap-codec@npm:^1.4.15": + version: 1.5.0 + resolution: "@jridgewell/sourcemap-codec@npm:1.5.0" + checksum: 10c0/2eb864f276eb1096c3c11da3e9bb518f6d9fc0023c78344cdc037abadc725172c70314bdb360f2d4b7bffec7f5d657ce006816bc5d4ecb35e61b66132db00c18 + languageName: node + linkType: hard + +"@jridgewell/trace-mapping@npm:^0.3.24": + version: 0.3.25 + resolution: "@jridgewell/trace-mapping@npm:0.3.25" + dependencies: + "@jridgewell/resolve-uri": "npm:^3.1.0" + "@jridgewell/sourcemap-codec": "npm:^1.4.14" + checksum: 10c0/3d1ce6ebc69df9682a5a8896b414c6537e428a1d68b02fcc8363b04284a8ca0df04d0ee3013132252ab14f2527bc13bea6526a912ecb5658f0e39fd2860b4df4 + languageName: node + linkType: hard + "@mui/base@npm:5.0.0-beta.37": version: 5.0.0-beta.37 resolution: "@mui/base@npm:5.0.0-beta.37" @@ -988,7 +1210,7 @@ __metadata: languageName: node linkType: hard -"@types/estree@npm:1.0.5": +"@types/estree@npm:1.0.5, @types/estree@npm:^1.0.0": version: 1.0.5 resolution: "@types/estree@npm:1.0.5" checksum: 10c0/b3b0e334288ddb407c7b3357ca67dbee75ee22db242ca7c56fe27db4e1a31989cb8af48a84dd401deb787fe10cc6b2ab1ee82dc4783be87ededbe3d53c79c70d @@ -1216,6 +1438,69 @@ __metadata: languageName: node linkType: hard +"@vitest/expect@npm:2.0.2": + version: 2.0.2 + resolution: "@vitest/expect@npm:2.0.2" + dependencies: + "@vitest/spy": "npm:2.0.2" + "@vitest/utils": "npm:2.0.2" + chai: "npm:^5.1.1" + tinyrainbow: "npm:^1.2.0" + checksum: 10c0/6f541f2f25244f41e9054699713ac9aedf1c82b82f6e0d4d4863565b352ff32794c2220f23603a01fc22b1eecbb9ea8e09eb2c93d80f7322c2b438a5e084ec08 + languageName: node + linkType: hard + +"@vitest/pretty-format@npm:2.0.2, @vitest/pretty-format@npm:^2.0.2": + version: 2.0.2 + resolution: "@vitest/pretty-format@npm:2.0.2" + dependencies: + tinyrainbow: "npm:^1.2.0" + checksum: 10c0/85749fae2ebcf7950c7a019a11b4272def00ee6568b6179e377e7374fb3b9ef6bd5bbef16c110b17881a3d1c772e315cb13a852758b08296b5a4bc665426952b + languageName: node + linkType: hard + +"@vitest/runner@npm:2.0.2": + version: 2.0.2 + resolution: "@vitest/runner@npm:2.0.2" + dependencies: + "@vitest/utils": "npm:2.0.2" + pathe: "npm:^1.1.2" + checksum: 10c0/f4454b67f0c11318515ed6498cf8aa58ae18b6630a13d31201b55626c1c166dc07ceedd11ef373229595559724a6d3e8c961a8dfd8cc627c3b82bb38de0be40e + languageName: node + linkType: hard + +"@vitest/snapshot@npm:2.0.2": + version: 2.0.2 + resolution: "@vitest/snapshot@npm:2.0.2" + dependencies: + "@vitest/pretty-format": "npm:2.0.2" + magic-string: "npm:^0.30.10" + pathe: "npm:^1.1.2" + checksum: 10c0/7cb5e16d8a10ce71ec33cec57b191d28b82c4204b986a0ad04a956b401ae47d019f28a3680ee4256105bf9259f6df1208bff93fc4dc4b3e333a8c273f6b39200 + languageName: node + linkType: hard + +"@vitest/spy@npm:2.0.2": + version: 2.0.2 + resolution: "@vitest/spy@npm:2.0.2" + dependencies: + tinyspy: "npm:^3.0.0" + checksum: 10c0/7ef32945fc2a83add963da9baf35c6c2fa5b35afb03ab96fe1289ef5bbf3e85ef30bb6b80706f06935351ef10399551d37a993d58958260fe4b21f605a08c1a0 + languageName: node + linkType: hard + +"@vitest/utils@npm:2.0.2": + version: 2.0.2 + resolution: "@vitest/utils@npm:2.0.2" + dependencies: + "@vitest/pretty-format": "npm:2.0.2" + estree-walker: "npm:^3.0.3" + loupe: "npm:^3.1.1" + tinyrainbow: "npm:^1.2.0" + checksum: 10c0/d1f99ab1ea38ff36150405b2df390ec09cd0f014d05d3ae500c44bdc8746a14f34be3bf76c2a44c352b1c40d130bdf477c1043159ac62f2d63765aa1a8bb82a5 + languageName: node + linkType: hard + "abbrev@npm:^2.0.0": version: 2.0.0 resolution: "abbrev@npm:2.0.0" @@ -1452,6 +1737,13 @@ __metadata: languageName: node linkType: hard +"assertion-error@npm:^2.0.1": + version: 2.0.1 + resolution: "assertion-error@npm:2.0.1" + checksum: 10c0/bbbcb117ac6480138f8c93cf7f535614282dea9dc828f540cdece85e3c665e8f78958b96afac52f29ff883c72638e6a87d469ecc9fe5bc902df03ed24a55dba8 + languageName: node + linkType: hard + "asynciterator.prototype@npm:^1.0.0": version: 1.0.0 resolution: "asynciterator.prototype@npm:1.0.0" @@ -1534,6 +1826,13 @@ __metadata: languageName: node linkType: hard +"cac@npm:^6.7.14": + version: 6.7.14 + resolution: "cac@npm:6.7.14" + checksum: 10c0/4ee06aaa7bab8981f0d54e5f5f9d4adcd64058e9697563ce336d8a3878ed018ee18ebe5359b2430eceae87e0758e62ea2019c3f52ae6e211b1bd2e133856cd10 + languageName: node + linkType: hard + "cacache@npm:^18.0.0": version: 18.0.2 resolution: "cacache@npm:18.0.2" @@ -1574,6 +1873,19 @@ __metadata: languageName: node linkType: hard +"chai@npm:^5.1.1": + version: 5.1.1 + resolution: "chai@npm:5.1.1" + dependencies: + assertion-error: "npm:^2.0.1" + check-error: "npm:^2.1.1" + deep-eql: "npm:^5.0.1" + loupe: "npm:^3.1.0" + pathval: "npm:^2.0.0" + checksum: 10c0/e7f00e5881e3d5224f08fe63966ed6566bd9fdde175863c7c16dd5240416de9b34c4a0dd925f4fd64ad56256ca6507d32cf6131c49e1db65c62578eb31d4566c + languageName: node + linkType: hard + "chalk@npm:^2.4.2": version: 2.4.2 resolution: "chalk@npm:2.4.2" @@ -1595,6 +1907,13 @@ __metadata: languageName: node linkType: hard +"check-error@npm:^2.1.1": + version: 2.1.1 + resolution: "check-error@npm:2.1.1" + checksum: 10c0/979f13eccab306cf1785fa10941a590b4e7ea9916ea2a4f8c87f0316fc3eab07eabefb6e587424ef0f88cbcd3805791f172ea739863ca3d7ce2afc54641c7f0e + languageName: node + linkType: hard + "chownr@npm:^2.0.0": version: 2.0.0 resolution: "chownr@npm:2.0.0" @@ -1691,7 +2010,7 @@ __metadata: languageName: node linkType: hard -"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.2": +"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.3": version: 7.0.3 resolution: "cross-spawn@npm:7.0.3" dependencies: @@ -1737,6 +2056,25 @@ __metadata: languageName: node linkType: hard +"debug@npm:^4.3.5": + version: 4.3.5 + resolution: "debug@npm:4.3.5" + dependencies: + ms: "npm:2.1.2" + peerDependenciesMeta: + supports-color: + optional: true + checksum: 10c0/082c375a2bdc4f4469c99f325ff458adad62a3fc2c482d59923c260cb08152f34e2659f72b3767db8bb2f21ca81a60a42d1019605a412132d7b9f59363a005cc + languageName: node + linkType: hard + +"deep-eql@npm:^5.0.1": + version: 5.0.2 + resolution: "deep-eql@npm:5.0.2" + checksum: 10c0/7102cf3b7bb719c6b9c0db2e19bf0aa9318d141581befe8c7ce8ccd39af9eaa4346e5e05adef7f9bd7015da0f13a3a25dcfe306ef79dc8668aedbecb658dd247 + languageName: node + linkType: hard + "deep-is@npm:^0.1.3": version: 0.1.4 resolution: "deep-is@npm:0.1.4" @@ -2069,6 +2407,86 @@ __metadata: languageName: node linkType: hard +"esbuild@npm:^0.21.3": + version: 0.21.5 + resolution: "esbuild@npm:0.21.5" + dependencies: + "@esbuild/aix-ppc64": "npm:0.21.5" + "@esbuild/android-arm": "npm:0.21.5" + "@esbuild/android-arm64": "npm:0.21.5" + "@esbuild/android-x64": "npm:0.21.5" + "@esbuild/darwin-arm64": "npm:0.21.5" + "@esbuild/darwin-x64": "npm:0.21.5" + "@esbuild/freebsd-arm64": "npm:0.21.5" + "@esbuild/freebsd-x64": "npm:0.21.5" + "@esbuild/linux-arm": "npm:0.21.5" + "@esbuild/linux-arm64": "npm:0.21.5" + "@esbuild/linux-ia32": "npm:0.21.5" + "@esbuild/linux-loong64": "npm:0.21.5" + "@esbuild/linux-mips64el": "npm:0.21.5" + "@esbuild/linux-ppc64": "npm:0.21.5" + "@esbuild/linux-riscv64": "npm:0.21.5" + "@esbuild/linux-s390x": "npm:0.21.5" + "@esbuild/linux-x64": "npm:0.21.5" + "@esbuild/netbsd-x64": "npm:0.21.5" + "@esbuild/openbsd-x64": "npm:0.21.5" + "@esbuild/sunos-x64": "npm:0.21.5" + "@esbuild/win32-arm64": "npm:0.21.5" + "@esbuild/win32-ia32": "npm:0.21.5" + "@esbuild/win32-x64": "npm:0.21.5" + dependenciesMeta: + "@esbuild/aix-ppc64": + optional: true + "@esbuild/android-arm": + optional: true + "@esbuild/android-arm64": + optional: true + "@esbuild/android-x64": + optional: true + "@esbuild/darwin-arm64": + optional: true + "@esbuild/darwin-x64": + optional: true + "@esbuild/freebsd-arm64": + optional: true + "@esbuild/freebsd-x64": + optional: true + "@esbuild/linux-arm": + optional: true + "@esbuild/linux-arm64": + optional: true + "@esbuild/linux-ia32": + optional: true + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": + optional: true + "@esbuild/linux-ppc64": + optional: true + "@esbuild/linux-riscv64": + optional: true + "@esbuild/linux-s390x": + optional: true + "@esbuild/linux-x64": + optional: true + "@esbuild/netbsd-x64": + optional: true + "@esbuild/openbsd-x64": + optional: true + "@esbuild/sunos-x64": + optional: true + "@esbuild/win32-arm64": + optional: true + "@esbuild/win32-ia32": + optional: true + "@esbuild/win32-x64": + optional: true + bin: + esbuild: bin/esbuild + checksum: 10c0/fa08508adf683c3f399e8a014a6382a6b65542213431e26206c0720e536b31c09b50798747c2a105a4bbba1d9767b8d3615a74c2f7bf1ddf6d836cd11eb672de + languageName: node + linkType: hard + "escape-string-regexp@npm:^1.0.5": version: 1.0.5 resolution: "escape-string-regexp@npm:1.0.5" @@ -2321,6 +2739,15 @@ __metadata: languageName: node linkType: hard +"estree-walker@npm:^3.0.3": + version: 3.0.3 + resolution: "estree-walker@npm:3.0.3" + dependencies: + "@types/estree": "npm:^1.0.0" + checksum: 10c0/c12e3c2b2642d2bcae7d5aa495c60fa2f299160946535763969a1c83fc74518ffa9c2cd3a8b69ac56aea547df6a8aac25f729a342992ef0bbac5f1c73e78995d + languageName: node + linkType: hard + "esutils@npm:^2.0.2": version: 2.0.3 resolution: "esutils@npm:2.0.3" @@ -2328,6 +2755,23 @@ __metadata: languageName: node linkType: hard +"execa@npm:^8.0.1": + version: 8.0.1 + resolution: "execa@npm:8.0.1" + dependencies: + cross-spawn: "npm:^7.0.3" + get-stream: "npm:^8.0.1" + human-signals: "npm:^5.0.0" + is-stream: "npm:^3.0.0" + merge-stream: "npm:^2.0.0" + npm-run-path: "npm:^5.1.0" + onetime: "npm:^6.0.0" + signal-exit: "npm:^4.1.0" + strip-final-newline: "npm:^3.0.0" + checksum: 10c0/2c52d8775f5bf103ce8eec9c7ab3059909ba350a5164744e9947ed14a53f51687c040a250bda833f906d1283aa8803975b84e6c8f7a7c42f99dc8ef80250d1af + languageName: node + linkType: hard + "exponential-backoff@npm:^3.1.1": version: 3.1.1 resolution: "exponential-backoff@npm:3.1.1" @@ -2548,6 +2992,13 @@ __metadata: languageName: node linkType: hard +"get-func-name@npm:^2.0.1": + version: 2.0.2 + resolution: "get-func-name@npm:2.0.2" + checksum: 10c0/89830fd07623fa73429a711b9daecdb304386d237c71268007f788f113505ef1d4cc2d0b9680e072c5082490aec9df5d7758bf5ac6f1c37062855e8e3dc0b9df + languageName: node + linkType: hard + "get-intrinsic@npm:^1.1.1, get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.1, get-intrinsic@npm:^1.2.2, get-intrinsic@npm:^1.2.3, get-intrinsic@npm:^1.2.4": version: 1.2.4 resolution: "get-intrinsic@npm:1.2.4" @@ -2561,6 +3012,13 @@ __metadata: languageName: node linkType: hard +"get-stream@npm:^8.0.1": + version: 8.0.1 + resolution: "get-stream@npm:8.0.1" + checksum: 10c0/5c2181e98202b9dae0bb4a849979291043e5892eb40312b47f0c22b9414fc9b28a3b6063d2375705eb24abc41ecf97894d9a51f64ff021511b504477b27b4290 + languageName: node + linkType: hard + "get-symbol-description@npm:^1.0.2": version: 1.0.2 resolution: "get-symbol-description@npm:1.0.2" @@ -2772,6 +3230,13 @@ __metadata: languageName: node linkType: hard +"human-signals@npm:^5.0.0": + version: 5.0.0 + resolution: "human-signals@npm:5.0.0" + checksum: 10c0/5a9359073fe17a8b58e5a085e9a39a950366d9f00217c4ff5878bd312e09d80f460536ea6a3f260b5943a01fe55c158d1cea3fc7bee3d0520aeef04f6d915c82 + languageName: node + linkType: hard + "iconv-lite@npm:^0.6.2": version: 0.6.3 resolution: "iconv-lite@npm:0.6.3" @@ -3031,6 +3496,13 @@ __metadata: languageName: node linkType: hard +"is-stream@npm:^3.0.0": + version: 3.0.0 + resolution: "is-stream@npm:3.0.0" + checksum: 10c0/eb2f7127af02ee9aa2a0237b730e47ac2de0d4e76a4a905a50a11557f2339df5765eaea4ceb8029f1efa978586abe776908720bfcb1900c20c6ec5145f6f29d8 + languageName: node + linkType: hard + "is-string@npm:^1.0.5, is-string@npm:^1.0.7": version: 1.0.7 resolution: "is-string@npm:1.0.7" @@ -3260,6 +3732,15 @@ __metadata: languageName: node linkType: hard +"loupe@npm:^3.1.0, loupe@npm:^3.1.1": + version: 3.1.1 + resolution: "loupe@npm:3.1.1" + dependencies: + get-func-name: "npm:^2.0.1" + checksum: 10c0/99f88badc47e894016df0c403de846fedfea61154aadabbf776c8428dd59e8d8378007135d385d737de32ae47980af07d22ba7bec5ef7beebd721de9baa0a0af + languageName: node + linkType: hard + "lru-cache@npm:^10.0.1, lru-cache@npm:^9.1.1 || ^10.0.0": version: 10.2.0 resolution: "lru-cache@npm:10.2.0" @@ -3276,6 +3757,15 @@ __metadata: languageName: node linkType: hard +"magic-string@npm:^0.30.10": + version: 0.30.10 + resolution: "magic-string@npm:0.30.10" + dependencies: + "@jridgewell/sourcemap-codec": "npm:^1.4.15" + checksum: 10c0/aa9ca17eae571a19bce92c8221193b6f93ee8511abb10f085e55ffd398db8e4c089a208d9eac559deee96a08b7b24d636ea4ab92f09c6cf42a7d1af51f7fd62b + languageName: node + linkType: hard + "make-fetch-happen@npm:^13.0.0": version: 13.0.0 resolution: "make-fetch-happen@npm:13.0.0" @@ -3295,6 +3785,13 @@ __metadata: languageName: node linkType: hard +"merge-stream@npm:^2.0.0": + version: 2.0.0 + resolution: "merge-stream@npm:2.0.0" + checksum: 10c0/867fdbb30a6d58b011449b8885601ec1690c3e41c759ecd5a9d609094f7aed0096c37823ff4a7190ef0b8f22cc86beb7049196ff68c016e3b3c671d0dac91ce5 + languageName: node + linkType: hard + "merge2@npm:^1.3.0, merge2@npm:^1.4.1": version: 1.4.1 resolution: "merge2@npm:1.4.1" @@ -3328,6 +3825,13 @@ __metadata: languageName: node linkType: hard +"mimic-fn@npm:^4.0.0": + version: 4.0.0 + resolution: "mimic-fn@npm:4.0.0" + checksum: 10c0/de9cc32be9996fd941e512248338e43407f63f6d497abe8441fa33447d922e927de54d4cc3c1a3c6d652857acd770389d5a3823f311a744132760ce2be15ccbf + languageName: node + linkType: hard + "minimatch@npm:9.0.3, minimatch@npm:^9.0.1": version: 9.0.3 resolution: "minimatch@npm:9.0.3" @@ -3466,6 +3970,7 @@ __metadata: dependencies: "@emotion/react": "npm:^11.11.4" "@emotion/styled": "npm:^11.11.0" + "@hookform/resolvers": "npm:^3.6.0" "@mui/icons-material": "npm:^5.15.11" "@mui/material": "npm:^5.15.11" "@types/node": "npm:^20.11.24" @@ -3486,10 +3991,13 @@ __metadata: prettier: "npm:^3.2.5" react: "npm:^18.2.0" react-dom: "npm:^18.2.0" + react-hook-form: "npm:^7.51.5" react-router-dom: "npm:^6.22.2" typescript: "npm:^5.3.3" - vite: "npm:^5.1.7" + vite: "npm:^5.1.4" + vitest: "npm:^2.0.2" web-vitals: "npm:^3.5.2" + zod: "npm:^3.23.8" languageName: unknown linkType: soft @@ -3547,6 +4055,15 @@ __metadata: languageName: node linkType: hard +"npm-run-path@npm:^5.1.0": + version: 5.3.0 + resolution: "npm-run-path@npm:5.3.0" + dependencies: + path-key: "npm:^4.0.0" + checksum: 10c0/124df74820c40c2eb9a8612a254ea1d557ddfab1581c3e751f825e3e366d9f00b0d76a3c94ecd8398e7f3eee193018622677e95816e8491f0797b21e30b2deba + languageName: node + linkType: hard + "object-assign@npm:^4.1.1": version: 4.1.1 resolution: "object-assign@npm:4.1.1" @@ -3645,6 +4162,15 @@ __metadata: languageName: node linkType: hard +"onetime@npm:^6.0.0": + version: 6.0.0 + resolution: "onetime@npm:6.0.0" + dependencies: + mimic-fn: "npm:^4.0.0" + checksum: 10c0/4eef7c6abfef697dd4479345a4100c382d73c149d2d56170a54a07418c50816937ad09500e1ed1e79d235989d073a9bade8557122aee24f0576ecde0f392bb6c + languageName: node + linkType: hard + "optionator@npm:^0.9.3": version: 0.9.3 resolution: "optionator@npm:0.9.3" @@ -3728,6 +4254,13 @@ __metadata: languageName: node linkType: hard +"path-key@npm:^4.0.0": + version: 4.0.0 + resolution: "path-key@npm:4.0.0" + checksum: 10c0/794efeef32863a65ac312f3c0b0a99f921f3e827ff63afa5cb09a377e202c262b671f7b3832a4e64731003fa94af0263713962d317b9887bd1e0c48a342efba3 + languageName: node + linkType: hard + "path-parse@npm:^1.0.7": version: 1.0.7 resolution: "path-parse@npm:1.0.7" @@ -3752,6 +4285,20 @@ __metadata: languageName: node linkType: hard +"pathe@npm:^1.1.2": + version: 1.1.2 + resolution: "pathe@npm:1.1.2" + checksum: 10c0/64ee0a4e587fb0f208d9777a6c56e4f9050039268faaaaecd50e959ef01bf847b7872785c36483fa5cdcdbdfdb31fef2ff222684d4fc21c330ab60395c681897 + languageName: node + linkType: hard + +"pathval@npm:^2.0.0": + version: 2.0.0 + resolution: "pathval@npm:2.0.0" + checksum: 10c0/602e4ee347fba8a599115af2ccd8179836a63c925c23e04bd056d0674a64b39e3a081b643cc7bc0b84390517df2d800a46fcc5598d42c155fe4977095c2f77c5 + languageName: node + linkType: hard + "picocolors@npm:^1.0.0": version: 1.0.0 resolution: "picocolors@npm:1.0.0" @@ -3759,6 +4306,13 @@ __metadata: languageName: node linkType: hard +"picocolors@npm:^1.0.1": + version: 1.0.1 + resolution: "picocolors@npm:1.0.1" + checksum: 10c0/c63cdad2bf812ef0d66c8db29583802355d4ca67b9285d846f390cc15c2f6ccb94e8cb7eb6a6e97fc5990a6d3ad4ae42d86c84d3146e667c739a4234ed50d400 + languageName: node + linkType: hard + "picomatch@npm:^2.3.1": version: 2.3.1 resolution: "picomatch@npm:2.3.1" @@ -3784,6 +4338,17 @@ __metadata: languageName: node linkType: hard +"postcss@npm:^8.4.39": + version: 8.4.39 + resolution: "postcss@npm:8.4.39" + dependencies: + nanoid: "npm:^3.3.7" + picocolors: "npm:^1.0.1" + source-map-js: "npm:^1.2.0" + checksum: 10c0/16f5ac3c4e32ee76d1582b3c0dcf1a1fdb91334a45ad755eeb881ccc50318fb8d64047de4f1601ac96e30061df203f0f2e2edbdc0bfc49b9c57bc9fb9bedaea3 + languageName: node + linkType: hard + "prelude-ls@npm:^1.2.1": version: 1.2.1 resolution: "prelude-ls@npm:1.2.1" @@ -3870,6 +4435,15 @@ __metadata: languageName: node linkType: hard +"react-hook-form@npm:^7.51.5": + version: 7.51.5 + resolution: "react-hook-form@npm:7.51.5" + peerDependencies: + react: ^16.8.0 || ^17 || ^18 + checksum: 10c0/5b13f99a125d92ee618f2d4e218d5ec854f8cac1b568e83b4b125efb002cc79eef0f5c06b54015c78e16a830cf1ea356a646e8c6abb00b7ca8225dba9a92091e + languageName: node + linkType: hard + "react-is@npm:^16.13.1, react-is@npm:^16.7.0": version: 16.13.1 resolution: "react-is@npm:16.13.1" @@ -4232,7 +4806,14 @@ __metadata: languageName: node linkType: hard -"signal-exit@npm:^4.0.1": +"siginfo@npm:^2.0.0": + version: 2.0.0 + resolution: "siginfo@npm:2.0.0" + checksum: 10c0/3def8f8e516fbb34cb6ae415b07ccc5d9c018d85b4b8611e3dc6f8be6d1899f693a4382913c9ed51a06babb5201639d76453ab297d1c54a456544acf5c892e34 + languageName: node + linkType: hard + +"signal-exit@npm:^4.0.1, signal-exit@npm:^4.1.0": version: 4.1.0 resolution: "signal-exit@npm:4.1.0" checksum: 10c0/41602dce540e46d599edba9d9860193398d135f7ff72cab629db5171516cfae628d21e7bfccde1bbfdf11c48726bc2a6d1a8fb8701125852fbfda7cf19c6aa83 @@ -4304,6 +4885,20 @@ __metadata: languageName: node linkType: hard +"stackback@npm:0.0.2": + version: 0.0.2 + resolution: "stackback@npm:0.0.2" + checksum: 10c0/89a1416668f950236dd5ac9f9a6b2588e1b9b62b1b6ad8dff1bfc5d1a15dbf0aafc9b52d2226d00c28dffff212da464eaeebfc6b7578b9d180cef3e3782c5983 + languageName: node + linkType: hard + +"std-env@npm:^3.7.0": + version: 3.7.0 + resolution: "std-env@npm:3.7.0" + checksum: 10c0/60edf2d130a4feb7002974af3d5a5f3343558d1ccf8d9b9934d225c638606884db4a20d2fe6440a09605bca282af6b042ae8070a10490c0800d69e82e478f41e + languageName: node + linkType: hard + "string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0": version: 4.2.3 resolution: "string-width@npm:4.2.3" @@ -4401,6 +4996,13 @@ __metadata: languageName: node linkType: hard +"strip-final-newline@npm:^3.0.0": + version: 3.0.0 + resolution: "strip-final-newline@npm:3.0.0" + checksum: 10c0/a771a17901427bac6293fd416db7577e2bc1c34a19d38351e9d5478c3c415f523f391003b42ed475f27e33a78233035df183525395f731d3bfb8cdcbd4da08ce + languageName: node + linkType: hard + "strip-json-comments@npm:^3.1.1": version: 3.1.1 resolution: "strip-json-comments@npm:3.1.1" @@ -4471,6 +5073,34 @@ __metadata: languageName: node linkType: hard +"tinybench@npm:^2.8.0": + version: 2.8.0 + resolution: "tinybench@npm:2.8.0" + checksum: 10c0/5a9a642351fa3e4955e0cbf38f5674be5f3ba6730fd872fd23a5c953ad6c914234d5aba6ea41ef88820180a81829ceece5bd8d3967c490c5171bca1141c2f24d + languageName: node + linkType: hard + +"tinypool@npm:^1.0.0": + version: 1.0.0 + resolution: "tinypool@npm:1.0.0" + checksum: 10c0/71b20b9c54366393831c286a0772380c20f8cad9546d724c484edb47aea3228f274c58e98cf51d28c40869b39f5273209ef3ea94a9d2a23f8b292f4731cd3e4e + languageName: node + linkType: hard + +"tinyrainbow@npm:^1.2.0": + version: 1.2.0 + resolution: "tinyrainbow@npm:1.2.0" + checksum: 10c0/7f78a4b997e5ba0f5ecb75e7ed786f30bab9063716e7dff24dd84013fb338802e43d176cb21ed12480561f5649a82184cf31efb296601a29d38145b1cdb4c192 + languageName: node + linkType: hard + +"tinyspy@npm:^3.0.0": + version: 3.0.0 + resolution: "tinyspy@npm:3.0.0" + checksum: 10c0/eb0dec264aa5370efd3d29743825eb115ed7f1ef8a72a431e9a75d5c9e7d67e99d04b0d61d86b8cd70c79ec27863f241ad0317bc453f78762e0cbd76d2c332d0 + languageName: node + linkType: hard + "to-fast-properties@npm:^2.0.0": version: 2.0.0 resolution: "to-fast-properties@npm:2.0.0" @@ -4649,9 +5279,64 @@ __metadata: languageName: node linkType: hard -"vite@npm:^5.1.7": - version: 5.2.8 - resolution: "vite@npm:5.2.8" +"vite-node@npm:2.0.2": + version: 2.0.2 + resolution: "vite-node@npm:2.0.2" + dependencies: + cac: "npm:^6.7.14" + debug: "npm:^4.3.5" + pathe: "npm:^1.1.2" + tinyrainbow: "npm:^1.2.0" + vite: "npm:^5.0.0" + bin: + vite-node: vite-node.mjs + checksum: 10c0/cf6fa40844134bd11d149ada94313ee2a47756ba7a98e143698b37c73b72c5850a9aaa799bd3076c09520e1be17079665846c763d3696b59359b88be77f299b6 + languageName: node + linkType: hard + +"vite@npm:^5.0.0": + version: 5.3.3 + resolution: "vite@npm:5.3.3" + dependencies: + esbuild: "npm:^0.21.3" + fsevents: "npm:~2.3.3" + postcss: "npm:^8.4.39" + rollup: "npm:^4.13.0" + peerDependencies: + "@types/node": ^18.0.0 || >=20.0.0 + less: "*" + lightningcss: ^1.21.0 + sass: "*" + stylus: "*" + sugarss: "*" + terser: ^5.4.0 + dependenciesMeta: + fsevents: + optional: true + peerDependenciesMeta: + "@types/node": + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + bin: + vite: bin/vite.js + checksum: 10c0/a796872e1d11875d994615cd00da185c80eeb7753034d35c096050bf3c269c02004070cf623c5fe2a4a90ea2f12488e6f9d13933ec810f117f1b931e1b5e3385 + languageName: node + linkType: hard + +"vite@npm:^5.1.4": + version: 5.2.13 + resolution: "vite@npm:5.2.13" dependencies: esbuild: "npm:^0.20.1" fsevents: "npm:~2.3.3" @@ -4685,7 +5370,56 @@ __metadata: optional: true bin: vite: bin/vite.js - checksum: 10c0/b5717bb00c2570c08ff6d8ed917655e79184efcafa9dd62d52eea19c5d6dfc5a708ec3de9ebc670a7165fc5d401c2bdf1563bb39e2748d8e51e1593d286a9a13 + checksum: 10c0/f7a99da71884e69cc581dcfb43d73c8d56d73b9668d6980131603c544d6323c6003a20f376531dc0cfcf36bf5009bc465f89e6c5f8bd9d22868987aba4e4af1b + languageName: node + linkType: hard + +"vitest@npm:^2.0.2": + version: 2.0.2 + resolution: "vitest@npm:2.0.2" + dependencies: + "@ampproject/remapping": "npm:^2.3.0" + "@vitest/expect": "npm:2.0.2" + "@vitest/pretty-format": "npm:^2.0.2" + "@vitest/runner": "npm:2.0.2" + "@vitest/snapshot": "npm:2.0.2" + "@vitest/spy": "npm:2.0.2" + "@vitest/utils": "npm:2.0.2" + chai: "npm:^5.1.1" + debug: "npm:^4.3.5" + execa: "npm:^8.0.1" + magic-string: "npm:^0.30.10" + pathe: "npm:^1.1.2" + std-env: "npm:^3.7.0" + tinybench: "npm:^2.8.0" + tinypool: "npm:^1.0.0" + tinyrainbow: "npm:^1.2.0" + vite: "npm:^5.0.0" + vite-node: "npm:2.0.2" + why-is-node-running: "npm:^2.2.2" + peerDependencies: + "@edge-runtime/vm": "*" + "@types/node": ^18.0.0 || >=20.0.0 + "@vitest/browser": 2.0.2 + "@vitest/ui": 2.0.2 + happy-dom: "*" + jsdom: "*" + peerDependenciesMeta: + "@edge-runtime/vm": + optional: true + "@types/node": + optional: true + "@vitest/browser": + optional: true + "@vitest/ui": + optional: true + happy-dom: + optional: true + jsdom: + optional: true + bin: + vitest: vitest.mjs + checksum: 10c0/4ef4d8d5a32ee91f34715b8ae1895062df9ca36be5a88ff916ee4bb2a5e01dfa3f105031f7a5939c98f6401b3a6b2bfd1de6caab0ac84cca0e2df364a19c3526 languageName: node linkType: hard @@ -4776,6 +5510,18 @@ __metadata: languageName: node linkType: hard +"why-is-node-running@npm:^2.2.2": + version: 2.3.0 + resolution: "why-is-node-running@npm:2.3.0" + dependencies: + siginfo: "npm:^2.0.0" + stackback: "npm:0.0.2" + bin: + why-is-node-running: cli.js + checksum: 10c0/1cde0b01b827d2cf4cb11db962f3958b9175d5d9e7ac7361d1a7b0e2dc6069a263e69118bd974c4f6d0a890ef4eedfe34cf3d5167ec14203dbc9a18620537054 + languageName: node + linkType: hard + "wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version: 7.0.0 resolution: "wrap-ansi@npm:7.0.0" @@ -4825,3 +5571,10 @@ __metadata: checksum: 10c0/dceb44c28578b31641e13695d200d34ec4ab3966a5729814d5445b194933c096b7ced71494ce53a0e8820685d1d010df8b2422e5bf2cdea7e469d97ffbea306f languageName: node linkType: hard + +"zod@npm:^3.23.8": + version: 3.23.8 + resolution: "zod@npm:3.23.8" + checksum: 10c0/8f14c87d6b1b53c944c25ce7a28616896319d95bc46a9660fe441adc0ed0a81253b02b5abdaeffedbeb23bdd25a0bf1c29d2c12dd919aef6447652dd295e3e69 + languageName: node + linkType: hard diff --git a/go.mod b/go.mod index 60355fe5..73611c3f 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 github.com/fclairamb/go-log v0.4.1 github.com/free5gc/chf v1.0.1 - github.com/free5gc/openapi v1.0.8 + github.com/free5gc/openapi v1.0.9-0.20240730084323-449098e08462 github.com/free5gc/util v1.0.6 github.com/gin-contrib/cors v1.4.0 github.com/gin-gonic/gin v1.9.1 @@ -41,6 +41,7 @@ require ( github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.14.0 // indirect github.com/goccy/go-json v0.10.2 // indirect + github.com/golang-jwt/jwt/v5 v5.2.1 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.4 // indirect @@ -62,7 +63,6 @@ require ( github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect github.com/pelletier/go-toml/v2 v2.0.8 // indirect github.com/pkg/sftp v1.13.5 // indirect - github.com/rogpeppe/go-internal v1.9.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/tidwall/gjson v1.14.4 // indirect github.com/tidwall/match v1.1.1 // indirect @@ -90,7 +90,7 @@ require ( ) require ( - github.com/free5gc/smf v1.2.3 + github.com/free5gc/smf v1.2.4 google.golang.org/grpc v1.56.3 // indirect ) @@ -102,6 +102,6 @@ require ( github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/spf13/afero v1.9.5 // indirect - golang.org/x/oauth2 v0.8.0 // indirect + golang.org/x/oauth2 v0.8.0 google.golang.org/api v0.122.0 // indirect ) diff --git a/go.sum b/go.sum index ea7befca..38593cee 100644 --- a/go.sum +++ b/go.sum @@ -154,10 +154,10 @@ github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVB github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/free5gc/chf v1.0.1 h1:VGPQRZMaV0v4yMq1kcc3ldJWXA76xgCbCT/A/0O8Yt4= github.com/free5gc/chf v1.0.1/go.mod h1:VSBqz2ryx4LVmNsswnk9q2yCI8XjnRBuwyTjnrWPL1w= -github.com/free5gc/openapi v1.0.8 h1:QjfQdB6VVA1GRnzOJ7nILzrI7gMiY0lH64JHVW7vF34= -github.com/free5gc/openapi v1.0.8/go.mod h1:w6y9P/uySczc1d9OJZAEuB2FImR/z60Wg2BekPAVt3M= -github.com/free5gc/smf v1.2.3 h1:S59mwcnTL0sHExWSwHmrGZNV0ypYo70CjS5zzJHreHg= -github.com/free5gc/smf v1.2.3/go.mod h1:jzkHW3A+eacBSuMYeUlajqASzFqiRwOITp8AleCwPLE= +github.com/free5gc/openapi v1.0.9-0.20240730084323-449098e08462 h1:bK9UWqOhoddpAW9RfZRp4DPZNnPfEUeHvo4I86YAuzA= +github.com/free5gc/openapi v1.0.9-0.20240730084323-449098e08462/go.mod h1:afeuEQ21QCQDxZHnFjCmYrq3gBi+cd/WDNSUbaMcILo= +github.com/free5gc/smf v1.2.4 h1:tYB/6UKWlUCEzWrg7icM7vxXkGlWKo2WixLtYmf2qW4= +github.com/free5gc/smf v1.2.4/go.mod h1:ggQ9oJ/m9rfQOjb2piz00O5S6629pHhv8AsE9Xvk5oA= github.com/free5gc/util v1.0.6 h1:dBt9drcXtYKE/cY5XuQcuffgsYclPIpIArhSeS6M+DQ= github.com/free5gc/util v1.0.6/go.mod h1:eSGN7POUM8LNTvg/E591XR6447a6/w1jFWGKNZPHcXw= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -209,6 +209,8 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt v3.2.1+incompatible h1:73Z+4BJcrTC+KczS6WvTPvRGOp1WmfEP4Q1lOd9Z/+c= github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= +github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -262,8 +264,8 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -369,8 +371,9 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -466,8 +469,8 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=