HTTP Interface
This commit is contained in:
+27
-2
@@ -70,11 +70,36 @@ export default class AASHelper {
|
|||||||
return current;
|
return current;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static findElement(environment: types.Class, checkFunction: (element: types.Class) => boolean): types.Class | null {
|
public static traverseByShortIds(start: types.Class, shortIds: string[]): types.Class | null {
|
||||||
for (const element of environment.descend()) {
|
let current: types.Class | null = start;
|
||||||
|
for (const shortId of shortIds) {
|
||||||
|
if (current === null) break;
|
||||||
|
|
||||||
|
current = AASHelper.findChildByIdShort(current, shortId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getElementByIdPath(env: types.Environment, submodelOrIdShort: string | types.Submodel, idShorts: string[]): types.Class | null {
|
||||||
|
const sm = typeof submodelOrIdShort === "string" ? AASHelper.findSMByIdShort(env, submodelOrIdShort) : submodelOrIdShort;
|
||||||
|
if (sm === null) return null;
|
||||||
|
return AASHelper.traverseByShortIds(sm, idShorts);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static findElement(start: types.Class, checkFunction: (element: types.Class) => boolean): types.Class | null {
|
||||||
|
for (const element of start.descend()) {
|
||||||
if (checkFunction(element)) return element;
|
if (checkFunction(element)) return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static findChildByIdShort(start: types.Class, idShort: string): types.Class | null {
|
||||||
|
for (const child of start.descendOnce()) {
|
||||||
|
if ((child as any).idShort === idShort) return child;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,8 @@ import type { Express } from "express";
|
|||||||
import express from "express";
|
import express from "express";
|
||||||
import { jsonization, types } from "@aas-core-works/aas-core3.0-typescript";
|
import { jsonization, types } from "@aas-core-works/aas-core3.0-typescript";
|
||||||
import AASInterfaceServer from "../server";
|
import AASInterfaceServer from "../server";
|
||||||
import type { OnRequestCallback, Request } from "types/requests";
|
import type { OnRequestCallback } from "types/requests";
|
||||||
|
import AASHelper from "../aasHelper";
|
||||||
|
|
||||||
export type Config = {
|
export type Config = {
|
||||||
bindAddress: string,
|
bindAddress: string,
|
||||||
@@ -23,9 +24,7 @@ export default class HTTPInterfaceServer extends AASInterfaceServer<Config> {
|
|||||||
|
|
||||||
public prepare(): void {
|
public prepare(): void {
|
||||||
this.app.use(express.json());
|
this.app.use(express.json());
|
||||||
this.app.get("/aas", (_, res) => {
|
this.createRoutes();
|
||||||
res.json(jsonization.toJsonable(this.aas));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public run(): void {
|
public run(): void {
|
||||||
@@ -45,11 +44,154 @@ export default class HTTPInterfaceServer extends AASInterfaceServer<Config> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected findElementByRequest(request: Request) {
|
private createRoutes(): void {
|
||||||
|
const NOT_IMPLEMENTED = (_req: express.Request, res: express.Response) => res.status(501).end();
|
||||||
|
|
||||||
}
|
const getSM = (id: string) => {
|
||||||
|
if (!id) return null;
|
||||||
|
|
||||||
private createRoutes()/*: express.Router*/ {
|
try {
|
||||||
// TODO
|
// This is what the specification says
|
||||||
|
const decoded = Buffer.from(id, "base64").toString("utf-8");
|
||||||
|
let sm = AASHelper.findSMById(this.aas, decoded);
|
||||||
|
|
||||||
|
if (sm === null) {
|
||||||
|
// Normally, at least in BaSyx just the shortId is used, so just test if we find something like this
|
||||||
|
sm = AASHelper.findSMByIdShort(this.aas, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sm;
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// /aas
|
||||||
|
this.app.get("/aas", (_, res) => res.json(jsonization.toJsonable(this.aas)).end());
|
||||||
|
this.app.put("/aas", NOT_IMPLEMENTED);
|
||||||
|
this.app.delete("/aas", NOT_IMPLEMENTED);
|
||||||
|
// /aas/$reference
|
||||||
|
this.app.get("/aas/$reference", NOT_IMPLEMENTED);
|
||||||
|
// /aas/asset-information
|
||||||
|
this.app.get("/aas/asset-information", NOT_IMPLEMENTED);
|
||||||
|
this.app.put("/aas/asset-information", NOT_IMPLEMENTED);
|
||||||
|
this.app.get("/aas/asset-information/thumbnail", NOT_IMPLEMENTED);
|
||||||
|
this.app.put("/aas/asset-information/thumbnail", NOT_IMPLEMENTED);
|
||||||
|
this.app.delete("/aas/asset-information/thumbnail", NOT_IMPLEMENTED);
|
||||||
|
// /aas/submodel-refs
|
||||||
|
this.app.get("/aas/submodel-refs", NOT_IMPLEMENTED);
|
||||||
|
this.app.post("/aas/submodel-refs", NOT_IMPLEMENTED);
|
||||||
|
this.app.delete("/aas/submodel-refs/:submodelIdentifier", NOT_IMPLEMENTED);
|
||||||
|
// /aas/submodels
|
||||||
|
this.app.get("/aas/submodels/:smId", (req, res) => {
|
||||||
|
const sm = getSM(req.params.smId);
|
||||||
|
|
||||||
|
if (sm === null) return res.status(404).end();
|
||||||
|
return res.json(jsonization.toJsonable(sm)).end()
|
||||||
|
});
|
||||||
|
this.app.put("/aas/submodels/:smId", NOT_IMPLEMENTED);
|
||||||
|
this.app.patch("/aas/submodels/:smId", NOT_IMPLEMENTED);
|
||||||
|
this.app.delete("/aas/submodels/:smId", NOT_IMPLEMENTED);
|
||||||
|
// /aas/submodels/:smId/submodel-elements
|
||||||
|
this.app.use("/aas/submodels/:smId/submodel-elements", (req, res) => {
|
||||||
|
if (req.method !== "GET") return res.status(501).end();
|
||||||
|
|
||||||
|
const sm = getSM(req.params.smId);
|
||||||
|
|
||||||
|
if (sm === null || sm.submodelElements === null) return res.status(404).end();
|
||||||
|
return res.json(sm.submodelElements.map(element => jsonization.toJsonable(element))).end();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Main function
|
||||||
|
this.app.use("/aas/submodels/:smId/submodel-elements/*", (req, res) => {
|
||||||
|
const idShortPathString = (req.params as any)[0];
|
||||||
|
console.log(idShortPathString);
|
||||||
|
if (idShortPathString.endsWith("/")) res.status(400).end();
|
||||||
|
|
||||||
|
const sm = getSM(req.params.smId);
|
||||||
|
if (sm === null || sm.submodelElements === null) return res.status(404).end();
|
||||||
|
|
||||||
|
if (idShortPathString === "") {
|
||||||
|
if (req.method === "GET") {
|
||||||
|
const json = sm.submodelElements.map(element => jsonization.toJsonable(element));
|
||||||
|
return res.json(json).end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Currently we don't support these operations
|
||||||
|
if (idShortPathString.endsWith("$value") ||
|
||||||
|
idShortPathString.endsWith("$reference") ||
|
||||||
|
idShortPathString.endsWith("$metadata") ||
|
||||||
|
idShortPathString.endsWith("$path")) return res.status(501).end();
|
||||||
|
|
||||||
|
// If operation ids are changed, you can change the format here
|
||||||
|
const endingMatchSearch = idShortPathString.match(/(\/attachment$|\/invoke$|\/invoke-async$|\/operation-status\/[a-zA-Z0-9\-]+$|\/operation-result\/[a-zA-Z0-9\-]+$)/)
|
||||||
|
const endingMatch = endingMatchSearch?.[0];
|
||||||
|
|
||||||
|
const idShortPath = endingMatch ? idShortPathString.replace(new RegExp(`${endingMatch}$`), "").split("/") : idShortPathString.split("/");
|
||||||
|
|
||||||
|
|
||||||
|
switch (endingMatch) {
|
||||||
|
case undefined: {
|
||||||
|
if (req.method !== "GET") return res.status(501).end();
|
||||||
|
const prop = AASHelper.getElementByIdPath(this.aas, sm, idShortPath);
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
//! Check if mapping and live data is available!
|
||||||
|
|
||||||
|
if (prop === null) return res.status(404).end();
|
||||||
|
return res.json(jsonization.toJsonable(prop)).end();
|
||||||
|
}
|
||||||
|
case "/attachment":
|
||||||
|
return res.status(501).end();
|
||||||
|
case "/invoke": {
|
||||||
|
if (req.method !== "POST") return res.status(405).end();
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// Mapping, dann Befehl
|
||||||
|
// Parameter?
|
||||||
|
|
||||||
|
return res.send(`Operation result for ${idShortPath}`).end();
|
||||||
|
}
|
||||||
|
case "/invoke-async": {
|
||||||
|
if (req.method !== "POST") return res.status(405).end();
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// Invoke operation and get handle
|
||||||
|
|
||||||
|
const handle = "TODO";
|
||||||
|
|
||||||
|
const operationPaths = [`${idShortPath.join("/")}/operation-status/${handle}`, `${idShortPath.join("/")}/operation-result/${handle}`];
|
||||||
|
return res.status(202).header("Location", operationPaths).end();
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
// operation-status, operation-result or anything else
|
||||||
|
const [_, operation, handle] = endingMatch.split("/");
|
||||||
|
|
||||||
|
switch (operation) {
|
||||||
|
case "operation-status": {
|
||||||
|
if (req.method !== "GET") return res.status(405).end();
|
||||||
|
// TODO
|
||||||
|
return res.json({
|
||||||
|
"status": "TODO",
|
||||||
|
handle
|
||||||
|
}).end();
|
||||||
|
}
|
||||||
|
case "operation-result": {
|
||||||
|
if (req.method !== "GET") return res.status(405).end();
|
||||||
|
// TODO
|
||||||
|
return res.json({
|
||||||
|
"status": "TODO",
|
||||||
|
handle
|
||||||
|
}).end();
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return res.status(404).end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -19,24 +19,4 @@ export default abstract class AASInterfaceServer<ConfigInterface> {
|
|||||||
public abstract stop(): void;
|
public abstract stop(): void;
|
||||||
public abstract notify(event: any): void;
|
public abstract notify(event: any): void;
|
||||||
|
|
||||||
protected abstract findElementByRequest(request: Request): any | null;
|
|
||||||
|
|
||||||
protected findElementBySemanticId(id: string): types.Class | null {
|
|
||||||
for (const element of this.aas.descend()) {
|
|
||||||
if ((element as any).semanticId === id) {
|
|
||||||
return element;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected findElementByShortId(id: string): types.Class | null {
|
|
||||||
for (const element of this.aas.descend()) {
|
|
||||||
if ((element as any).idShort === id) {
|
|
||||||
return element;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user