placementt-core 1.400.1111 → 1.400.1113
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/lib/callables/__tests__/registry.test.js +2 -1
- package/lib/callables/__tests__/registry.test.js.map +1 -1
- package/lib/callables/e2e.d.ts +33 -0
- package/lib/callables/e2e.js +27 -0
- package/lib/callables/e2e.js.map +1 -0
- package/lib/callables/index.d.ts +2 -0
- package/lib/callables/index.js +2 -0
- package/lib/callables/index.js.map +1 -1
- package/lib/callables/registry.d.ts +86 -0
- package/lib/callables/registry.js +5 -1
- package/lib/callables/registry.js.map +1 -1
- package/lib/callables/wonde.d.ts +35 -0
- package/lib/callables/wonde.js +27 -0
- package/lib/callables/wonde.js.map +1 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/studentData.d.ts +55 -0
- package/lib/studentData.js +66 -0
- package/lib/studentData.js.map +1 -0
- package/lib/studentData.test.d.ts +1 -0
- package/lib/studentData.test.js +50 -0
- package/lib/studentData.test.js.map +1 -0
- package/lib/typeDefinitions.d.ts +10 -1
- package/lib/typeDefinitions.js.map +1 -1
- package/package.json +1 -1
- package/src/callables/__tests__/registry.test.ts +2 -1
- package/src/callables/e2e.ts +25 -0
- package/src/callables/index.ts +2 -0
- package/src/callables/registry.ts +5 -1
- package/src/callables/wonde.ts +25 -0
- package/src/index.ts +1 -0
- package/src/studentData.test.ts +67 -0
- package/src/studentData.ts +69 -0
- package/src/typeDefinitions.ts +10 -1
|
@@ -19,7 +19,8 @@ describe("callable registry", () => {
|
|
|
19
19
|
});
|
|
20
20
|
it("names every callable as `<module>-<export>`", () => {
|
|
21
21
|
for (const name of names) {
|
|
22
|
-
|
|
22
|
+
// Letters first; digits allowed after, for modules like `e2e`.
|
|
23
|
+
expect(name).toMatch(/^[a-zA-Z][a-zA-Z0-9]*-[a-zA-Z][a-zA-Z0-9]*$/);
|
|
23
24
|
}
|
|
24
25
|
});
|
|
25
26
|
it("gives every callable a request and response zod schema", () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.test.js","sourceRoot":"","sources":["../../../src/callables/__tests__/registry.test.ts"],"names":[],"mappings":";;AAAA,6BAAsB;AACtB,0CAAyD;AAEzD;;;;;GAKG;AACH,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IAC/B,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,uBAAY,CAAC,CAAC;IAExC,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACpD,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC,yBAAc,CAAC;aAC9C,MAAM,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAChE,wEAAwE;QACxE,sEAAsE;QACtE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACnD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACvB,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"registry.test.js","sourceRoot":"","sources":["../../../src/callables/__tests__/registry.test.ts"],"names":[],"mappings":";;AAAA,6BAAsB;AACtB,0CAAyD;AAEzD;;;;;GAKG;AACH,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IAC/B,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,uBAAY,CAAC,CAAC;IAExC,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACpD,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC,yBAAc,CAAC;aAC9C,MAAM,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAChE,wEAAwE;QACxE,sEAAsE;QACtE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACnD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACvB,+DAA+D;YAC/D,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,6CAA6C,CAAC,CAAC;QACxE,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAC9D,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACvB,MAAM,QAAQ,GAAG,uBAAY,CAAC,IAAiC,CAAC,CAAC;YACjE,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,cAAc,CAAC,OAAC,CAAC,OAAO,CAAC,CAAC;YACnD,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,cAAc,CAAC,OAAC,CAAC,OAAO,CAAC,CAAC;QACxD,CAAC;IACL,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Contracts for the end-to-end and UX-audit harness
|
|
4
|
+
* (`Placementt-Backend/functions/src/e2e.ts`).
|
|
5
|
+
*
|
|
6
|
+
* `redeemSignInLink` is public: the token is the credential. Links are only
|
|
7
|
+
* ever minted by the harness (service account, straight into Firestore) and
|
|
8
|
+
* only ever sign in a reserved `e2e.*@careerthread.co.uk` user, so the UX audit
|
|
9
|
+
* can walk a registered school without anyone typing a password.
|
|
10
|
+
*/
|
|
11
|
+
export declare const e2eCallables: {
|
|
12
|
+
"e2e-redeemSignInLink": {
|
|
13
|
+
request: z.ZodObject<{
|
|
14
|
+
/** The raw token from `/e2eSignIn/:token`. Stored only as a SHA-256 hash. */
|
|
15
|
+
token: z.ZodString;
|
|
16
|
+
}, "strip", z.ZodTypeAny, {
|
|
17
|
+
token: string;
|
|
18
|
+
}, {
|
|
19
|
+
token: string;
|
|
20
|
+
}>;
|
|
21
|
+
response: z.ZodObject<{
|
|
22
|
+
code: z.ZodEnum<["ok", "notFound", "expired"]>;
|
|
23
|
+
/** Firebase custom token for the linked e2e user. Only set when code is "ok". */
|
|
24
|
+
authToken: z.ZodOptional<z.ZodString>;
|
|
25
|
+
}, "strip", z.ZodTypeAny, {
|
|
26
|
+
code: "expired" | "ok" | "notFound";
|
|
27
|
+
authToken?: string | undefined;
|
|
28
|
+
}, {
|
|
29
|
+
code: "expired" | "ok" | "notFound";
|
|
30
|
+
authToken?: string | undefined;
|
|
31
|
+
}>;
|
|
32
|
+
};
|
|
33
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.e2eCallables = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
/**
|
|
6
|
+
* Contracts for the end-to-end and UX-audit harness
|
|
7
|
+
* (`Placementt-Backend/functions/src/e2e.ts`).
|
|
8
|
+
*
|
|
9
|
+
* `redeemSignInLink` is public: the token is the credential. Links are only
|
|
10
|
+
* ever minted by the harness (service account, straight into Firestore) and
|
|
11
|
+
* only ever sign in a reserved `e2e.*@careerthread.co.uk` user, so the UX audit
|
|
12
|
+
* can walk a registered school without anyone typing a password.
|
|
13
|
+
*/
|
|
14
|
+
exports.e2eCallables = {
|
|
15
|
+
"e2e-redeemSignInLink": {
|
|
16
|
+
request: zod_1.z.object({
|
|
17
|
+
/** The raw token from `/e2eSignIn/:token`. Stored only as a SHA-256 hash. */
|
|
18
|
+
token: zod_1.z.string().min(32).max(128).regex(/^[A-Za-z0-9_-]+$/),
|
|
19
|
+
}),
|
|
20
|
+
response: zod_1.z.object({
|
|
21
|
+
code: zod_1.z.enum(["ok", "notFound", "expired"]),
|
|
22
|
+
/** Firebase custom token for the linked e2e user. Only set when code is "ok". */
|
|
23
|
+
authToken: zod_1.z.string().optional(),
|
|
24
|
+
}),
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=e2e.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"e2e.js","sourceRoot":"","sources":["../../src/callables/e2e.ts"],"names":[],"mappings":";;;AAAA,6BAAsB;AAGtB;;;;;;;;GAQG;AACU,QAAA,YAAY,GAAG;IACxB,sBAAsB,EAAE;QACpB,OAAO,EAAE,OAAC,CAAC,MAAM,CAAC;YACd,6EAA6E;YAC7E,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,kBAAkB,CAAC;SAC/D,CAAC;QACF,QAAQ,EAAE,OAAC,CAAC,MAAM,CAAC;YACf,IAAI,EAAE,OAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC;YAC3C,iFAAiF;YACjF,SAAS,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;SACnC,CAAC;KACL;CACoB,CAAC"}
|
package/lib/callables/index.d.ts
CHANGED
package/lib/callables/index.js
CHANGED
|
@@ -25,4 +25,6 @@ __exportStar(require("./he"), exports);
|
|
|
25
25
|
__exportStar(require("./studentLaunches"), exports);
|
|
26
26
|
__exportStar(require("./billing"), exports);
|
|
27
27
|
__exportStar(require("./hubImpactReport"), exports);
|
|
28
|
+
__exportStar(require("./e2e"), exports);
|
|
29
|
+
__exportStar(require("./wonde"), exports);
|
|
28
30
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/callables/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,0CAAwB;AACxB,6CAA2B;AAC3B,uDAAqC;AACrC,8CAA4B;AAC5B,2CAAyB;AACzB,6CAA2B;AAC3B,oDAAkC;AAClC,uCAAqB;AACrB,oDAAkC;AAClC,4CAA0B;AAC1B,oDAAkC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/callables/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,0CAAwB;AACxB,6CAA2B;AAC3B,uDAAqC;AACrC,8CAA4B;AAC5B,2CAAyB;AACzB,6CAA2B;AAC3B,oDAAkC;AAClC,uCAAqB;AACrB,oDAAkC;AAClC,4CAA0B;AAC1B,oDAAkC;AAClC,wCAAsB;AACtB,0CAAwB"}
|
|
@@ -11,6 +11,47 @@ import { z } from "zod";
|
|
|
11
11
|
* equals the sum of the group key counts.
|
|
12
12
|
*/
|
|
13
13
|
export declare const allCallables: {
|
|
14
|
+
"wonde-requestConnection": {
|
|
15
|
+
request: z.ZodObject<{
|
|
16
|
+
oId: z.ZodString;
|
|
17
|
+
itManagerName: z.ZodString;
|
|
18
|
+
itManagerEmail: z.ZodString;
|
|
19
|
+
}, "strip", z.ZodTypeAny, {
|
|
20
|
+
oId: string;
|
|
21
|
+
itManagerName: string;
|
|
22
|
+
itManagerEmail: string;
|
|
23
|
+
}, {
|
|
24
|
+
oId: string;
|
|
25
|
+
itManagerName: string;
|
|
26
|
+
itManagerEmail: string;
|
|
27
|
+
}>;
|
|
28
|
+
response: z.ZodObject<{
|
|
29
|
+
taskId: z.ZodString;
|
|
30
|
+
}, "strip", z.ZodTypeAny, {
|
|
31
|
+
taskId: string;
|
|
32
|
+
}, {
|
|
33
|
+
taskId: string;
|
|
34
|
+
}>;
|
|
35
|
+
};
|
|
36
|
+
"e2e-redeemSignInLink": {
|
|
37
|
+
request: z.ZodObject<{
|
|
38
|
+
token: z.ZodString;
|
|
39
|
+
}, "strip", z.ZodTypeAny, {
|
|
40
|
+
token: string;
|
|
41
|
+
}, {
|
|
42
|
+
token: string;
|
|
43
|
+
}>;
|
|
44
|
+
response: z.ZodObject<{
|
|
45
|
+
code: z.ZodEnum<["ok", "notFound", "expired"]>;
|
|
46
|
+
authToken: z.ZodOptional<z.ZodString>;
|
|
47
|
+
}, "strip", z.ZodTypeAny, {
|
|
48
|
+
code: "expired" | "ok" | "notFound";
|
|
49
|
+
authToken?: string | undefined;
|
|
50
|
+
}, {
|
|
51
|
+
code: "expired" | "ok" | "notFound";
|
|
52
|
+
authToken?: string | undefined;
|
|
53
|
+
}>;
|
|
54
|
+
};
|
|
14
55
|
"hubImpactReport-createHubImpactReport": {
|
|
15
56
|
request: z.ZodObject<{
|
|
16
57
|
oId: z.ZodOptional<z.ZodString>;
|
|
@@ -2515,4 +2556,49 @@ export declare const callableGroups: {
|
|
|
2515
2556
|
response: z.ZodString;
|
|
2516
2557
|
};
|
|
2517
2558
|
};
|
|
2559
|
+
e2eCallables: {
|
|
2560
|
+
"e2e-redeemSignInLink": {
|
|
2561
|
+
request: z.ZodObject<{
|
|
2562
|
+
token: z.ZodString;
|
|
2563
|
+
}, "strip", z.ZodTypeAny, {
|
|
2564
|
+
token: string;
|
|
2565
|
+
}, {
|
|
2566
|
+
token: string;
|
|
2567
|
+
}>;
|
|
2568
|
+
response: z.ZodObject<{
|
|
2569
|
+
code: z.ZodEnum<["ok", "notFound", "expired"]>;
|
|
2570
|
+
authToken: z.ZodOptional<z.ZodString>;
|
|
2571
|
+
}, "strip", z.ZodTypeAny, {
|
|
2572
|
+
code: "expired" | "ok" | "notFound";
|
|
2573
|
+
authToken?: string | undefined;
|
|
2574
|
+
}, {
|
|
2575
|
+
code: "expired" | "ok" | "notFound";
|
|
2576
|
+
authToken?: string | undefined;
|
|
2577
|
+
}>;
|
|
2578
|
+
};
|
|
2579
|
+
};
|
|
2580
|
+
wondeCallables: {
|
|
2581
|
+
"wonde-requestConnection": {
|
|
2582
|
+
request: z.ZodObject<{
|
|
2583
|
+
oId: z.ZodString;
|
|
2584
|
+
itManagerName: z.ZodString;
|
|
2585
|
+
itManagerEmail: z.ZodString;
|
|
2586
|
+
}, "strip", z.ZodTypeAny, {
|
|
2587
|
+
oId: string;
|
|
2588
|
+
itManagerName: string;
|
|
2589
|
+
itManagerEmail: string;
|
|
2590
|
+
}, {
|
|
2591
|
+
oId: string;
|
|
2592
|
+
itManagerName: string;
|
|
2593
|
+
itManagerEmail: string;
|
|
2594
|
+
}>;
|
|
2595
|
+
response: z.ZodObject<{
|
|
2596
|
+
taskId: z.ZodString;
|
|
2597
|
+
}, "strip", z.ZodTypeAny, {
|
|
2598
|
+
taskId: string;
|
|
2599
|
+
}, {
|
|
2600
|
+
taskId: string;
|
|
2601
|
+
}>;
|
|
2602
|
+
};
|
|
2603
|
+
};
|
|
2518
2604
|
};
|
|
@@ -13,6 +13,8 @@ const studentLaunches_1 = require("./studentLaunches");
|
|
|
13
13
|
const billing_1 = require("./billing");
|
|
14
14
|
const jobRoles_1 = require("./jobRoles");
|
|
15
15
|
const hubImpactReport_1 = require("./hubImpactReport");
|
|
16
|
+
const e2e_1 = require("./e2e");
|
|
17
|
+
const wonde_1 = require("./wonde");
|
|
16
18
|
/**
|
|
17
19
|
* The single source of truth for every typed callable. Add a feature group's
|
|
18
20
|
* spread here and its callables are automatically covered by:
|
|
@@ -37,7 +39,9 @@ exports.allCallables = {
|
|
|
37
39
|
...billing_1.billingCallables,
|
|
38
40
|
...jobRoles_1.jobRolesCallables,
|
|
39
41
|
...hubImpactReport_1.hubImpactReportCallables,
|
|
42
|
+
...e2e_1.e2eCallables,
|
|
43
|
+
...wonde_1.wondeCallables,
|
|
40
44
|
};
|
|
41
45
|
/** The per-group maps, exported so tests can assert no key collisions. */
|
|
42
|
-
exports.callableGroups = { destinationsConfigCallables: destinationsConfig_1.destinationsConfigCallables, placementCallables: placement_1.placementCallables, eventsCallables: events_1.eventsCallables, surveyCallables: surveys_1.surveyCallables, checksCallables: checks_1.checksCallables, benchmarksCallables: benchmarks_1.benchmarksCallables, apprenticeshipsCallables: apprenticeships_1.apprenticeshipsCallables, heCallables: he_1.heCallables, studentLaunchCallables: studentLaunches_1.studentLaunchCallables, billingCallables: billing_1.billingCallables, jobRolesCallables: jobRoles_1.jobRolesCallables, hubImpactReportCallables: hubImpactReport_1.hubImpactReportCallables };
|
|
46
|
+
exports.callableGroups = { destinationsConfigCallables: destinationsConfig_1.destinationsConfigCallables, placementCallables: placement_1.placementCallables, eventsCallables: events_1.eventsCallables, surveyCallables: surveys_1.surveyCallables, checksCallables: checks_1.checksCallables, benchmarksCallables: benchmarks_1.benchmarksCallables, apprenticeshipsCallables: apprenticeships_1.apprenticeshipsCallables, heCallables: he_1.heCallables, studentLaunchCallables: studentLaunches_1.studentLaunchCallables, billingCallables: billing_1.billingCallables, jobRolesCallables: jobRoles_1.jobRolesCallables, hubImpactReportCallables: hubImpactReport_1.hubImpactReportCallables, e2eCallables: e2e_1.e2eCallables, wondeCallables: wonde_1.wondeCallables };
|
|
43
47
|
//# sourceMappingURL=registry.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/callables/registry.ts"],"names":[],"mappings":";;;AAEA,6DAAiE;AACjE,2CAA+C;AAC/C,qCAAyC;AACzC,uCAA0C;AAC1C,qCAAyC;AACzC,6CAAiD;AACjD,uDAA2D;AAC3D,6BAAiC;AACjC,uDAAyD;AACzD,uCAA2C;AAC3C,yCAA6C;AAC7C,uDAA2D;
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/callables/registry.ts"],"names":[],"mappings":";;;AAEA,6DAAiE;AACjE,2CAA+C;AAC/C,qCAAyC;AACzC,uCAA0C;AAC1C,qCAAyC;AACzC,6CAAiD;AACjD,uDAA2D;AAC3D,6BAAiC;AACjC,uDAAyD;AACzD,uCAA2C;AAC3C,yCAA6C;AAC7C,uDAA2D;AAC3D,+BAAmC;AACnC,mCAAuC;AAEvC;;;;;;;;;;GAUG;AACU,QAAA,YAAY,GAAG;IACxB,GAAG,gDAA2B;IAC9B,GAAG,8BAAkB;IACrB,GAAG,wBAAe;IAClB,GAAG,yBAAe;IAClB,GAAG,wBAAe;IAClB,GAAG,gCAAmB;IACtB,GAAG,0CAAwB;IAC3B,GAAG,gBAAW;IACd,GAAG,wCAAsB;IACzB,GAAG,0BAAgB;IACnB,GAAG,4BAAiB;IACpB,GAAG,0CAAwB;IAC3B,GAAG,kBAAY;IACf,GAAG,sBAAc;CACI,CAAC;AAiB1B,0EAA0E;AAC7D,QAAA,cAAc,GAAG,EAAC,2BAA2B,EAA3B,gDAA2B,EAAE,kBAAkB,EAAlB,8BAAkB,EAAE,eAAe,EAAf,wBAAe,EAAE,eAAe,EAAf,yBAAe,EAAE,eAAe,EAAf,wBAAe,EAAE,mBAAmB,EAAnB,gCAAmB,EAAE,wBAAwB,EAAxB,0CAAwB,EAAE,WAAW,EAAX,gBAAW,EAAE,sBAAsB,EAAtB,wCAAsB,EAAE,gBAAgB,EAAhB,0BAAgB,EAAE,iBAAiB,EAAjB,4BAAiB,EAAE,wBAAwB,EAAxB,0CAAwB,EAAE,YAAY,EAAZ,kBAAY,EAAE,cAAc,EAAd,sBAAc,EAAC,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Contracts for the Wonde connection request
|
|
4
|
+
* (`Placementt-Backend/functions/src/wonde.ts`).
|
|
5
|
+
*
|
|
6
|
+
* Wonde only approves a connection once the school's IT manager signs it off, so
|
|
7
|
+
* the school gives us their details and we raise the request with Wonde. Nothing
|
|
8
|
+
* is emailed from the platform: the request lands as a support task.
|
|
9
|
+
*/
|
|
10
|
+
export declare const wondeCallables: {
|
|
11
|
+
"wonde-requestConnection": {
|
|
12
|
+
request: z.ZodObject<{
|
|
13
|
+
/** The school the connection is for. */
|
|
14
|
+
oId: z.ZodString;
|
|
15
|
+
itManagerName: z.ZodString;
|
|
16
|
+
itManagerEmail: z.ZodString;
|
|
17
|
+
}, "strip", z.ZodTypeAny, {
|
|
18
|
+
oId: string;
|
|
19
|
+
itManagerName: string;
|
|
20
|
+
itManagerEmail: string;
|
|
21
|
+
}, {
|
|
22
|
+
oId: string;
|
|
23
|
+
itManagerName: string;
|
|
24
|
+
itManagerEmail: string;
|
|
25
|
+
}>;
|
|
26
|
+
response: z.ZodObject<{
|
|
27
|
+
/** supportTasks/{id} raised, or the open one it updated. */
|
|
28
|
+
taskId: z.ZodString;
|
|
29
|
+
}, "strip", z.ZodTypeAny, {
|
|
30
|
+
taskId: string;
|
|
31
|
+
}, {
|
|
32
|
+
taskId: string;
|
|
33
|
+
}>;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.wondeCallables = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
/**
|
|
6
|
+
* Contracts for the Wonde connection request
|
|
7
|
+
* (`Placementt-Backend/functions/src/wonde.ts`).
|
|
8
|
+
*
|
|
9
|
+
* Wonde only approves a connection once the school's IT manager signs it off, so
|
|
10
|
+
* the school gives us their details and we raise the request with Wonde. Nothing
|
|
11
|
+
* is emailed from the platform: the request lands as a support task.
|
|
12
|
+
*/
|
|
13
|
+
exports.wondeCallables = {
|
|
14
|
+
"wonde-requestConnection": {
|
|
15
|
+
request: zod_1.z.object({
|
|
16
|
+
/** The school the connection is for. */
|
|
17
|
+
oId: zod_1.z.string().min(1),
|
|
18
|
+
itManagerName: zod_1.z.string().trim().min(1).max(200),
|
|
19
|
+
itManagerEmail: zod_1.z.string().trim().email().max(320),
|
|
20
|
+
}),
|
|
21
|
+
response: zod_1.z.object({
|
|
22
|
+
/** supportTasks/{id} raised, or the open one it updated. */
|
|
23
|
+
taskId: zod_1.z.string(),
|
|
24
|
+
}),
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=wonde.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wonde.js","sourceRoot":"","sources":["../../src/callables/wonde.ts"],"names":[],"mappings":";;;AAAA,6BAAsB;AAGtB;;;;;;;GAOG;AACU,QAAA,cAAc,GAAG;IAC1B,yBAAyB,EAAE;QACvB,OAAO,EAAE,OAAC,CAAC,MAAM,CAAC;YACd,wCAAwC;YACxC,GAAG,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;YACtB,aAAa,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC;YAChD,cAAc,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC;SACrD,CAAC;QACF,QAAQ,EAAE,OAAC,CAAC,MAAM,CAAC;YACf,4DAA4D;YAC5D,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE;SACrB,CAAC;KACL;CACoB,CAAC"}
|
package/lib/index.d.ts
CHANGED
|
@@ -22,6 +22,7 @@ export * from "./activityLogging";
|
|
|
22
22
|
export * from "./studentTools";
|
|
23
23
|
export * from "./featureMarketing";
|
|
24
24
|
export * from "./onboardingPathways";
|
|
25
|
+
export * from "./studentData";
|
|
25
26
|
export * from "./lmi";
|
|
26
27
|
export * from "./lmiCategories";
|
|
27
28
|
export * from "./pathways";
|
package/lib/index.js
CHANGED
|
@@ -42,6 +42,7 @@ __exportStar(require("./activityLogging"), exports);
|
|
|
42
42
|
__exportStar(require("./studentTools"), exports);
|
|
43
43
|
__exportStar(require("./featureMarketing"), exports);
|
|
44
44
|
__exportStar(require("./onboardingPathways"), exports);
|
|
45
|
+
__exportStar(require("./studentData"), exports);
|
|
45
46
|
__exportStar(require("./lmi"), exports);
|
|
46
47
|
__exportStar(require("./lmiCategories"), exports);
|
|
47
48
|
__exportStar(require("./pathways"), exports);
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AACA,sDAAoC;AACpC,gEAA8C;AAC9C,0DAAwC;AACxC,2DAAyC;AACzC,kDAAgC;AAChC,uDAAqC;AACrC,4DAA0C;AAC1C,2DAAyC;AAEzC,oDAAkC;AAClC,8CAA4B;AAC5B,kDAAgC;AAChC,kDAAgC;AAChC,qDAAmC;AACnC,mDAAiC;AACjC,qDAAmC;AACnC,qDAAmC;AACnC,sDAAoC;AACpC,kDAAgC;AAChC,mDAAiC;AACjC,+CAA6B;AAC7B,oDAAkC;AAClC,iDAA+B;AAC/B,qDAAmC;AACnC,uDAAqC;AACrC,wCAAsB;AACtB,kDAAgC;AAChC,6CAA2B;AAC3B,oDAAkC;AAClC,4CAA0B;AAC1B,8CAA4B;AAC5B,2CAAyB;AACzB,kDAAgC;AAChC,uDAAqC;AACrC,oDAAkC;AAClC,kDAAgC;AAChC,0DAAwC;AACxC,iDAA+B;AAC/B,2DAAyC;AACzC,uDAAqC;AACrC,uDAAqC;AACrC,iDAA+B;AAC/B,8CAA4B;AAC5B,qDAAmC;AACnC,0CAAwB;AACxB,kDAAgC;AAChC,yCAAuB;AACvB,4DAA0C;AAC1C,kDAAgC;AAChC,iDAA+B;AAC/B,2CAAyB;AACzB,4DAA0C;AAC1C,oEAAiD;AACjD,yEAAuD;AACvD,kEAAgD;AAChD,kEAAgD;AAChD,oEAAkD;AAClD,yGAAuF;AACvF,0GAAwF;AACxF,+FAA6E;AAC7E,4DAA0C;AAC1C,oEAAkD;AAClD,iEAA+C;AAC/C,gEAA8C;AAC9C,2EAAyD;AACzD,6CAA2B;AAC3B,+CAA6B;AAC7B,8CAA4B;AAC5B,6DAAuE;AAA9D,2IAAA,OAAO,OAAuB"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AACA,sDAAoC;AACpC,gEAA8C;AAC9C,0DAAwC;AACxC,2DAAyC;AACzC,kDAAgC;AAChC,uDAAqC;AACrC,4DAA0C;AAC1C,2DAAyC;AAEzC,oDAAkC;AAClC,8CAA4B;AAC5B,kDAAgC;AAChC,kDAAgC;AAChC,qDAAmC;AACnC,mDAAiC;AACjC,qDAAmC;AACnC,qDAAmC;AACnC,sDAAoC;AACpC,kDAAgC;AAChC,mDAAiC;AACjC,+CAA6B;AAC7B,oDAAkC;AAClC,iDAA+B;AAC/B,qDAAmC;AACnC,uDAAqC;AACrC,gDAA8B;AAC9B,wCAAsB;AACtB,kDAAgC;AAChC,6CAA2B;AAC3B,oDAAkC;AAClC,4CAA0B;AAC1B,8CAA4B;AAC5B,2CAAyB;AACzB,kDAAgC;AAChC,uDAAqC;AACrC,oDAAkC;AAClC,kDAAgC;AAChC,0DAAwC;AACxC,iDAA+B;AAC/B,2DAAyC;AACzC,uDAAqC;AACrC,uDAAqC;AACrC,iDAA+B;AAC/B,8CAA4B;AAC5B,qDAAmC;AACnC,0CAAwB;AACxB,kDAAgC;AAChC,yCAAuB;AACvB,4DAA0C;AAC1C,kDAAgC;AAChC,iDAA+B;AAC/B,2CAAyB;AACzB,4DAA0C;AAC1C,oEAAiD;AACjD,yEAAuD;AACvD,kEAAgD;AAChD,kEAAgD;AAChD,oEAAkD;AAClD,yGAAuF;AACvF,0GAAwF;AACxF,+FAA6E;AAC7E,4DAA0C;AAC1C,oEAAkD;AAClD,iEAA+C;AAC/C,gEAA8C;AAC9C,2EAAyD;AACzD,6CAA2B;AAC3B,+CAA6B;AAC7B,8CAA4B;AAC5B,6DAAuE;AAA9D,2IAAA,OAAO,OAAuB"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { InstituteData } from "./typeDefinitions";
|
|
2
|
+
/**
|
|
3
|
+
* Whether a school has the student data an area of the platform needs before it
|
|
4
|
+
* can be set up.
|
|
5
|
+
*
|
|
6
|
+
* Destinations, Development and the careers calendar are all set up per year
|
|
7
|
+
* group. A school with no students, or students with no year group, cannot answer
|
|
8
|
+
* a single question those setups ask, so instead of the setup flow they are shown
|
|
9
|
+
* what the area does and asked to bring their students in first.
|
|
10
|
+
*
|
|
11
|
+
* Destinations and Development go further and need Wonde: they run all year off
|
|
12
|
+
* the school's roll, and a CSV goes stale the week after it is uploaded.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* The connection token the E2E and UX-audit harness writes to stand in for a
|
|
16
|
+
* real Wonde connection. The platform only ever checks that a connection exists,
|
|
17
|
+
* so this is enough to make a test school look Wonde-connected. The backend sync
|
|
18
|
+
* skips any institute holding it, so nothing ever calls Wonde with it.
|
|
19
|
+
*/
|
|
20
|
+
export declare const WONDE_TEST_CONNECTION_TOKEN = "e2e-test-connection";
|
|
21
|
+
/** What is missing, most fundamental first. */
|
|
22
|
+
export type StudentDataGate = "ready" | "needsStudents" | "needsWonde";
|
|
23
|
+
/**
|
|
24
|
+
* Whether the Wonde connection is the harness's stand-in rather than a real one.
|
|
25
|
+
*
|
|
26
|
+
* @param {InstituteData["wonde"]} [wonde] the institute's Wonde block.
|
|
27
|
+
* @return {boolean} true for a test connection.
|
|
28
|
+
*/
|
|
29
|
+
export declare const isWondeTestConnection: (wonde?: Pick<NonNullable<InstituteData["wonde"]>, "connectionToken">) => boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Whether the school has students with a year group on them. `yearGroups` is
|
|
32
|
+
* only ever added to, so the student count is checked as well: a school that
|
|
33
|
+
* deleted every student still has its old year groups listed.
|
|
34
|
+
*
|
|
35
|
+
* @param {Partial<InstituteData>} [institute] the school.
|
|
36
|
+
* @return {boolean} true when there are students to set anything up for.
|
|
37
|
+
*/
|
|
38
|
+
export declare const hasStudentYearGroups: (institute?: Partial<InstituteData>) => boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Whether the school has a Wonde connection. The connection id is what the sync
|
|
41
|
+
* runs off, so it is the thing checked, not the `hasIntegration` switch.
|
|
42
|
+
*
|
|
43
|
+
* @param {Partial<InstituteData>} [institute] the school.
|
|
44
|
+
* @return {boolean} true when a Wonde connection id is stored.
|
|
45
|
+
*/
|
|
46
|
+
export declare const hasWondeConnection: (institute?: Partial<InstituteData>) => boolean;
|
|
47
|
+
/**
|
|
48
|
+
* What stands between the school and setting an area up.
|
|
49
|
+
*
|
|
50
|
+
* @param {Partial<InstituteData>} [institute] the school.
|
|
51
|
+
* @param {boolean} needsWonde whether the area needs Wonde (Destinations,
|
|
52
|
+
* Development) or just students with year groups (the careers calendar).
|
|
53
|
+
* @return {StudentDataGate} "ready", or the first thing missing.
|
|
54
|
+
*/
|
|
55
|
+
export declare const studentDataGate: (institute: Partial<InstituteData> | undefined, needsWonde: boolean) => StudentDataGate;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.studentDataGate = exports.hasWondeConnection = exports.hasStudentYearGroups = exports.isWondeTestConnection = exports.WONDE_TEST_CONNECTION_TOKEN = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Whether a school has the student data an area of the platform needs before it
|
|
6
|
+
* can be set up.
|
|
7
|
+
*
|
|
8
|
+
* Destinations, Development and the careers calendar are all set up per year
|
|
9
|
+
* group. A school with no students, or students with no year group, cannot answer
|
|
10
|
+
* a single question those setups ask, so instead of the setup flow they are shown
|
|
11
|
+
* what the area does and asked to bring their students in first.
|
|
12
|
+
*
|
|
13
|
+
* Destinations and Development go further and need Wonde: they run all year off
|
|
14
|
+
* the school's roll, and a CSV goes stale the week after it is uploaded.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* The connection token the E2E and UX-audit harness writes to stand in for a
|
|
18
|
+
* real Wonde connection. The platform only ever checks that a connection exists,
|
|
19
|
+
* so this is enough to make a test school look Wonde-connected. The backend sync
|
|
20
|
+
* skips any institute holding it, so nothing ever calls Wonde with it.
|
|
21
|
+
*/
|
|
22
|
+
exports.WONDE_TEST_CONNECTION_TOKEN = "e2e-test-connection";
|
|
23
|
+
/**
|
|
24
|
+
* Whether the Wonde connection is the harness's stand-in rather than a real one.
|
|
25
|
+
*
|
|
26
|
+
* @param {InstituteData["wonde"]} [wonde] the institute's Wonde block.
|
|
27
|
+
* @return {boolean} true for a test connection.
|
|
28
|
+
*/
|
|
29
|
+
const isWondeTestConnection = (wonde) => wonde?.connectionToken === exports.WONDE_TEST_CONNECTION_TOKEN;
|
|
30
|
+
exports.isWondeTestConnection = isWondeTestConnection;
|
|
31
|
+
/**
|
|
32
|
+
* Whether the school has students with a year group on them. `yearGroups` is
|
|
33
|
+
* only ever added to, so the student count is checked as well: a school that
|
|
34
|
+
* deleted every student still has its old year groups listed.
|
|
35
|
+
*
|
|
36
|
+
* @param {Partial<InstituteData>} [institute] the school.
|
|
37
|
+
* @return {boolean} true when there are students to set anything up for.
|
|
38
|
+
*/
|
|
39
|
+
const hasStudentYearGroups = (institute) => Boolean((institute?.students ?? 0) > 0 && institute?.yearGroups?.length);
|
|
40
|
+
exports.hasStudentYearGroups = hasStudentYearGroups;
|
|
41
|
+
/**
|
|
42
|
+
* Whether the school has a Wonde connection. The connection id is what the sync
|
|
43
|
+
* runs off, so it is the thing checked, not the `hasIntegration` switch.
|
|
44
|
+
*
|
|
45
|
+
* @param {Partial<InstituteData>} [institute] the school.
|
|
46
|
+
* @return {boolean} true when a Wonde connection id is stored.
|
|
47
|
+
*/
|
|
48
|
+
const hasWondeConnection = (institute) => Boolean(institute?.wonde?.id);
|
|
49
|
+
exports.hasWondeConnection = hasWondeConnection;
|
|
50
|
+
/**
|
|
51
|
+
* What stands between the school and setting an area up.
|
|
52
|
+
*
|
|
53
|
+
* @param {Partial<InstituteData>} [institute] the school.
|
|
54
|
+
* @param {boolean} needsWonde whether the area needs Wonde (Destinations,
|
|
55
|
+
* Development) or just students with year groups (the careers calendar).
|
|
56
|
+
* @return {StudentDataGate} "ready", or the first thing missing.
|
|
57
|
+
*/
|
|
58
|
+
const studentDataGate = (institute, needsWonde) => {
|
|
59
|
+
if (needsWonde && !(0, exports.hasWondeConnection)(institute))
|
|
60
|
+
return "needsWonde";
|
|
61
|
+
if (!(0, exports.hasStudentYearGroups)(institute))
|
|
62
|
+
return "needsStudents";
|
|
63
|
+
return "ready";
|
|
64
|
+
};
|
|
65
|
+
exports.studentDataGate = studentDataGate;
|
|
66
|
+
//# sourceMappingURL=studentData.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"studentData.js","sourceRoot":"","sources":["../src/studentData.ts"],"names":[],"mappings":";;;AAEA;;;;;;;;;;;GAWG;AAEH;;;;;GAKG;AACU,QAAA,2BAA2B,GAAG,qBAAqB,CAAC;AAKjE;;;;;GAKG;AACI,MAAM,qBAAqB,GAAG,CAAC,KAAoE,EAAW,EAAE,CACnH,KAAK,EAAE,eAAe,KAAK,mCAA2B,CAAC;AAD9C,QAAA,qBAAqB,yBACyB;AAE3D;;;;;;;GAOG;AACI,MAAM,oBAAoB,GAAG,CAAC,SAAkC,EAAW,EAAE,CAChF,OAAO,CAAC,CAAC,SAAS,EAAE,QAAQ,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,SAAS,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC;AADhE,QAAA,oBAAoB,wBAC4C;AAE7E;;;;;;GAMG;AACI,MAAM,kBAAkB,GAAG,CAAC,SAAkC,EAAW,EAAE,CAC9E,OAAO,CAAC,SAAS,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;AADrB,QAAA,kBAAkB,sBACG;AAElC;;;;;;;GAOG;AACI,MAAM,eAAe,GAAG,CAAC,SAA2C,EAAE,UAAmB,EAAmB,EAAE;IACjH,IAAI,UAAU,IAAI,CAAC,IAAA,0BAAkB,EAAC,SAAS,CAAC;QAAE,OAAO,YAAY,CAAC;IACtE,IAAI,CAAC,IAAA,4BAAoB,EAAC,SAAS,CAAC;QAAE,OAAO,eAAe,CAAC;IAC7D,OAAO,OAAO,CAAC;AACnB,CAAC,CAAC;AAJW,QAAA,eAAe,mBAI1B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const studentData_1 = require("./studentData");
|
|
4
|
+
const wonde = (id, connectionToken = "real-token") => ({ wonde: { id, connectionToken, hasIntegration: true } });
|
|
5
|
+
describe("hasStudentYearGroups", () => {
|
|
6
|
+
it("is false with no students", () => {
|
|
7
|
+
expect((0, studentData_1.hasStudentYearGroups)({ students: 0, yearGroups: ["7"] })).toBe(false);
|
|
8
|
+
});
|
|
9
|
+
it("is false when students have no year group", () => {
|
|
10
|
+
expect((0, studentData_1.hasStudentYearGroups)({ students: 30 })).toBe(false);
|
|
11
|
+
expect((0, studentData_1.hasStudentYearGroups)({ students: 30, yearGroups: [] })).toBe(false);
|
|
12
|
+
});
|
|
13
|
+
it("is true with students and year groups", () => {
|
|
14
|
+
expect((0, studentData_1.hasStudentYearGroups)({ students: 30, yearGroups: ["7", "8"] })).toBe(true);
|
|
15
|
+
});
|
|
16
|
+
it("is false with no institute", () => {
|
|
17
|
+
expect((0, studentData_1.hasStudentYearGroups)(undefined)).toBe(false);
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
describe("hasWondeConnection", () => {
|
|
21
|
+
it("reads the connection id", () => {
|
|
22
|
+
expect((0, studentData_1.hasWondeConnection)({})).toBe(false);
|
|
23
|
+
expect((0, studentData_1.hasWondeConnection)(wonde(""))).toBe(false);
|
|
24
|
+
expect((0, studentData_1.hasWondeConnection)(wonde("A123"))).toBe(true);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
describe("isWondeTestConnection", () => {
|
|
28
|
+
it("recognises only the harness token", () => {
|
|
29
|
+
expect((0, studentData_1.isWondeTestConnection)({ connectionToken: studentData_1.WONDE_TEST_CONNECTION_TOKEN })).toBe(true);
|
|
30
|
+
expect((0, studentData_1.isWondeTestConnection)({ connectionToken: "real-token" })).toBe(false);
|
|
31
|
+
expect((0, studentData_1.isWondeTestConnection)(undefined)).toBe(false);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
describe("studentDataGate", () => {
|
|
35
|
+
const withStudents = { students: 30, yearGroups: ["7"] };
|
|
36
|
+
it("asks for students first when Wonde is not needed", () => {
|
|
37
|
+
expect((0, studentData_1.studentDataGate)({}, false)).toBe("needsStudents");
|
|
38
|
+
expect((0, studentData_1.studentDataGate)(withStudents, false)).toBe("ready");
|
|
39
|
+
});
|
|
40
|
+
it("asks for Wonde when it is needed and missing, even with CSV students", () => {
|
|
41
|
+
expect((0, studentData_1.studentDataGate)(withStudents, true)).toBe("needsWonde");
|
|
42
|
+
});
|
|
43
|
+
it("asks for students when Wonde is connected but nothing has synced", () => {
|
|
44
|
+
expect((0, studentData_1.studentDataGate)(wonde("A123"), true)).toBe("needsStudents");
|
|
45
|
+
});
|
|
46
|
+
it("is ready with Wonde and year groups", () => {
|
|
47
|
+
expect((0, studentData_1.studentDataGate)({ ...wonde("A123"), ...withStudents }, true)).toBe("ready");
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
//# sourceMappingURL=studentData.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"studentData.test.js","sourceRoot":"","sources":["../src/studentData.test.ts"],"names":[],"mappings":"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"}
|
package/lib/typeDefinitions.d.ts
CHANGED
|
@@ -1127,6 +1127,15 @@ export type InstituteData = {
|
|
|
1127
1127
|
connectionToken: string;
|
|
1128
1128
|
fields?: string[];
|
|
1129
1129
|
};
|
|
1130
|
+
/** The school asked us to set Wonde up, from an area that needs it. Wonde
|
|
1131
|
+
* approves a connection through the school's IT manager, so their details are
|
|
1132
|
+
* what we pass on. Written by wonde-requestConnection. */
|
|
1133
|
+
wondeRequest?: {
|
|
1134
|
+
itManagerName: string;
|
|
1135
|
+
itManagerEmail: string;
|
|
1136
|
+
requestedAt: string;
|
|
1137
|
+
requestedBy: string;
|
|
1138
|
+
};
|
|
1130
1139
|
microsoft?: {
|
|
1131
1140
|
enabled: boolean;
|
|
1132
1141
|
tenantId?: string;
|
|
@@ -4157,7 +4166,7 @@ export type SupportTask = {
|
|
|
4157
4166
|
assignee: string;
|
|
4158
4167
|
commId?: string;
|
|
4159
4168
|
commType?: "leads" | "users";
|
|
4160
|
-
source?: "customerQuery" | "general" | "seasonalTask" | "onboardingPack" | "midYearPack" | "welcomePack";
|
|
4169
|
+
source?: "customerQuery" | "general" | "seasonalTask" | "onboardingPack" | "midYearPack" | "welcomePack" | "wondeRequest";
|
|
4161
4170
|
dueAt: string;
|
|
4162
4171
|
inbox?: SupportInbox;
|
|
4163
4172
|
messageId?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"typeDefinitions.js","sourceRoot":"","sources":["../src/typeDefinitions.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"typeDefinitions.js","sourceRoot":"","sources":["../src/typeDefinitions.ts"],"names":[],"mappings":";;;AAqjIA,sDASC;AAeD,oDAKC;AAaD,0CAEC;AA/nED;;;;;kFAKkF;AACrE,QAAA,qBAAqB,GAAG,SAAS,CAAC;AAClC,QAAA,iBAAiB,GAAG,OAAO,CAAC;AA2iEzC;yEACyE;AAC5D,QAAA,0BAA0B,GAA2C;IAC9E,gBAAgB,EAAE,2BAA2B;IAC7C,mBAAmB,EAAE,sBAAsB;IAC3C,cAAc,EAAE,iBAAiB;IACjC,kBAAkB,EAAE,qBAAqB;IACzC,UAAU,EAAE,oCAAoC;IAChD,aAAa,EAAE,yBAAyB;IACxC,aAAa,EAAE,wBAAwB;IACvC,KAAK,EAAE,OAAO;CACjB,CAAC;AAEW,QAAA,0BAA0B,GAA+C;IAClF,QAAQ,EAAE,WAAW;IACrB,KAAK,EAAE,YAAY;IACnB,KAAK,EAAE,OAAO;CACjB,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,SAAgB,qBAAqB,CAAC,SAAsC;IACxE,QAAQ,SAAS,EAAE,CAAC;QACpB,KAAK,MAAM,CAAC,CAAC,OAAO,OAAO,CAAC;QAC5B,KAAK,OAAO,CAAC,CAAC,OAAO,OAAO,CAAC;QAC7B,KAAK,WAAW,CAAC;QACjB,KAAK,OAAO,CAAC;QACb,KAAK,OAAO,CAAC;QACb,OAAO,CAAC,CAAC,OAAO,UAAU,CAAC;IAC3B,CAAC;AACL,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,SAAgB,oBAAoB,CAChC,IAAwC,EACxC,MAA6C;IAE7C,OAAO,IAAI,EAAE,WAAW,IAAI,MAAM,EAAE,YAAY,IAAI,KAAK,CAAC;AAC9D,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAgB,eAAe,CAAC,KAA6D;IACzF,OAAO,GAAG,KAAK,CAAC,cAAc,IAAI,EAAE,IAAI,KAAK,CAAC,aAAa,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC;AAC/E,CAAC"}
|
package/package.json
CHANGED
|
@@ -20,7 +20,8 @@ describe("callable registry", () => {
|
|
|
20
20
|
|
|
21
21
|
it("names every callable as `<module>-<export>`", () => {
|
|
22
22
|
for (const name of names) {
|
|
23
|
-
|
|
23
|
+
// Letters first; digits allowed after, for modules like `e2e`.
|
|
24
|
+
expect(name).toMatch(/^[a-zA-Z][a-zA-Z0-9]*-[a-zA-Z][a-zA-Z0-9]*$/);
|
|
24
25
|
}
|
|
25
26
|
});
|
|
26
27
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import {z} from "zod";
|
|
2
|
+
import {CallableGroup} from "./types";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Contracts for the end-to-end and UX-audit harness
|
|
6
|
+
* (`Placementt-Backend/functions/src/e2e.ts`).
|
|
7
|
+
*
|
|
8
|
+
* `redeemSignInLink` is public: the token is the credential. Links are only
|
|
9
|
+
* ever minted by the harness (service account, straight into Firestore) and
|
|
10
|
+
* only ever sign in a reserved `e2e.*@careerthread.co.uk` user, so the UX audit
|
|
11
|
+
* can walk a registered school without anyone typing a password.
|
|
12
|
+
*/
|
|
13
|
+
export const e2eCallables = {
|
|
14
|
+
"e2e-redeemSignInLink": {
|
|
15
|
+
request: z.object({
|
|
16
|
+
/** The raw token from `/e2eSignIn/:token`. Stored only as a SHA-256 hash. */
|
|
17
|
+
token: z.string().min(32).max(128).regex(/^[A-Za-z0-9_-]+$/),
|
|
18
|
+
}),
|
|
19
|
+
response: z.object({
|
|
20
|
+
code: z.enum(["ok", "notFound", "expired"]),
|
|
21
|
+
/** Firebase custom token for the linked e2e user. Only set when code is "ok". */
|
|
22
|
+
authToken: z.string().optional(),
|
|
23
|
+
}),
|
|
24
|
+
},
|
|
25
|
+
} satisfies CallableGroup;
|
package/src/callables/index.ts
CHANGED
|
@@ -12,6 +12,8 @@ import {studentLaunchCallables} from "./studentLaunches";
|
|
|
12
12
|
import {billingCallables} from "./billing";
|
|
13
13
|
import {jobRolesCallables} from "./jobRoles";
|
|
14
14
|
import {hubImpactReportCallables} from "./hubImpactReport";
|
|
15
|
+
import {e2eCallables} from "./e2e";
|
|
16
|
+
import {wondeCallables} from "./wonde";
|
|
15
17
|
|
|
16
18
|
/**
|
|
17
19
|
* The single source of truth for every typed callable. Add a feature group's
|
|
@@ -37,6 +39,8 @@ export const allCallables = {
|
|
|
37
39
|
...billingCallables,
|
|
38
40
|
...jobRolesCallables,
|
|
39
41
|
...hubImpactReportCallables,
|
|
42
|
+
...e2eCallables,
|
|
43
|
+
...wondeCallables,
|
|
40
44
|
} satisfies CallableGroup;
|
|
41
45
|
|
|
42
46
|
/** Every registered callable name. */
|
|
@@ -55,4 +59,4 @@ export type CallableContracts = {
|
|
|
55
59
|
};
|
|
56
60
|
|
|
57
61
|
/** The per-group maps, exported so tests can assert no key collisions. */
|
|
58
|
-
export const callableGroups = {destinationsConfigCallables, placementCallables, eventsCallables, surveyCallables, checksCallables, benchmarksCallables, apprenticeshipsCallables, heCallables, studentLaunchCallables, billingCallables, jobRolesCallables, hubImpactReportCallables};
|
|
62
|
+
export const callableGroups = {destinationsConfigCallables, placementCallables, eventsCallables, surveyCallables, checksCallables, benchmarksCallables, apprenticeshipsCallables, heCallables, studentLaunchCallables, billingCallables, jobRolesCallables, hubImpactReportCallables, e2eCallables, wondeCallables};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import {z} from "zod";
|
|
2
|
+
import {CallableGroup} from "./types";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Contracts for the Wonde connection request
|
|
6
|
+
* (`Placementt-Backend/functions/src/wonde.ts`).
|
|
7
|
+
*
|
|
8
|
+
* Wonde only approves a connection once the school's IT manager signs it off, so
|
|
9
|
+
* the school gives us their details and we raise the request with Wonde. Nothing
|
|
10
|
+
* is emailed from the platform: the request lands as a support task.
|
|
11
|
+
*/
|
|
12
|
+
export const wondeCallables = {
|
|
13
|
+
"wonde-requestConnection": {
|
|
14
|
+
request: z.object({
|
|
15
|
+
/** The school the connection is for. */
|
|
16
|
+
oId: z.string().min(1),
|
|
17
|
+
itManagerName: z.string().trim().min(1).max(200),
|
|
18
|
+
itManagerEmail: z.string().trim().email().max(320),
|
|
19
|
+
}),
|
|
20
|
+
response: z.object({
|
|
21
|
+
/** supportTasks/{id} raised, or the open one it updated. */
|
|
22
|
+
taskId: z.string(),
|
|
23
|
+
}),
|
|
24
|
+
},
|
|
25
|
+
} satisfies CallableGroup;
|
package/src/index.ts
CHANGED
|
@@ -24,6 +24,7 @@ export * from "./activityLogging";
|
|
|
24
24
|
export * from "./studentTools";
|
|
25
25
|
export * from "./featureMarketing";
|
|
26
26
|
export * from "./onboardingPathways";
|
|
27
|
+
export * from "./studentData";
|
|
27
28
|
export * from "./lmi";
|
|
28
29
|
export * from "./lmiCategories";
|
|
29
30
|
export * from "./pathways";
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import {InstituteData} from "./typeDefinitions";
|
|
2
|
+
import {
|
|
3
|
+
WONDE_TEST_CONNECTION_TOKEN,
|
|
4
|
+
hasStudentYearGroups,
|
|
5
|
+
hasWondeConnection,
|
|
6
|
+
isWondeTestConnection,
|
|
7
|
+
studentDataGate,
|
|
8
|
+
} from "./studentData";
|
|
9
|
+
|
|
10
|
+
const wonde = (id: string, connectionToken = "real-token") =>
|
|
11
|
+
({wonde: {id, connectionToken, hasIntegration: true}} as unknown as Partial<InstituteData>);
|
|
12
|
+
|
|
13
|
+
describe("hasStudentYearGroups", () => {
|
|
14
|
+
it("is false with no students", () => {
|
|
15
|
+
expect(hasStudentYearGroups({students: 0, yearGroups: ["7"]})).toBe(false);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("is false when students have no year group", () => {
|
|
19
|
+
expect(hasStudentYearGroups({students: 30})).toBe(false);
|
|
20
|
+
expect(hasStudentYearGroups({students: 30, yearGroups: []})).toBe(false);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("is true with students and year groups", () => {
|
|
24
|
+
expect(hasStudentYearGroups({students: 30, yearGroups: ["7", "8"]})).toBe(true);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("is false with no institute", () => {
|
|
28
|
+
expect(hasStudentYearGroups(undefined)).toBe(false);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe("hasWondeConnection", () => {
|
|
33
|
+
it("reads the connection id", () => {
|
|
34
|
+
expect(hasWondeConnection({})).toBe(false);
|
|
35
|
+
expect(hasWondeConnection(wonde(""))).toBe(false);
|
|
36
|
+
expect(hasWondeConnection(wonde("A123"))).toBe(true);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
describe("isWondeTestConnection", () => {
|
|
41
|
+
it("recognises only the harness token", () => {
|
|
42
|
+
expect(isWondeTestConnection({connectionToken: WONDE_TEST_CONNECTION_TOKEN})).toBe(true);
|
|
43
|
+
expect(isWondeTestConnection({connectionToken: "real-token"})).toBe(false);
|
|
44
|
+
expect(isWondeTestConnection(undefined)).toBe(false);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe("studentDataGate", () => {
|
|
49
|
+
const withStudents = {students: 30, yearGroups: ["7"]};
|
|
50
|
+
|
|
51
|
+
it("asks for students first when Wonde is not needed", () => {
|
|
52
|
+
expect(studentDataGate({}, false)).toBe("needsStudents");
|
|
53
|
+
expect(studentDataGate(withStudents, false)).toBe("ready");
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("asks for Wonde when it is needed and missing, even with CSV students", () => {
|
|
57
|
+
expect(studentDataGate(withStudents, true)).toBe("needsWonde");
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("asks for students when Wonde is connected but nothing has synced", () => {
|
|
61
|
+
expect(studentDataGate(wonde("A123"), true)).toBe("needsStudents");
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("is ready with Wonde and year groups", () => {
|
|
65
|
+
expect(studentDataGate({...wonde("A123"), ...withStudents}, true)).toBe("ready");
|
|
66
|
+
});
|
|
67
|
+
});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import {InstituteData} from "./typeDefinitions";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Whether a school has the student data an area of the platform needs before it
|
|
5
|
+
* can be set up.
|
|
6
|
+
*
|
|
7
|
+
* Destinations, Development and the careers calendar are all set up per year
|
|
8
|
+
* group. A school with no students, or students with no year group, cannot answer
|
|
9
|
+
* a single question those setups ask, so instead of the setup flow they are shown
|
|
10
|
+
* what the area does and asked to bring their students in first.
|
|
11
|
+
*
|
|
12
|
+
* Destinations and Development go further and need Wonde: they run all year off
|
|
13
|
+
* the school's roll, and a CSV goes stale the week after it is uploaded.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The connection token the E2E and UX-audit harness writes to stand in for a
|
|
18
|
+
* real Wonde connection. The platform only ever checks that a connection exists,
|
|
19
|
+
* so this is enough to make a test school look Wonde-connected. The backend sync
|
|
20
|
+
* skips any institute holding it, so nothing ever calls Wonde with it.
|
|
21
|
+
*/
|
|
22
|
+
export const WONDE_TEST_CONNECTION_TOKEN = "e2e-test-connection";
|
|
23
|
+
|
|
24
|
+
/** What is missing, most fundamental first. */
|
|
25
|
+
export type StudentDataGate = "ready"|"needsStudents"|"needsWonde";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Whether the Wonde connection is the harness's stand-in rather than a real one.
|
|
29
|
+
*
|
|
30
|
+
* @param {InstituteData["wonde"]} [wonde] the institute's Wonde block.
|
|
31
|
+
* @return {boolean} true for a test connection.
|
|
32
|
+
*/
|
|
33
|
+
export const isWondeTestConnection = (wonde?: Pick<NonNullable<InstituteData["wonde"]>, "connectionToken">): boolean =>
|
|
34
|
+
wonde?.connectionToken === WONDE_TEST_CONNECTION_TOKEN;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Whether the school has students with a year group on them. `yearGroups` is
|
|
38
|
+
* only ever added to, so the student count is checked as well: a school that
|
|
39
|
+
* deleted every student still has its old year groups listed.
|
|
40
|
+
*
|
|
41
|
+
* @param {Partial<InstituteData>} [institute] the school.
|
|
42
|
+
* @return {boolean} true when there are students to set anything up for.
|
|
43
|
+
*/
|
|
44
|
+
export const hasStudentYearGroups = (institute?: Partial<InstituteData>): boolean =>
|
|
45
|
+
Boolean((institute?.students ?? 0) > 0 && institute?.yearGroups?.length);
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Whether the school has a Wonde connection. The connection id is what the sync
|
|
49
|
+
* runs off, so it is the thing checked, not the `hasIntegration` switch.
|
|
50
|
+
*
|
|
51
|
+
* @param {Partial<InstituteData>} [institute] the school.
|
|
52
|
+
* @return {boolean} true when a Wonde connection id is stored.
|
|
53
|
+
*/
|
|
54
|
+
export const hasWondeConnection = (institute?: Partial<InstituteData>): boolean =>
|
|
55
|
+
Boolean(institute?.wonde?.id);
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* What stands between the school and setting an area up.
|
|
59
|
+
*
|
|
60
|
+
* @param {Partial<InstituteData>} [institute] the school.
|
|
61
|
+
* @param {boolean} needsWonde whether the area needs Wonde (Destinations,
|
|
62
|
+
* Development) or just students with year groups (the careers calendar).
|
|
63
|
+
* @return {StudentDataGate} "ready", or the first thing missing.
|
|
64
|
+
*/
|
|
65
|
+
export const studentDataGate = (institute: Partial<InstituteData>|undefined, needsWonde: boolean): StudentDataGate => {
|
|
66
|
+
if (needsWonde && !hasWondeConnection(institute)) return "needsWonde";
|
|
67
|
+
if (!hasStudentYearGroups(institute)) return "needsStudents";
|
|
68
|
+
return "ready";
|
|
69
|
+
};
|
package/src/typeDefinitions.ts
CHANGED
|
@@ -1155,6 +1155,15 @@ export type InstituteData = {
|
|
|
1155
1155
|
connectionToken: string,
|
|
1156
1156
|
fields?: string[]
|
|
1157
1157
|
},
|
|
1158
|
+
/** The school asked us to set Wonde up, from an area that needs it. Wonde
|
|
1159
|
+
* approves a connection through the school's IT manager, so their details are
|
|
1160
|
+
* what we pass on. Written by wonde-requestConnection. */
|
|
1161
|
+
wondeRequest?: {
|
|
1162
|
+
itManagerName: string,
|
|
1163
|
+
itManagerEmail: string,
|
|
1164
|
+
requestedAt: string, // ISO string
|
|
1165
|
+
requestedBy: string, // uid
|
|
1166
|
+
},
|
|
1158
1167
|
microsoft?: {
|
|
1159
1168
|
enabled: boolean,
|
|
1160
1169
|
tenantId?: string,
|
|
@@ -4302,7 +4311,7 @@ export type SupportTask = {
|
|
|
4302
4311
|
assignee: string; // uid of the assigned team member
|
|
4303
4312
|
commId?: string; // optional link to a Communication doc
|
|
4304
4313
|
commType?: "leads" | "users"; // collection the comm lives in
|
|
4305
|
-
source?: "customerQuery" | "general" | "seasonalTask" | "onboardingPack" | "midYearPack" | "welcomePack";
|
|
4314
|
+
source?: "customerQuery" | "general" | "seasonalTask" | "onboardingPack" | "midYearPack" | "welcomePack" | "wondeRequest";
|
|
4306
4315
|
dueAt: string; // ISO string
|
|
4307
4316
|
inbox?: SupportInbox; // which task inbox triggered this (absent for non-email tasks)
|
|
4308
4317
|
messageId?: string; // Graph message ID — used to prevent duplicates
|