save other attributes
This commit is contained in:
parent
d4b3b74b5f
commit
f5b9494d26
7 changed files with 120 additions and 21 deletions
|
@ -1,11 +1,20 @@
|
|||
import type { Context } from "koa";
|
||||
import type {} from "@koa/bodyparser";
|
||||
|
||||
export async function createHeartbeats(ctx: Context) {
|
||||
const results = [];
|
||||
for (const heartbeat of ctx.request.body) {
|
||||
console.log("heartbeat", heartbeat);
|
||||
const time = new Date(heartbeat.time * 1000.0);
|
||||
const resp = await fetch("http://localhost:3000/node", {
|
||||
method: "PUT",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
attributes: [
|
||||
["panorama::time/start", time.toISOString()],
|
||||
["panorama/codetrack::project", heartbeat.project],
|
||||
],
|
||||
}),
|
||||
});
|
||||
const data = await resp.json();
|
||||
results.push({
|
||||
|
@ -13,6 +22,6 @@ export async function createHeartbeats(ctx: Context) {
|
|||
});
|
||||
}
|
||||
ctx.status = 400;
|
||||
console.log("results", results);
|
||||
// console.log("results", results);
|
||||
ctx.body = {};
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ attributes:
|
|||
- panorama::time/start
|
||||
|
||||
- name: project
|
||||
type: option<string>
|
||||
type: string
|
||||
|
||||
endpoints:
|
||||
- route: /api/v1/users/current/heartbeats.bulk
|
||||
|
|
|
@ -17,7 +17,10 @@ export function sanitizeName(name: string): string {
|
|||
|
||||
export async function loadApps(): Promise<Map<string, CustomApp>> {
|
||||
const apps = new Map();
|
||||
const paths = ["/Users/michael/Projects/panorama/apps/codetrack"];
|
||||
const paths = [
|
||||
"/Users/michael/Projects/panorama/apps/std",
|
||||
"/Users/michael/Projects/panorama/apps/codetrack",
|
||||
];
|
||||
|
||||
for (const path of paths) {
|
||||
const app = await loadApp(path);
|
||||
|
@ -32,15 +35,18 @@ export async function loadApp(path: string): Promise<CustomApp> {
|
|||
const manifest = manifestSchema.parse(manifestRaw);
|
||||
const sanitizedName = sanitizeName(manifest.name);
|
||||
|
||||
// load code
|
||||
const codePath = join(path, manifest.code);
|
||||
const codeModule = await import(codePath);
|
||||
|
||||
// wire up routes
|
||||
const router = new Router();
|
||||
for (const endpoint of manifest.endpoints || []) {
|
||||
const func = codeModule[endpoint.handler];
|
||||
router.all(endpoint.route, func);
|
||||
|
||||
// load code
|
||||
if (manifest.code) {
|
||||
const codePath = join(path, manifest.code);
|
||||
const codeModule = await import(codePath);
|
||||
|
||||
// wire up routes
|
||||
for (const endpoint of manifest.endpoints || []) {
|
||||
const func = codeModule[endpoint.handler];
|
||||
router.all(endpoint.route, func);
|
||||
}
|
||||
}
|
||||
|
||||
await dataSource.transaction(async (em) => {
|
||||
|
@ -52,8 +58,6 @@ export async function loadApp(path: string): Promise<CustomApp> {
|
|||
.getOne();
|
||||
let appId = app?.id;
|
||||
|
||||
console.log("app id", appId);
|
||||
|
||||
if (!appId) {
|
||||
const result = await em.getRepository(App).insert({
|
||||
id: sanitizedName,
|
||||
|
@ -63,6 +67,8 @@ export async function loadApp(path: string): Promise<CustomApp> {
|
|||
appId = result.identifiers[0].id;
|
||||
}
|
||||
|
||||
if (!appId) throw new Error("could not initialize");
|
||||
|
||||
// register all the attributes
|
||||
for (const attribute of manifest.attributes || []) {
|
||||
await em
|
||||
|
|
|
@ -2,7 +2,7 @@ import { z } from "zod";
|
|||
|
||||
export const manifestSchema = z.object({
|
||||
name: z.string(),
|
||||
code: z.string(),
|
||||
code: z.string().optional(),
|
||||
|
||||
attributes: z
|
||||
.array(
|
||||
|
|
|
@ -7,7 +7,7 @@ const AppDataSource = new DataSource({
|
|||
|
||||
entities: [PNode, App, Attribute, NodeHasAttribute],
|
||||
synchronize: true,
|
||||
// logging: true,
|
||||
logging: true,
|
||||
|
||||
migrationsTableName: "migrations",
|
||||
migrations: ["migrations/*"],
|
||||
|
|
|
@ -1,9 +1,26 @@
|
|||
import { Column, Entity, ManyToOne, OneToMany, PrimaryColumn } from "typeorm";
|
||||
import {
|
||||
Column,
|
||||
Entity,
|
||||
Index,
|
||||
ManyToOne,
|
||||
OneToMany,
|
||||
PrimaryColumn,
|
||||
UpdateDateColumn,
|
||||
} from "typeorm";
|
||||
|
||||
@Entity({ name: "node" })
|
||||
export class PNode {
|
||||
@PrimaryColumn()
|
||||
id!: string;
|
||||
|
||||
@UpdateDateColumn()
|
||||
lastUpdated!: Date;
|
||||
|
||||
@OneToMany(
|
||||
() => NodeHasAttribute,
|
||||
(hasAttr) => hasAttr.nodeId,
|
||||
)
|
||||
attributes!: NodeHasAttribute[];
|
||||
}
|
||||
|
||||
@Entity()
|
||||
|
@ -11,6 +28,7 @@ export class App {
|
|||
@PrimaryColumn()
|
||||
id!: string;
|
||||
|
||||
@Column()
|
||||
name!: string;
|
||||
|
||||
@OneToMany(
|
||||
|
@ -33,19 +51,24 @@ export class Attribute {
|
|||
name!: string;
|
||||
|
||||
@Column()
|
||||
@Index({})
|
||||
type!: string;
|
||||
}
|
||||
|
||||
@Entity()
|
||||
export class NodeHasAttribute {
|
||||
@PrimaryColumn()
|
||||
@ManyToOne(
|
||||
() => PNode,
|
||||
(node) => node.id,
|
||||
)
|
||||
nodeId!: string;
|
||||
|
||||
@PrimaryColumn()
|
||||
appId!: string;
|
||||
|
||||
@PrimaryColumn()
|
||||
name!: string;
|
||||
attrName!: string;
|
||||
|
||||
@Column({ nullable: true })
|
||||
nodeRef: number | undefined;
|
||||
|
@ -57,6 +80,4 @@ export class NodeHasAttribute {
|
|||
boolean: boolean | undefined;
|
||||
@Column({ nullable: true })
|
||||
instant: Date | undefined;
|
||||
@Column({ nullable: true })
|
||||
url: string | undefined;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import Router from "@koa/router";
|
||||
import { PNode } from "../models";
|
||||
import { App, Attribute, NodeHasAttribute, PNode } from "../models";
|
||||
import { uuidv7 } from "uuidv7";
|
||||
import { dataSource } from "../db";
|
||||
|
||||
|
@ -8,7 +8,70 @@ export const nodeRouter = new Router();
|
|||
nodeRouter.put("/", async (ctx) => {
|
||||
const id = uuidv7();
|
||||
const body = ctx.request.body;
|
||||
await dataSource.getRepository(PNode).insert({ id });
|
||||
await dataSource.transaction(async (em) => {
|
||||
await em.getRepository(PNode).insert({ id });
|
||||
|
||||
const attributes: [string, any][] = body?.attributes ?? [];
|
||||
const attributeNames = attributes
|
||||
.map(([name, _]) => name)
|
||||
.map((name) => name.split("::"));
|
||||
const appNames = new Set(attributeNames.map(([app, _]) => app));
|
||||
const attrNames = new Set(attributeNames.map(([_, attr]) => attr));
|
||||
console.log("attribute names", appNames);
|
||||
|
||||
const result = await em
|
||||
.createQueryBuilder(App, "app")
|
||||
.where("app.name IN (:...appNames)", { appNames: [...appNames] })
|
||||
.getMany();
|
||||
const appIdMappingList: [string, string][] = result.map((app) => [
|
||||
app.name,
|
||||
app.id,
|
||||
]);
|
||||
const appIds = new Set([...appIdMappingList.map(([_, id]) => id)]);
|
||||
const appIdMapping = new Map([...appIdMappingList]);
|
||||
|
||||
const result2 = await em
|
||||
.createQueryBuilder(Attribute, "attr")
|
||||
.where("attr.appId IN (:...appIds)", { appIds: [...appIds] })
|
||||
.andWhere("attr.name IN (:...attrNames)", { attrNames: [...attrNames] })
|
||||
.getMany();
|
||||
const attributeTypesList: [string, string][] = result2.map((attr) => [
|
||||
`${attr.appId}::${attr.name}`,
|
||||
attr.type,
|
||||
]);
|
||||
const attributeTypes = new Map(attributeTypesList);
|
||||
console.log("attribute types", attributeTypes);
|
||||
|
||||
const convertValue = (
|
||||
type: string,
|
||||
value: string,
|
||||
): Partial<NodeHasAttribute> => {
|
||||
switch (type) {
|
||||
case "string":
|
||||
return { string: value };
|
||||
case "datetime":
|
||||
return { instant: new Date(value) };
|
||||
default:
|
||||
console.error(`fuck, unknown type ${type}`);
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
const attributesMapped: Partial<NodeHasAttribute>[] = attributes.map(
|
||||
([name, value]) => {
|
||||
const [app, attrName] = name.split("::");
|
||||
const appId = appIdMapping.get(app)!;
|
||||
const type = attributeTypes.get(`${appId}::${attrName}`)!;
|
||||
console.log("converting type", type, value);
|
||||
return { nodeId: id, appId, attrName, ...convertValue(type, value) };
|
||||
},
|
||||
);
|
||||
|
||||
console.log("mappped", attributesMapped);
|
||||
|
||||
const result3 = await em.insert(NodeHasAttribute, attributesMapped);
|
||||
});
|
||||
|
||||
ctx.body = { id };
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in a new issue