Skip to content

Commit b3f5222

Browse files
committed
add domains
1 parent 27f414c commit b3f5222

19 files changed

+625
-77
lines changed

docker-compose.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ services:
3737
- ES_PASSWORD=
3838
- ES_USERNAME=
3939
- MONGO_URI=mongodb://mongodb:27017
40+
- MONGO_DBNAME=paysage
4041
env_file:
4142
- .env
4243
volumes:

src/api/app.js

Lines changed: 37 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,26 @@ import health from "@cloudnative/health-connect";
22
import cors from "cors";
33
import express from "express";
44
import "express-async-errors";
5-
import path from "path";
65
import * as OAV from "express-openapi-validator";
76
import multer from "multer";
7+
import path from "path";
88
import YAML from "yamljs";
9-
import { authenticate } from "./commons/middlewares/authenticate.middlewares";
10-
import { handleErrors } from "./commons/middlewares/handle-errors.middlewares";
11-
129
import annuaireRoutes from "./annuaire/annuaire.routes";
1310
import apiKeysRoutes from "./apikeys/apikeys.routes";
1411
import assetsRoutes from "./assets/assets.routes";
1512
import authRoutes from "./auth/auth.routes";
1613
import categoriesRoutes from "./categories/categories.routes";
14+
import { authenticate } from "./commons/middlewares/authenticate.middlewares";
15+
import { handleErrors } from "./commons/middlewares/handle-errors.middlewares";
1716
import {
18-
forbidReadersToWrite,
19-
requireAuth,
17+
forbidReadersToWrite,
18+
requireAuth,
2019
} from "./commons/middlewares/rbac.middlewares";
2120
import contactRoutes from "./contacts/contacts.routes";
2221
import curiexploreRoutes from "./curiexplore/curiexplore.routes";
2322
import documentTypesRoutes from "./document-types/document-types.routes";
2423
import documentsRoutes from "./documents/documents.routes";
24+
import domainsRoutes from "./domains/domains.routes";
2525
import emailTypesRoutes from "./email-types/email-types.routes";
2626
import followUpsRoutes from "./followups/followups.routes";
2727
import geographicalcategoriesRoutes from "./geographicalcategories/geographicalcategories.routes";
@@ -38,17 +38,17 @@ import personsRoutes from "./persons/persons.routes";
3838
import pressRoutes from "./press/press.routes";
3939
import prizesRoutes from "./prizes/prizes.routes";
4040
import projectsRoutes from "./projects/projects.routes";
41-
import relationsGroupsRoutes from "./relations-groups/relations-groups.routes";
4241
import relationsRoutes from "./relations/relations.routes";
42+
import relationsGroupsRoutes from "./relations-groups/relations-groups.routes";
4343
import relationTypesRoutes from "./relationtypes/relationtypes.routes";
4444
import searchRoutes from "./search/search.routes";
4545
import sireneRoutes from "./sirene/sirene.routes";
4646
import structuresRoutes from "./structures/structures.routes";
4747
import supervisingMinistersRoutes from "./supervising-ministers/supervising-ministers.routes";
4848
import termsRoutes from "./terms/terms.routes";
4949
import usersRoutes from "./users/users.routes";
50-
import weblinksRoutes from "./weblinks/weblinks.routes";
5150
import utilitiesRoutes from "./utilities.routes";
51+
import weblinksRoutes from "./weblinks/weblinks.routes";
5252

5353
// Load API specifications
5454
const apiSpec = path.join(path.resolve(), "docs/reference/api.yml");
@@ -60,26 +60,26 @@ app.use(express.json());
6060
app.use(express.urlencoded({ extended: false }));
6161
app.disable("x-powered-by");
6262
if (process.env.NODE_ENV === "development") {
63-
app.use(
64-
cors({
65-
origin: "*",
66-
methods: ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"],
67-
}),
68-
);
63+
app.use(
64+
cors({
65+
origin: "*",
66+
methods: ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"],
67+
}),
68+
);
6969
}
7070
app.set("trust proxy", ["loopback", "linklocal", "uniquelocal"]);
7171

7272
// Health checker
7373
const healthcheck = new health.HealthChecker();
7474
const isReady = async (expressApp) => {
75-
if (!expressApp.isReady) {
76-
throw new Error("App in not running yet.");
77-
}
78-
return "Listening to requests";
75+
if (!expressApp.isReady) {
76+
throw new Error("App in not running yet.");
77+
}
78+
return "Listening to requests";
7979
};
8080
const liveCheck = new health.LivenessCheck("LivenessCheck", () => isReady(app));
8181
const readyCheck = new health.ReadinessCheck("ReadinessCheck", () =>
82-
isReady(app),
82+
isReady(app),
8383
);
8484
healthcheck.registerLivenessCheck(liveCheck);
8585
healthcheck.registerReadinessCheck(readyCheck);
@@ -89,29 +89,29 @@ app.use("/readyz", health.ReadinessEndpoint(healthcheck));
8989
// Expose swagger API documentation
9090
const { schemas } = apiDocument.components;
9191
app.get("/docs/specs", (req, res) => {
92-
res.status(200).json(apiDocument);
92+
res.status(200).json(apiDocument);
9393
});
9494
app.get("/docs/enums", (req, res) => {
95-
res
96-
.status(200)
97-
.json(
98-
Object.fromEntries(
99-
Object.entries(schemas).filter(([key]) => key.match(/Enum$/)),
100-
),
101-
);
95+
res
96+
.status(200)
97+
.json(
98+
Object.fromEntries(
99+
Object.entries(schemas).filter(([key]) => key.match(/Enum$/)),
100+
),
101+
);
102102
});
103103

104104
// express-openapi-validator setup to validate requests
105105
app.use(
106-
OAV.middleware({
107-
apiSpec,
108-
validateRequests: {
109-
removeAdditional: true,
110-
},
111-
validateResponses: true,
112-
fileUploader: { storage: multer.memoryStorage() },
113-
ignoreUndocumented: true,
114-
}),
106+
OAV.middleware({
107+
apiSpec,
108+
validateRequests: {
109+
removeAdditional: true,
110+
},
111+
validateResponses: true,
112+
fileUploader: { storage: multer.memoryStorage() },
113+
ignoreUndocumented: true,
114+
}),
115115
);
116116

117117
// Authenticate currentUser
@@ -131,6 +131,7 @@ app.use(contactRoutes);
131131
app.use(curiexploreRoutes);
132132
app.use(documentsRoutes);
133133
app.use(documentTypesRoutes);
134+
app.use(domainsRoutes);
134135
app.use(emailTypesRoutes);
135136
app.use(followUpsRoutes);
136137
app.use(geographicalcategoriesRoutes);
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import metas from "./metas.query";
2+
3+
export default [
4+
...metas,
5+
{
6+
$project: {
7+
_id: 0,
8+
id: 1,
9+
domainId: "$resourceId",
10+
structureId: 1,
11+
type: 1,
12+
createdBy: 1,
13+
createdAt: 1,
14+
updatedBy: 1,
15+
updatedAt: 1,
16+
},
17+
},
18+
];
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import metas from "./metas.query";
2+
import structuresUltraLightQuery from "./structures.ultralight.query";
3+
4+
export default [
5+
...metas,
6+
{
7+
$lookup: {
8+
from: "structures",
9+
let: { structureIds: "$structures.structureId" },
10+
pipeline: [
11+
{ $match: { $expr: { $in: ["$id", "$$structureIds"] } } },
12+
...structuresUltraLightQuery,
13+
],
14+
as: "structuresData",
15+
},
16+
},
17+
{
18+
$addFields: {
19+
structures: {
20+
$map: {
21+
input: "$structures",
22+
as: "structureRef",
23+
in: {
24+
id: "$$structureRef.id",
25+
type: "$$structureRef.type",
26+
structureId: "$$structureRef.structureId",
27+
structure: {
28+
$arrayElemAt: [
29+
{
30+
$filter: {
31+
input: "$structuresData",
32+
cond: { $eq: ["$$this.id", "$$structureRef.structureId"] },
33+
},
34+
},
35+
0,
36+
],
37+
},
38+
},
39+
},
40+
},
41+
},
42+
},
43+
{
44+
$project: {
45+
_id: 0,
46+
id: 1,
47+
domainName: 1,
48+
archived: 1,
49+
structures: 1,
50+
createdBy: 1,
51+
createdAt: 1,
52+
updatedBy: 1,
53+
updatedAt: 1,
54+
// structuresData: 0,
55+
},
56+
},
57+
];
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import currentNameQuery from './current-name.query';
2+
3+
4+
export default [
5+
...currentNameQuery,
6+
{
7+
$project: {
8+
_id: 0,
9+
id: 1,
10+
displayName: '$currentName.usualName',
11+
href: { $concat: ['/structures/', '$id'] },
12+
},
13+
},
14+
];

src/api/commons/repositories.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,15 @@ export const documentTypesRepository = new BaseMongoRepository({
2828
db,
2929
collection: 'documenttypes',
3030
});
31+
export const domainsRepository = new BaseMongoRepository({
32+
db,
33+
collection: 'domains',
34+
});
35+
export const domainsStructuresRepository = new NestedMongoRepository({
36+
db,
37+
collection: 'domains',
38+
field: 'structures',
39+
});
3140
export const emailsRepository = new BaseMongoRepository({
3241
db,
3342
collection: 'emails',
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import {
2+
domainsRepository,
3+
domainsStructuresRepository,
4+
structuresRepository,
5+
} from "../commons/repositories";
6+
import catalog from "../commons/catalog";
7+
8+
export async function validateDomainName(req, res, next) {
9+
const { domainName: inputDomain } = req.body;
10+
11+
const domainName = inputDomain.trim();
12+
13+
if (
14+
!domainName ||
15+
typeof domainName !== "string" ||
16+
!/^[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(domainName)
17+
) {
18+
return res.status(400).json({
19+
error:
20+
"Invalid domain name format. Must be a valid domain name (e.g., example.com)",
21+
});
22+
}
23+
req.body.domainName = domainName;
24+
25+
next();
26+
}
27+
export async function ensureDomainNameIsUnique(req, res, next) {
28+
const { domainName } = req.body;
29+
30+
const exists = await domainsRepository.findOne({ domainName });
31+
32+
if (exists) {
33+
return res.status(400).json({
34+
error: "Domain name already exists",
35+
});
36+
}
37+
38+
next();
39+
}
40+
41+
export async function ensureAddStructureIsPossible(req, res, next) {
42+
const { domainId } = req.params;
43+
const { structureId } = req.body;
44+
const exists = await structuresRepository.findOne({id: structureId });
45+
const alreadyThere = await domainsStructuresRepository.get(domainId, { structureId });
46+
47+
if (!exists) {
48+
return res.status(404).json({
49+
error: `Structure ${structureId} not found`,
50+
});
51+
}
52+
if (alreadyThere) {
53+
return res.status(400).json({
54+
error: `Structure ${structureId} already exists for the domain`,
55+
});
56+
}
57+
next();
58+
}
59+
60+
export async function createWithStructuresMetas(req, res, next) {
61+
const { structures: inputStructures = [], ...rest } = req.body;
62+
const errors = [];
63+
64+
const structureIds = inputStructures.map((el) => el.structureId);
65+
const { data: structuresData } = await structuresRepository.find({
66+
filters: { id: { $in: structureIds } },
67+
});
68+
const savedStructures = structuresData.map((structure) => structure.id);
69+
console.log("afterSaved", savedStructures)
70+
const notFoundStructures = structureIds.filter(
71+
(id) => !savedStructures.includes(id),
72+
);
73+
74+
75+
if (notFoundStructures.length > 0) {
76+
notFoundStructures.forEach((structureId) => {
77+
const index = structureIds.indexOf(structureId);
78+
errors.push({
79+
path: `structures[${index}].id`,
80+
message: `Structure '${structureId}' does not exist`,
81+
});
82+
});
83+
}
84+
85+
86+
if (errors.length > 0) {
87+
return res.status(400).json({ error: "Structure not found", errors });
88+
}
89+
90+
const structures = [];
91+
const userId = req.currentUser?.id;
92+
const now = new Date();
93+
for (const struct of inputStructures) {
94+
let id = await catalog.getUniqueId("domain-structure", 15);
95+
structures.push({
96+
id,
97+
...struct,
98+
createdBy: userId,
99+
createdAt: now,
100+
updatedBy: userId,
101+
updatedAt: now,
102+
});
103+
}
104+
req.body = {
105+
...rest,
106+
structures,
107+
};
108+
next();
109+
}

0 commit comments

Comments
 (0)