tabletcommand-backend-models 7.4.91 → 7.4.92
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/build/models/esri.js +26 -0
- package/build/models/esri.js.map +1 -1
- package/build/test/esri.js +38 -0
- package/build/test/esri.js.map +1 -1
- package/definitions/models/esri.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/models/esri.ts +28 -0
- package/src/test/esri.ts +46 -0
package/build/models/esri.js
CHANGED
|
@@ -102,6 +102,32 @@ function EsriSchema(mongoose) {
|
|
|
102
102
|
name: "departmentId_unique",
|
|
103
103
|
unique: true,
|
|
104
104
|
});
|
|
105
|
+
// cspell:disable-next-line
|
|
106
|
+
// arcGISAuth must be set-or-null, never a subdoc with an empty username.
|
|
107
|
+
// EsriAuth.username defaults to "" and that schema is shared with `auth`, so we
|
|
108
|
+
// normalize here rather than change the default: an arcGISAuth present without a
|
|
109
|
+
// username collapses to null, keeping it out of the $exists unique index below.
|
|
110
|
+
modelSchema.pre("save", function (next) {
|
|
111
|
+
const arcGISAuth = this.get("arcGISAuth");
|
|
112
|
+
if (arcGISAuth && !arcGISAuth.username) {
|
|
113
|
+
this.set("arcGISAuth", null);
|
|
114
|
+
}
|
|
115
|
+
next();
|
|
116
|
+
});
|
|
117
|
+
// arcGISAuth.username is generated lowercase (cron-maps domain.ts:
|
|
118
|
+
// name.toLowerCase() -> `tc_<slug>`), so this case-sensitive unique index is
|
|
119
|
+
// sufficient in practice; case-insensitive enforcement is intentionally deferred.
|
|
120
|
+
// $exists (not $gt: "") is correct because the pre-save hook above guarantees
|
|
121
|
+
// arcGISAuth is set-or-null, so the username field is present only on real records.
|
|
122
|
+
modelSchema.index({
|
|
123
|
+
"arcGISAuth.username": 1,
|
|
124
|
+
}, {
|
|
125
|
+
name: "arcGISAuth.username_1_unique",
|
|
126
|
+
unique: true,
|
|
127
|
+
partialFilterExpression: {
|
|
128
|
+
"arcGISAuth.username": { $exists: true },
|
|
129
|
+
},
|
|
130
|
+
});
|
|
105
131
|
return modelSchema;
|
|
106
132
|
}
|
|
107
133
|
async function EsriModule(mongoose) {
|
package/build/models/esri.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"esri.js","sourceRoot":"","sources":["../../src/models/esri.ts"],"names":[],"mappings":";;AAYA,
|
|
1
|
+
{"version":3,"file":"esri.js","sourceRoot":"","sources":["../../src/models/esri.ts"],"names":[],"mappings":"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"}
|
package/build/test/esri.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const chai_1 = require("chai");
|
|
4
4
|
const node_test_1 = require("node:test");
|
|
5
|
+
const strict_1 = require("node:assert/strict");
|
|
5
6
|
const m = require("../index");
|
|
6
7
|
const config = require("./config");
|
|
7
8
|
const mock_1 = require("./mock");
|
|
@@ -57,5 +58,42 @@ const mock_1 = require("./mock");
|
|
|
57
58
|
const props = mapPropsFound[0];
|
|
58
59
|
chai_1.assert.equal(props === null || props === void 0 ? void 0 : props.download, true);
|
|
59
60
|
});
|
|
61
|
+
(0, node_test_1.it)("enforces unique arcGISAuth.username constraint", async function () {
|
|
62
|
+
await new models.Esri({
|
|
63
|
+
departmentId: new mongoose.Types.ObjectId(),
|
|
64
|
+
arcGISAuth: { username: "tc_dup_user" },
|
|
65
|
+
}).save();
|
|
66
|
+
// A second department claiming the same non-empty username must be rejected.
|
|
67
|
+
// assertRejects fails if the save does NOT reject, so there is no false-positive.
|
|
68
|
+
await (0, strict_1.rejects)(new models.Esri({
|
|
69
|
+
departmentId: new mongoose.Types.ObjectId(),
|
|
70
|
+
arcGISAuth: { username: "tc_dup_user" },
|
|
71
|
+
}).save(), /duplicate key/);
|
|
72
|
+
});
|
|
73
|
+
(0, node_test_1.it)("normalizes an empty arcGISAuth.username to null", async function () {
|
|
74
|
+
// EsriAuth.username defaults to "" (schema shared with auth); an arcGISAuth
|
|
75
|
+
// present without a real username collapses to null so it is never indexed.
|
|
76
|
+
const saved = await new models.Esri({
|
|
77
|
+
departmentId: new mongoose.Types.ObjectId(),
|
|
78
|
+
arcGISAuth: { username: "" },
|
|
79
|
+
}).save();
|
|
80
|
+
chai_1.assert.isNull(saved.arcGISAuth);
|
|
81
|
+
});
|
|
82
|
+
(0, node_test_1.it)("allows multiple missing/empty arcGISAuth.username and distinct values", async function () {
|
|
83
|
+
// missing arcGISAuth entirely (defaults to null) — not indexed
|
|
84
|
+
await new models.Esri({ departmentId: new mongoose.Types.ObjectId() }).save();
|
|
85
|
+
await new models.Esri({ departmentId: new mongoose.Types.ObjectId() }).save();
|
|
86
|
+
// empty-string username — normalized to null by the pre-save hook, so not indexed.
|
|
87
|
+
// Both must coexist: without normalization the second would collide on "" under $exists.
|
|
88
|
+
const empty1 = await new models.Esri({ departmentId: new mongoose.Types.ObjectId(), arcGISAuth: { username: "" } }).save();
|
|
89
|
+
const empty2 = await new models.Esri({ departmentId: new mongoose.Types.ObjectId(), arcGISAuth: { username: "" } }).save();
|
|
90
|
+
chai_1.assert.isNull(empty1.arcGISAuth);
|
|
91
|
+
chai_1.assert.isNull(empty2.arcGISAuth);
|
|
92
|
+
// distinct non-empty usernames — allowed
|
|
93
|
+
await new models.Esri({ departmentId: new mongoose.Types.ObjectId(), arcGISAuth: { username: "tc_user_a" } }).save();
|
|
94
|
+
await new models.Esri({ departmentId: new mongoose.Types.ObjectId(), arcGISAuth: { username: "tc_user_b" } }).save();
|
|
95
|
+
const count = await models.Esri.countDocuments({});
|
|
96
|
+
chai_1.assert.equal(count, 6);
|
|
97
|
+
});
|
|
60
98
|
});
|
|
61
99
|
//# sourceMappingURL=esri.js.map
|
package/build/test/esri.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"esri.js","sourceRoot":"","sources":["../../src/test/esri.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"esri.js","sourceRoot":"","sources":["../../src/test/esri.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"esri.d.ts","sourceRoot":"","sources":["../../src/models/esri.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AACjC,OAAO,EACL,cAAc,EACf,MAAM,YAAY,CAAC;AAIpB,OAAO,EAAE,QAAQ,EAAqB,MAAM,eAAe,CAAC;AAE5D,MAAM,WAAW,IAAK,SAAQ,QAAQ;CAAI;AAE1C,wBAAgB,UAAU,CAAC,QAAQ,EAAE,cAAc;;;;;;;;
|
|
1
|
+
{"version":3,"file":"esri.d.ts","sourceRoot":"","sources":["../../src/models/esri.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AACjC,OAAO,EACL,cAAc,EACf,MAAM,YAAY,CAAC;AAIpB,OAAO,EAAE,QAAQ,EAAqB,MAAM,eAAe,CAAC;AAE5D,MAAM,WAAW,IAAK,SAAQ,QAAQ;CAAI;AAE1C,wBAAgB,UAAU,CAAC,QAAQ,EAAE,cAAc;;;;;;;;GAsIlD;AAED,wBAA8B,UAAU,CAAC,QAAQ,EAAE,cAAc;;;;SAGhE;AAED,MAAM,WAAW,SAAU,SAAQ,KAAK,CAAC,IAAI,CAAC;CAAI"}
|
package/package.json
CHANGED
package/src/models/esri.ts
CHANGED
|
@@ -115,6 +115,34 @@ export function EsriSchema(mongoose: MongooseModule) {
|
|
|
115
115
|
unique: true,
|
|
116
116
|
});
|
|
117
117
|
|
|
118
|
+
// cspell:disable-next-line
|
|
119
|
+
// arcGISAuth must be set-or-null, never a subdoc with an empty username.
|
|
120
|
+
// EsriAuth.username defaults to "" and that schema is shared with `auth`, so we
|
|
121
|
+
// normalize here rather than change the default: an arcGISAuth present without a
|
|
122
|
+
// username collapses to null, keeping it out of the $exists unique index below.
|
|
123
|
+
modelSchema.pre("save", function(next) {
|
|
124
|
+
const arcGISAuth = this.get("arcGISAuth");
|
|
125
|
+
if (arcGISAuth && !arcGISAuth.username) {
|
|
126
|
+
this.set("arcGISAuth", null);
|
|
127
|
+
}
|
|
128
|
+
next();
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// arcGISAuth.username is generated lowercase (cron-maps domain.ts:
|
|
132
|
+
// name.toLowerCase() -> `tc_<slug>`), so this case-sensitive unique index is
|
|
133
|
+
// sufficient in practice; case-insensitive enforcement is intentionally deferred.
|
|
134
|
+
// $exists (not $gt: "") is correct because the pre-save hook above guarantees
|
|
135
|
+
// arcGISAuth is set-or-null, so the username field is present only on real records.
|
|
136
|
+
modelSchema.index({
|
|
137
|
+
"arcGISAuth.username": 1,
|
|
138
|
+
}, {
|
|
139
|
+
name: "arcGISAuth.username_1_unique",
|
|
140
|
+
unique: true,
|
|
141
|
+
partialFilterExpression: {
|
|
142
|
+
"arcGISAuth.username": { $exists: true },
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
|
|
118
146
|
return modelSchema;
|
|
119
147
|
}
|
|
120
148
|
|
package/src/test/esri.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { assert } from "chai";
|
|
2
2
|
import { describe, it, beforeEach, afterEach } from "node:test";
|
|
3
|
+
import { rejects as assertRejects } from "node:assert/strict";
|
|
3
4
|
import * as m from "../index";
|
|
4
5
|
import * as config from "./config";
|
|
5
6
|
import mockModule from "./mock";
|
|
@@ -59,4 +60,49 @@ describe("Esri", function() {
|
|
|
59
60
|
const props = mapPropsFound[0];
|
|
60
61
|
assert.equal(props?.download, true);
|
|
61
62
|
});
|
|
63
|
+
|
|
64
|
+
it("enforces unique arcGISAuth.username constraint", async function() {
|
|
65
|
+
await new models.Esri({
|
|
66
|
+
departmentId: new mongoose.Types.ObjectId(),
|
|
67
|
+
arcGISAuth: { username: "tc_dup_user" },
|
|
68
|
+
}).save();
|
|
69
|
+
|
|
70
|
+
// A second department claiming the same non-empty username must be rejected.
|
|
71
|
+
// assertRejects fails if the save does NOT reject, so there is no false-positive.
|
|
72
|
+
await assertRejects(
|
|
73
|
+
new models.Esri({
|
|
74
|
+
departmentId: new mongoose.Types.ObjectId(),
|
|
75
|
+
arcGISAuth: { username: "tc_dup_user" },
|
|
76
|
+
}).save(),
|
|
77
|
+
/duplicate key/
|
|
78
|
+
);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("normalizes an empty arcGISAuth.username to null", async function() {
|
|
82
|
+
// EsriAuth.username defaults to "" (schema shared with auth); an arcGISAuth
|
|
83
|
+
// present without a real username collapses to null so it is never indexed.
|
|
84
|
+
const saved = await new models.Esri({
|
|
85
|
+
departmentId: new mongoose.Types.ObjectId(),
|
|
86
|
+
arcGISAuth: { username: "" },
|
|
87
|
+
}).save();
|
|
88
|
+
assert.isNull(saved.arcGISAuth);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("allows multiple missing/empty arcGISAuth.username and distinct values", async function() {
|
|
92
|
+
// missing arcGISAuth entirely (defaults to null) — not indexed
|
|
93
|
+
await new models.Esri({ departmentId: new mongoose.Types.ObjectId() }).save();
|
|
94
|
+
await new models.Esri({ departmentId: new mongoose.Types.ObjectId() }).save();
|
|
95
|
+
// empty-string username — normalized to null by the pre-save hook, so not indexed.
|
|
96
|
+
// Both must coexist: without normalization the second would collide on "" under $exists.
|
|
97
|
+
const empty1 = await new models.Esri({ departmentId: new mongoose.Types.ObjectId(), arcGISAuth: { username: "" } }).save();
|
|
98
|
+
const empty2 = await new models.Esri({ departmentId: new mongoose.Types.ObjectId(), arcGISAuth: { username: "" } }).save();
|
|
99
|
+
assert.isNull(empty1.arcGISAuth);
|
|
100
|
+
assert.isNull(empty2.arcGISAuth);
|
|
101
|
+
// distinct non-empty usernames — allowed
|
|
102
|
+
await new models.Esri({ departmentId: new mongoose.Types.ObjectId(), arcGISAuth: { username: "tc_user_a" } }).save();
|
|
103
|
+
await new models.Esri({ departmentId: new mongoose.Types.ObjectId(), arcGISAuth: { username: "tc_user_b" } }).save();
|
|
104
|
+
|
|
105
|
+
const count = await models.Esri.countDocuments({});
|
|
106
|
+
assert.equal(count, 6);
|
|
107
|
+
});
|
|
62
108
|
});
|