ngsi-ld public api code
This commit is contained in:
@@ -0,0 +1,10 @@
|
|||||||
|
import express from "express";
|
||||||
|
import { getEntities, getEntitiesOfType, getEntity } from "./livedata.controller.js";
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
router.get("/entities", getEntities);
|
||||||
|
router.get("/entities", getEntitiesOfType);
|
||||||
|
router.get("/entities/:urn", getEntity);
|
||||||
|
|
||||||
|
export default router;
|
||||||
@@ -0,0 +1,172 @@
|
|||||||
|
import type { Request, Response, NextFunction } from "express";
|
||||||
|
import { getActiveUserData, getRedisRoomData, type RedisRoomData, type RedisUserData } from "./livedata.repository.js";
|
||||||
|
import { prisma } from "../../lib/prisma.js";
|
||||||
|
|
||||||
|
interface Params {
|
||||||
|
urn: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type Relationship = {
|
||||||
|
type: "Relationship";
|
||||||
|
object: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Property<T = any> = {
|
||||||
|
type: "Property";
|
||||||
|
value: T;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface Entity {
|
||||||
|
id: string;
|
||||||
|
type: string;
|
||||||
|
name: {
|
||||||
|
type: "Property";
|
||||||
|
value: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface User extends Entity {
|
||||||
|
type: "User";
|
||||||
|
locatedIn: Relationship;
|
||||||
|
rssi: Property<number>;
|
||||||
|
authenticationStatus: Property<string>;
|
||||||
|
observedAt: Property<string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Room extends Entity {
|
||||||
|
type: "Room";
|
||||||
|
occupancy: Property<number>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function convertToNGSIUser(user: RedisUserData): User {
|
||||||
|
return {
|
||||||
|
id: `urn:ngsi-ld:User:${user.userID}`,
|
||||||
|
type: "User",
|
||||||
|
name: {
|
||||||
|
type: "Property",
|
||||||
|
value: user.name
|
||||||
|
},
|
||||||
|
locatedIn: {
|
||||||
|
type: "Relationship",
|
||||||
|
object: user.room
|
||||||
|
},
|
||||||
|
rssi: {
|
||||||
|
type: "Property",
|
||||||
|
value: user.rssi,
|
||||||
|
},
|
||||||
|
authenticationStatus: {
|
||||||
|
type: "Property",
|
||||||
|
value: user.verified ? "verified" : "unverified"
|
||||||
|
},
|
||||||
|
observedAt: {
|
||||||
|
type: "Property",
|
||||||
|
value: (new Date(user.timestamp)).toISOString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function convertToNGSIRoom(room: RedisRoomData): Promise<Room | null> {
|
||||||
|
const roomName = await prisma.room.findUnique({
|
||||||
|
where: { id: room.roomID },
|
||||||
|
select: { name: true }
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!roomName?.name) return null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: `urn:ngsi-ld:Room:${room.roomID}`,
|
||||||
|
type: "Room",
|
||||||
|
name: {
|
||||||
|
type: "Property",
|
||||||
|
value: roomName.name
|
||||||
|
},
|
||||||
|
occupancy: {
|
||||||
|
type: "Property",
|
||||||
|
value: room.occupancy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getNGSIUsers(): Promise<User[]> {
|
||||||
|
let users: User[] = [];
|
||||||
|
|
||||||
|
const result: RedisUserData[] = await getActiveUserData();
|
||||||
|
|
||||||
|
for (const r of result) {
|
||||||
|
const user = convertToNGSIUser(r);
|
||||||
|
|
||||||
|
users.push(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
return users;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getNGSIRooms(): Promise<Room[]> {
|
||||||
|
let rooms: Room[] = [];
|
||||||
|
|
||||||
|
const result: RedisRoomData[] = await getRedisRoomData();
|
||||||
|
|
||||||
|
for (const r of result) {
|
||||||
|
const room = await convertToNGSIRoom(r);
|
||||||
|
if (room == null) continue;
|
||||||
|
|
||||||
|
rooms.push(room);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rooms;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getEntities(req: Request, res: Response, next: NextFunction) {
|
||||||
|
if (Object.keys(req.query).length > 0) return next();
|
||||||
|
|
||||||
|
let entities: Entity[] = [];
|
||||||
|
|
||||||
|
entities.push(...await getNGSIUsers());
|
||||||
|
entities.push(...await getNGSIRooms());
|
||||||
|
|
||||||
|
return res.status(200).send(entities);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getEntitiesOfType(req: Request, res: Response) {
|
||||||
|
const { type } = req.query;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case "user": return res.status(200).send(await getNGSIUsers());
|
||||||
|
case "room": return res.status(200).send(await getNGSIRooms());
|
||||||
|
default:
|
||||||
|
return res.sendStatus(400);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getEntity(req: Request<Params>, res: Response) {
|
||||||
|
const { urn } = req.params;
|
||||||
|
|
||||||
|
if (!urn || !urn.startsWith("urn:ngsi-ld:")) return res.sendStatus(400);
|
||||||
|
|
||||||
|
const entity = urn.slice(12);
|
||||||
|
|
||||||
|
if (!entity) return res.sendStatus(400);
|
||||||
|
|
||||||
|
if (entity.toLowerCase().startsWith("room:")) {
|
||||||
|
const roomID = entity.slice(5);
|
||||||
|
|
||||||
|
if (!roomID) return res.sendStatus(400);
|
||||||
|
|
||||||
|
const data = await getRedisRoomData(roomID);
|
||||||
|
|
||||||
|
if (!data.length) return res.sendStatus(404);
|
||||||
|
|
||||||
|
return res.status(200).send(await convertToNGSIRoom(data[0]!));
|
||||||
|
} else if (entity.toLowerCase().startsWith("user:")) {
|
||||||
|
const userID = entity.slice(5);
|
||||||
|
|
||||||
|
if (!userID) return res.sendStatus(400);
|
||||||
|
|
||||||
|
const data = await getActiveUserData(userID);
|
||||||
|
|
||||||
|
if (!data.length) return res.sendStatus(404);
|
||||||
|
|
||||||
|
return res.status(200).send(convertToNGSIUser(data[0]!));
|
||||||
|
} else
|
||||||
|
return res.sendStatus(400);
|
||||||
|
}
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
import { prisma } from "../../lib/prisma.js";
|
||||||
|
import { getRedis } from "../../redis/redis.js";
|
||||||
|
|
||||||
|
export interface RedisUserData {
|
||||||
|
userID: string;
|
||||||
|
name: string;
|
||||||
|
rssi: number;
|
||||||
|
room: string;
|
||||||
|
verified: boolean;
|
||||||
|
timestamp: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RedisRoomData {
|
||||||
|
roomID: string;
|
||||||
|
occupancy: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getActiveUserData(userID?: string): Promise<RedisUserData[]> {
|
||||||
|
const redis = getRedis();
|
||||||
|
|
||||||
|
if (!userID) {
|
||||||
|
let cursor = "0";
|
||||||
|
let result: RedisUserData[] = [];
|
||||||
|
|
||||||
|
do {
|
||||||
|
const res = await redis.scan(cursor, {
|
||||||
|
MATCH: "user:*",
|
||||||
|
COUNT: 100,
|
||||||
|
});
|
||||||
|
|
||||||
|
cursor = res.cursor;
|
||||||
|
|
||||||
|
const values = await Promise.all(
|
||||||
|
res.keys.map((key) => redis.get(key))
|
||||||
|
);
|
||||||
|
|
||||||
|
result.push(...values
|
||||||
|
.filter((v): v is string => v !== null)
|
||||||
|
.map((v) => JSON.parse(v))
|
||||||
|
);
|
||||||
|
|
||||||
|
} while (cursor !== "0");
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const _userID = await redis.get(`_user:${userID}`);
|
||||||
|
|
||||||
|
if (!_userID) return [];
|
||||||
|
|
||||||
|
const res = await redis.get(`user:${_userID}`);
|
||||||
|
|
||||||
|
if (!res) return [];
|
||||||
|
|
||||||
|
const resObj: RedisUserData = JSON.parse(res);
|
||||||
|
|
||||||
|
return [resObj];
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getRedisRoomData(roomID?: string): Promise<RedisRoomData[]> {
|
||||||
|
const redis = getRedis();
|
||||||
|
|
||||||
|
if (!roomID) {
|
||||||
|
let cursor = "0";
|
||||||
|
let result: RedisRoomData[] = [];
|
||||||
|
|
||||||
|
do {
|
||||||
|
const res = await redis.scan(cursor, {
|
||||||
|
MATCH: "room:*",
|
||||||
|
COUNT: 100,
|
||||||
|
});
|
||||||
|
|
||||||
|
cursor = res.cursor;
|
||||||
|
|
||||||
|
const values = await Promise.all(
|
||||||
|
res.keys.map(async (key) => ({
|
||||||
|
roomID: key,
|
||||||
|
occupancy: await redis.get(key),
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
|
||||||
|
result.push(...values
|
||||||
|
.filter((v) => v.occupancy !== null)
|
||||||
|
.map((v) => ({
|
||||||
|
roomID: v.roomID.slice(5),
|
||||||
|
occupancy: Number(v.occupancy),
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
|
||||||
|
} while (cursor !== "0");
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await redis.get(`room:${roomID}`);
|
||||||
|
|
||||||
|
if (!res) return [];
|
||||||
|
|
||||||
|
return [{ roomID: roomID, occupancy: Number(res) }];
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getRoomIDs(): Promise<{ id: string; }[]> {
|
||||||
|
return await prisma.room.findMany({
|
||||||
|
select: { id: true }
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user