Relação entre Entidades

Open in GitHub

Arquivos
monitor-app-prismajs-relation
├── back
│   ├── package-lock.json
│   ├── package.json
│   ├── prisma
│   │   ├── dev.db
│   │   ├── erd.svg
│   │   ├── migrations
│   │   │   ├── 20240228124425_init
│   │   │   │   └── migration.sql
│   │   │   ├── 20240303172942_create_ping_stats_icmp_user_tags
│   │   │   │   └── migration.sql
│   │   │   └── migration_lock.toml
│   │   ├── schema.prisma
│   │   └── seed.js
│   ├── requests.http
│   └── src
│       ├── database
│       │   └── database.js
│       ├── index.js
│       ├── lib
│       │   └── ping.js
│       ├── models
│       │   ├── Hosts.js
│       │   ├── Pings.js
│       │   └── Tags.js
│       ├── routes.js
│       └── routes.test.js
└── front
    ├── css
    │   └── style.css
    ├── index.html
    ├── js
    │   ├── components
    │   │   ├── HostForm.js
    │   │   ├── HostTableRow.js
    │   │   ├── LineChart.js
    │   │   └── Modal.js
    │   ├── lib
    │   │   ├── dom.js
    │   │   └── hosts.js
    │   ├── main.js
    │   └── services
    │       └── storage.js
    ├── package-lock.json
    ├── package.json
    ├── public
    │   └── vite.svg
    └── vite.config.js
Arquivos
monitor-app-prismajs-relation
├── back
│   ├── package-lock.json
│   ├── package.json
│   ├── prisma
│   │   ├── dev.db
│   │   ├── erd.svg
│   │   ├── migrations
│   │   │   ├── 20240228124425_init
│   │   │   │   └── migration.sql
│   │   │   ├── 20240303172942_create_ping_stats_icmp_user_tags
│   │   │   │   └── migration.sql
│   │   │   └── migration_lock.toml
│   │   ├── schema.prisma
│   │   └── seed.js
│   ├── requests.http
│   └── src
│       ├── database
│       │   └── database.js
│       ├── index.js
│       ├── lib
│       │   └── ping.js
│       ├── models
│       │   ├── Hosts.js
│       │   ├── Pings.js
│       │   └── Tags.js
│       ├── routes.js
│       └── routes.test.js
└── front
    ├── css
    │   └── style.css
    ├── index.html
    ├── js
    │   ├── components
    │   │   ├── HostForm.js
    │   │   ├── HostTableRow.js
    │   │   ├── LineChart.js
    │   │   └── Modal.js
    │   ├── lib
    │   │   ├── dom.js
    │   │   └── hosts.js
    │   ├── main.js
    │   └── services
    │       └── storage.js
    ├── package-lock.json
    ├── package.json
    ├── public
    │   └── vite.svg
    └── vite.config.js

Banco de Dados

Modelo Lógico

Modelo Físico

Prisma ERD Generator (online)

$ npm i -D prisma-erd-generator @mermaid-js/mermaid-cli
 
$ npx prisma generate
$ npm i -D prisma-erd-generator @mermaid-js/mermaid-cli
 
$ npx prisma generate

Migration

/codes/expressjs/monitor-app-prismajs-relation/back/prisma/schema.prisma
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
 
generator client {
  provider = "prisma-client-js"
}
 
// generator erd {
//   provider = "prisma-erd-generator"
// }
 
datasource db {
  provider = "sqlite"
  url      = env("DATABASE_URL")
}
 
model Tag {
  id    String        @id @default(uuid())
  name  String        @unique
  hosts TagsOnHosts[]
}
 
model Host {
  id      String        @id @default(uuid())
  name    String
  address String
  pings   Ping[]
  tags    TagsOnHosts[]
}
 
model TagsOnHosts {
  tag    Tag    @relation(fields: [tagId], references: [id], onDelete: Cascade, onUpdate: Cascade)
  tagId  String
  host   Host   @relation(fields: [hostId], references: [id], onDelete: Cascade, onUpdate: Cascade)
  hostId String
 
  @@id([tagId, hostId])
}
 
model Ping {
  id       String   @id @default(uuid())
  createAt DateTime @default(now())
  host     Host     @relation(fields: [hostId], references: [id], onDelete: Cascade, onUpdate: Cascade)
  hostId   String
  icmps    Icmp[]
  stats    Stats?
}
 
model Stats {
  id          String @id @default(uuid())
  transmitted Int
  received    Int
  time        Float
  ping        Ping   @relation(fields: [pingId], references: [id], onDelete: Cascade, onUpdate: Cascade)
  pingId      String @unique
}
 
model Icmp {
  id     String @id @default(uuid())
  seq    Int
  ttl    Int
  time   Float
  ping   Ping   @relation(fields: [pingId], references: [id], onDelete: Cascade, onUpdate: Cascade)
  pingId String
}
 
/codes/expressjs/monitor-app-prismajs-relation/back/prisma/schema.prisma
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
 
generator client {
  provider = "prisma-client-js"
}
 
// generator erd {
//   provider = "prisma-erd-generator"
// }
 
datasource db {
  provider = "sqlite"
  url      = env("DATABASE_URL")
}
 
model Tag {
  id    String        @id @default(uuid())
  name  String        @unique
  hosts TagsOnHosts[]
}
 
model Host {
  id      String        @id @default(uuid())
  name    String
  address String
  pings   Ping[]
  tags    TagsOnHosts[]
}
 
model TagsOnHosts {
  tag    Tag    @relation(fields: [tagId], references: [id], onDelete: Cascade, onUpdate: Cascade)
  tagId  String
  host   Host   @relation(fields: [hostId], references: [id], onDelete: Cascade, onUpdate: Cascade)
  hostId String
 
  @@id([tagId, hostId])
}
 
model Ping {
  id       String   @id @default(uuid())
  createAt DateTime @default(now())
  host     Host     @relation(fields: [hostId], references: [id], onDelete: Cascade, onUpdate: Cascade)
  hostId   String
  icmps    Icmp[]
  stats    Stats?
}
 
model Stats {
  id          String @id @default(uuid())
  transmitted Int
  received    Int
  time        Float
  ping        Ping   @relation(fields: [pingId], references: [id], onDelete: Cascade, onUpdate: Cascade)
  pingId      String @unique
}
 
model Icmp {
  id     String @id @default(uuid())
  seq    Int
  ttl    Int
  time   Float
  ping   Ping   @relation(fields: [pingId], references: [id], onDelete: Cascade, onUpdate: Cascade)
  pingId String
}
 
$ npx prisma migrate reset
 
$ npx prisma migrate dev --name create_ping_stats_icmp_user_tags
$ npx prisma migrate reset
 
$ npx prisma migrate dev --name create_ping_stats_icmp_user_tags

Referência

Seed

via prisma

await prisma.host.create({
  data: {
    id: 'e4cfb6bb-4431-42a9-b660-d5701b2f49cd',
    name: 'Google DNS',
    address: '8.8.8.8',
    tags: {
      create: [
        {
          tag: {
            connectOrCreate: {
              where: {
                name: 'DNS',
              },
              create: {
                name: 'DNS',
              },
            },
          },
        },
        {
          tag: {
            connectOrCreate: {
              where: {
                name: 'Google',
              },
              create: {
                name: 'Google',
              },
            },
          },
        },
      ],
    },
  },
});
await prisma.host.create({
  data: {
    id: 'e4cfb6bb-4431-42a9-b660-d5701b2f49cd',
    name: 'Google DNS',
    address: '8.8.8.8',
    tags: {
      create: [
        {
          tag: {
            connectOrCreate: {
              where: {
                name: 'DNS',
              },
              create: {
                name: 'DNS',
              },
            },
          },
        },
        {
          tag: {
            connectOrCreate: {
              where: {
                name: 'Google',
              },
              create: {
                name: 'Google',
              },
            },
          },
        },
      ],
    },
  },
});

via model

await Host.create({
    id: 'e4cfb6bb-4431-42a9-b660-d5701b2f49cd',
    name: 'Google DNS',
    address: '8.8.8.8',
    tags: ['DNS', 'Google'],
  });
await Host.create({
    id: 'e4cfb6bb-4431-42a9-b660-d5701b2f49cd',
    name: 'Google DNS',
    address: '8.8.8.8',
    tags: ['DNS', 'Google'],
  });
/codes/expressjs/monitor-app-prismajs-relation/back/prisma/seed.js
import { PrismaClient } from '@prisma/client';
import Host from '../src/models/Hosts.js';
 
const prisma = new PrismaClient();
 
async function main() {
  await Host.create({
    id: 'e4cfb6bb-4431-42a9-b660-d5701b2f49cd',
    name: 'Google DNS',
    address: '8.8.8.8',
    tags: ['DNS', 'Google'],
  });
 
  await Host.create({
    id: 'a2bb615a-6153-41bf-8cbe-0bfb538ce511',
    name: 'Google Search',
    address: 'www.google.com',
    tags: ['Motor de Busca', 'Google'],
  });
}
main()
  .then(async () => {
    await prisma.$disconnect();
  })
  .catch(async (e) => {
    console.error(e);
    await prisma.$disconnect();
    process.exit(1);
  });
 
/codes/expressjs/monitor-app-prismajs-relation/back/prisma/seed.js
import { PrismaClient } from '@prisma/client';
import Host from '../src/models/Hosts.js';
 
const prisma = new PrismaClient();
 
async function main() {
  await Host.create({
    id: 'e4cfb6bb-4431-42a9-b660-d5701b2f49cd',
    name: 'Google DNS',
    address: '8.8.8.8',
    tags: ['DNS', 'Google'],
  });
 
  await Host.create({
    id: 'a2bb615a-6153-41bf-8cbe-0bfb538ce511',
    name: 'Google Search',
    address: 'www.google.com',
    tags: ['Motor de Busca', 'Google'],
  });
}
main()
  .then(async () => {
    await prisma.$disconnect();
  })
  .catch(async (e) => {
    console.error(e);
    await prisma.$disconnect();
    process.exit(1);
  });
 
$ npx prisma db seed
 
$ npx prisma studio
$ npx prisma db seed
 
$ npx prisma studio

Model

/codes/expressjs/monitor-app-prismajs-relation/back/src/database/database.js
import { PrismaClient } from '@prisma/client';
 
const prisma = new PrismaClient({
  log: ['query', 'info', 'warn', 'error'],
});
 
export default prisma;
 
/codes/expressjs/monitor-app-prismajs-relation/back/src/database/database.js
import { PrismaClient } from '@prisma/client';
 
const prisma = new PrismaClient({
  log: ['query', 'info', 'warn', 'error'],
});
 
export default prisma;
 
/codes/expressjs/monitor-app-prismajs-relation/back/src/models/Hosts.js
import prisma from '../database/database.js';
 
async function create({ id, name, address, tags }) {
  const data = id ? { id, name, address } : { name, address };
 
  if (tags?.length) {
    data.tags = {
      create: tags.map((tag) => ({
        tag: {
          connectOrCreate: {
            where: {
              name: tag,
            },
            create: {
              name: tag,
            },
          },
        },
      })),
    };
  }
 
  const createdHost = await prisma.host.create({
    data,
    include: {
      tags: {
        include: {
          tag: true,
        },
      },
    },
  });
 
  return createdHost;
}
 
async function read(where = {}) {
  if (where?.name) {
    where.name = {
      contains: where.name,
    };
  }
 
  if (where?.tags) {
    where.tags = {
      some: {
        tag: {
          name: {
            contains: where.tags,
          },
        },
      },
    };
  }
 
  const hosts = await prisma.host.findMany({
    where,
    include: {
      tags: {
        include: {
          tag: true,
        },
      },
    },
  });
 
  if (hosts.length === 1 && where) {
    return hosts[0];
  }
 
  return hosts;
}
 
async function readById(id) {
  const host = await prisma.host.findUnique({
    where: {
      id,
    },
    include: {
      tags: {
        include: {
          tag: true,
        },
      },
    },
  });
 
  return host;
}
 
async function update({ id, name, address, tags }) {
  const data = { id, name, address };
 
  if (tags?.length) {
    data.tags = {
      create: tags.map((tag) => ({
        tag: {
          connectOrCreate: {
            where: {
              name: tag,
            },
            create: {
              name: tag,
            },
          },
        },
      })),
    };
  }
 
  const updatedHost = await prisma.host.update({
    where: {
      id,
    },
    data,
    include: {
      tags: {
        include: {
          tag: true,
        },
      },
    },
  });
 
  return updatedHost;
}
 
async function remove(id) {
  await prisma.host.delete({
    where: {
      id,
    },
  });
}
 
export default { create, read, readById, update, remove };
 
/codes/expressjs/monitor-app-prismajs-relation/back/src/models/Hosts.js
import prisma from '../database/database.js';
 
async function create({ id, name, address, tags }) {
  const data = id ? { id, name, address } : { name, address };
 
  if (tags?.length) {
    data.tags = {
      create: tags.map((tag) => ({
        tag: {
          connectOrCreate: {
            where: {
              name: tag,
            },
            create: {
              name: tag,
            },
          },
        },
      })),
    };
  }
 
  const createdHost = await prisma.host.create({
    data,
    include: {
      tags: {
        include: {
          tag: true,
        },
      },
    },
  });
 
  return createdHost;
}
 
async function read(where = {}) {
  if (where?.name) {
    where.name = {
      contains: where.name,
    };
  }
 
  if (where?.tags) {
    where.tags = {
      some: {
        tag: {
          name: {
            contains: where.tags,
          },
        },
      },
    };
  }
 
  const hosts = await prisma.host.findMany({
    where,
    include: {
      tags: {
        include: {
          tag: true,
        },
      },
    },
  });
 
  if (hosts.length === 1 && where) {
    return hosts[0];
  }
 
  return hosts;
}
 
async function readById(id) {
  const host = await prisma.host.findUnique({
    where: {
      id,
    },
    include: {
      tags: {
        include: {
          tag: true,
        },
      },
    },
  });
 
  return host;
}
 
async function update({ id, name, address, tags }) {
  const data = { id, name, address };
 
  if (tags?.length) {
    data.tags = {
      create: tags.map((tag) => ({
        tag: {
          connectOrCreate: {
            where: {
              name: tag,
            },
            create: {
              name: tag,
            },
          },
        },
      })),
    };
  }
 
  const updatedHost = await prisma.host.update({
    where: {
      id,
    },
    data,
    include: {
      tags: {
        include: {
          tag: true,
        },
      },
    },
  });
 
  return updatedHost;
}
 
async function remove(id) {
  await prisma.host.delete({
    where: {
      id,
    },
  });
}
 
export default { create, read, readById, update, remove };
 
/codes/expressjs/monitor-app-prismajs-relation/back/src/models/Tags.js
import prisma from '../database/database.js';
 
async function read() {
  const tags = await prisma.tag.findMany();
 
  return tags;
}
 
export default { read };
 
/codes/expressjs/monitor-app-prismajs-relation/back/src/models/Tags.js
import prisma from '../database/database.js';
 
async function read() {
  const tags = await prisma.tag.findMany();
 
  return tags;
}
 
export default { read };
 
/codes/expressjs/monitor-app-prismajs-relation/back/src/models/Pings.js
import prisma from '../database/database.js';
 
async function create({ icmps, stats, host }) {
  if (!icmps || !stats || !host) {
    throw new Error('Error when passing parameters');
  }
 
  const createdPing = await prisma.ping.create({
    data: {
      icmps: {
        create: icmps,
      },
      stats: {
        create: stats,
      },
      host: {
        connect: {
          id: host.id,
        },
      },
    },
    include: {
      icmps: true,
      stats: true,
      host: true,
    },
  });
 
  return createdPing;
}
 
async function read(where = {}) {
  const pings = await prisma.ping.findMany({
    where,
    include: {
      icmps: true,
      stats: true,
      host: true,
    },
  });
 
  return pings;
}
 
export default { create, read };
 
/codes/expressjs/monitor-app-prismajs-relation/back/src/models/Pings.js
import prisma from '../database/database.js';
 
async function create({ icmps, stats, host }) {
  if (!icmps || !stats || !host) {
    throw new Error('Error when passing parameters');
  }
 
  const createdPing = await prisma.ping.create({
    data: {
      icmps: {
        create: icmps,
      },
      stats: {
        create: stats,
      },
      host: {
        connect: {
          id: host.id,
        },
      },
    },
    include: {
      icmps: true,
      stats: true,
      host: true,
    },
  });
 
  return createdPing;
}
 
async function read(where = {}) {
  const pings = await prisma.ping.findMany({
    where,
    include: {
      icmps: true,
      stats: true,
      host: true,
    },
  });
 
  return pings;
}
 
export default { create, read };
 

Router

/codes/expressjs/monitor-app-prismajs-relation/back/src/routes.js
import express from 'express';
import Host from './models/Hosts.js';
import Ping from './models/Pings.js';
import Tag from './models/Tags.js';
import { ping } from './lib/ping.js';
 
class HttpError extends Error {
  constructor(message, code = 400) {
    super(message);
    this.code = code;
  }
}
 
const router = express.Router();
 
router.post('/hosts', async (req, res) => {
  const { name, address, tags } = req.body;
 
  if (!name || !address) {
    throw new HttpError('Error when passing parameters');
  }
 
  try {
    const createdHost = await Host.create({ name, address, tags });
 
    return res.status(201).json(createdHost);
  } catch (error) {
    throw new HttpError('Unable to create a host');
  }
});
 
router.get('/hosts', async (req, res) => {
  const { name } = req.query;
 
  try {
    if (name) {
      const filteredHosts = await Host.read({ name });
 
      return res.json(filteredHosts);
    }
 
    const hosts = await Host.read();
 
    return res.json(hosts);
  } catch (error) {
    throw new HttpError('Unable to read hosts');
  }
});
 
router.get('/hosts/:id', async (req, res) => {
  const { id } = req.params;
 
  try {
    const host = await Host.readById(id);
 
    if (host) {
      return res.json(host);
    } else {
      throw new HttpError('Host not found');
    }
  } catch (error) {
    throw new HttpError('Unable to read a host');
  }
});
 
router.put('/hosts/:id', async (req, res) => {
  const { name, address, tags } = req.body;
 
  const { id } = req.params;
 
  if (!name || !address) {
    throw new HttpError('Error when passing parameters');
  }
 
  try {
    const updatedHost = await Host.update({ id, name, address, tags });
 
    return res.json(updatedHost);
  } catch (error) {
    throw new HttpError('Unable to update a host');
  }
});
 
router.delete('/hosts/:id', async (req, res) => {
  const { id } = req.params;
 
  try {
    await Host.remove(id);
 
    return res.sendStatus(204);
  } catch (error) {
    throw new HttpError('Unable to delete a host');
  }
});
 
router.post('/hosts/:hostId/pings/:count', async (req, res) => {
  const { hostId, count } = req.params;
 
  try {
    const host = await Host.readById(hostId);
 
    const pingResult = await ping(host.address, count);
 
    const createdPing = await Ping.create({ ...pingResult, host });
 
    return res.json(createdPing);
  } catch (error) {
    throw new HttpError('Unable to create a ping for a host');
  }
});
 
router.get('/hosts/:hostId/pings', async (req, res) => {
  const { hostId: id } = req.params;
 
  try {
    const pings = await Ping.read({ host: { id } });
 
    return res.json(pings);
  } catch (error) {
    throw new HttpError('Unable to read pings by host');
  }
});
 
router.get('/tags', async (req, res) => {
  try {
    const tags = await Tag.read();
 
    return res.json(tags);
  } catch (error) {
    throw new HttpError('Unable to read tags');
  }
});
 
router.get('/tags/:tag/hosts', async (req, res) => {
  const { tag } = req.params;
 
  try {
    const host = await Host.read({ tags: tag });
 
    return res.json(host);
  } catch (error) {
    throw new HttpError('Unable to read hosts by tag');
  }
});
 
router.get('/pings', async (req, res) => {
  try {
    const pings = await Ping.read();
 
    return res.json(pings);
  } catch (error) {
    throw new HttpError('Unable to read pings');
  }
});
 
// 404 handler
router.use((req, res, next) => {
  return res.status(404).json({ message: 'Content not found!' });
});
 
// Error handler
router.use((err, req, res, next) => {
  // console.error(err.message);
  console.error(err.stack);
 
  if (err instanceof HttpError) {
    return res.status(err.code).json({ message: err.message });
  }
 
  // next(err);
  return res.status(500).json({ message: 'Something broke!' });
});
 
export default router;
 
/codes/expressjs/monitor-app-prismajs-relation/back/src/routes.js
import express from 'express';
import Host from './models/Hosts.js';
import Ping from './models/Pings.js';
import Tag from './models/Tags.js';
import { ping } from './lib/ping.js';
 
class HttpError extends Error {
  constructor(message, code = 400) {
    super(message);
    this.code = code;
  }
}
 
const router = express.Router();
 
router.post('/hosts', async (req, res) => {
  const { name, address, tags } = req.body;
 
  if (!name || !address) {
    throw new HttpError('Error when passing parameters');
  }
 
  try {
    const createdHost = await Host.create({ name, address, tags });
 
    return res.status(201).json(createdHost);
  } catch (error) {
    throw new HttpError('Unable to create a host');
  }
});
 
router.get('/hosts', async (req, res) => {
  const { name } = req.query;
 
  try {
    if (name) {
      const filteredHosts = await Host.read({ name });
 
      return res.json(filteredHosts);
    }
 
    const hosts = await Host.read();
 
    return res.json(hosts);
  } catch (error) {
    throw new HttpError('Unable to read hosts');
  }
});
 
router.get('/hosts/:id', async (req, res) => {
  const { id } = req.params;
 
  try {
    const host = await Host.readById(id);
 
    if (host) {
      return res.json(host);
    } else {
      throw new HttpError('Host not found');
    }
  } catch (error) {
    throw new HttpError('Unable to read a host');
  }
});
 
router.put('/hosts/:id', async (req, res) => {
  const { name, address, tags } = req.body;
 
  const { id } = req.params;
 
  if (!name || !address) {
    throw new HttpError('Error when passing parameters');
  }
 
  try {
    const updatedHost = await Host.update({ id, name, address, tags });
 
    return res.json(updatedHost);
  } catch (error) {
    throw new HttpError('Unable to update a host');
  }
});
 
router.delete('/hosts/:id', async (req, res) => {
  const { id } = req.params;
 
  try {
    await Host.remove(id);
 
    return res.sendStatus(204);
  } catch (error) {
    throw new HttpError('Unable to delete a host');
  }
});
 
router.post('/hosts/:hostId/pings/:count', async (req, res) => {
  const { hostId, count } = req.params;
 
  try {
    const host = await Host.readById(hostId);
 
    const pingResult = await ping(host.address, count);
 
    const createdPing = await Ping.create({ ...pingResult, host });
 
    return res.json(createdPing);
  } catch (error) {
    throw new HttpError('Unable to create a ping for a host');
  }
});
 
router.get('/hosts/:hostId/pings', async (req, res) => {
  const { hostId: id } = req.params;
 
  try {
    const pings = await Ping.read({ host: { id } });
 
    return res.json(pings);
  } catch (error) {
    throw new HttpError('Unable to read pings by host');
  }
});
 
router.get('/tags', async (req, res) => {
  try {
    const tags = await Tag.read();
 
    return res.json(tags);
  } catch (error) {
    throw new HttpError('Unable to read tags');
  }
});
 
router.get('/tags/:tag/hosts', async (req, res) => {
  const { tag } = req.params;
 
  try {
    const host = await Host.read({ tags: tag });
 
    return res.json(host);
  } catch (error) {
    throw new HttpError('Unable to read hosts by tag');
  }
});
 
router.get('/pings', async (req, res) => {
  try {
    const pings = await Ping.read();
 
    return res.json(pings);
  } catch (error) {
    throw new HttpError('Unable to read pings');
  }
});
 
// 404 handler
router.use((req, res, next) => {
  return res.status(404).json({ message: 'Content not found!' });
});
 
// Error handler
router.use((err, req, res, next) => {
  // console.error(err.message);
  console.error(err.stack);
 
  if (err instanceof HttpError) {
    return res.status(err.code).json({ message: err.message });
  }
 
  // next(err);
  return res.status(500).json({ message: 'Something broke!' });
});
 
export default router;
 
/codes/expressjs/monitor-app-prismajs-relation/back/requests.http
@server=http://localhost:3000
@createdHostId = {{createHost.response.body.$.id}}
 
### Create a host
# @name createHost
POST {{server}}/hosts
Content-Type: application/json
 
{
  "name": "DNS Server",
  "address": "1.1.1.1"
}
 
### Create a host without name or address
POST {{server}}/hosts
Content-Type: application/json
 
{
  "name": "DNS Server"
}
 
### Read hosts
GET {{server}}/hosts
 
### Read a host by name
GET {{server}}/hosts?name=Google%20DNS
# GET {{server}}/hosts?name=DNS
# GET {{server}}/hosts?name=dns
 
### Read a host by id
GET {{server}}/hosts/{{createdHostId}}
 
### Read a host by id with invalid id
GET {{server}}/hosts/x
 
### Update a host
PUT {{server}}/hosts/{{createdHostId}}
Content-Type: application/json
 
{
  "name": "Cloudflare DNS",
  "address": "1.1.1.1",
  "tags": [ "DNS", "Cloudflare"]
}
 
### Update a host without name or address
PUT {{server}}/hosts/{{createdHostId}}
Content-Type: application/json
 
{
  "name": "Cloudflare DNS"
}
 
### Update a host with invalid id
PUT {{server}}/hosts/x
Content-Type: application/json
 
{
  "name": "Cloudflare DNS",
  "address": "1.1.1.1"
}
 
### Delete a host
DELETE {{server}}/hosts/{{createdHostId}}
 
### Delete a host with invalid id
DELETE {{server}}/hosts/x
 
### Create a ping
POST {{server}}/hosts/{{createdHostId}}/pings/3
 
### Read pings
GET {{server}}/hosts/{{createdHostId}}/pings
 
### Read tags
GET {{server}}/tags
 
### Read hosts by tag
GET {{server}}/tags/dns/hosts
 
### Read pings
GET {{server}}/pings
 
/codes/expressjs/monitor-app-prismajs-relation/back/requests.http
@server=http://localhost:3000
@createdHostId = {{createHost.response.body.$.id}}
 
### Create a host
# @name createHost
POST {{server}}/hosts
Content-Type: application/json
 
{
  "name": "DNS Server",
  "address": "1.1.1.1"
}
 
### Create a host without name or address
POST {{server}}/hosts
Content-Type: application/json
 
{
  "name": "DNS Server"
}
 
### Read hosts
GET {{server}}/hosts
 
### Read a host by name
GET {{server}}/hosts?name=Google%20DNS
# GET {{server}}/hosts?name=DNS
# GET {{server}}/hosts?name=dns
 
### Read a host by id
GET {{server}}/hosts/{{createdHostId}}
 
### Read a host by id with invalid id
GET {{server}}/hosts/x
 
### Update a host
PUT {{server}}/hosts/{{createdHostId}}
Content-Type: application/json
 
{
  "name": "Cloudflare DNS",
  "address": "1.1.1.1",
  "tags": [ "DNS", "Cloudflare"]
}
 
### Update a host without name or address
PUT {{server}}/hosts/{{createdHostId}}
Content-Type: application/json
 
{
  "name": "Cloudflare DNS"
}
 
### Update a host with invalid id
PUT {{server}}/hosts/x
Content-Type: application/json
 
{
  "name": "Cloudflare DNS",
  "address": "1.1.1.1"
}
 
### Delete a host
DELETE {{server}}/hosts/{{createdHostId}}
 
### Delete a host with invalid id
DELETE {{server}}/hosts/x
 
### Create a ping
POST {{server}}/hosts/{{createdHostId}}/pings/3
 
### Read pings
GET {{server}}/hosts/{{createdHostId}}/pings
 
### Read tags
GET {{server}}/tags
 
### Read hosts by tag
GET {{server}}/tags/dns/hosts
 
### Read pings
GET {{server}}/pings
 

Teste

Jest, Supertest:

$ npm i jest supertest -D
 
$ npm run test
$ npm i jest supertest -D
 
$ npm run test
/codes/expressjs/monitor-app-prismajs-relation/back/src/routes.test.js
import request from 'supertest';
import app from './index.js';
 
let createdHost;
 
const newHost = {
  name: 'DNS Server',
  address: '1.1.1.1',
};
 
const updatedHost = {
  name: 'Cloudflare DNS',
  address: '1.1.1.1',
  tags: ['DNS', 'Cloudflare'],
};
 
describe('Moniotr App', () => {
  describe('Hosts Endpoints', () => {
    describe('POST /hosts', () => {
      it('should create a new host', async () => {
        const response = await request(app).post('/hosts').send(newHost);
 
        createdHost = response.body;
 
        expect(response.statusCode).toBe(201);
      });
 
      it('should create a new host with tags', async () => {
        const tags = ['DNS'];
 
        const response = await request(app)
          .post('/hosts')
          .send({ name: 'Google DNS', address: '8.8.4.4', tags });
 
        expect(response.statusCode).toBe(201);
 
        const hasValidTag = response.body.tags
          .map((_) => _.tag)
          .some((tag) => tag.name === tags[0]);
 
        expect(hasValidTag).toBeTruthy();
      });
 
      it('should not create a new host without name or address', async () => {
        const response = await request(app).post('/hosts').send({
          name: 'DNS Server',
        });
 
        expect(response.statusCode).toBe(400);
      });
    });
 
    describe('GET /hosts', () => {
      it('should show all hosts', async () => {
        const response = await request(app).get('/hosts');
 
        expect(response.statusCode).toBe(200);
      });
 
      it('should list the valid host', async () => {
        const response = await request(app).get('/hosts');
 
        const hasValidHost = response.body.some(
          (host) => host.address === createdHost.address
        );
 
        expect(hasValidHost).toBeTruthy();
      });
 
      it('should show all hosts by name', async () => {
        const response = await request(app).get('/hosts?name=DNS');
 
        expect(response.statusCode).toBe(200);
      });
    });
 
    describe('GET /hosts/:hostId', () => {
      it('should show a host by id', async () => {
        const response = await request(app).get(`/hosts/${createdHost.id}`);
 
        expect(response.statusCode).toBe(200);
 
        expect(response.body.name).toBe(createdHost.name);
      });
 
      it('should not show a host with invalid id', async () => {
        const response = await request(app).get(`/hosts/x`);
 
        expect(response.statusCode).toBe(400);
 
        expect(response.body.message).toBe('Unable to read a host');
      });
    });
 
    describe('PUT /hosts/:hostId', () => {
      it('should update a host', async () => {
        const response = await request(app)
          .put(`/hosts/${createdHost.id}`)
          .send(updatedHost);
 
        expect(response.statusCode).toBe(200);
      });
 
      it('should list an updated host', async () => {
        const response = await request(app).get('/hosts');
 
        const hasValidHost = response.body.some(
          (host) => host.address === updatedHost.address
        );
 
        expect(hasValidHost).toBeTruthy();
      });
 
      it('should not update a host without name or address', async () => {
        const response = await request(app)
          .put(`/hosts/${createdHost.id}`)
          .send({
            name: 'Cloudflare DNS',
          });
 
        expect(response.statusCode).toBe(400);
      });
 
      it('should not update a host with invalid id', async () => {
        const response = await request(app).put(`/hosts/x`).send(updatedHost);
 
        expect(response.statusCode).toBe(400);
 
        expect(response.body.message).toBe('Unable to update a host');
      });
    });
 
    describe('DELETE /hosts/:hostId', () => {
      it('should remove a host', async () => {
        const response = await request(app).delete(`/hosts/${createdHost.id}`);
 
        expect(response.statusCode).toBe(204);
      });
 
      it('should not delete a host with invalid id', async () => {
        const response = await request(app).delete(`/hosts/x`);
 
        expect(response.statusCode).toBe(400);
 
        expect(response.body.message).toBe('Unable to delete a host');
      });
    });
  });
 
  describe('Host Ping Endpoints', () => {
    describe('POST /hosts/:hostId/pings/:count', () => {
      it('should create a ping with valid host', async () => {
        let response = await request(app).post('/hosts').send(newHost);
 
        createdHost = response.body;
 
        response = await request(app).post(`/hosts/${createdHost.id}/pings/3`);
 
        expect(response.statusCode).toBe(200);
 
        expect(response.body.icmps.length).toBe(3);
      });
 
      it('should not create a ping with unknown ip', async () => {
        let response = await request(app)
          .post('/hosts')
          .send({ name: 'unknown host', address: '172.16.0.1' });
 
        createdHost = response.body;
 
        response = await request(app).post(`/hosts/${createdHost.id}/pings/3`);
 
        expect(response.statusCode).toBe(400);
      });
 
      it('should not create a ping with unknown domain', async () => {
        let response = await request(app)
          .post('/hosts')
          .send({ name: 'unknown host', address: 'www.unknownhost.com' });
 
        createdHost = response.body;
 
        response = await request(app).post(`/hosts/${createdHost.id}/pings/3`);
 
        expect(response.statusCode).toBe(400);
      });
    });
 
    describe('GET /hosts/:hostId/pings', () => {
      it('should show a ping by hostId', async () => {
        const response = await request(app).get(
          `/hosts/${createdHost.id}/pings`
        );
 
        expect(response.statusCode).toBe(200);
      });
    });
  });
 
  describe('Tag Endpoints', () => {
    describe('GET /tags', () => {
      it('should show tags', async () => {
        const response = await request(app).get(`/tags`);
 
        expect(response.statusCode).toBe(200);
      });
    });
 
    describe('GET /tags/:tagName/hosts', () => {
      it('should show hosts by tagName', async () => {
        const response = await request(app).get(
          `/tags/${createdHost.id}/hosts`
        );
 
        expect(response.statusCode).toBe(200);
      });
    });
  });
 
  describe('Ping Endpoints', () => {
    describe('GET /pings', () => {
      it('should show pings', async () => {
        const response = await request(app).get(`/pings`);
 
        expect(response.statusCode).toBe(200);
      });
    });
  });
 
  describe('404 Endpoints', () => {
    describe('GET /unknown-endpoint', () => {
      it('should show pings', async () => {
        const response = await request(app).get(`/unknown-endpoint`);
 
        expect(response.statusCode).toBe(404);
      });
    });
  });
});
 
/codes/expressjs/monitor-app-prismajs-relation/back/src/routes.test.js
import request from 'supertest';
import app from './index.js';
 
let createdHost;
 
const newHost = {
  name: 'DNS Server',
  address: '1.1.1.1',
};
 
const updatedHost = {
  name: 'Cloudflare DNS',
  address: '1.1.1.1',
  tags: ['DNS', 'Cloudflare'],
};
 
describe('Moniotr App', () => {
  describe('Hosts Endpoints', () => {
    describe('POST /hosts', () => {
      it('should create a new host', async () => {
        const response = await request(app).post('/hosts').send(newHost);
 
        createdHost = response.body;
 
        expect(response.statusCode).toBe(201);
      });
 
      it('should create a new host with tags', async () => {
        const tags = ['DNS'];
 
        const response = await request(app)
          .post('/hosts')
          .send({ name: 'Google DNS', address: '8.8.4.4', tags });
 
        expect(response.statusCode).toBe(201);
 
        const hasValidTag = response.body.tags
          .map((_) => _.tag)
          .some((tag) => tag.name === tags[0]);
 
        expect(hasValidTag).toBeTruthy();
      });
 
      it('should not create a new host without name or address', async () => {
        const response = await request(app).post('/hosts').send({
          name: 'DNS Server',
        });
 
        expect(response.statusCode).toBe(400);
      });
    });
 
    describe('GET /hosts', () => {
      it('should show all hosts', async () => {
        const response = await request(app).get('/hosts');
 
        expect(response.statusCode).toBe(200);
      });
 
      it('should list the valid host', async () => {
        const response = await request(app).get('/hosts');
 
        const hasValidHost = response.body.some(
          (host) => host.address === createdHost.address
        );
 
        expect(hasValidHost).toBeTruthy();
      });
 
      it('should show all hosts by name', async () => {
        const response = await request(app).get('/hosts?name=DNS');
 
        expect(response.statusCode).toBe(200);
      });
    });
 
    describe('GET /hosts/:hostId', () => {
      it('should show a host by id', async () => {
        const response = await request(app).get(`/hosts/${createdHost.id}`);
 
        expect(response.statusCode).toBe(200);
 
        expect(response.body.name).toBe(createdHost.name);
      });
 
      it('should not show a host with invalid id', async () => {
        const response = await request(app).get(`/hosts/x`);
 
        expect(response.statusCode).toBe(400);
 
        expect(response.body.message).toBe('Unable to read a host');
      });
    });
 
    describe('PUT /hosts/:hostId', () => {
      it('should update a host', async () => {
        const response = await request(app)
          .put(`/hosts/${createdHost.id}`)
          .send(updatedHost);
 
        expect(response.statusCode).toBe(200);
      });
 
      it('should list an updated host', async () => {
        const response = await request(app).get('/hosts');
 
        const hasValidHost = response.body.some(
          (host) => host.address === updatedHost.address
        );
 
        expect(hasValidHost).toBeTruthy();
      });
 
      it('should not update a host without name or address', async () => {
        const response = await request(app)
          .put(`/hosts/${createdHost.id}`)
          .send({
            name: 'Cloudflare DNS',
          });
 
        expect(response.statusCode).toBe(400);
      });
 
      it('should not update a host with invalid id', async () => {
        const response = await request(app).put(`/hosts/x`).send(updatedHost);
 
        expect(response.statusCode).toBe(400);
 
        expect(response.body.message).toBe('Unable to update a host');
      });
    });
 
    describe('DELETE /hosts/:hostId', () => {
      it('should remove a host', async () => {
        const response = await request(app).delete(`/hosts/${createdHost.id}`);
 
        expect(response.statusCode).toBe(204);
      });
 
      it('should not delete a host with invalid id', async () => {
        const response = await request(app).delete(`/hosts/x`);
 
        expect(response.statusCode).toBe(400);
 
        expect(response.body.message).toBe('Unable to delete a host');
      });
    });
  });
 
  describe('Host Ping Endpoints', () => {
    describe('POST /hosts/:hostId/pings/:count', () => {
      it('should create a ping with valid host', async () => {
        let response = await request(app).post('/hosts').send(newHost);
 
        createdHost = response.body;
 
        response = await request(app).post(`/hosts/${createdHost.id}/pings/3`);
 
        expect(response.statusCode).toBe(200);
 
        expect(response.body.icmps.length).toBe(3);
      });
 
      it('should not create a ping with unknown ip', async () => {
        let response = await request(app)
          .post('/hosts')
          .send({ name: 'unknown host', address: '172.16.0.1' });
 
        createdHost = response.body;
 
        response = await request(app).post(`/hosts/${createdHost.id}/pings/3`);
 
        expect(response.statusCode).toBe(400);
      });
 
      it('should not create a ping with unknown domain', async () => {
        let response = await request(app)
          .post('/hosts')
          .send({ name: 'unknown host', address: 'www.unknownhost.com' });
 
        createdHost = response.body;
 
        response = await request(app).post(`/hosts/${createdHost.id}/pings/3`);
 
        expect(response.statusCode).toBe(400);
      });
    });
 
    describe('GET /hosts/:hostId/pings', () => {
      it('should show a ping by hostId', async () => {
        const response = await request(app).get(
          `/hosts/${createdHost.id}/pings`
        );
 
        expect(response.statusCode).toBe(200);
      });
    });
  });
 
  describe('Tag Endpoints', () => {
    describe('GET /tags', () => {
      it('should show tags', async () => {
        const response = await request(app).get(`/tags`);
 
        expect(response.statusCode).toBe(200);
      });
    });
 
    describe('GET /tags/:tagName/hosts', () => {
      it('should show hosts by tagName', async () => {
        const response = await request(app).get(
          `/tags/${createdHost.id}/hosts`
        );
 
        expect(response.statusCode).toBe(200);
      });
    });
  });
 
  describe('Ping Endpoints', () => {
    describe('GET /pings', () => {
      it('should show pings', async () => {
        const response = await request(app).get(`/pings`);
 
        expect(response.statusCode).toBe(200);
      });
    });
  });
 
  describe('404 Endpoints', () => {
    describe('GET /unknown-endpoint', () => {
      it('should show pings', async () => {
        const response = await request(app).get(`/unknown-endpoint`);
 
        expect(response.statusCode).toBe(404);
      });
    });
  });
});
 

.skip()

describe.skip()
 
it.skip()
describe.skip()
 
it.skip()

Cobertura de Testes

/codes/expressjs/monitor-app-prismajs-relation/back/package.json
{
  "name": "invest-app",
  "type": "module",
  "scripts": {
    "start": "node src/index.js",
    "dev": "node --watch src/index.js",
    "db:reset": "prisma migrate reset --force",
    "test": "npm run db:reset && node --experimental-vm-modules ./node_modules/.bin/jest src",
    "test:coverage": "npm run db:reset && node --experimental-vm-modules ./node_modules/.bin/jest src --coverage"
  },
  "prisma": {
    "seed": "node prisma/seed.js"
  },
  "jest": {
    "collectCoverage": true,
    "testTimeout": 20000,
    "coverageReporters": [
      "json",
      "html"
    ]
  },
  "dependencies": {
    "@prisma/client": "^5.10.2",
    "cors": "^2.8.5",
    "express": "^4.18.2",
    "express-async-errors": "^3.1.1",
    "morgan": "^1.10.0",
    "prisma": "^5.10.2",
    "uuid": "^9.0.0"
  },
  "devDependencies": {
    "@mermaid-js/mermaid-cli": "^10.8.0",
    "jest": "^29.7.0",
    "prisma-erd-generator": "^1.11.2",
    "supertest": "^6.3.4"
  }
}
 
/codes/expressjs/monitor-app-prismajs-relation/back/package.json
{
  "name": "invest-app",
  "type": "module",
  "scripts": {
    "start": "node src/index.js",
    "dev": "node --watch src/index.js",
    "db:reset": "prisma migrate reset --force",
    "test": "npm run db:reset && node --experimental-vm-modules ./node_modules/.bin/jest src",
    "test:coverage": "npm run db:reset && node --experimental-vm-modules ./node_modules/.bin/jest src --coverage"
  },
  "prisma": {
    "seed": "node prisma/seed.js"
  },
  "jest": {
    "collectCoverage": true,
    "testTimeout": 20000,
    "coverageReporters": [
      "json",
      "html"
    ]
  },
  "dependencies": {
    "@prisma/client": "^5.10.2",
    "cors": "^2.8.5",
    "express": "^4.18.2",
    "express-async-errors": "^3.1.1",
    "morgan": "^1.10.0",
    "prisma": "^5.10.2",
    "uuid": "^9.0.0"
  },
  "devDependencies": {
    "@mermaid-js/mermaid-cli": "^10.8.0",
    "jest": "^29.7.0",
    "prisma-erd-generator": "^1.11.2",
    "supertest": "^6.3.4"
  }
}
 
$ npm run test:coverage
$ npm run test:coverage

View

Editar esta página