1 | import { Context } from "./Context";
|
2 | import { Strategy } from "./Strategy";
|
3 | import { GraphQLFieldConfig, GraphQLNamedType } from "graphql";
|
4 | import { Authority, AuthorityData, Credential, CredentialData } from "./model";
|
5 |
|
6 | export class StrategyCollection {
|
7 | public map: { [name: string]: Strategy } = {};
|
8 |
|
9 | public authorityMap: {
|
10 | readonly [name: string]: { new (data: AuthorityData<any>): Authority<any> };
|
11 | } = {};
|
12 |
|
13 | public credentialMap: {
|
14 | readonly [name: string]: {
|
15 | new (data: CredentialData<any>): Credential<any>;
|
16 | };
|
17 | } = {};
|
18 |
|
19 | public queryFields: {
|
20 | readonly [field: string]: GraphQLFieldConfig<any, any, Context>;
|
21 | } = {};
|
22 |
|
23 | public mutationFields: {
|
24 | readonly [field: string]: GraphQLFieldConfig<any, any, Context>;
|
25 | } = {};
|
26 |
|
27 | public types: GraphQLNamedType[] = [];
|
28 |
|
29 | public constructor(strategies?: Iterable<Strategy>) {
|
30 | if (strategies)
|
31 | for (const strategy of strategies) {
|
32 | this.add(strategy);
|
33 | }
|
34 | }
|
35 |
|
36 | public add(s: Strategy): StrategyCollection {
|
37 | if (this.map[s.name])
|
38 | throw new Error(
|
39 | `INVARIANT: Multiple strategies cannot use the same identifier; "${s.name}" is used twice.`
|
40 | );
|
41 |
|
42 | const queryFields = { ...this.queryFields };
|
43 | for (const f of Object.keys(s.queryFields)) {
|
44 | if (queryFields[f]) {
|
45 | throw new Error(
|
46 | `INVARIANT: Multiple strategies cannot use the query field; "${f}" is used twice.`
|
47 | );
|
48 | }
|
49 |
|
50 | queryFields[f] = s.queryFields[f];
|
51 | }
|
52 |
|
53 | const mutationFields = { ...this.mutationFields };
|
54 | for (const f of Object.keys(s.mutationFields)) {
|
55 | if (mutationFields[f]) {
|
56 | throw new Error(
|
57 | `INVARIANT: Multiple strategies cannot use the mutation field; "${f}" is used twice.`
|
58 | );
|
59 | }
|
60 |
|
61 | mutationFields[f] = s.mutationFields[f];
|
62 | }
|
63 |
|
64 | this.map = { ...this.map, [s.name]: s };
|
65 | this.authorityMap = { ...this.authorityMap, [s.name]: s.authorityModel };
|
66 | this.credentialMap = { ...this.credentialMap, [s.name]: s.credentialModel };
|
67 | this.queryFields = queryFields;
|
68 | this.mutationFields = mutationFields;
|
69 | this.types = [...this.types, ...s.types];
|
70 |
|
71 | return this;
|
72 | }
|
73 |
|
74 | public delete(s: Strategy): boolean {
|
75 | if (!this.map[s.name]) return false;
|
76 |
|
77 | const map = { ...this.map };
|
78 | delete map[s.name];
|
79 |
|
80 | const authorityMap = { ...this.authorityMap };
|
81 | delete authorityMap[s.name];
|
82 |
|
83 | const credentialMap = { ...this.credentialMap };
|
84 | delete credentialMap[s.name];
|
85 |
|
86 | const queryFields = { ...this.queryFields };
|
87 | for (const f of Object.keys(s.queryFields)) {
|
88 | delete queryFields[f];
|
89 | }
|
90 |
|
91 | const mutationFields = { ...this.mutationFields };
|
92 | for (const f of Object.keys(s.mutationFields)) {
|
93 | delete mutationFields[f];
|
94 | }
|
95 |
|
96 | this.map = map;
|
97 | this.authorityMap = authorityMap;
|
98 | this.credentialMap = credentialMap;
|
99 | this.queryFields = queryFields;
|
100 | this.mutationFields = mutationFields;
|
101 | this.types = this.types.filter(t => !s.types.includes(t));
|
102 |
|
103 | return true;
|
104 | }
|
105 | }
|