All files / src/http api.ts

38.33% Statements 23/60
0% Branches 0/21
28.57% Functions 6/21
37.93% Lines 22/58

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 1455x         5x 6x   6x         6x       6x             6x             6x                                             6x             3x     3x 3x       3x 2x 2x 2x       2x         3x 2x 2x 2x       3x                                                                                                                
import express, {Router} from "express";
import {Collection} from "../collection-abstract";
import {Delta} from "../delta";
import {RhizomeNode} from "../node";
 
export class HttpApi {
  router = Router();
 
  constructor(readonly rhizomeNode: RhizomeNode) {
    // --------------- deltas ----------------
 
    // Serve list of all deltas accepted
    // TODO: This won't scale well
    this.router.get("/deltas", (_req: express.Request, res: express.Response) => {
      res.json(this.rhizomeNode.deltaStream.deltasAccepted);
    });
 
    this.router.get("/delta/ids", (_req: express.Request, res: express.Response) => {
      res.json({
        ids: this.rhizomeNode.deltaStream.deltasAccepted.map(({id}) => id)
      });
    });
 
    // Get the number of deltas ingested by this node
    this.router.get("/deltas/count", (_req: express.Request, res: express.Response) => {
      res.json(this.rhizomeNode.deltaStream.deltasAccepted.length);
    });
 
    // --------------- peers ----------------
 
    // Get the list of peers seen by this node (including itself)
    this.router.get("/peers", (_req: express.Request, res: express.Response) => {
      res.json(this.rhizomeNode.peers.peers.map(
        ({reqAddr, publishAddr, isSelf, isSeedPeer}) => {
          const deltasAcceptedCount = this.rhizomeNode.deltaStream.deltasAccepted
            .filter((delta: Delta) => {
              return delta.receivedFrom?.addr == reqAddr.addr &&
                delta.receivedFrom?.port == reqAddr.port;
            })
            .length;
          const peerInfo = {
            reqAddr: reqAddr.toAddrString(),
            publishAddr: publishAddr?.toAddrString(),
            isSelf,
            isSeedPeer,
            deltaCount: {
              accepted: deltasAcceptedCount
            }
          };
          return peerInfo;
        }));
    });
 
    // Get the number of peers seen by this node (including itself)
    this.router.get("/peers/count", (_req: express.Request, res: express.Response) => {
      res.json(this.rhizomeNode.peers.peers.length);
    });
  }
 
  // serveCollection<T extends Collection>(collection: T) {
  serveCollection<View>(collection: Collection<View>) {
    const {name} = collection;
 
    // Get the ID of all domain entities
    this.router.get(`/${name}/ids`, (_req: express.Request, res: express.Response) => {
      res.json({ids: collection.getIds()});
    });
 
    // Get a single domain entity by ID
    this.router.get(`/${name}/:id`, (req: express.Request, res: express.Response) => {
      const {params: {id}} = req;
      const ent = collection.resolve(id);
      Iif (!ent) {
        res.status(404).send({error: "Not Found"});
        return;
      }
      res.json(ent);
    });
 
    // Add a new domain entity
    // TODO: schema validation
    this.router.put(`/${name}`, async (req: express.Request, res: express.Response) => {
      const {body: {id, properties}} = req;
      const ent = await collection.put(id, properties);
      res.json(ent);
    });
 
    // Update a domain entity
    this.router.put(`/${name}/:id`, async (req: express.Request, res: express.Response) => {
      const {body: properties, params: {id}} = req;
      Iif (properties.id && properties.id !== id) {
        res.status(400).json({error: "ID Mismatch", param: id, property: properties.id});
        return;
      }
      const ent = await collection.put(id, properties);
      res.json(ent);
    });
  }
 
  serveLossless() {
    // Get all domain entity IDs. TODO: This won't scale
    this.router.get('/lossless/ids', (_req: express.Request, res: express.Response) => {
      res.json({
        ids: Array.from(this.rhizomeNode.lossless.domainEntities.keys())
      });
    });
 
    // Get all transaction IDs. TODO: This won't scale
    this.router.get('/transaction/ids', (_req: express.Request, res: express.Response) => {
      const set = this.rhizomeNode.lossless.referencedAs.get("_transaction");
      res.json({
        ids: set ? Array.from(set.values()) : []
      });
    });
 
    // View a single transaction
    this.router.get('/transaction/:id', (req: express.Request, res: express.Response) => {
      const {params: {id}} = req;
      const v = this.rhizomeNode.lossless.view([id]);
      const ent = v[id];
      Iif (!ent.referencedAs.includes("_transaction")) {
        res.status(400).json({error: "Entity is not a transaction", id});
        return;
      }
 
      res.json({
        ...ent,
        isComplete: this.rhizomeNode.lossless.transactions.isComplete(id)
      });
    });
 
    // Get a lossless view of a single domain entity
    this.router.get('/lossless/:id', (req: express.Request, res: express.Response) => {
      const {params: {id}} = req;
      const v = this.rhizomeNode.lossless.view([id]);
      const ent = v[id];
 
      res.json({
        ...ent,
        isComplete: this.rhizomeNode.lossless.transactions.isComplete(id)
      });
    });
  }
}