Skip to content

Commit

Permalink
feat(playground): Playground now gets updated in the DB
Browse files Browse the repository at this point in the history
  • Loading branch information
endevii committed Apr 21, 2024
2 parents b7d1717 + 69e00a3 commit f435db2
Show file tree
Hide file tree
Showing 7 changed files with 316 additions and 59 deletions.
58 changes: 56 additions & 2 deletions app/api/v1/antenna/[id]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { isAntenna } from '@/app/api/v1/antenna/validate';
import { pool } from '@/app/api/v1/connection';
import StatusError from '@/app/api/(utils)/StatusError';

export async function DELETE(_: Request, context: { params: { id: string } }) {
export async function DELETE(_: Request, context: { params: { id: number } }) {
try {
if (context.params.id.trim().length == 0)
if (!context.params.id)
throw new StatusError(
500,
'Internal Error: Id is missing from parameter'
Expand Down Expand Up @@ -42,3 +42,57 @@ export async function DELETE(_: Request, context: { params: { id: string } }) {
return NextResponse.json({ message }, { status });
}
}
export async function PUT(
request: Request,
context: { params: { id: number } }
) {
try {
if (!context.params.id) {
throw {
status: 500,
message: 'Internal Error: Id is missing from parameter',
};
}

const id = context.params.id;

const { frequency } = (await request.json()) as { frequency: number };

const client = await pool.connect();

const query = `
UPDATE Antennas
SET playground_frequency = $1
WHERE id = $2
RETURNING *;
`;
const result = await client.query(query, [frequency, id]);

if (result.rowCount === 0) {
throw new StatusError(404, `Antenna with ${id} does not exist.`);
}
if (!isAntenna(result.rows[0])) {
throw new StatusError(
500,
'Antenna must be updated with correct data types / values.'
);
}

const updatedAntenna = result.rows[0];

client.release();

return NextResponse.json(updatedAntenna, { status: 200 });
} catch (error) {
let message = 'Internal Server Error';
let status = 500;
if (typeof error === 'object') {
if (error && 'message' in error && typeof error.message === 'string')
message = error.message;
if (error && 'status' in error && typeof error.status === 'number')
status = error.status;
}

return NextResponse.json({ message }, { status });
}
}
53 changes: 53 additions & 0 deletions app/api/v1/antenna/route.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { NextResponse } from 'next/server';
import { pool } from '../connection';
import { isAntenna, Antenna } from '@/app/api/v1/antenna/validate';
import StatusError from '@/app/api/(utils)/StatusError';
import { labeledFrequencies } from '@/app/types';

export async function GET() {
try {
Expand All @@ -14,3 +17,53 @@ export async function GET() {
return NextResponse.json({ message }, { status: 500 });
}
}

// request body should be:
// {
// "dataArray": [
// {labeledFrequencies}
// ]
// }
export async function PUT(request: Request) {
const updatedAntennas: Antenna[] = [];
try {
const { dataArray }: { dataArray: labeledFrequencies[] } =
(await request.json()) as { dataArray: labeledFrequencies[] };
const client = await pool.connect();

for (const { id, frequency } of dataArray) {
const query = `
UPDATE Antennas
SET playground_frequency = $1
WHERE id = $2
RETURNING *
`;
const result = await client.query(query, [frequency, id]);
if (result.rowCount === 0) {
throw new StatusError(404, `Antenna with ${id} does not exist.`);
}
if (!isAntenna(result.rows[0])) {
throw new StatusError(
500,
'Antenna must be updated with correct data types/values'
);
}
const updatedAntenna = result.rows[0];
updatedAntennas.push(updatedAntenna);
}
client.release();

return NextResponse.json(updatedAntennas, { status: 200 });
} catch (error) {
let message = 'Internal Server Error';
let status = 500;
if (typeof error === 'object') {
if (error && 'message' in error && typeof error.message === 'string')
message = error.message;
if (error && 'status' in error && typeof error.status === 'number')
status = error.status;
}

return NextResponse.json({ message }, { status });
}
}
17 changes: 9 additions & 8 deletions app/api/v1/antenna/validate.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
import { z } from 'zod';

export type Antenna = {
id: string;
id: number;
name: string;
hostname: string;
model: string;
modelname: string;
frequency: number;
playground_frequency: number;
latitude: string;
longitude: number;
longitude: string;
azimuth: number;
typeAntenna: number;
typeantenna: number;
antenna_status: string;
cpu: number;
ram: string;
ram: number;
};

export function isAntenna(antenna: unknown): antenna is Antenna {
const antennaSchema = z.object({
id: z.string(),
id: z.number(),
name: z.string(),
hostname: z.string(),
model: z.string(),
Expand All @@ -29,9 +29,10 @@ export function isAntenna(antenna: unknown): antenna is Antenna {
latitude: z.string(),
longitude: z.string(),
azimuth: z.number().int().gte(0).lt(360),
typeAntenna: z.number().gte(0).lte(2),
cpu: z.string(),
ram: z.string(),
typeantenna: z.number().gte(0).lte(2),
antenna_status: z.string(),
cpu: z.number(),
ram: z.number(),
});

const res = antennaSchema.safeParse(antenna);
Expand Down
152 changes: 124 additions & 28 deletions app/components/Map.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import {
initializePlayground,
replacePlayground,
replaceOldPlayground,
clearToUpdatePlayground,
replaceToUpdatePlayground,
} from '../../lib/features/playground/playgroundSlice';

import {
Expand Down Expand Up @@ -139,6 +141,8 @@ export default function Map() {

const antennasData = useAppSelector((state) => state.currentAntennas.value);

const toBeUpdated = useAppSelector((state) => state.playground.toBeUpdated);

const dispatch = useAppDispatch();

useEffect(() => {
Expand All @@ -151,7 +155,7 @@ export default function Map() {
}

const responseJson = (await response.json()) as Antenna[];
console.log('Antennas Data:', responseJson);

return responseJson;
} catch (e) {
if (e instanceof Error) {
Expand Down Expand Up @@ -182,9 +186,23 @@ export default function Map() {
})
);

const playgroundAntennasData: AccessPoint[] = accessPoints.map(
(ap: Antenna) => ({
id: ap.id,
modelName: ap.modelname,
lat: ap.latitude,
lon: ap.longitude,
frequency: ap.playground_frequency || 0,
azimuth: ap.azimuth || 0,
antenna_status: ap.antenna_status || 'N/A',
cpu: ap.cpu || -1,
ram: ap.ram || -1,
})
);

if (!initialized.current) {
store.dispatch(initializeActual(antennasData));
store.dispatch(initializePlayground(antennasData));
store.dispatch(initializePlayground(playgroundAntennasData));
store.dispatch(initializeCurrent(antennasData));
initialized.current = true;
}
Expand Down Expand Up @@ -212,7 +230,8 @@ export default function Map() {
fetchDataAndSetAntennasData().catch((error) => {
console.error(error);
});
}, [store]);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

useEffect(() => {
const sectorlobesData: SectorlobeData[] = antennasData.data.map((ap) => {
Expand Down Expand Up @@ -346,6 +365,43 @@ export default function Map() {
return amount;
}, [intersections]);

const handleSavePlayground = async () => {
const url = '/api/v1/antenna/';
if (toBeUpdated.length === 0) {
alert('No changes to save');
return;
}
for (let i = 0; i < 3; i++) {
try {
const response = await fetch(url, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ dataArray: toBeUpdated }),
});
if (!response.ok) {
throw new Error(
`${response.status} error: Failed to save playground data`
);
}
dispatch(replacePlayground(antennasData.data));
dispatch(replaceOldPlayground(antennasData.data));
dispatch(clearToUpdatePlayground());
alert('Playground data saved successfully');
break;
} catch (e) {
if (e instanceof Error) {
console.error(`Attempt ${i + 1} failed: ${e.message}`);
if (i === 2) {
alert('Failed to save playground data');
}
await new Promise((resolve) => setTimeout(resolve, 1000));
}
}
}
};

return (
<>
<div className="absolute right-0 top-0 z-[1001] flex flex-col justify-center rounded-md bg-black p-2">
Expand Down Expand Up @@ -431,48 +487,88 @@ export default function Map() {
</MapContainer>
<div className="m-5 flex flex-row justify-between align-middle">
<div className="flex flex-row justify-center">
<h1 className="mx-2 p-1 align-baseline">Current mode: </h1>
<button
className="rounded-md border-[1px] border-black bg-gray-400 p-1 transition-all duration-300 ease-in-out hover:bg-gray-900"
onClick={() => {
if (antennasData.mode === 'actual') {
dispatch(
changeCurrent({
mode: 'playground',
data: playgroundData,
})
<div>
<button
className="rounded-md border-[1px] border-black bg-gray-400 p-1 transition-all duration-300 ease-in-out hover:bg-gray-900"
onClick={() => alert('Updating from NYCMesh server...')}
>
Update from Server
</button>
</div>
<div className="flex flex-row justify-center">
<h1 className="mx-2 p-1 align-baseline">Current mode: </h1>
<button
className="rounded-md border-[1px] border-black bg-gray-400 p-1 transition-all duration-300 ease-in-out hover:bg-gray-900"
onClick={() => {
if (antennasData.mode === 'actual') {
dispatch(
changeCurrent({
mode: 'playground',
data: playgroundData,
})
);
} else {
dispatch(replacePlayground(antennasData.data));
dispatch(
changeCurrent({
mode: 'actual',
data: actualData,
})
);
}
}}
>
{antennasData.mode === 'actual' ? 'Current' : 'Playground'}
</button>
</div>
</div>
{antennasData.mode === 'playground' ? (
<div>
<button
className="mx-2 rounded-md border-[1px] border-black bg-gray-400 p-1 transition-all duration-300 ease-in-out hover:bg-gray-900"
onClick={() => {
if (toBeUpdated.length > 0) {
dispatch(replaceToUpdatePlayground([]));
}
const updatedData: AccessPoint[] = actualData.filter(
(antenna) => {
return (
playgroundData.find(
(oldAntenna) =>
oldAntenna.id === antenna.id &&
oldAntenna.frequency === antenna.frequency &&
oldAntenna.azimuth === antenna.azimuth
) === undefined
);
}
);
} else {
dispatch(replacePlayground(antennasData.data));
if (updatedData.length > 0) {
dispatch(replaceToUpdatePlayground(updatedData));
}
dispatch(
changeCurrent({
mode: 'actual',
mode: 'playground',
data: actualData,
})
);
}
}}
>
{antennasData.mode === 'actual' ? 'Current' : 'Playground'}
</button>
</div>
{antennasData.mode === 'playground' ? (
<div>
}}
>
Revert All Changes
</button>
<button
className="mx-2 rounded-md border-[1px] border-black bg-gray-400 p-1 transition-all duration-300 ease-in-out hover:bg-gray-900"
onClick={() => {
dispatch(
changeCurrent({ mode: 'playground', data: oldPlaygroundData })
);
dispatch(clearToUpdatePlayground());
}}
>
Revert Changes
Revert Current Changes
</button>
<button
className="mx-2 rounded-md border-[1px] border-black bg-gray-400 p-1 transition-all duration-300 ease-in-out hover:bg-gray-900"
onClick={() => {
dispatch(replaceOldPlayground(antennasData.data));
}}
onClick={() => void handleSavePlayground()}
>
Save Changes
</button>
Expand Down
Loading

0 comments on commit f435db2

Please sign in to comment.