Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Prevent duplicated subscribers creation #247

Merged
merged 3 commits into from
Dec 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 26 additions & 12 deletions components/CreateSubscriberModal.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
"use client";
import React, { useCallback, useEffect } from "react";
import React, { useCallback, useEffect, useState } from "react";
import {
Modal,
ActionButton,
Form,
Input,
Modal,
Notification,
Select,
ActionButton,
} from "@canonical/react-components";
import { createSubscriber } from "@/utils/createSubscriber";
import { getNetworkSlices } from "@/utils/getNetworkSlices";
Expand All @@ -29,6 +30,7 @@ type Props = {

const CreateSubscriberModal = ({ toggleModal }: Props) => {
const queryClient = useQueryClient();
const [apiError, setApiError] = useState<string | null>(null);

const SubscriberSchema = Yup.object().shape({
imsi: Yup.string()
Expand Down Expand Up @@ -63,15 +65,22 @@ const CreateSubscriberModal = ({ toggleModal }: Props) => {
},
validationSchema: SubscriberSchema,
onSubmit: async (values) => {
await createSubscriber({
imsi: values.imsi,
opc: values.opc,
key: values.key,
sequenceNumber: values.sequenceNumber,
deviceGroupName: values.deviceGroup,
});
void queryClient.invalidateQueries({ queryKey: [queryKeys.subscribers] });
toggleModal();
try{
await createSubscriber({
imsi: values.imsi,
opc: values.opc,
key: values.key,
sequenceNumber: values.sequenceNumber,
deviceGroupName: values.deviceGroup,
});
void queryClient.invalidateQueries({ queryKey: [queryKeys.subscribers] });
toggleModal();
} catch (error) {
console.error(error);
setApiError(
(error as Error).message || "An unexpected error occurred.",
);
}
},
});

Expand Down Expand Up @@ -141,6 +150,11 @@ const CreateSubscriberModal = ({ toggleModal }: Props) => {
</>
}
>
{apiError && (
<Notification severity="negative" title="Error">
{apiError}
</Notification>
)}
<Form stacked>
<Input
type="text"
Expand Down
2 changes: 1 addition & 1 deletion pages/api/device-group/[name].ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ async function handleGET(req: NextApiRequest, res: NextApiResponse) {
});

if (!response.ok) {
res.status(response.status).json({ error: "Error retrieving network slice." });
res.status(response.status).json({ error: "Error retrieving device group." });
return;
}

Expand Down
48 changes: 43 additions & 5 deletions pages/api/subscriber/[name].ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,58 @@ const WEBUI_ENDPOINT = process.env.WEBUI_ENDPOINT;

export default async function handleSubscriber(req: NextApiRequest, res: NextApiResponse) {
switch (req.method) {
case "GET":
return handleGET(req, res);
case "POST":
return handlePOST(req, res);
case "DELETE":
return handleDELETE(req, res);
default:
res.setHeader("Allow", ["POST", "DELETE"]);
res.setHeader("Allow", ["GET", "POST", "DELETE"]);
res.status(405).end(`Method ${req.method} Not Allowed`);
}
}

function isValidName(name: string): boolean {
return /^[a-zA-Z0-9-_]+$/.test(name);
}

async function handleGET(req: NextApiRequest, res: NextApiResponse) {
const { name } = req.query

if (typeof name !== 'string') {
res.status(400).json({ error: "Invalid name provided." });
return;
}

if (!isValidName(name)) {
res.status(400).json({ error: "Invalid name provided." });
return;
}

const url = `${WEBUI_ENDPOINT}/api/subscriber/${name}`;

try {
const response = await fetch(url, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});

if (!response.ok) {
throw new Error(`Error getting subscriber. Error code: ${response.status}`);
}

const data = await response.json();
res.status(200).json(data);
} catch (error) {
console.error("Error details:", error);
res.status(500).json({
error: "An error occurred while retrieving the subscriber.",
});
}
}

async function handlePOST(req: NextApiRequest, res: NextApiResponse) {
const { name } = req.query
Expand Down Expand Up @@ -52,7 +90,7 @@ async function handlePOST(req: NextApiRequest, res: NextApiResponse) {
} catch (error) {
console.error("Error details:", error);
res.status(500).json({
error: "An error occurred while creating the subscriber",
error: "An error occurred while creating the subscriber.",
});
}
}
Expand Down Expand Up @@ -84,11 +122,11 @@ async function handleDELETE(req: NextApiRequest, res: NextApiResponse) {
throw new Error(`Error deleting subscriber. Error code: ${response.status}`);
}

res.status(200).json({ message: "Subscriber deleted successfully" });
res.status(200).json({ message: "Subscriber deleted successfully." });
} catch (error) {
console.error("Error details:", error);
res.status(500).json({
error: "An error occurred while deleting the subscriber",
error: "An error occurred while deleting the subscriber.",
});
}
}
19 changes: 18 additions & 1 deletion utils/createSubscriber.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,19 @@ export const createSubscriber = async ({
};

try {
const checkResponse = await fetch(`/api/subscriber/imsi-${imsi}`, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});

// Workaround for https://github.com/omec-project/webconsole/issues/109
const existingSubscriberData = await checkResponse.json();
if (checkResponse.ok && existingSubscriberData["AuthenticationSubscription"]["authenticationMethod"]) {
throw new Error("Subscriber already exists.");
}

const response = await fetch(`/api/subscriber/imsi-${imsi}`, {
method: "POST",
headers: {
Expand Down Expand Up @@ -73,6 +86,10 @@ export const createSubscriber = async ({
return response.json();
} catch (error) {
console.error(error);
throw new Error("Failed to create the subscriber.");
const details =
error instanceof Error
? error.message
: "Failed to create the subscriber.";
throw new Error(details);
}
};