@openinc/parse-server-opendash 4.1.1 → 4.1.2
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/dist/featuremap.json +27 -12
- package/dist/features/config/helper/customOptions.js +9 -0
- package/dist/features/config/helper/isFeatureEnabled.d.ts +1 -1
- package/dist/features/config/helper/isFeatureEnabled.js +2 -0
- package/dist/features/meta/index.d.ts +1 -0
- package/dist/features/meta/index.js +5 -0
- package/dist/features/meta/services/registerMetaContext.d.ts +29 -0
- package/dist/features/meta/services/registerMetaContext.js +208 -0
- package/dist/features/permissions/types/Permissions.d.ts +24 -1
- package/dist/features/permissions/types/Permissions.js +34 -0
- package/dist/features/ruleset/applyRuleSet.d.ts +41 -0
- package/dist/features/ruleset/applyRuleSet.js +81 -0
- package/dist/features/schema/helper/defaultHandler.js +13 -0
- package/dist/features/schema/helper/migrationMode.d.ts +26 -0
- package/dist/features/schema/helper/migrationMode.js +48 -0
- package/dist/features/schema/index.d.ts +1 -0
- package/dist/features/schema/index.js +3 -1
- package/dist/features/schema/services/beforeSaveHook.js +21 -0
- package/dist/features/translations/services/initTranslation.js +1 -1
- package/dist/hooks/Chat_Message.d.ts +1 -0
- package/dist/hooks/Chat_Message.js +179 -0
- package/dist/hooks/Meta_Config.js +19 -10
- package/dist/hooks/Meta_Context.d.ts +6 -0
- package/dist/hooks/Meta_Context.js +16 -0
- package/dist/hooks/Meta_Entry.js +20 -10
- package/dist/hooks/Service_Equipment.d.ts +1 -0
- package/dist/hooks/Service_Equipment.js +38 -0
- package/dist/hooks/Service_FormConfig.d.ts +1 -0
- package/dist/hooks/Service_FormConfig.js +17 -0
- package/dist/hooks/Service_Project.d.ts +1 -0
- package/dist/hooks/Service_Project.js +17 -0
- package/dist/hooks/Service_Schedule.d.ts +1 -0
- package/dist/hooks/Service_Schedule.js +24 -0
- package/dist/hooks/Service_Schedule_Execution.d.ts +1 -0
- package/dist/hooks/Service_Schedule_Execution.js +17 -0
- package/dist/hooks/Service_Schedule_Template.d.ts +1 -0
- package/dist/hooks/Service_Schedule_Template.js +17 -0
- package/dist/hooks/Service_Status.d.ts +1 -0
- package/dist/hooks/Service_Status.js +32 -0
- package/dist/hooks/Service_Tag.d.ts +1 -0
- package/dist/hooks/Service_Tag.js +168 -0
- package/dist/hooks/Service_Ticket.d.ts +1 -0
- package/dist/hooks/Service_Ticket.js +433 -0
- package/dist/hooks/Service_TriggerState.d.ts +1 -0
- package/dist/hooks/Service_TriggerState.js +17 -0
- package/dist/hooks/_ChangeLog.js +27 -6
- package/dist/i18n/de-DE/chat.json +18 -0
- package/dist/i18n/de-DE/openservice.json +14 -0
- package/dist/i18n/en-US/chat.json +18 -0
- package/dist/i18n/en-US/openservice.json +14 -0
- package/dist/scripts/backfill-meta-contexts.d.ts +1 -0
- package/dist/scripts/backfill-meta-contexts.js +100 -0
- package/dist/scripts/migration/lib/acl.d.ts +52 -0
- package/dist/scripts/migration/lib/acl.js +79 -0
- package/dist/scripts/migration/lib/articleMeta.d.ts +47 -0
- package/dist/scripts/migration/lib/articleMeta.js +132 -0
- package/dist/scripts/migration/lib/env.d.ts +56 -0
- package/dist/scripts/migration/lib/env.js +106 -0
- package/dist/scripts/migration/lib/latest.d.ts +47 -0
- package/dist/scripts/migration/lib/latest.js +115 -0
- package/dist/scripts/migration/lib/map.d.ts +53 -0
- package/dist/scripts/migration/lib/map.js +113 -0
- package/dist/scripts/migration/lib/material.d.ts +47 -0
- package/dist/scripts/migration/lib/material.js +51 -0
- package/dist/scripts/migration/lib/message.d.ts +78 -0
- package/dist/scripts/migration/lib/message.js +115 -0
- package/dist/scripts/migration/lib/pass.d.ts +67 -0
- package/dist/scripts/migration/lib/pass.js +89 -0
- package/dist/scripts/migration/lib/ptr.d.ts +32 -0
- package/dist/scripts/migration/lib/ptr.js +38 -0
- package/dist/scripts/migration/lib/report.d.ts +41 -0
- package/dist/scripts/migration/lib/report.js +78 -0
- package/dist/scripts/migration/lib/rest.d.ts +47 -0
- package/dist/scripts/migration/lib/rest.js +68 -0
- package/dist/scripts/migration/lib/ticket.d.ts +72 -0
- package/dist/scripts/migration/lib/ticket.js +192 -0
- package/dist/scripts/migration/passes/01-statuses.d.ts +12 -0
- package/dist/scripts/migration/passes/01-statuses.js +122 -0
- package/dist/scripts/migration/passes/02-tags.d.ts +13 -0
- package/dist/scripts/migration/passes/02-tags.js +150 -0
- package/dist/scripts/migration/passes/03-equipment.d.ts +14 -0
- package/dist/scripts/migration/passes/03-equipment.js +176 -0
- package/dist/scripts/migration/passes/04-projects.d.ts +27 -0
- package/dist/scripts/migration/passes/04-projects.js +241 -0
- package/dist/scripts/migration/passes/05-articles.d.ts +25 -0
- package/dist/scripts/migration/passes/05-articles.js +203 -0
- package/dist/scripts/migration/passes/06-formconfigs.d.ts +17 -0
- package/dist/scripts/migration/passes/06-formconfigs.js +166 -0
- package/dist/scripts/migration/passes/07-meta-configs.d.ts +46 -0
- package/dist/scripts/migration/passes/07-meta-configs.js +185 -0
- package/dist/scripts/migration/passes/08-tickets.d.ts +27 -0
- package/dist/scripts/migration/passes/08-tickets.js +353 -0
- package/dist/scripts/migration/passes/09-ticket-meta.d.ts +12 -0
- package/dist/scripts/migration/passes/09-ticket-meta.js +136 -0
- package/dist/scripts/migration/passes/10-chat.d.ts +17 -0
- package/dist/scripts/migration/passes/10-chat.js +230 -0
- package/dist/scripts/migration/passes/11-assets.d.ts +17 -0
- package/dist/scripts/migration/passes/11-assets.js +93 -0
- package/dist/scripts/migration/passes/12-history.d.ts +24 -0
- package/dist/scripts/migration/passes/12-history.js +332 -0
- package/dist/scripts/migration/run.d.ts +1 -0
- package/dist/scripts/migration/run.js +148 -0
- package/dist/scripts/migration/verify.d.ts +1 -0
- package/dist/scripts/migration/verify.js +620 -0
- package/dist/scripts/seed-v1-testdata.d.ts +41 -0
- package/dist/scripts/seed-v1-testdata.js +543 -0
- package/dist/types/BDE_Article.d.ts +3 -0
- package/dist/types/BDE_Article.js +6 -0
- package/dist/types/Chat_Message.d.ts +49 -0
- package/dist/types/Chat_Message.js +87 -0
- package/dist/types/MES_RessourceOverview.d.ts +38 -0
- package/dist/types/MES_RessourceOverview.js +63 -0
- package/dist/types/Meta_Context.d.ts +43 -0
- package/dist/types/Meta_Context.js +75 -0
- package/dist/types/Service_Equipment.d.ts +33 -0
- package/dist/types/Service_Equipment.js +57 -0
- package/dist/types/Service_FormConfig.d.ts +33 -0
- package/dist/types/Service_FormConfig.js +57 -0
- package/dist/types/Service_MeterReading.d.ts +35 -0
- package/dist/types/Service_MeterReading.js +57 -0
- package/dist/types/Service_Project.d.ts +53 -0
- package/dist/types/Service_Project.js +93 -0
- package/dist/types/Service_Schedule.d.ts +66 -0
- package/dist/types/Service_Schedule.js +117 -0
- package/dist/types/Service_Schedule_Execution.d.ts +38 -0
- package/dist/types/Service_Schedule_Execution.js +63 -0
- package/dist/types/Service_Schedule_Template.d.ts +36 -0
- package/dist/types/Service_Schedule_Template.js +60 -0
- package/dist/types/Service_Status.d.ts +30 -0
- package/dist/types/Service_Status.js +51 -0
- package/dist/types/Service_Tag.d.ts +30 -0
- package/dist/types/Service_Tag.js +51 -0
- package/dist/types/Service_Ticket.d.ts +92 -0
- package/dist/types/Service_Ticket.js +165 -0
- package/dist/types/Service_TicketCounter.d.ts +18 -0
- package/dist/types/Service_TicketCounter.js +27 -0
- package/dist/types/Service_TriggerState.d.ts +31 -0
- package/dist/types/Service_TriggerState.js +51 -0
- package/dist/types/_Session.d.ts +0 -1
- package/dist/types/_User.d.ts +2 -6
- package/dist/types/custom/MetaField.d.ts +9 -0
- package/dist/types/custom/TicketMaterial.d.ts +32 -0
- package/dist/types/custom/TicketMaterial.js +2 -0
- package/dist/types/index.d.ts +30 -0
- package/dist/types/index.js +33 -3
- package/package.json +6 -3
- package/schema/BDE_Article.json +3 -0
- package/schema/Changelog.json +3 -6
- package/schema/Chat_Message.json +88 -0
- package/schema/Meta_Context.json +78 -0
- package/schema/Service_Equipment.json +55 -0
- package/schema/Service_FormConfig.json +66 -0
- package/schema/Service_MeterReading.json +54 -0
- package/schema/Service_MigrationMap.json +55 -0
- package/schema/Service_Project.json +77 -0
- package/schema/Service_Schedule.json +101 -0
- package/schema/Service_Schedule_Execution.json +58 -0
- package/schema/Service_Schedule_Template.json +73 -0
- package/schema/Service_Status.json +50 -0
- package/schema/Service_Tag.json +50 -0
- package/schema/Service_Ticket.json +114 -0
- package/schema/Service_TicketCounter.json +38 -0
- package/schema/Service_TriggerState.json +50 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"message": {
|
|
3
|
+
"mention": {
|
|
4
|
+
"user": {
|
|
5
|
+
"notification": {
|
|
6
|
+
"title": "Sie wurden in einer Nachricht erwähnt",
|
|
7
|
+
"description": "{{username}} hat Sie in einer Nachricht erwähnt: {{- messageText}}"
|
|
8
|
+
}
|
|
9
|
+
},
|
|
10
|
+
"role": {
|
|
11
|
+
"notification": {
|
|
12
|
+
"title": "Ihre Rolle wurde in einer Nachricht erwähnt",
|
|
13
|
+
"description": "{{username}} hat die Rolle {{role}} in einer Nachricht erwähnt: {{- messageText}}"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"ticket": {
|
|
3
|
+
"assignment": {
|
|
4
|
+
"assigned": {
|
|
5
|
+
"title": "Ihnen wurde ein Ticket zugewiesen",
|
|
6
|
+
"description": "{{username}} hat Ihnen das Ticket {{ticketName}} zugewiesen.\n\nLink: {{- ticketLink}}"
|
|
7
|
+
},
|
|
8
|
+
"watcher": {
|
|
9
|
+
"title": "Ein Ticket wurde zugewiesen",
|
|
10
|
+
"description": "{{username}} hat das Ticket {{ticketName}} zugewiesen.\n\nLink: {{- ticketLink}}"
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"message": {
|
|
3
|
+
"mention": {
|
|
4
|
+
"user": {
|
|
5
|
+
"notification": {
|
|
6
|
+
"title": "You were mentioned in a message",
|
|
7
|
+
"description": "{{username}} mentioned you in a message: {{- messageText}}"
|
|
8
|
+
}
|
|
9
|
+
},
|
|
10
|
+
"role": {
|
|
11
|
+
"notification": {
|
|
12
|
+
"title": "Your role was mentioned in a message",
|
|
13
|
+
"description": "{{username}} mentioned the role {{role}} in a message: {{- messageText}}"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"ticket": {
|
|
3
|
+
"assignment": {
|
|
4
|
+
"assigned": {
|
|
5
|
+
"title": "You were assigned to a ticket",
|
|
6
|
+
"description": "{{username}} assigned you to ticket {{ticketName}}.\n\nLink: {{- ticketLink}}"
|
|
7
|
+
},
|
|
8
|
+
"watcher": {
|
|
9
|
+
"title": "A ticket was assigned",
|
|
10
|
+
"description": "{{username}} assigned ticket {{ticketName}}.\n\nLink: {{- ticketLink}}"
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const node_1 = __importDefault(require("parse/node"));
|
|
7
|
+
const index_js_1 = require("../features/meta/index.js");
|
|
8
|
+
const index_js_2 = require("../types/index.js");
|
|
9
|
+
/**
|
|
10
|
+
* One-off backfill for the `OD3_Meta_Context` directory.
|
|
11
|
+
*
|
|
12
|
+
* From here on the `Meta_Config` / `Meta_Entry` afterSave hooks keep the
|
|
13
|
+
* directory current, but they only see writes — everything already in the
|
|
14
|
+
* database predates them. This walks both classes once and registers what it
|
|
15
|
+
* finds, so the directory starts complete rather than filling in as rows happen
|
|
16
|
+
* to be touched.
|
|
17
|
+
*
|
|
18
|
+
* PARSE_URL=… PARSE_APP_ID=… PARSE_MASTER_KEY=… \
|
|
19
|
+
* node dist/scripts/backfill-meta-contexts.js [--dry-run]
|
|
20
|
+
*
|
|
21
|
+
* Safe to re-run: `registerMetaContext` upserts and only writes on a change.
|
|
22
|
+
*/
|
|
23
|
+
const BATCH_SIZE = 500;
|
|
24
|
+
function requireEnv(name) {
|
|
25
|
+
const value = process.env[name];
|
|
26
|
+
if (!value) {
|
|
27
|
+
console.error(`Missing required environment variable ${name}.`);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
return value;
|
|
31
|
+
}
|
|
32
|
+
/** Iterate a class in `createdAt` order, page by page — no skip, no cursor drift. */
|
|
33
|
+
async function* pages(ctor) {
|
|
34
|
+
let after;
|
|
35
|
+
for (;;) {
|
|
36
|
+
const query = new node_1.default.Query(ctor)
|
|
37
|
+
.ascending("createdAt")
|
|
38
|
+
.limit(BATCH_SIZE);
|
|
39
|
+
// `createdAt` is not part of the generic attribute map, hence the cast.
|
|
40
|
+
if (after)
|
|
41
|
+
query.greaterThan("createdAt", after);
|
|
42
|
+
const results = await query.find({ useMasterKey: true });
|
|
43
|
+
if (results.length === 0)
|
|
44
|
+
return;
|
|
45
|
+
yield results;
|
|
46
|
+
after = results[results.length - 1].createdAt;
|
|
47
|
+
if (results.length < BATCH_SIZE)
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
async function main() {
|
|
52
|
+
const dryRun = process.argv.includes("--dry-run");
|
|
53
|
+
node_1.default.initialize(requireEnv("PARSE_APP_ID"), undefined, requireEnv("PARSE_MASTER_KEY"));
|
|
54
|
+
node_1.default.serverURL =
|
|
55
|
+
process.env.PARSE_URL ?? "http://localhost:1337/parse";
|
|
56
|
+
const seen = new Set();
|
|
57
|
+
let configs = 0;
|
|
58
|
+
let entries = 0;
|
|
59
|
+
// Configs first: they carry label/description, so a context that has one is
|
|
60
|
+
// named from the start rather than being created bare by an entry.
|
|
61
|
+
for await (const page of pages(index_js_2.Meta_Config)) {
|
|
62
|
+
for (const config of page) {
|
|
63
|
+
const context = config.context;
|
|
64
|
+
if (!context)
|
|
65
|
+
continue;
|
|
66
|
+
configs++;
|
|
67
|
+
seen.add(context);
|
|
68
|
+
if (dryRun)
|
|
69
|
+
continue;
|
|
70
|
+
await (0, index_js_1.registerMetaContext)({
|
|
71
|
+
context,
|
|
72
|
+
tenant: config.tenant,
|
|
73
|
+
config,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
for await (const page of pages(index_js_2.Meta_Entry)) {
|
|
78
|
+
for (const entry of page) {
|
|
79
|
+
const context = entry.context;
|
|
80
|
+
if (!context)
|
|
81
|
+
continue;
|
|
82
|
+
entries++;
|
|
83
|
+
seen.add(context);
|
|
84
|
+
if (dryRun)
|
|
85
|
+
continue;
|
|
86
|
+
await (0, index_js_1.registerMetaContext)({
|
|
87
|
+
context,
|
|
88
|
+
tenant: entry.tenant,
|
|
89
|
+
entryClassName: entry.entryClassName,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
console.log(`${dryRun ? "[dry run] " : ""}scanned ${configs} configs and ${entries} entries → ${seen.size} distinct contexts`);
|
|
94
|
+
for (const context of [...seen].sort())
|
|
95
|
+
console.log(` ${context}`);
|
|
96
|
+
}
|
|
97
|
+
main().catch((error) => {
|
|
98
|
+
console.error(error);
|
|
99
|
+
process.exit(1);
|
|
100
|
+
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { MigrationConfig } from "./env.js";
|
|
2
|
+
/**
|
|
3
|
+
* Replicates `defaultAclHandler` for migration writes.
|
|
4
|
+
*
|
|
5
|
+
* The migration runs with `OD_MIGRATION_MODE=1`, which makes that handler return
|
|
6
|
+
* early (it would otherwise fight the historical owner/tenant the migration is
|
|
7
|
+
* reconstructing). So the ACLs it would have applied have to be applied here
|
|
8
|
+
* instead — deliberately mirroring
|
|
9
|
+
* `src/features/schema/helper/defaultHandler.ts` rather than inventing a policy.
|
|
10
|
+
*
|
|
11
|
+
* Keep these two in sync: if the default handler's grants change, this changes.
|
|
12
|
+
*/
|
|
13
|
+
export declare function migrationAcl(options: {
|
|
14
|
+
/** The object's tenant, if it has one. */
|
|
15
|
+
tenantId?: string;
|
|
16
|
+
/** The object's owning user, if it has one (v1 `user` / v2 `createdBy`). */
|
|
17
|
+
userId?: string;
|
|
18
|
+
/** Mirrors `defaultAclHandler`'s `allowTenantUserWrite` option. */
|
|
19
|
+
allowTenantUserWrite?: boolean;
|
|
20
|
+
}): Parse.ACL;
|
|
21
|
+
/**
|
|
22
|
+
* Save options for SDK-path writes (updates).
|
|
23
|
+
*
|
|
24
|
+
* `skipChangelog` matters as much as the key: `hooks/_ChangeLog.ts` registers an
|
|
25
|
+
* afterSave on *every* class, so without this each migrated object would generate
|
|
26
|
+
* a `masterkey`-flagged, author-less, `createdAt = now` changelog entry — six
|
|
27
|
+
* figures of noise burying the reconstructed history it sits beside. The hook
|
|
28
|
+
* already honours this context flag, so no new switch is needed.
|
|
29
|
+
*/
|
|
30
|
+
export declare const MIGRATION_SAVE: {
|
|
31
|
+
readonly context: {
|
|
32
|
+
readonly skipChangelog: true;
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* The single write path every pass must use.
|
|
37
|
+
*
|
|
38
|
+
* Routes by operation, because the two need different transports:
|
|
39
|
+
*
|
|
40
|
+
* - **Create** goes over REST (`lib/rest.ts`), because the SDK silently discards
|
|
41
|
+
* `createdAt` / `updatedAt` and would leave every migrated row stamped `now`.
|
|
42
|
+
* - **Update** goes over the SDK, which is fine — Parse ignores `createdAt` on
|
|
43
|
+
* update anyway, and the object already has its id.
|
|
44
|
+
*
|
|
45
|
+
* Returns the object's id so callers can record the mapping.
|
|
46
|
+
*/
|
|
47
|
+
export declare function saveMigrated(config: MigrationConfig, object: Parse.Object, options: {
|
|
48
|
+
className: string;
|
|
49
|
+
isNew: boolean;
|
|
50
|
+
createdAt?: Date;
|
|
51
|
+
updatedAt?: Date;
|
|
52
|
+
}): Promise<string>;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.MIGRATION_SAVE = void 0;
|
|
7
|
+
exports.migrationAcl = migrationAcl;
|
|
8
|
+
exports.saveMigrated = saveMigrated;
|
|
9
|
+
const node_1 = __importDefault(require("parse/node"));
|
|
10
|
+
const rest_js_1 = require("./rest.js");
|
|
11
|
+
/**
|
|
12
|
+
* Replicates `defaultAclHandler` for migration writes.
|
|
13
|
+
*
|
|
14
|
+
* The migration runs with `OD_MIGRATION_MODE=1`, which makes that handler return
|
|
15
|
+
* early (it would otherwise fight the historical owner/tenant the migration is
|
|
16
|
+
* reconstructing). So the ACLs it would have applied have to be applied here
|
|
17
|
+
* instead — deliberately mirroring
|
|
18
|
+
* `src/features/schema/helper/defaultHandler.ts` rather than inventing a policy.
|
|
19
|
+
*
|
|
20
|
+
* Keep these two in sync: if the default handler's grants change, this changes.
|
|
21
|
+
*/
|
|
22
|
+
function migrationAcl(options) {
|
|
23
|
+
const acl = new node_1.default.ACL();
|
|
24
|
+
acl.setRoleReadAccess("od-admin", true);
|
|
25
|
+
acl.setRoleWriteAccess("od-admin", true);
|
|
26
|
+
if (options.userId) {
|
|
27
|
+
acl.setReadAccess(options.userId, true);
|
|
28
|
+
acl.setWriteAccess(options.userId, true);
|
|
29
|
+
}
|
|
30
|
+
if (options.tenantId) {
|
|
31
|
+
acl.setRoleReadAccess(`od-tenant-user-${options.tenantId}`, true);
|
|
32
|
+
if (options.allowTenantUserWrite) {
|
|
33
|
+
acl.setRoleWriteAccess(`od-tenant-user-${options.tenantId}`, true);
|
|
34
|
+
}
|
|
35
|
+
acl.setRoleReadAccess(`od-tenant-admin-${options.tenantId}`, true);
|
|
36
|
+
acl.setRoleWriteAccess(`od-tenant-admin-${options.tenantId}`, true);
|
|
37
|
+
}
|
|
38
|
+
return acl;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Save options for SDK-path writes (updates).
|
|
42
|
+
*
|
|
43
|
+
* `skipChangelog` matters as much as the key: `hooks/_ChangeLog.ts` registers an
|
|
44
|
+
* afterSave on *every* class, so without this each migrated object would generate
|
|
45
|
+
* a `masterkey`-flagged, author-less, `createdAt = now` changelog entry — six
|
|
46
|
+
* figures of noise burying the reconstructed history it sits beside. The hook
|
|
47
|
+
* already honours this context flag, so no new switch is needed.
|
|
48
|
+
*/
|
|
49
|
+
exports.MIGRATION_SAVE = {
|
|
50
|
+
// No `useMaintenanceKey` here: the key travels as a default header (see
|
|
51
|
+
// initParse). Passing the option would append `_MaintenanceKey` to the payload,
|
|
52
|
+
// which Parse rejects on reads and ignores on writes.
|
|
53
|
+
context: { skipChangelog: true },
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* The single write path every pass must use.
|
|
57
|
+
*
|
|
58
|
+
* Routes by operation, because the two need different transports:
|
|
59
|
+
*
|
|
60
|
+
* - **Create** goes over REST (`lib/rest.ts`), because the SDK silently discards
|
|
61
|
+
* `createdAt` / `updatedAt` and would leave every migrated row stamped `now`.
|
|
62
|
+
* - **Update** goes over the SDK, which is fine — Parse ignores `createdAt` on
|
|
63
|
+
* update anyway, and the object already has its id.
|
|
64
|
+
*
|
|
65
|
+
* Returns the object's id so callers can record the mapping.
|
|
66
|
+
*/
|
|
67
|
+
async function saveMigrated(config, object, options) {
|
|
68
|
+
if (!options.isNew) {
|
|
69
|
+
await object.save(null, exports.MIGRATION_SAVE);
|
|
70
|
+
if (!object.id)
|
|
71
|
+
throw new Error("update returned no objectId");
|
|
72
|
+
return object.id;
|
|
73
|
+
}
|
|
74
|
+
const created = await (0, rest_js_1.restCreate)(config, options.className, object, options.createdAt, options.updatedAt);
|
|
75
|
+
// Keep the in-memory object consistent with what the server stored, so callers
|
|
76
|
+
// can read `object.id` as usual.
|
|
77
|
+
object.id = created.objectId;
|
|
78
|
+
return created.objectId;
|
|
79
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Meta_Entry } from "../../../types/index.js";
|
|
2
|
+
import type { MetaFieldConfig } from "../../../types/custom/MetaField.js";
|
|
3
|
+
import type { MigrationConfig } from "./env.js";
|
|
4
|
+
/**
|
|
5
|
+
* Stock-keeping data for an article, kept as plugin-meta rather than columns.
|
|
6
|
+
*
|
|
7
|
+
* v1's `Maintenance_Item` tracked price, stock levels, storage location and
|
|
8
|
+
* supplier. The v2 catalogue is `OD3_BDE_Article`, which is **shared with BDE
|
|
9
|
+
* and its ABAS sync** and deliberately lean — widening it is not ours to do, and
|
|
10
|
+
* the sync overwrites the columns it owns on every run.
|
|
11
|
+
*
|
|
12
|
+
* So the data lands in a `Meta_Entry` instead, under the same
|
|
13
|
+
* `["service", <entity>, "meta"]` context every other core master-data entity
|
|
14
|
+
* uses (`constants.metaContext`). That makes it editable through the article
|
|
15
|
+
* admin view in `openservice-core` with no extra machinery.
|
|
16
|
+
*
|
|
17
|
+
* **Deliberately not `["bde","article","custom"]`.** That context belongs to the
|
|
18
|
+
* ABAS sync (`node-stack-scheduler`, `syncArticle.ts`), which replaces `config`
|
|
19
|
+
* and `values` wholesale on every run and fetches its entries with
|
|
20
|
+
* `.limit(<number of ABAS articles>)`. Sharing it would let migrated entries
|
|
21
|
+
* crowd real ABAS entries out of that limit — and any article present in both
|
|
22
|
+
* would have its values clobbered on the next sync.
|
|
23
|
+
*/
|
|
24
|
+
export declare const ARTICLE_META_CONTEXT: string[];
|
|
25
|
+
/** The field definitions written to the shared `Meta_Config`. */
|
|
26
|
+
export declare const ARTICLE_META_FIELDS: MetaFieldConfig[];
|
|
27
|
+
/**
|
|
28
|
+
* v1 stores `price` as a String, and these are German-language instances.
|
|
29
|
+
*
|
|
30
|
+
* Returns `undefined` for anything unparseable so the caller can report it —
|
|
31
|
+
* silently nulling a price is exactly the kind of loss that surfaces months
|
|
32
|
+
* later as "the migration ate our prices".
|
|
33
|
+
*/
|
|
34
|
+
export declare function parsePrice(raw: unknown): number | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* Ensure the shared `Meta_Config` exists for a tenant, returning its id.
|
|
37
|
+
*
|
|
38
|
+
* Scoped by tenant deliberately. `getMetaConfig()` in the meta plugin filters by
|
|
39
|
+
* *context only* and relies on Parse ACLs to show each tenant its own record —
|
|
40
|
+
* but the migration reads with the maintenance key, which bypasses ACL entirely.
|
|
41
|
+
* An unscoped `.first()` would find some other tenant's config and overwrite it.
|
|
42
|
+
*/
|
|
43
|
+
export declare function ensureArticleMetaConfig(config: MigrationConfig, tenantId: string): Promise<string | undefined>;
|
|
44
|
+
/** Existing article meta entries, keyed by the article they belong to. */
|
|
45
|
+
export declare function loadArticleMetaEntries(config: MigrationConfig): Promise<Map<string, Meta_Entry>>;
|
|
46
|
+
/** The Meta_Config ids already present, keyed by tenant. */
|
|
47
|
+
export declare function loadArticleMetaConfigs(config: MigrationConfig): Promise<Map<string, string>>;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ARTICLE_META_FIELDS = exports.ARTICLE_META_CONTEXT = void 0;
|
|
7
|
+
exports.parsePrice = parsePrice;
|
|
8
|
+
exports.ensureArticleMetaConfig = ensureArticleMetaConfig;
|
|
9
|
+
exports.loadArticleMetaEntries = loadArticleMetaEntries;
|
|
10
|
+
exports.loadArticleMetaConfigs = loadArticleMetaConfigs;
|
|
11
|
+
const node_1 = __importDefault(require("parse/node"));
|
|
12
|
+
const index_js_1 = require("../../../types/index.js");
|
|
13
|
+
const acl_js_1 = require("./acl.js");
|
|
14
|
+
const ptr_js_1 = require("./ptr.js");
|
|
15
|
+
const pass_js_1 = require("./pass.js");
|
|
16
|
+
/**
|
|
17
|
+
* Stock-keeping data for an article, kept as plugin-meta rather than columns.
|
|
18
|
+
*
|
|
19
|
+
* v1's `Maintenance_Item` tracked price, stock levels, storage location and
|
|
20
|
+
* supplier. The v2 catalogue is `OD3_BDE_Article`, which is **shared with BDE
|
|
21
|
+
* and its ABAS sync** and deliberately lean — widening it is not ours to do, and
|
|
22
|
+
* the sync overwrites the columns it owns on every run.
|
|
23
|
+
*
|
|
24
|
+
* So the data lands in a `Meta_Entry` instead, under the same
|
|
25
|
+
* `["service", <entity>, "meta"]` context every other core master-data entity
|
|
26
|
+
* uses (`constants.metaContext`). That makes it editable through the article
|
|
27
|
+
* admin view in `openservice-core` with no extra machinery.
|
|
28
|
+
*
|
|
29
|
+
* **Deliberately not `["bde","article","custom"]`.** That context belongs to the
|
|
30
|
+
* ABAS sync (`node-stack-scheduler`, `syncArticle.ts`), which replaces `config`
|
|
31
|
+
* and `values` wholesale on every run and fetches its entries with
|
|
32
|
+
* `.limit(<number of ABAS articles>)`. Sharing it would let migrated entries
|
|
33
|
+
* crowd real ABAS entries out of that limit — and any article present in both
|
|
34
|
+
* would have its values clobbered on the next sync.
|
|
35
|
+
*/
|
|
36
|
+
exports.ARTICLE_META_CONTEXT = ["service", "article", "meta"];
|
|
37
|
+
/** The field definitions written to the shared `Meta_Config`. */
|
|
38
|
+
exports.ARTICLE_META_FIELDS = [
|
|
39
|
+
{ type: "number", name: "price", label: "Preis" },
|
|
40
|
+
{ type: "number", name: "stock", label: "Bestand" },
|
|
41
|
+
{ type: "number", name: "stockAlarmAt", label: "Meldebestand" },
|
|
42
|
+
{ type: "string", name: "stockUnit", label: "Lagereinheit" },
|
|
43
|
+
{ type: "string", name: "storageLocation", label: "Lagerort" },
|
|
44
|
+
{ type: "string", name: "supplier", label: "Lieferant" },
|
|
45
|
+
{ type: "string", name: "category", label: "Kategorie" },
|
|
46
|
+
{ type: "string", name: "orderNumber", label: "Bestellnummer" },
|
|
47
|
+
{
|
|
48
|
+
type: "select_parse",
|
|
49
|
+
name: "equipment",
|
|
50
|
+
label: "Equipment",
|
|
51
|
+
className: "OD3_Service_Equipment",
|
|
52
|
+
display: "label",
|
|
53
|
+
multiple: true,
|
|
54
|
+
},
|
|
55
|
+
];
|
|
56
|
+
/**
|
|
57
|
+
* v1 stores `price` as a String, and these are German-language instances.
|
|
58
|
+
*
|
|
59
|
+
* Returns `undefined` for anything unparseable so the caller can report it —
|
|
60
|
+
* silently nulling a price is exactly the kind of loss that surfaces months
|
|
61
|
+
* later as "the migration ate our prices".
|
|
62
|
+
*/
|
|
63
|
+
function parsePrice(raw) {
|
|
64
|
+
if (raw === undefined || raw === null)
|
|
65
|
+
return undefined;
|
|
66
|
+
const cleaned = String(raw)
|
|
67
|
+
.replace(/[^\d,.-]/g, "")
|
|
68
|
+
.replace(/\.(?=\d{3}\b)/g, "") // thousands separator
|
|
69
|
+
.replace(",", ".");
|
|
70
|
+
if (cleaned === "" || cleaned === "-")
|
|
71
|
+
return undefined;
|
|
72
|
+
const value = Number(cleaned);
|
|
73
|
+
return Number.isFinite(value) ? value : undefined;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Ensure the shared `Meta_Config` exists for a tenant, returning its id.
|
|
77
|
+
*
|
|
78
|
+
* Scoped by tenant deliberately. `getMetaConfig()` in the meta plugin filters by
|
|
79
|
+
* *context only* and relies on Parse ACLs to show each tenant its own record —
|
|
80
|
+
* but the migration reads with the maintenance key, which bypasses ACL entirely.
|
|
81
|
+
* An unscoped `.first()` would find some other tenant's config and overwrite it.
|
|
82
|
+
*/
|
|
83
|
+
async function ensureArticleMetaConfig(config, tenantId) {
|
|
84
|
+
const contextString = (0, ptr_js_1.contextToString)(exports.ARTICLE_META_CONTEXT);
|
|
85
|
+
const query = new node_1.default.Query(index_js_1.Meta_Config).equalTo("context", contextString);
|
|
86
|
+
if (tenantId)
|
|
87
|
+
query.equalTo("tenant", (0, ptr_js_1.tenantPtr)(tenantId));
|
|
88
|
+
else
|
|
89
|
+
query.doesNotExist("tenant");
|
|
90
|
+
const existing = await query.first({});
|
|
91
|
+
const target = existing ?? new index_js_1.Meta_Config();
|
|
92
|
+
target.set("context", contextString);
|
|
93
|
+
target.set("config", { fields: exports.ARTICLE_META_FIELDS });
|
|
94
|
+
if (tenantId)
|
|
95
|
+
target.set("tenant", (0, ptr_js_1.tenantPtr)(tenantId));
|
|
96
|
+
if (!existing)
|
|
97
|
+
target.setACL((0, acl_js_1.migrationAcl)({ tenantId }));
|
|
98
|
+
if (config.dryRun)
|
|
99
|
+
return existing?.id;
|
|
100
|
+
// No historical timestamp to preserve: the shared config is a v2 artefact
|
|
101
|
+
// synthesised by the migration, not a migrated row.
|
|
102
|
+
await (0, acl_js_1.saveMigrated)(config, target, {
|
|
103
|
+
className: index_js_1.Meta_Config.className,
|
|
104
|
+
isNew: !existing,
|
|
105
|
+
});
|
|
106
|
+
return target.id;
|
|
107
|
+
}
|
|
108
|
+
/** Existing article meta entries, keyed by the article they belong to. */
|
|
109
|
+
async function loadArticleMetaEntries(config) {
|
|
110
|
+
const byArticle = new Map();
|
|
111
|
+
const contextString = (0, ptr_js_1.contextToString)(exports.ARTICLE_META_CONTEXT);
|
|
112
|
+
for await (const page of (0, pass_js_1.pages)(() => new node_1.default.Query(index_js_1.Meta_Entry)
|
|
113
|
+
.equalTo("context", contextString)
|
|
114
|
+
.equalTo("entryClassName", index_js_1.BDE_Article.className), config.batchSize)) {
|
|
115
|
+
for (const row of page) {
|
|
116
|
+
const objectId = row.get("entryObjectId");
|
|
117
|
+
if (objectId)
|
|
118
|
+
byArticle.set(objectId, row);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return byArticle;
|
|
122
|
+
}
|
|
123
|
+
/** The Meta_Config ids already present, keyed by tenant. */
|
|
124
|
+
async function loadArticleMetaConfigs(config) {
|
|
125
|
+
const byTenant = new Map();
|
|
126
|
+
const contextString = (0, ptr_js_1.contextToString)(exports.ARTICLE_META_CONTEXT);
|
|
127
|
+
for (const row of await (0, pass_js_1.all)(() => new node_1.default.Query(index_js_1.Meta_Config).equalTo("context", contextString), config.batchSize)) {
|
|
128
|
+
if (row.id)
|
|
129
|
+
byTenant.set((0, pass_js_1.refId)(row.get("tenant")) ?? "", row.id);
|
|
130
|
+
}
|
|
131
|
+
return byTenant;
|
|
132
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime configuration for the openservice v1 → v2 migration.
|
|
3
|
+
*
|
|
4
|
+
* See `plugins/openservice/MIGRATION_PLAN.md` in the opendash repo for the full
|
|
5
|
+
* plan; §3 covers why this runs on the maintenance key.
|
|
6
|
+
*/
|
|
7
|
+
export interface MigrationConfig {
|
|
8
|
+
parseUrl: string;
|
|
9
|
+
appId: string;
|
|
10
|
+
maintenanceKey: string;
|
|
11
|
+
/** Restrict the run to one tenant's data. Omit to process every tenant. */
|
|
12
|
+
tenantId?: string;
|
|
13
|
+
/** Run only these passes (by id, e.g. `["01", "08"]`). Omit to run all. */
|
|
14
|
+
only?: string[];
|
|
15
|
+
/** Read and report, write nothing. */
|
|
16
|
+
dryRun: boolean;
|
|
17
|
+
/** Reuse a previous run id so its MigrationMap rows are recognised. */
|
|
18
|
+
runId: string;
|
|
19
|
+
/** Rows fetched per query page. */
|
|
20
|
+
batchSize: number;
|
|
21
|
+
}
|
|
22
|
+
export declare class MigrationConfigError extends Error {
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Build the config from argv + env, failing loudly on anything missing.
|
|
26
|
+
*
|
|
27
|
+
* The maintenance key is deliberately required rather than falling back to the
|
|
28
|
+
* master key: a master-key write silently stamps `createdAt = now`
|
|
29
|
+
* (`parse-server/lib/RestWrite.js` — the custom-timestamp branch is guarded by
|
|
30
|
+
* `auth.isMaintenance`), which would destroy ticket age and the reconstructed
|
|
31
|
+
* history without raising a single error. Failing to start is far better than
|
|
32
|
+
* completing with wrong timestamps.
|
|
33
|
+
*/
|
|
34
|
+
export declare function readConfig(argv: string[]): MigrationConfig;
|
|
35
|
+
/**
|
|
36
|
+
* Point the Parse SDK at the target server, authenticating as maintenance.
|
|
37
|
+
*
|
|
38
|
+
* The key goes in a **default request header**, not through the SDK's
|
|
39
|
+
* `useMaintenanceKey` option. That option puts `_MaintenanceKey` into the request
|
|
40
|
+
* payload — which for a GET becomes a query parameter, and Parse Server rejects it
|
|
41
|
+
* outright: `Invalid parameter for query: _MaintenanceKey`
|
|
42
|
+
* (`Routers/ClassesRouter.js`). So every read would fail. The header form is what
|
|
43
|
+
* `middlewares.js` actually reads (`X-Parse-Maintenance-Key`), and it applies
|
|
44
|
+
* uniformly to reads and writes.
|
|
45
|
+
*/
|
|
46
|
+
export declare function initParse(config: MigrationConfig): void;
|
|
47
|
+
/**
|
|
48
|
+
* Read options for every query in the migration.
|
|
49
|
+
*
|
|
50
|
+
* Deliberately empty: authentication rides on the default header set in
|
|
51
|
+
* {@link initParse}, because the SDK's `useMaintenanceKey` option would be sent as
|
|
52
|
+
* a query parameter and rejected. Kept as a named constant so call sites still
|
|
53
|
+
* document that they are reading with elevated rights — RestQuery treats
|
|
54
|
+
* `isMaintenance` alongside `isMaster`, so these reads bypass ACL and CLP.
|
|
55
|
+
*/
|
|
56
|
+
export declare const MK: {};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.MK = exports.MigrationConfigError = void 0;
|
|
7
|
+
exports.readConfig = readConfig;
|
|
8
|
+
exports.initParse = initParse;
|
|
9
|
+
const node_1 = __importDefault(require("parse/node"));
|
|
10
|
+
class MigrationConfigError extends Error {
|
|
11
|
+
}
|
|
12
|
+
exports.MigrationConfigError = MigrationConfigError;
|
|
13
|
+
function requireEnv(name, hint) {
|
|
14
|
+
const value = process.env[name];
|
|
15
|
+
if (!value) {
|
|
16
|
+
throw new MigrationConfigError(`Missing required environment variable ${name}.\n ${hint}`);
|
|
17
|
+
}
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Build the config from argv + env, failing loudly on anything missing.
|
|
22
|
+
*
|
|
23
|
+
* The maintenance key is deliberately required rather than falling back to the
|
|
24
|
+
* master key: a master-key write silently stamps `createdAt = now`
|
|
25
|
+
* (`parse-server/lib/RestWrite.js` — the custom-timestamp branch is guarded by
|
|
26
|
+
* `auth.isMaintenance`), which would destroy ticket age and the reconstructed
|
|
27
|
+
* history without raising a single error. Failing to start is far better than
|
|
28
|
+
* completing with wrong timestamps.
|
|
29
|
+
*/
|
|
30
|
+
function readConfig(argv) {
|
|
31
|
+
const flag = (name) => {
|
|
32
|
+
const hit = argv.find((a) => a.startsWith(`--${name}=`));
|
|
33
|
+
return hit ? hit.slice(name.length + 3) : undefined;
|
|
34
|
+
};
|
|
35
|
+
const has = (name) => argv.includes(`--${name}`);
|
|
36
|
+
if (process.env.OD_MIGRATION_MODE !== "1") {
|
|
37
|
+
throw new MigrationConfigError("OD_MIGRATION_MODE must be set to 1 on BOTH this process and the Parse " +
|
|
38
|
+
"Server it talks to.\n" +
|
|
39
|
+
" Server-side it gates defaultHandler/defaultAclHandler (without it every " +
|
|
40
|
+
"maintenance-key write fails on the missing user) and suppresses assignment " +
|
|
41
|
+
"notifications.\n" +
|
|
42
|
+
" See src/features/schema/helper/migrationMode.ts");
|
|
43
|
+
}
|
|
44
|
+
const only = flag("only");
|
|
45
|
+
return {
|
|
46
|
+
parseUrl: process.env.PARSE_URL ?? "http://localhost:1337/parse",
|
|
47
|
+
appId: requireEnv("PARSE_APP_ID", "The Parse application id, e.g. openinc-node-docker-dev."),
|
|
48
|
+
maintenanceKey: requireEnv("PARSE_MAINTENANCE_KEY", "Parse Server's `maintenanceKey` — the only auth level allowed to write a " +
|
|
49
|
+
"historical createdAt. Set PARSE_SERVER_MAINTENANCE_KEY on the server, and " +
|
|
50
|
+
"note maintenanceKeyIps defaults to localhost, so run this ON the Parse host."),
|
|
51
|
+
tenantId: flag("tenant"),
|
|
52
|
+
only: only ? only.split(",").map((s) => s.trim()) : undefined,
|
|
53
|
+
dryRun: has("dry-run"),
|
|
54
|
+
runId: flag("resume") ?? `run-${new Date().toISOString()}`,
|
|
55
|
+
batchSize: readBatchSize(flag("batch")),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Page size, validated rather than trusted.
|
|
60
|
+
*
|
|
61
|
+
* `Number("abc")` is `NaN`, and `.limit(NaN)` produces a query that fails in a way
|
|
62
|
+
* that points nowhere near the flag that caused it. The upper bound is a guard
|
|
63
|
+
* against Parse's `maxLimit` clamping: asking for more than the server allows makes
|
|
64
|
+
* every page come back short, which is safe for the paging loop (it stops only on
|
|
65
|
+
* an empty page) but wastes the request — so refuse the obviously-wrong value
|
|
66
|
+
* instead of quietly doing something else.
|
|
67
|
+
*/
|
|
68
|
+
function readBatchSize(raw) {
|
|
69
|
+
if (raw === undefined)
|
|
70
|
+
return 500;
|
|
71
|
+
const value = Number(raw);
|
|
72
|
+
if (!Number.isInteger(value) || value < 1 || value > 1000) {
|
|
73
|
+
throw new MigrationConfigError(`--batch must be an integer between 1 and 1000 (got "${raw}").\n` +
|
|
74
|
+
` 1000 is Parse's practical page ceiling; the default of 500 is fine at ` +
|
|
75
|
+
`this data volume.`);
|
|
76
|
+
}
|
|
77
|
+
return value;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Point the Parse SDK at the target server, authenticating as maintenance.
|
|
81
|
+
*
|
|
82
|
+
* The key goes in a **default request header**, not through the SDK's
|
|
83
|
+
* `useMaintenanceKey` option. That option puts `_MaintenanceKey` into the request
|
|
84
|
+
* payload — which for a GET becomes a query parameter, and Parse Server rejects it
|
|
85
|
+
* outright: `Invalid parameter for query: _MaintenanceKey`
|
|
86
|
+
* (`Routers/ClassesRouter.js`). So every read would fail. The header form is what
|
|
87
|
+
* `middlewares.js` actually reads (`X-Parse-Maintenance-Key`), and it applies
|
|
88
|
+
* uniformly to reads and writes.
|
|
89
|
+
*/
|
|
90
|
+
function initParse(config) {
|
|
91
|
+
node_1.default.initialize(config.appId);
|
|
92
|
+
node_1.default.serverURL = config.parseUrl;
|
|
93
|
+
node_1.default.CoreManager.set("REQUEST_HEADERS", {
|
|
94
|
+
"X-Parse-Maintenance-Key": config.maintenanceKey,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Read options for every query in the migration.
|
|
99
|
+
*
|
|
100
|
+
* Deliberately empty: authentication rides on the default header set in
|
|
101
|
+
* {@link initParse}, because the SDK's `useMaintenanceKey` option would be sent as
|
|
102
|
+
* a query parameter and rejected. Kept as a named constant so call sites still
|
|
103
|
+
* document that they are reading with elevated rights — RestQuery treats
|
|
104
|
+
* `isMaintenance` alongside `isMaster`, so these reads bypass ACL and CLP.
|
|
105
|
+
*/
|
|
106
|
+
exports.MK = {};
|