@oondemand/oon-core-back 0.3.45 → 0.3.47
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -1
- package/package.json +1 -1
- package/src/boot/loadCentral.js +15 -1
- package/src/core/define.js +9 -21
- package/src/core/factories/crudController.js +110 -42
- package/src/core/factories/crudService.js +24 -30
- package/src/delivery/buildImage.js +19 -0
- package/src/index.js +20 -1
- package/src/manifest/domainRules.js +95 -26
- package/src/manifest/processManifest.js +411 -0
- package/src/manifest/processRuntime.js +578 -0
- package/src/manifest/referencePolicies.js +108 -0
package/README.md
CHANGED
|
@@ -21,6 +21,15 @@ Compatibilidade temporária: `CENTRAL_ATIVACAO_BACKEND_URL` e `MEUS_APPS_BACKEND
|
|
|
21
21
|
|
|
22
22
|
Campos adicionais podem ser declarados em `activation.fields`; `password` e `secret` são sanitizados na configuração retornada ao frontend. Hooks opcionais: `validate`, `beforeComplete`, `afterComplete`.
|
|
23
23
|
|
|
24
|
+
## Imagem de entrega
|
|
25
|
+
|
|
26
|
+
O comando de delivery preserva `central.app.json` em dois pontos da imagem:
|
|
27
|
+
|
|
28
|
+
- `/src/central.app.json` durante o build do frontend declarativo;
|
|
29
|
+
- `/app/central.app.json` para descoberta pelo backend em runtime.
|
|
30
|
+
|
|
31
|
+
Centrais legadas sem o manifesto continuam suportadas. O empacotador cria uma pasta intermediária vazia, evitando tornar `central.app.json` obrigatório para aplicações que ainda usam somente `central.config.js`.
|
|
32
|
+
|
|
24
33
|
## Rótulos de campos relacionados
|
|
25
34
|
|
|
26
35
|
Por padrão, campos declarados com `fields.ref(...)` continuam sendo devolvidos pelo CRUD como `ObjectId`. Uma model pode optar pela população segura das referências usadas em grids e cards:
|
|
@@ -38,4 +47,4 @@ defineModel({
|
|
|
38
47
|
});
|
|
39
48
|
```
|
|
40
49
|
|
|
41
|
-
Use `populateRefs: true` para todas as referências da model ou informe uma lista explícita. O CRUD devolve somente `_id` e campos usuais de identificação, como nome, razão social, descrição, código, e-mail e campos pesquisáveis da model referenciada. Exportações mantêm os identificadores originais.
|
|
50
|
+
Use `populateRefs: true` para todas as referências da model ou informe uma lista explícita. O CRUD devolve somente `_id` e campos usuais de identificação, como nome, razão social, descrição, código, e-mail e campos pesquisáveis da model referenciada. Exportações mantêm os identificadores originais.
|
package/package.json
CHANGED
package/src/boot/loadCentral.js
CHANGED
|
@@ -11,6 +11,10 @@ const {
|
|
|
11
11
|
DOMAIN_MANIFEST_FILENAME,
|
|
12
12
|
loadDomainManifest,
|
|
13
13
|
} = require("../manifest/domainManifest");
|
|
14
|
+
const {
|
|
15
|
+
PROCESS_MANIFEST_FILENAME,
|
|
16
|
+
loadProcessManifest,
|
|
17
|
+
} = require("../manifest/processManifest");
|
|
14
18
|
|
|
15
19
|
const DOMAIN_DIRS = [
|
|
16
20
|
"models",
|
|
@@ -76,6 +80,11 @@ function loadCentral(cwd = process.cwd()) {
|
|
|
76
80
|
loadDomainManifest(domainManifestPath);
|
|
77
81
|
}
|
|
78
82
|
|
|
83
|
+
const processManifestPath = path.join(cwd, PROCESS_MANIFEST_FILENAME);
|
|
84
|
+
if (fs.existsSync(processManifestPath)) {
|
|
85
|
+
loadProcessManifest(processManifestPath);
|
|
86
|
+
}
|
|
87
|
+
|
|
79
88
|
const srcDir = path.join(cwd, "src");
|
|
80
89
|
for (const dir of DOMAIN_DIRS) {
|
|
81
90
|
const full = path.join(srcDir, dir);
|
|
@@ -93,4 +102,9 @@ function requireDir(dir) {
|
|
|
93
102
|
}
|
|
94
103
|
}
|
|
95
104
|
|
|
96
|
-
module.exports = {
|
|
105
|
+
module.exports = {
|
|
106
|
+
loadCentral,
|
|
107
|
+
DOMAIN_DIRS,
|
|
108
|
+
APP_CONFIG_KEYS,
|
|
109
|
+
assertConfigBoundary,
|
|
110
|
+
};
|
package/src/core/define.js
CHANGED
|
@@ -7,10 +7,6 @@ const auth = require("./middlewares/auth");
|
|
|
7
7
|
const { rbac } = require("./middlewares/rbac");
|
|
8
8
|
const { mutationAudit } = require("./middlewares/mutationAudit");
|
|
9
9
|
|
|
10
|
-
/**
|
|
11
|
-
* Constrói o schema Mongoose a partir dos descriptors de `fields`,
|
|
12
|
-
* separando o bloco `__meta` (consumido apenas por /core/metadata).
|
|
13
|
-
*/
|
|
14
10
|
function buildSchema(schemaDef, options = {}) {
|
|
15
11
|
const mongooseShape = {};
|
|
16
12
|
const metaFields = {};
|
|
@@ -23,12 +19,19 @@ function buildSchema(schemaDef, options = {}) {
|
|
|
23
19
|
metaFields[key] = { name: key, ...__meta };
|
|
24
20
|
if (__meta.kind === "string" && __meta.searchable) searchableFields.push(key);
|
|
25
21
|
} else {
|
|
26
|
-
// Descriptor cru (compatível com Mongoose puro).
|
|
27
22
|
mongooseShape[key] = descriptor;
|
|
28
23
|
metaFields[key] = { name: key, kind: "raw" };
|
|
29
24
|
}
|
|
30
25
|
}
|
|
31
26
|
|
|
27
|
+
if (!Object.prototype.hasOwnProperty.call(mongooseShape, "__oonInvariantVersion")) {
|
|
28
|
+
mongooseShape.__oonInvariantVersion = {
|
|
29
|
+
type: Number,
|
|
30
|
+
default: 0,
|
|
31
|
+
select: false,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
32
35
|
const schema = new mongoose.Schema(mongooseShape, {
|
|
33
36
|
timestamps: true,
|
|
34
37
|
...options,
|
|
@@ -37,10 +40,6 @@ function buildSchema(schemaDef, options = {}) {
|
|
|
37
40
|
return { schema, metaFields, searchableFields };
|
|
38
41
|
}
|
|
39
42
|
|
|
40
|
-
/**
|
|
41
|
-
* defineModel — registra a model e gera derivações automáticas
|
|
42
|
-
* (schema, CRUD, RBAC, paginação, validação, auditoria, metadata).
|
|
43
|
-
*/
|
|
44
43
|
function defineModel(definition) {
|
|
45
44
|
if (!definition?.name) throw new Error("defineModel requer `name`.");
|
|
46
45
|
if (!definition?.schema) throw new Error(`defineModel("${definition.name}") requer \`schema\`.`);
|
|
@@ -50,7 +49,6 @@ function defineModel(definition) {
|
|
|
50
49
|
definition.options
|
|
51
50
|
);
|
|
52
51
|
|
|
53
|
-
// Hooks de domínio (defineTrigger) ligados ao ciclo do documento.
|
|
54
52
|
schema.pre("save", async function (next) {
|
|
55
53
|
try {
|
|
56
54
|
const { before } = registry.getTriggers(definition.name);
|
|
@@ -77,7 +75,6 @@ function defineModel(definition) {
|
|
|
77
75
|
searchableFields,
|
|
78
76
|
});
|
|
79
77
|
|
|
80
|
-
// Permissões/actions derivadas para o front.
|
|
81
78
|
if (definition.crud?.enabled !== false) {
|
|
82
79
|
registry.registerFeature(definition.name, {
|
|
83
80
|
model: definition.name,
|
|
@@ -89,10 +86,6 @@ function defineModel(definition) {
|
|
|
89
86
|
return entry;
|
|
90
87
|
}
|
|
91
88
|
|
|
92
|
-
/**
|
|
93
|
-
* defineCentral — declara metadados da Central (módulos ativos + paths de
|
|
94
|
-
* domínio). Mescla na config de runtime.
|
|
95
|
-
*/
|
|
96
89
|
function defineCentral(config = {}) {
|
|
97
90
|
ctx.setConfig(config);
|
|
98
91
|
return ctx.config;
|
|
@@ -122,11 +115,6 @@ function defineOmieMapping(name, config) {
|
|
|
122
115
|
return registry.registerOmieMapping(name, { name, ...config });
|
|
123
116
|
}
|
|
124
117
|
|
|
125
|
-
/**
|
|
126
|
-
* defineRoutes — rotas customizadas com o contrato de segurança do Core.
|
|
127
|
-
* Entrega `router.private.*` (auth + RBAC + mutationAudit) e
|
|
128
|
-
* `router.public.*`. NUNCA expõe `express.Router()` cru.
|
|
129
|
-
*/
|
|
130
118
|
function defineRoutes(basePath, builder) {
|
|
131
119
|
const expressRouter = express.Router();
|
|
132
120
|
|
|
@@ -168,4 +156,4 @@ module.exports = {
|
|
|
168
156
|
definePipeline,
|
|
169
157
|
defineOmieMapping,
|
|
170
158
|
defineRoutes,
|
|
171
|
-
};
|
|
159
|
+
};
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
const Helpers = require("../utils/helpers");
|
|
2
2
|
const registry = require("../registry");
|
|
3
|
-
const {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
3
|
+
const {
|
|
4
|
+
prepareProcessMutation,
|
|
5
|
+
assertAtomicInvariants,
|
|
6
|
+
assertDeleteAllowed,
|
|
7
|
+
withProcessTransaction,
|
|
8
|
+
recalculateDependents,
|
|
9
|
+
toPlainRecord,
|
|
10
|
+
} = require("../../manifest/processRuntime");
|
|
11
|
+
const { assertReferencePolicies } = require("../../manifest/referencePolicies");
|
|
12
12
|
|
|
13
13
|
function hasDomainRules(entry) {
|
|
14
14
|
const rules = entry?.definition?.domainRules;
|
|
@@ -20,28 +20,31 @@ function hasDomainRules(entry) {
|
|
|
20
20
|
);
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
23
|
+
function hasProcessRules(entry) {
|
|
24
|
+
const rules = entry?.definition?.processRules;
|
|
25
|
+
return Boolean(
|
|
26
|
+
rules
|
|
27
|
+
&& (rules.workflow
|
|
28
|
+
|| (rules.bindings || []).length
|
|
29
|
+
|| (rules.references || []).length
|
|
30
|
+
|| (rules.deleteProtection || []).length
|
|
31
|
+
|| (rules.atomicInvariants || []).length),
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
31
35
|
function createCrudController(entry, service) {
|
|
32
36
|
const runCustomValidation = async (data, context) => {
|
|
33
37
|
const validate = registry.getValidation(entry.name);
|
|
34
38
|
if (validate) await validate(data, context);
|
|
35
39
|
};
|
|
36
40
|
|
|
37
|
-
const prepareCreate = async (data, method = "post") => {
|
|
38
|
-
const prepared =
|
|
39
|
-
rules: entry.definition?.domainRules,
|
|
40
|
-
current: null,
|
|
41
|
-
changes: data,
|
|
41
|
+
const prepareCreate = async (data, method = "post", session = null) => {
|
|
42
|
+
const prepared = await prepareProcessMutation(entry, {}, data, {
|
|
42
43
|
op: "create",
|
|
44
|
+
session,
|
|
43
45
|
});
|
|
44
46
|
|
|
47
|
+
await assertReferencePolicies(entry, prepared.consolidated, { session });
|
|
45
48
|
await runCustomValidation(prepared.consolidated, {
|
|
46
49
|
op: "create",
|
|
47
50
|
method,
|
|
@@ -49,31 +52,33 @@ function createCrudController(entry, service) {
|
|
|
49
52
|
requestedChanges: prepared.requestedChanges,
|
|
50
53
|
changes: prepared.changes,
|
|
51
54
|
consolidated: prepared.consolidated,
|
|
55
|
+
changedFields: prepared.changedFields,
|
|
56
|
+
session,
|
|
52
57
|
});
|
|
53
58
|
return prepared;
|
|
54
59
|
};
|
|
55
60
|
|
|
56
|
-
const prepareUpdate = async (id, changes, method) => {
|
|
61
|
+
const prepareUpdate = async (id, changes, method, session = null) => {
|
|
57
62
|
const validate = registry.getValidation(entry.name);
|
|
58
|
-
if (!validate && !hasDomainRules(entry)) {
|
|
63
|
+
if (!validate && !hasDomainRules(entry) && !hasProcessRules(entry)) {
|
|
59
64
|
const normalizedChanges = changes && typeof changes === "object" ? { ...changes } : {};
|
|
60
65
|
return {
|
|
61
66
|
current: null,
|
|
62
67
|
requestedChanges: normalizedChanges,
|
|
63
68
|
changes: normalizedChanges,
|
|
64
69
|
consolidated: normalizedChanges,
|
|
70
|
+
changedFields: Object.keys(normalizedChanges),
|
|
65
71
|
};
|
|
66
72
|
}
|
|
67
73
|
|
|
68
|
-
const currentRecord = await service.getById(id);
|
|
74
|
+
const currentRecord = await service.getById(id, { session });
|
|
69
75
|
const current = toPlainRecord(currentRecord);
|
|
70
|
-
const prepared =
|
|
71
|
-
rules: entry.definition?.domainRules,
|
|
72
|
-
current,
|
|
73
|
-
changes,
|
|
76
|
+
const prepared = await prepareProcessMutation(entry, current, changes, {
|
|
74
77
|
op: "update",
|
|
78
|
+
session,
|
|
75
79
|
});
|
|
76
80
|
|
|
81
|
+
await assertReferencePolicies(entry, prepared.consolidated, { session });
|
|
77
82
|
if (validate) {
|
|
78
83
|
await validate(prepared.consolidated, {
|
|
79
84
|
op: "update",
|
|
@@ -83,12 +88,36 @@ function createCrudController(entry, service) {
|
|
|
83
88
|
requestedChanges: prepared.requestedChanges,
|
|
84
89
|
changes: prepared.changes,
|
|
85
90
|
consolidated: prepared.consolidated,
|
|
91
|
+
changedFields: prepared.changedFields,
|
|
92
|
+
session,
|
|
86
93
|
});
|
|
87
94
|
}
|
|
88
95
|
|
|
89
96
|
return { current, ...prepared };
|
|
90
97
|
};
|
|
91
98
|
|
|
99
|
+
async function createOne(data, method, session) {
|
|
100
|
+
const prepared = await prepareCreate(data, method, session);
|
|
101
|
+
await assertAtomicInvariants(entry, prepared.consolidated, null, session);
|
|
102
|
+
const record = await service.create(prepared.changes, { session });
|
|
103
|
+
await recalculateDependents(entry, null, toPlainRecord(record), {
|
|
104
|
+
session,
|
|
105
|
+
changedFields: prepared.changedFields,
|
|
106
|
+
});
|
|
107
|
+
return record;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function updateOne(id, changes, method, session) {
|
|
111
|
+
const prepared = await prepareUpdate(id, changes, method, session);
|
|
112
|
+
await assertAtomicInvariants(entry, prepared.consolidated, prepared.current, session);
|
|
113
|
+
const record = await service.update(id, prepared.changes, { session });
|
|
114
|
+
await recalculateDependents(entry, prepared.current, toPlainRecord(record), {
|
|
115
|
+
session,
|
|
116
|
+
changedFields: prepared.changedFields,
|
|
117
|
+
});
|
|
118
|
+
return record;
|
|
119
|
+
}
|
|
120
|
+
|
|
92
121
|
return {
|
|
93
122
|
async list(req, res) {
|
|
94
123
|
const { pageIndex, pageSize, searchTerm, sort, ...filtros } = req.query;
|
|
@@ -108,35 +137,69 @@ function createCrudController(entry, service) {
|
|
|
108
137
|
},
|
|
109
138
|
|
|
110
139
|
async create(req, res) {
|
|
111
|
-
const
|
|
112
|
-
|
|
140
|
+
const registro = await withProcessTransaction(
|
|
141
|
+
entry,
|
|
142
|
+
(session) => createOne(req.body ?? {}, "post", session),
|
|
143
|
+
);
|
|
113
144
|
Helpers.sendResponse({ res, statusCode: 201, [entry.singular]: registro });
|
|
114
145
|
},
|
|
115
146
|
|
|
116
147
|
async update(req, res) {
|
|
117
|
-
const
|
|
118
|
-
|
|
148
|
+
const registro = await withProcessTransaction(
|
|
149
|
+
entry,
|
|
150
|
+
(session) => updateOne(req.params.id, req.body ?? {}, "put", session),
|
|
151
|
+
);
|
|
119
152
|
Helpers.sendResponse({ res, statusCode: 200, [entry.singular]: registro });
|
|
120
153
|
},
|
|
121
154
|
|
|
122
155
|
async patch(req, res) {
|
|
123
|
-
const
|
|
124
|
-
|
|
156
|
+
const registro = await withProcessTransaction(
|
|
157
|
+
entry,
|
|
158
|
+
(session) => updateOne(req.params.id, req.body ?? {}, "patch", session),
|
|
159
|
+
);
|
|
125
160
|
Helpers.sendResponse({ res, statusCode: 200, [entry.singular]: registro });
|
|
126
161
|
},
|
|
127
162
|
|
|
128
163
|
async remove(req, res) {
|
|
129
|
-
const registro = await
|
|
164
|
+
const registro = await withProcessTransaction(entry, async (session) => {
|
|
165
|
+
const currentRecord = await service.getById(req.params.id, { session });
|
|
166
|
+
const current = toPlainRecord(currentRecord);
|
|
167
|
+
await assertDeleteAllowed(entry, req.params.id, { session });
|
|
168
|
+
const removed = await service.remove(req.params.id, { session });
|
|
169
|
+
await recalculateDependents(entry, current, null, {
|
|
170
|
+
session,
|
|
171
|
+
changedFields: Object.keys(current),
|
|
172
|
+
});
|
|
173
|
+
return removed;
|
|
174
|
+
});
|
|
130
175
|
Helpers.sendResponse({ res, statusCode: 200, [entry.singular]: registro });
|
|
131
176
|
},
|
|
132
177
|
|
|
133
178
|
async importMany(req, res) {
|
|
134
179
|
const rows = req.body?.rows || req.body;
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
:
|
|
138
|
-
|
|
139
|
-
|
|
180
|
+
if (!Array.isArray(rows)) {
|
|
181
|
+
const result = await service.importMany(rows);
|
|
182
|
+
Helpers.sendResponse({ res, statusCode: 201, ...result });
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (!hasProcessRules(entry)) {
|
|
187
|
+
const preparedRows = await Promise.all(
|
|
188
|
+
rows.map(async (row) => (await prepareCreate(row, "import")).changes),
|
|
189
|
+
);
|
|
190
|
+
const result = await service.importMany(preparedRows);
|
|
191
|
+
Helpers.sendResponse({ res, statusCode: 201, ...result });
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
let inserted = 0;
|
|
196
|
+
for (const row of rows) {
|
|
197
|
+
await withProcessTransaction(entry, async (session) => {
|
|
198
|
+
await createOne(row, "import", session);
|
|
199
|
+
inserted += 1;
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
Helpers.sendResponse({ res, statusCode: 201, inserted });
|
|
140
203
|
},
|
|
141
204
|
|
|
142
205
|
async exportAll(req, res) {
|
|
@@ -146,4 +209,9 @@ function createCrudController(entry, service) {
|
|
|
146
209
|
};
|
|
147
210
|
}
|
|
148
211
|
|
|
149
|
-
module.exports = {
|
|
212
|
+
module.exports = {
|
|
213
|
+
createCrudController,
|
|
214
|
+
toPlainRecord,
|
|
215
|
+
hasDomainRules,
|
|
216
|
+
hasProcessRules,
|
|
217
|
+
};
|
|
@@ -15,11 +15,6 @@ const COMMON_REFERENCE_LABEL_FIELDS = [
|
|
|
15
15
|
"sigla",
|
|
16
16
|
];
|
|
17
17
|
|
|
18
|
-
/**
|
|
19
|
-
* Gera especificações de populate somente quando a model opta explicitamente
|
|
20
|
-
* por `crud.populateRefs`. O select é restrito a campos de identificação para
|
|
21
|
-
* evitar devolver o documento relacionado inteiro nas listagens genéricas.
|
|
22
|
-
*/
|
|
23
18
|
function referencePopulateSpecs(entry) {
|
|
24
19
|
const setting = entry.definition?.crud?.populateRefs;
|
|
25
20
|
if (!setting) return [];
|
|
@@ -52,10 +47,6 @@ function populateQuery(query, specs) {
|
|
|
52
47
|
return specs.length ? query.populate(specs) : query;
|
|
53
48
|
}
|
|
54
49
|
|
|
55
|
-
/**
|
|
56
|
-
* Aceita tanto uma Query Mongoose quanto uma Promise/Document devolvida por
|
|
57
|
-
* uma model que sobrescreveu métodos como `findByIdAndUpdate`.
|
|
58
|
-
*/
|
|
59
50
|
async function resolvePopulated(result, specs) {
|
|
60
51
|
if (!specs.length) return result;
|
|
61
52
|
if (result && typeof result.populate === "function") {
|
|
@@ -67,10 +58,10 @@ async function resolvePopulated(result, specs) {
|
|
|
67
58
|
return doc;
|
|
68
59
|
}
|
|
69
60
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
61
|
+
function withSession(query, session) {
|
|
62
|
+
return session && query && typeof query.session === "function" ? query.session(session) : query;
|
|
63
|
+
}
|
|
64
|
+
|
|
74
65
|
function createCrudService(entry) {
|
|
75
66
|
const { mongooseModel: Model, searchableFields } = entry;
|
|
76
67
|
const populateSpecs = referencePopulateSpecs(entry);
|
|
@@ -106,40 +97,44 @@ function createCrudService(entry) {
|
|
|
106
97
|
};
|
|
107
98
|
}
|
|
108
99
|
|
|
109
|
-
async function getById(id) {
|
|
110
|
-
const
|
|
100
|
+
async function getById(id, options = {}) {
|
|
101
|
+
const query = withSession(Model.findById(id), options.session);
|
|
102
|
+
const doc = await resolvePopulated(query, populateSpecs);
|
|
111
103
|
if (!doc) throw new GenericError("Registro não encontrado.", { statusCode: 404 });
|
|
112
104
|
return doc;
|
|
113
105
|
}
|
|
114
106
|
|
|
115
|
-
async function create(data) {
|
|
116
|
-
const doc =
|
|
107
|
+
async function create(data, options = {}) {
|
|
108
|
+
const doc = new Model(data);
|
|
109
|
+
await doc.save({ session: options.session || undefined });
|
|
117
110
|
return resolvePopulated(doc, populateSpecs);
|
|
118
111
|
}
|
|
119
112
|
|
|
120
|
-
async function update(id, data) {
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
);
|
|
113
|
+
async function update(id, data, options = {}) {
|
|
114
|
+
const query = Model.findByIdAndUpdate(id, data, {
|
|
115
|
+
new: true,
|
|
116
|
+
runValidators: true,
|
|
117
|
+
session: options.session || undefined,
|
|
118
|
+
});
|
|
119
|
+
const doc = await resolvePopulated(query, populateSpecs);
|
|
128
120
|
if (!doc) throw new GenericError("Registro não encontrado.", { statusCode: 404 });
|
|
129
121
|
return doc;
|
|
130
122
|
}
|
|
131
123
|
|
|
132
|
-
async function remove(id) {
|
|
133
|
-
const doc = await Model.findByIdAndDelete(id);
|
|
124
|
+
async function remove(id, options = {}) {
|
|
125
|
+
const doc = await withSession(Model.findByIdAndDelete(id), options.session);
|
|
134
126
|
if (!doc) throw new GenericError("Registro não encontrado.", { statusCode: 404 });
|
|
135
127
|
return doc;
|
|
136
128
|
}
|
|
137
129
|
|
|
138
|
-
async function importMany(rows = []) {
|
|
130
|
+
async function importMany(rows = [], options = {}) {
|
|
139
131
|
if (!Array.isArray(rows)) {
|
|
140
132
|
throw new GenericError("Esperado um array de registros.", { statusCode: 400 });
|
|
141
133
|
}
|
|
142
|
-
const docs = await Model.insertMany(rows, {
|
|
134
|
+
const docs = await Model.insertMany(rows, {
|
|
135
|
+
ordered: false,
|
|
136
|
+
session: options.session || undefined,
|
|
137
|
+
});
|
|
143
138
|
return { inserted: docs.length };
|
|
144
139
|
}
|
|
145
140
|
|
|
@@ -160,7 +155,6 @@ function cleanFilters(filtros) {
|
|
|
160
155
|
|
|
161
156
|
function parseSort(sort) {
|
|
162
157
|
if (!sort) return { createdAt: -1 };
|
|
163
|
-
// "campo:asc" | "campo:desc" | "campo"
|
|
164
158
|
const [field, dir] = String(sort).split(":");
|
|
165
159
|
return { [field]: dir === "asc" ? 1 : -1 };
|
|
166
160
|
}
|
|
@@ -10,6 +10,7 @@ WORKDIR /src/frontend
|
|
|
10
10
|
COPY frontend/package*.json ./
|
|
11
11
|
RUN if [ -f package-lock.json ]; then npm ci; else npm install; fi
|
|
12
12
|
COPY frontend/ ./
|
|
13
|
+
COPY .oon-delivery/app-manifest/ /src/
|
|
13
14
|
ARG VITE_API_URL=/api
|
|
14
15
|
ENV VITE_API_URL=$VITE_API_URL
|
|
15
16
|
RUN npm run build
|
|
@@ -25,6 +26,7 @@ RUN apk add --no-cache nginx curl
|
|
|
25
26
|
WORKDIR /app
|
|
26
27
|
ENV NODE_ENV=production
|
|
27
28
|
COPY --from=backend /app/backend /app/backend
|
|
29
|
+
COPY .oon-delivery/app-manifest/ /app/
|
|
28
30
|
COPY --from=frontend /src/frontend/dist /usr/share/nginx/html
|
|
29
31
|
COPY .oon-delivery/nginx.conf /etc/nginx/http.d/default.conf
|
|
30
32
|
COPY .oon-delivery/entrypoint.sh /entrypoint.sh
|
|
@@ -60,9 +62,25 @@ trap 'kill $backend_pid 2>/dev/null || true' TERM INT EXIT
|
|
|
60
62
|
exec nginx -g 'daemon off;'
|
|
61
63
|
`;
|
|
62
64
|
|
|
65
|
+
function prepareAppManifest(workspace, deliveryDirectory) {
|
|
66
|
+
const manifestDirectory = path.join(deliveryDirectory, "app-manifest");
|
|
67
|
+
const manifestSource = path.join(workspace, "central.app.json");
|
|
68
|
+
const manifestTarget = path.join(manifestDirectory, "central.app.json");
|
|
69
|
+
|
|
70
|
+
fs.rmSync(manifestDirectory, { recursive: true, force: true });
|
|
71
|
+
fs.mkdirSync(manifestDirectory, { recursive: true });
|
|
72
|
+
|
|
73
|
+
if (fs.existsSync(manifestSource)) {
|
|
74
|
+
fs.copyFileSync(manifestSource, manifestTarget);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return manifestTarget;
|
|
78
|
+
}
|
|
79
|
+
|
|
63
80
|
function prepareDeliveryFiles(workspace) {
|
|
64
81
|
const target = path.join(workspace, ".oon-delivery");
|
|
65
82
|
fs.mkdirSync(target, { recursive: true });
|
|
83
|
+
prepareAppManifest(workspace, target);
|
|
66
84
|
fs.writeFileSync(path.join(target, "Dockerfile"), DOCKERFILE);
|
|
67
85
|
fs.writeFileSync(path.join(target, "nginx.conf"), NGINX);
|
|
68
86
|
fs.writeFileSync(path.join(target, "entrypoint.sh"), ENTRYPOINT, {
|
|
@@ -82,6 +100,7 @@ module.exports = {
|
|
|
82
100
|
DOCKERFILE,
|
|
83
101
|
NGINX,
|
|
84
102
|
ENTRYPOINT,
|
|
103
|
+
prepareAppManifest,
|
|
85
104
|
prepareDeliveryFiles,
|
|
86
105
|
buildImage,
|
|
87
106
|
};
|
package/src/index.js
CHANGED
|
@@ -11,6 +11,9 @@ const { omie } = require("./modules/omie");
|
|
|
11
11
|
const appManifest = require("./manifest/appManifest");
|
|
12
12
|
const domainManifest = require("./manifest/domainManifest");
|
|
13
13
|
const domainRules = require("./manifest/domainRules");
|
|
14
|
+
const processManifest = require("./manifest/processManifest");
|
|
15
|
+
const processRuntime = require("./manifest/processRuntime");
|
|
16
|
+
const referencePolicies = require("./manifest/referencePolicies");
|
|
14
17
|
|
|
15
18
|
module.exports = {
|
|
16
19
|
start,
|
|
@@ -49,9 +52,25 @@ module.exports = {
|
|
|
49
52
|
loadDomainManifest: domainManifest.loadDomainManifest,
|
|
50
53
|
evaluateDomainExpression: domainRules.evaluateDomainExpression,
|
|
51
54
|
applyDomainMutation: domainRules.applyDomainMutation,
|
|
55
|
+
validateDomainRecord: domainRules.validateDomainRecord,
|
|
56
|
+
detectChangedFields: domainRules.detectChangedFields,
|
|
57
|
+
|
|
58
|
+
PROCESS_MANIFEST_FILENAME: processManifest.PROCESS_MANIFEST_FILENAME,
|
|
59
|
+
PROCESS_MANIFEST_SCHEMA_VERSION: processManifest.PROCESS_MANIFEST_SCHEMA_VERSION,
|
|
60
|
+
ProcessManifestError: processManifest.ProcessManifestError,
|
|
61
|
+
validateProcessManifest: processManifest.validateProcessManifest,
|
|
62
|
+
registerProcessManifest: processManifest.registerProcessManifest,
|
|
63
|
+
loadProcessManifest: processManifest.loadProcessManifest,
|
|
64
|
+
prepareProcessMutation: processRuntime.prepareProcessMutation,
|
|
65
|
+
resolveProcessBindings: processRuntime.resolveBindingsForRecords,
|
|
66
|
+
assertReferencePolicies: referencePolicies.assertReferencePolicies,
|
|
67
|
+
assertAtomicInvariants: processRuntime.assertAtomicInvariants,
|
|
68
|
+
assertDeleteAllowed: processRuntime.assertDeleteAllowed,
|
|
69
|
+
recalculateDependents: processRuntime.recalculateDependents,
|
|
70
|
+
drainProcessJobs: processRuntime.drainProcessJobs,
|
|
52
71
|
|
|
53
72
|
fields,
|
|
54
73
|
omie,
|
|
55
74
|
GenericError,
|
|
56
75
|
registry,
|
|
57
|
-
};
|
|
76
|
+
};
|