@magda/typescript-common 1.1.0-alpha.2 → 1.1.0-rc.0

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.
Files changed (252) hide show
  1. package/dist/AspectBuilder.d.ts +7 -0
  2. package/dist/AspectBuilder.js +3 -0
  3. package/dist/AspectBuilder.js.map +1 -0
  4. package/dist/AspectCreationFailure.d.ts +5 -0
  5. package/dist/AspectCreationFailure.js +10 -0
  6. package/dist/AspectCreationFailure.js.map +1 -0
  7. package/dist/AsyncPage.d.ts +18 -0
  8. package/dist/AsyncPage.js +140 -0
  9. package/dist/AsyncPage.js.map +1 -0
  10. package/dist/ConnectionResult.d.ts +16 -0
  11. package/dist/ConnectionResult.js +77 -0
  12. package/dist/ConnectionResult.js.map +1 -0
  13. package/dist/ConnectorRecordId.d.ts +15 -0
  14. package/dist/ConnectorRecordId.js +46 -0
  15. package/dist/ConnectorRecordId.js.map +1 -0
  16. package/dist/JsonConnector.d.ts +195 -0
  17. package/dist/JsonConnector.js +427 -0
  18. package/dist/JsonConnector.js.map +1 -0
  19. package/dist/JsonTransformer.d.ts +112 -0
  20. package/dist/JsonTransformer.js +225 -0
  21. package/dist/JsonTransformer.js.map +1 -0
  22. package/dist/OpaCompileResponseParser.d.ts +480 -0
  23. package/dist/OpaCompileResponseParser.js +1134 -0
  24. package/dist/OpaCompileResponseParser.js.map +1 -0
  25. package/dist/RecordCreationFailure.d.ts +7 -0
  26. package/dist/RecordCreationFailure.js +11 -0
  27. package/dist/RecordCreationFailure.js.map +1 -0
  28. package/dist/SimpleOpaSQLTranslator.d.ts +14 -0
  29. package/dist/SimpleOpaSQLTranslator.js +79 -0
  30. package/dist/SimpleOpaSQLTranslator.js.map +1 -0
  31. package/dist/addTrailingSlash.d.ts +1 -0
  32. package/dist/addTrailingSlash.js +15 -0
  33. package/dist/addTrailingSlash.js.map +1 -0
  34. package/dist/appendUrlSegments.d.ts +2 -0
  35. package/dist/appendUrlSegments.js +18 -0
  36. package/dist/appendUrlSegments.js.map +1 -0
  37. package/dist/authorization-api/AccessControlError.d.ts +4 -0
  38. package/dist/authorization-api/AccessControlError.js +13 -0
  39. package/dist/authorization-api/AccessControlError.js.map +1 -0
  40. package/dist/authorization-api/ApiClient.d.ts +141 -0
  41. package/dist/authorization-api/ApiClient.js +335 -0
  42. package/dist/authorization-api/ApiClient.js.map +1 -0
  43. package/dist/authorization-api/AuthError.d.ts +4 -0
  44. package/dist/authorization-api/AuthError.js +13 -0
  45. package/dist/authorization-api/AuthError.js.map +1 -0
  46. package/dist/authorization-api/GenericError.d.ts +9 -0
  47. package/dist/authorization-api/GenericError.js +17 -0
  48. package/dist/authorization-api/GenericError.js.map +1 -0
  49. package/dist/authorization-api/authMiddleware.d.ts +7 -0
  50. package/dist/authorization-api/authMiddleware.js +52 -0
  51. package/dist/authorization-api/authMiddleware.js.map +1 -0
  52. package/dist/authorization-api/model.d.ts +99 -0
  53. package/dist/authorization-api/model.js +3 -0
  54. package/dist/authorization-api/model.js.map +1 -0
  55. package/dist/coerceJson.d.ts +2 -0
  56. package/dist/coerceJson.js +13 -0
  57. package/dist/coerceJson.js.map +1 -0
  58. package/dist/createServiceError.d.ts +19 -0
  59. package/dist/createServiceError.js +45 -0
  60. package/dist/createServiceError.js.map +1 -0
  61. package/dist/delay.d.ts +2 -0
  62. package/dist/delay.js +9 -0
  63. package/dist/delay.js.map +1 -0
  64. package/dist/express/status.d.ts +30 -0
  65. package/dist/express/status.js +180 -0
  66. package/dist/express/status.js.map +1 -0
  67. package/dist/formatServiceError.d.ts +1 -0
  68. package/dist/formatServiceError.js +23 -0
  69. package/dist/formatServiceError.js.map +1 -0
  70. package/dist/generated/registry/api.d.ts +596 -0
  71. package/dist/generated/registry/api.js +2060 -0
  72. package/dist/generated/registry/api.js.map +1 -0
  73. package/dist/getAbsoluteUrl.d.ts +12 -0
  74. package/dist/getAbsoluteUrl.js +35 -0
  75. package/dist/getAbsoluteUrl.js.map +1 -0
  76. package/dist/getBasePathFromUrl.d.ts +10 -0
  77. package/dist/getBasePathFromUrl.js +27 -0
  78. package/dist/getBasePathFromUrl.js.map +1 -0
  79. package/dist/markdownToHtml.d.ts +3 -0
  80. package/dist/markdownToHtml.js +38 -0
  81. package/dist/markdownToHtml.js.map +1 -0
  82. package/dist/opa/OpaTypes.d.ts +41 -0
  83. package/dist/opa/OpaTypes.js +3 -0
  84. package/dist/opa/OpaTypes.js.map +1 -0
  85. package/dist/opa/getAuthDecision.d.ts +19 -0
  86. package/dist/opa/getAuthDecision.js +94 -0
  87. package/dist/opa/getAuthDecision.js.map +1 -0
  88. package/dist/opa/queryOpa.d.ts +1 -0
  89. package/dist/opa/queryOpa.js +41 -0
  90. package/dist/opa/queryOpa.js.map +1 -0
  91. package/dist/registry/AuthorizedRegistryClient.d.ts +29 -0
  92. package/dist/registry/AuthorizedRegistryClient.js +115 -0
  93. package/dist/registry/AuthorizedRegistryClient.js.map +1 -0
  94. package/dist/registry/RegistryClient.d.ts +36 -0
  95. package/dist/registry/RegistryClient.js +59 -0
  96. package/dist/registry/RegistryClient.js.map +1 -0
  97. package/dist/registry/TenantConsts.d.ts +31 -0
  98. package/dist/registry/TenantConsts.js +35 -0
  99. package/dist/registry/TenantConsts.js.map +1 -0
  100. package/dist/registry-manual/api.d.ts +9 -0
  101. package/dist/registry-manual/api.js +10 -0
  102. package/dist/registry-manual/api.js.map +1 -0
  103. package/dist/request.d.ts +3 -0
  104. package/dist/request.js +19 -0
  105. package/dist/request.js.map +1 -0
  106. package/dist/retry.d.ts +1 -0
  107. package/dist/retry.js +21 -0
  108. package/dist/retry.js.map +1 -0
  109. package/dist/retryBackoff.d.ts +1 -0
  110. package/dist/retryBackoff.js +21 -0
  111. package/dist/retryBackoff.js.map +1 -0
  112. package/dist/runLater.d.ts +1 -0
  113. package/dist/runLater.js +16 -0
  114. package/dist/runLater.js.map +1 -0
  115. package/dist/session/GetUserId.d.ts +5 -0
  116. package/dist/session/GetUserId.js +41 -0
  117. package/dist/session/GetUserId.js.map +1 -0
  118. package/dist/session/GetUserSession.d.ts +3 -0
  119. package/dist/session/GetUserSession.js +22 -0
  120. package/dist/session/GetUserSession.js.map +1 -0
  121. package/dist/session/addJwtSecretFromEnvVar.d.ts +14 -0
  122. package/dist/session/addJwtSecretFromEnvVar.js +23 -0
  123. package/dist/session/addJwtSecretFromEnvVar.js.map +1 -0
  124. package/dist/session/buildJwt.d.ts +1 -0
  125. package/dist/session/buildJwt.js +8 -0
  126. package/dist/session/buildJwt.js.map +1 -0
  127. package/dist/session/cookieUtils.d.ts +16 -0
  128. package/dist/session/cookieUtils.js +19 -0
  129. package/dist/session/cookieUtils.js.map +1 -0
  130. package/dist/session/destroySession.d.ts +10 -0
  131. package/dist/session/destroySession.js +44 -0
  132. package/dist/session/destroySession.js.map +1 -0
  133. package/dist/session/getSessionId.d.ts +2 -0
  134. package/dist/session/getSessionId.js +28 -0
  135. package/dist/session/getSessionId.js.map +1 -0
  136. package/dist/tenant-api/AuthorizedTenantClient.d.ts +25 -0
  137. package/dist/tenant-api/AuthorizedTenantClient.js +61 -0
  138. package/dist/tenant-api/AuthorizedTenantClient.js.map +1 -0
  139. package/dist/tenant-api/Tenant.d.ts +17 -0
  140. package/dist/tenant-api/Tenant.js +10 -0
  141. package/dist/tenant-api/Tenant.js.map +1 -0
  142. package/dist/test/JsonConnector.spec.d.ts +1 -0
  143. package/dist/test/JsonConnector.spec.js +352 -0
  144. package/dist/test/JsonConnector.spec.js.map +1 -0
  145. package/dist/test/JsonTransformer.spec.d.ts +1 -0
  146. package/dist/test/JsonTransformer.spec.js +98 -0
  147. package/dist/test/JsonTransformer.spec.js.map +1 -0
  148. package/dist/test/arbitraries.d.ts +98 -0
  149. package/dist/test/arbitraries.js +264 -0
  150. package/dist/test/arbitraries.js.map +1 -0
  151. package/dist/test/aspect-templates/organization-details.d.ts +4 -0
  152. package/dist/test/aspect-templates/organization-details.js +11 -0
  153. package/dist/test/aspect-templates/organization-details.js.map +1 -0
  154. package/dist/test/buildApiClient.spec.d.ts +1 -0
  155. package/dist/test/buildApiClient.spec.js +177 -0
  156. package/dist/test/buildApiClient.spec.js.map +1 -0
  157. package/dist/test/buildAuthorizedTenantClient.spec.d.ts +1 -0
  158. package/dist/test/buildAuthorizedTenantClient.spec.js +60 -0
  159. package/dist/test/buildAuthorizedTenantClient.spec.js.map +1 -0
  160. package/dist/test/connectors/MockExpressServer.d.ts +5 -0
  161. package/dist/test/connectors/MockExpressServer.js +28 -0
  162. package/dist/test/connectors/MockExpressServer.js.map +1 -0
  163. package/dist/test/connectors/MockRegistry.d.ts +7 -0
  164. package/dist/test/connectors/MockRegistry.js +93 -0
  165. package/dist/test/connectors/MockRegistry.js.map +1 -0
  166. package/dist/test/connectors/runConnectorTest.d.ts +5 -0
  167. package/dist/test/connectors/runConnectorTest.js +106 -0
  168. package/dist/test/connectors/runConnectorTest.js.map +1 -0
  169. package/dist/test/db/getTestDBConfig.d.ts +7 -0
  170. package/dist/test/db/getTestDBConfig.js +13 -0
  171. package/dist/test/db/getTestDBConfig.js.map +1 -0
  172. package/dist/test/db/runMigrationSql.d.ts +9 -0
  173. package/dist/test/db/runMigrationSql.js +82 -0
  174. package/dist/test/db/runMigrationSql.js.map +1 -0
  175. package/dist/test/express/status.spec.d.ts +1 -0
  176. package/dist/test/express/status.spec.js +160 -0
  177. package/dist/test/express/status.spec.js.map +1 -0
  178. package/dist/test/fakeArgv.d.ts +2 -0
  179. package/dist/test/fakeArgv.js +7 -0
  180. package/dist/test/fakeArgv.js.map +1 -0
  181. package/dist/test/getAuthDecision.spec.d.ts +1 -0
  182. package/dist/test/getAuthDecision.spec.js +22 -0
  183. package/dist/test/getAuthDecision.spec.js.map +1 -0
  184. package/dist/test/getBasePathFromUrl.spec.d.ts +1 -0
  185. package/dist/test/getBasePathFromUrl.spec.js +25 -0
  186. package/dist/test/getBasePathFromUrl.spec.js.map +1 -0
  187. package/dist/test/jsverify.d.ts +2 -0
  188. package/dist/test/jsverify.js +8 -0
  189. package/dist/test/jsverify.js.map +1 -0
  190. package/dist/test/mockAuthApiHost.d.ts +14 -0
  191. package/dist/test/mockAuthApiHost.js +174 -0
  192. package/dist/test/mockAuthApiHost.js.map +1 -0
  193. package/dist/test/mockAuthorization.d.ts +3 -0
  194. package/dist/test/mockAuthorization.js +24 -0
  195. package/dist/test/mockAuthorization.js.map +1 -0
  196. package/dist/test/mockTenantDataStore.d.ts +8 -0
  197. package/dist/test/mockTenantDataStore.js +41 -0
  198. package/dist/test/mockTenantDataStore.js.map +1 -0
  199. package/dist/test/mockUserDataStore.d.ts +11 -0
  200. package/dist/test/mockUserDataStore.js +56 -0
  201. package/dist/test/mockUserDataStore.js.map +1 -0
  202. package/dist/test/registry/buildAuthorizedClient.spec.d.ts +1 -0
  203. package/dist/test/registry/buildAuthorizedClient.spec.js +101 -0
  204. package/dist/test/registry/buildAuthorizedClient.spec.js.map +1 -0
  205. package/dist/test/sampleOpaResponse.json +480 -0
  206. package/dist/test/sampleOpaResponseSimple.json +173 -0
  207. package/dist/test/sampleOpaResponseUnconditionalTrue.json +193 -0
  208. package/dist/test/sampleOpaResponseUnconditionalTrueWithDefaultRule.json +303 -0
  209. package/dist/test/sampleOpaResponseWithDefaultRule.json +303 -0
  210. package/dist/test/session/buildJwt.spec.d.ts +1 -0
  211. package/dist/test/session/buildJwt.spec.js +50 -0
  212. package/dist/test/session/buildJwt.spec.js.map +1 -0
  213. package/dist/test/session/buildJwtForRegistryEsriOpaGroupsAndOwnerTest.spec.d.ts +1 -0
  214. package/dist/test/session/buildJwtForRegistryEsriOpaGroupsAndOwnerTest.spec.js +64 -0
  215. package/dist/test/session/buildJwtForRegistryEsriOpaGroupsAndOwnerTest.spec.js.map +1 -0
  216. package/dist/test/session/buildJwtForRegistryEsriOpaGroupsTest.spec.d.ts +1 -0
  217. package/dist/test/session/buildJwtForRegistryEsriOpaGroupsTest.spec.js +52 -0
  218. package/dist/test/session/buildJwtForRegistryEsriOpaGroupsTest.spec.js.map +1 -0
  219. package/dist/test/session/buildJwtForRegistryEsriOpaOwnerTest.spec.d.ts +1 -0
  220. package/dist/test/session/buildJwtForRegistryEsriOpaOwnerTest.spec.js +36 -0
  221. package/dist/test/session/buildJwtForRegistryEsriOpaOwnerTest.spec.js.map +1 -0
  222. package/dist/test/testAsyncPage.spec.d.ts +1 -0
  223. package/dist/test/testAsyncPage.spec.js +463 -0
  224. package/dist/test/testAsyncPage.spec.js.map +1 -0
  225. package/dist/test/testOpaCompileResponseParser.spec.d.ts +1 -0
  226. package/dist/test/testOpaCompileResponseParser.spec.js +148 -0
  227. package/dist/test/testOpaCompileResponseParser.spec.js.map +1 -0
  228. package/dist/test/util.d.ts +1 -0
  229. package/dist/test/util.js +8 -0
  230. package/dist/test/util.js.map +1 -0
  231. package/dist/util/arrayToMaybe.d.ts +2 -0
  232. package/dist/util/arrayToMaybe.js +8 -0
  233. package/dist/util/arrayToMaybe.js.map +1 -0
  234. package/dist/util/cleanOrgTitle.d.ts +1 -0
  235. package/dist/util/cleanOrgTitle.js +13 -0
  236. package/dist/util/cleanOrgTitle.js.map +1 -0
  237. package/dist/util/escapeRegExp.d.ts +2 -0
  238. package/dist/util/escapeRegExp.js +5 -0
  239. package/dist/util/escapeRegExp.js.map +1 -0
  240. package/dist/util/getMinikubeIP.d.ts +1 -0
  241. package/dist/util/getMinikubeIP.js +13 -0
  242. package/dist/util/getMinikubeIP.js.map +1 -0
  243. package/dist/util/isUuid.d.ts +2 -0
  244. package/dist/util/isUuid.js +6 -0
  245. package/dist/util/isUuid.js.map +1 -0
  246. package/dist/util/unionToThrowable.d.ts +1 -0
  247. package/dist/util/unionToThrowable.js +12 -0
  248. package/dist/util/unionToThrowable.js.map +1 -0
  249. package/dist/utilityTypes.d.ts +1 -0
  250. package/dist/utilityTypes.js +3 -0
  251. package/dist/utilityTypes.js.map +1 -0
  252. package/package.json +3 -3
@@ -0,0 +1,1134 @@
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.unknown2Ref = exports.value2String = exports.RegoRef = exports.RegoExp = exports.RegoTerm = exports.RegoOperators = exports.RegoRule = void 0;
7
+ const lodash_1 = __importDefault(require("lodash"));
8
+ /**
9
+ * @class RegoRule
10
+ * @export
11
+ *
12
+ * RegoRule represents [Rule](https://www.openpolicyagent.org/docs/latest/how-do-i-write-policies/#rules) concept in Rego language.
13
+ * - A simple rule is made up of Rule head, Rule value & Rule body
14
+ * - The Rule value defines the final result of the rule if the rule is matched (i.e. all expressions in rule body are true)
15
+ * - If you didn't sepcify the rule value, it will be assume as boolean value `true`
16
+ * - The rule body is made up of one of more rego expressions (see @class RegoExp) and each of the expression is made up of terms (see @class RegoTerm)
17
+ * - The rule is considered as matched if all expressions in rule body are `true`
18
+ * You can opt to define a `default` rule. A default rule has no rule body and will only considered as matched if all other rules are not matched.
19
+ */
20
+ class RegoRule {
21
+ constructor(options) {
22
+ this.isCompleteEvaluated = false;
23
+ this.name = lodash_1.default.isString(options.name) ? options.name : "";
24
+ this.fullName = lodash_1.default.isString(options.fullName) ? options.fullName : "";
25
+ this.isDefault = lodash_1.default.isBoolean(options.isDefault)
26
+ ? options.isDefault
27
+ : false;
28
+ this.value = lodash_1.default.isUndefined(options.value) ? true : options.value;
29
+ this.expressions = lodash_1.default.isArray(options.expressions)
30
+ ? options.expressions
31
+ : [];
32
+ this.isCompleteEvaluated = lodash_1.default.isBoolean(options.isCompleteEvaluated)
33
+ ? options.isCompleteEvaluated
34
+ : false;
35
+ this.parser = options.parser;
36
+ if (this.name === "") {
37
+ throw new Error("Rule name can't be empty");
38
+ }
39
+ if (this.fullName === "") {
40
+ throw new Error("Rule fullName can't be empty");
41
+ }
42
+ if (!(this.parser instanceof OpaCompileResponseParser)) {
43
+ throw new Error("Require parser parameter to create a RegoRule");
44
+ }
45
+ }
46
+ clone(options = {}) {
47
+ const regoRule = new RegoRule(Object.assign({ name: this.name, fullName: this.fullName, isDefault: this.isDefault, value: this.value, isCompleteEvaluated: this.isCompleteEvaluated, expressions: this.expressions.map((e) => e.clone()), parser: this.parser }, options));
48
+ regoRule.isMatched = this.isMatched;
49
+ return regoRule;
50
+ }
51
+ /**
52
+ * Re-evaluate this rule
53
+ * If fully evaluated, this.isCompleteEvaluated will be set to true
54
+ *
55
+ * @returns
56
+ * @memberof RegoRule
57
+ */
58
+ evaluate() {
59
+ this.expressions = this.expressions.map((exp) => exp.evaluate());
60
+ const falseExpression = this.expressions.find((exp) => exp.isMatch() === false);
61
+ if (!lodash_1.default.isUndefined(falseExpression)) {
62
+ // --- rule expressions are always evaluated in the context of AND
63
+ // --- any false expression will make the rule not match
64
+ this.isCompleteEvaluated = true;
65
+ this.isMatched = false;
66
+ }
67
+ else {
68
+ // --- filter out all expressions are evaluated
69
+ // --- note any non-false value will considered as a match (true) i.e. 0 is equivalent to true
70
+ // --- empty expression array indicates unconditional match (true)
71
+ const idx = this.expressions.findIndex((exp) => !exp.isCompleteEvaluated);
72
+ if (idx === -1) {
73
+ this.isCompleteEvaluated = true;
74
+ this.isMatched = true;
75
+ }
76
+ else {
77
+ // --- further dry the rule if the rule has unsolved exps
78
+ // --- if a exp is matched (i.e. true) it can be strip out as true AND xxxx = xxxx
79
+ this.expressions = this.expressions.filter((exp) => exp.isMatch() !== true);
80
+ }
81
+ }
82
+ return this;
83
+ }
84
+ /**
85
+ * Generate Human Readable string of this rule
86
+ * If it's fully evaluated, the output will be true or false (or actual rule value)
87
+ * Otherwise, will generate expressions concate with `AND`
88
+ *
89
+ * @returns {string}
90
+ * @memberof RegoRule
91
+ */
92
+ toHumanReadableString() {
93
+ if (this.isCompleteEvaluated) {
94
+ if (this.isMatched) {
95
+ return value2String(this.value);
96
+ }
97
+ else {
98
+ return "";
99
+ }
100
+ }
101
+ else {
102
+ const parts = this.expressions.map((e) => e.toHumanReadableString());
103
+ return parts.join(" AND \n");
104
+ }
105
+ }
106
+ /**
107
+ * Create RegoRule from Opa response data
108
+ *
109
+ * @static
110
+ * @param {*} r
111
+ * @param {string} packageName
112
+ * @param {OpaCompileResponseParser} parser
113
+ * @returns {RegoRule}
114
+ * @memberof RegoRule
115
+ */
116
+ static parseFromData(r, packageName, parser) {
117
+ const ruleName = r.head && r.head.name ? r.head.name : "";
118
+ const ruleFullName = [packageName, ruleName].join(".");
119
+ const ruleIsDefault = r.default === true;
120
+ const ruleValue = r.head && r.head.value && !lodash_1.default.isUndefined(r.head.value.value)
121
+ ? r.head.value.value
122
+ : true;
123
+ const ruleOptions = {
124
+ name: ruleName,
125
+ fullName: ruleFullName,
126
+ isDefault: ruleIsDefault,
127
+ value: ruleValue,
128
+ expressions: RegoRule.createExpressionsFromRuleBodyData(r.body, parser),
129
+ parser
130
+ };
131
+ const regoRule = new RegoRule(ruleOptions);
132
+ regoRule.evaluate();
133
+ return regoRule;
134
+ }
135
+ static createExpressionsFromRuleBodyData(data, parser) {
136
+ if (!lodash_1.default.isArray(data) || !data.length) {
137
+ throw new Error(`Encountered empty rule body.`);
138
+ }
139
+ return data.map((expData) => RegoExp.parseFromData(expData, parser));
140
+ }
141
+ static randomRuleName(prefix) {
142
+ return (prefix + Math.random()).replace(".", "_");
143
+ }
144
+ static createFromValue(val, parser) {
145
+ const ruleName = RegoRule.randomRuleName("fixed_value_rule_");
146
+ return new RegoRule({
147
+ isDefault: false,
148
+ name: ruleName,
149
+ fullName: ruleName,
150
+ isCompleteEvaluated: true,
151
+ expressions: [],
152
+ value: val,
153
+ parser
154
+ });
155
+ }
156
+ }
157
+ exports.RegoRule = RegoRule;
158
+ exports.RegoOperators = {
159
+ eq: "=",
160
+ equal: "=",
161
+ neq: "!=",
162
+ lt: "<",
163
+ gt: ">",
164
+ lte: "<=",
165
+ gte: ">="
166
+ };
167
+ /**
168
+ * RegoTerm represent the basic elements that creates an expressions.
169
+ * e.g. An expression `a > 4` is made up of 3 terms
170
+ * - Reference Term: `a`
171
+ * - Operator Term `>`
172
+ * - Value Term: `4`
173
+ *
174
+ * @export
175
+ * @class RegoTerm
176
+ */
177
+ class RegoTerm {
178
+ constructor(type, value, parser) {
179
+ this.type = type;
180
+ this.value = value;
181
+ this.parser = parser;
182
+ }
183
+ clone() {
184
+ return new RegoTerm(this.type, this.value, this.parser);
185
+ }
186
+ /**
187
+ * If it's a reference term, return its full string representation
188
+ *
189
+ * @returns
190
+ * @memberof RegoTerm
191
+ */
192
+ asString() {
193
+ if (this.value instanceof RegoRef)
194
+ return this.value.fullRefString();
195
+ else
196
+ return this.value;
197
+ }
198
+ /**
199
+ * If it's a reference term. A operator is an Reference term as well
200
+ *
201
+ * @returns {boolean}
202
+ * @memberof RegoTerm
203
+ */
204
+ isRef() {
205
+ if (this.value instanceof RegoRef)
206
+ return true;
207
+ return false;
208
+ }
209
+ /**
210
+ * Return RegoRef instance if this term is a RegoRef.
211
+ *
212
+ * @returns {RegoRef}
213
+ * @memberof RegoTerm
214
+ */
215
+ getRef() {
216
+ if (this.isRef()) {
217
+ return this.value;
218
+ }
219
+ throw new Error(`Term ${this.asString()} is not a ref`);
220
+ }
221
+ /**
222
+ * If the term is a reference and it contains any collection lookup
223
+ * e.g.
224
+ * - objectA.propB.collectionC[_]
225
+ * - objectA.propB.collectionC[_].ABC[_].name
226
+ * - objectA.propB.collectionC[_].id
227
+ *
228
+ * @returns {boolean}
229
+ * @memberof RegoTerm
230
+ */
231
+ hasCollectionLookup() {
232
+ if (!this.isRef())
233
+ return false;
234
+ const ref = this.getRef();
235
+ return ref.hasCollectionLookup();
236
+ }
237
+ /**
238
+ * The term is not only a Reference but a reference contains simple collection lookup
239
+ * i.e. only contains one collection lookup and the whole ref ends with the only collection lookup
240
+ * e.g. objectA.propB.collectionC[_]
241
+ * Note: objectA.propB.collectionC[_].name is not a simple collection lookup as it resolve to single value (`name` property)
242
+ * rather than a collection
243
+ *
244
+ * @returns {boolean}
245
+ * @memberof RegoTerm
246
+ */
247
+ isSimpleCollectionLookup() {
248
+ if (!this.isRef())
249
+ return false;
250
+ const ref = this.getRef();
251
+ return ref.isSimpleCollectionLookup();
252
+ }
253
+ /**
254
+ *
255
+ *
256
+ * @returns {boolean}
257
+ * @memberof RegoTerm
258
+ */
259
+ isResolveAsCollectionValue() {
260
+ if (!this.isRef())
261
+ return false;
262
+ const ref = this.getRef();
263
+ return ref.isResolveAsCollectionValue();
264
+ }
265
+ /**
266
+ * If it's a reference term, return its full string representation
267
+ * Otherwise, throw exception
268
+ *
269
+ * @param {string[]} [removalPrefixs=[]]
270
+ * @returns {string}
271
+ * @memberof RegoTerm
272
+ */
273
+ fullRefString(removalPrefixs = []) {
274
+ if (this.value instanceof RegoRef) {
275
+ return this.value.fullRefString(removalPrefixs);
276
+ }
277
+ else {
278
+ throw new Error("Tried to call `fullRefString` on non Ref term.");
279
+ }
280
+ }
281
+ /**
282
+ * If it's a reference term, return its string representation (not include ending [_])
283
+ * Otherwise, throw exception
284
+ *
285
+ * @param {string[]} [removalPrefixs=[]]
286
+ * @returns {string}
287
+ * @memberof RegoTerm
288
+ */
289
+ refString(removalPrefixs = []) {
290
+ if (this.value instanceof RegoRef) {
291
+ return this.value.refString(removalPrefixs);
292
+ }
293
+ else {
294
+ throw new Error("Tried to call `refString` on non Ref term.");
295
+ }
296
+ }
297
+ /**
298
+ * Return term as operator string e.g. `=`, `>=` etc.
299
+ *
300
+ * @returns {string}
301
+ * @memberof RegoTerm
302
+ */
303
+ asOperator() {
304
+ if (this.value instanceof RegoRef) {
305
+ return this.value.asOperator();
306
+ }
307
+ else {
308
+ throw new Error("Tried to call `asOperator` on non Ref term.");
309
+ }
310
+ }
311
+ /**
312
+ * If it's a operator term
313
+ *
314
+ * @returns {boolean}
315
+ * @memberof RegoTerm
316
+ */
317
+ isOperator() {
318
+ if (this.value instanceof RegoRef) {
319
+ return this.value.isOperator();
320
+ }
321
+ else {
322
+ return false;
323
+ }
324
+ }
325
+ /**
326
+ * Tried to determine the value of the term
327
+ *
328
+ * @returns {RegoValue}
329
+ * @memberof RegoTerm
330
+ */
331
+ getValue() {
332
+ if (!this.isRef()) {
333
+ return this.value;
334
+ }
335
+ else {
336
+ if (this.isOperator()) {
337
+ return undefined;
338
+ }
339
+ else {
340
+ const fullName = this.fullRefString();
341
+ const result = this.parser.completeRuleResults[fullName];
342
+ if (lodash_1.default.isUndefined(result))
343
+ return undefined;
344
+ return result.value;
345
+ }
346
+ }
347
+ }
348
+ static parseFromData(data, parser) {
349
+ if (data.type === "ref") {
350
+ return new RegoTerm(data.type, RegoRef.parseFromData(data), parser);
351
+ }
352
+ else {
353
+ return new RegoTerm(data.type, data.value, parser);
354
+ }
355
+ }
356
+ }
357
+ exports.RegoTerm = RegoTerm;
358
+ /**
359
+ * Represents Rego expression
360
+ *
361
+ * @export
362
+ * @class RegoExp
363
+ */
364
+ class RegoExp {
365
+ constructor(terms, isNegated = false, isCompleteEvaluated = false, value = null, parser) {
366
+ /**
367
+ * If it's complete evaluated
368
+ *
369
+ * @type {boolean}
370
+ * @memberof RegoExp
371
+ */
372
+ this.isCompleteEvaluated = false;
373
+ /**
374
+ * The value of the expression
375
+ *
376
+ * @type {RegoValue}
377
+ * @memberof RegoExp
378
+ */
379
+ this.value = null;
380
+ this.terms = terms;
381
+ this.isNegated = isNegated;
382
+ this.isCompleteEvaluated = isCompleteEvaluated;
383
+ this.value = value;
384
+ this.parser = parser;
385
+ }
386
+ clone() {
387
+ const regoExp = new RegoExp(this.terms.map((t) => t.clone()), this.isNegated, this.isCompleteEvaluated, this.value, this.parser);
388
+ return regoExp;
389
+ }
390
+ /**
391
+ * For debug usage, print terms as easy to ready short string
392
+ *
393
+ * @returns
394
+ * @memberof RegoExp
395
+ */
396
+ termsAsString() {
397
+ return this.terms.map((t) => t.asString());
398
+ }
399
+ /**
400
+ * Output human readable string
401
+ *
402
+ * @returns {string}
403
+ * @memberof RegoExp
404
+ */
405
+ toHumanReadableString() {
406
+ if (this.terms.length === 1) {
407
+ const value = this.terms[0].getValue();
408
+ const parts = [];
409
+ if (this.isNegated)
410
+ parts.push("NOT");
411
+ if (!lodash_1.default.isUndefined(value)) {
412
+ parts.push(value2String(value));
413
+ }
414
+ else {
415
+ parts.push(this.terms[0].fullRefString());
416
+ }
417
+ return parts.join(" ");
418
+ }
419
+ else if (this.terms.length === 3) {
420
+ const [operator, operands] = this.toOperatorOperandsArray();
421
+ const parts = [];
422
+ if (operands[0].isRef()) {
423
+ parts.push(operands[0].fullRefString());
424
+ }
425
+ else {
426
+ parts.push(value2String(operands[0].getValue()));
427
+ }
428
+ parts.push(operator);
429
+ if (operands[1].isRef()) {
430
+ parts.push(operands[1].fullRefString());
431
+ }
432
+ else {
433
+ parts.push(value2String(operands[1].getValue()));
434
+ }
435
+ const expStr = parts.join(" ");
436
+ if (this.isNegated)
437
+ return `NOT (${expStr})`;
438
+ return expStr;
439
+ }
440
+ throw new Error(`Invalid rego expression: ${this.termsAsString()}`);
441
+ }
442
+ /**
443
+ * Try to determins its value
444
+ *
445
+ * @returns
446
+ * @memberof RegoExp
447
+ */
448
+ getValue() {
449
+ this.evaluate();
450
+ if (!this.isCompleteEvaluated)
451
+ return undefined;
452
+ if (this.isNegated) {
453
+ return this.value === false ? true : false;
454
+ }
455
+ else {
456
+ // --- undefined is a common value in Rego similar to false
457
+ // --- we set to false here to tell the difference between
458
+ // --- real undefined (not full resolved) and undefined value
459
+ if (lodash_1.default.isUndefined(this.value))
460
+ return false;
461
+ else
462
+ return this.value;
463
+ }
464
+ }
465
+ isMatch() {
466
+ const value = this.getValue();
467
+ if (lodash_1.default.isUndefined(value)) {
468
+ return undefined;
469
+ }
470
+ else {
471
+ if (value === false || lodash_1.default.isUndefined(value))
472
+ return false;
473
+ // --- 0 is a match
474
+ return true;
475
+ }
476
+ }
477
+ /**
478
+ * Convert operator term to string and put rest operands into an array.
479
+ * And then return a [Operator, Operands] structure
480
+ *
481
+ * @returns {[string, RegoTerm[]]}
482
+ * @memberof RegoExp
483
+ */
484
+ toOperatorOperandsArray() {
485
+ if (this.terms.length !== 3) {
486
+ throw new Error(`Can't get Operator & Operands from non 3 terms expression: ${this.termsAsString()}`);
487
+ }
488
+ const operands = [];
489
+ let operator = null;
490
+ this.terms.forEach((t) => {
491
+ if (t.isOperator()) {
492
+ operator = t.asOperator();
493
+ }
494
+ else {
495
+ const value = t.getValue();
496
+ if (!lodash_1.default.isUndefined(value)) {
497
+ operands.push(new RegoTerm(typeof value, value, this.parser));
498
+ }
499
+ else
500
+ operands.push(t);
501
+ }
502
+ });
503
+ if (!operator) {
504
+ throw new Error(`Invalid 3 terms rego expression, can't locate operator: ${this.termsAsString()}`);
505
+ }
506
+ if (operands.length !== 2) {
507
+ throw new Error(`Invalid 3 terms rego expression, the number of operands should be 2: ${this.termsAsString()}`);
508
+ }
509
+ return [operator, operands];
510
+ }
511
+ /**
512
+ * Try to evaluate the expression
513
+ *
514
+ * @returns
515
+ * @memberof RegoExp
516
+ */
517
+ evaluate() {
518
+ if (this.terms.length === 0) {
519
+ // --- exp should be considered as matched (true)
520
+ // --- unless isNegated is true
521
+ // --- will try to normalise isNegated here
522
+ this.isCompleteEvaluated = true;
523
+ this.value = this.isNegated ? false : true;
524
+ this.isNegated = false;
525
+ }
526
+ if (this.terms.length === 1) {
527
+ const term = this.terms[0];
528
+ const value = term.getValue();
529
+ if (lodash_1.default.isUndefined(value))
530
+ return this;
531
+ this.value = value;
532
+ this.isCompleteEvaluated = true;
533
+ return this;
534
+ }
535
+ else if (this.terms.length === 3) {
536
+ // --- 3 terms expression e.g. true == true or x >= 3
537
+ // --- we only evalute some redundant expression e.g. true == true or false != true
538
+ const [operator, operands] = this.toOperatorOperandsArray();
539
+ if (operands.findIndex((op) => op.isRef()) !== -1) {
540
+ // --- this expression involve unknown no need to evalute
541
+ return this;
542
+ }
543
+ else {
544
+ const operandsValues = operands.map((op) => op.getValue());
545
+ let value = null;
546
+ switch (operator) {
547
+ case "=":
548
+ value = operandsValues[0] === operandsValues[1];
549
+ break;
550
+ case ">":
551
+ value = operandsValues[0] > operandsValues[1];
552
+ break;
553
+ case "<":
554
+ value = operandsValues[0] < operandsValues[1];
555
+ break;
556
+ case ">=":
557
+ value = operandsValues[0] >= operandsValues[1];
558
+ break;
559
+ case "<=":
560
+ value = operandsValues[0] <= operandsValues[1];
561
+ break;
562
+ case "!=":
563
+ value = operandsValues[0] != operandsValues[1];
564
+ break;
565
+ default:
566
+ throw new Error(`Invalid 3 terms rego expression, Unknown operator "${operator}": ${this.termsAsString()}`);
567
+ }
568
+ this.isCompleteEvaluated = true;
569
+ this.value = value;
570
+ return this;
571
+ }
572
+ }
573
+ else {
574
+ throw new Error(`Invalid ${this.terms.length} terms rego expression: ${this.termsAsString()}`);
575
+ }
576
+ // --- so far there is no 2 terms expression e.g. ! x
577
+ // --- builtin function should never be included in residual rule
578
+ // --- as we won't apply them on unknowns
579
+ return this;
580
+ }
581
+ static parseFromData(expData, parser) {
582
+ const isNegated = expData.negated === true;
583
+ if (lodash_1.default.isEmpty(expData.terms)) {
584
+ if (isNegated)
585
+ throw new Error("Invalid negated empty term!");
586
+ return new RegoExp([], isNegated, false, null, parser);
587
+ }
588
+ let termsData = [];
589
+ if (lodash_1.default.isArray(expData.terms)) {
590
+ termsData = expData.terms;
591
+ }
592
+ else {
593
+ termsData.push(expData.terms);
594
+ }
595
+ const terms = termsData.map((termData) => RegoTerm.parseFromData(termData, parser));
596
+ const exp = new RegoExp(terms, isNegated, false, null, parser);
597
+ exp.evaluate();
598
+ return exp;
599
+ }
600
+ }
601
+ exports.RegoExp = RegoExp;
602
+ /**
603
+ * Represents a special Rego Term type: reference term
604
+ * You shouldn't use this class directly
605
+ *
606
+ * @export
607
+ * @class RegoRef
608
+ */
609
+ class RegoRef {
610
+ constructor(parts) {
611
+ this.parts = parts;
612
+ }
613
+ clone() {
614
+ return new RegoRef(this.parts.map((p) => (Object.assign({}, p))));
615
+ }
616
+ static parseFromData(data) {
617
+ if (data.type === "ref") {
618
+ return new RegoRef(data.value);
619
+ }
620
+ else {
621
+ return null;
622
+ }
623
+ }
624
+ static convertToFullRefString(parts) {
625
+ return new RegoRef(parts).fullRefString();
626
+ }
627
+ removeAllPrefixs(str, removalPrefixs = []) {
628
+ if (!removalPrefixs.length)
629
+ return str;
630
+ let result = str;
631
+ removalPrefixs
632
+ // --- starts from longest prefix
633
+ .sort((a, b) => b.length - a.length)
634
+ .forEach((prefix) => {
635
+ if (!prefix)
636
+ return;
637
+ const idx = result.indexOf(prefix);
638
+ if (idx !== 0)
639
+ return;
640
+ result = result.substring(prefix.length);
641
+ });
642
+ return result;
643
+ }
644
+ fullRefString(removalPrefixs = []) {
645
+ let isFirstPart = true;
646
+ const str = this.parts
647
+ .map((part) => {
648
+ let partStr = "";
649
+ if (isFirstPart) {
650
+ partStr = part.value;
651
+ }
652
+ else {
653
+ if (part.type == "var") {
654
+ // --- it's a collection lookup
655
+ // --- var name doesn't matter for most cases
656
+ partStr = "[_]";
657
+ }
658
+ else {
659
+ partStr = part.value;
660
+ }
661
+ }
662
+ if (isFirstPart)
663
+ isFirstPart = false;
664
+ return partStr;
665
+ //--- a.[_].[_] should be a[_][_]
666
+ })
667
+ .join(".")
668
+ .replace(/\.\[/g, "[");
669
+ return this.removeAllPrefixs(str, removalPrefixs);
670
+ }
671
+ refString(removalPrefixs = []) {
672
+ return this.fullRefString(removalPrefixs).replace("\\[_\\]$", "");
673
+ }
674
+ asCollectionRefs(removalPrefixs = []) {
675
+ return this.fullRefString(removalPrefixs)
676
+ .split("[_]")
677
+ .map((refStr) => refStr.replace(/^\./, ""));
678
+ }
679
+ isOperator() {
680
+ return Object.keys(exports.RegoOperators).indexOf(this.fullRefString()) !== -1;
681
+ }
682
+ // --- the first var type won't count as collection lookup
683
+ hasCollectionLookup() {
684
+ if (this.parts.length <= 1)
685
+ return false;
686
+ else {
687
+ return (this.parts.slice(1).findIndex((part) => part.type === "var") !==
688
+ -1);
689
+ }
690
+ }
691
+ // -- simple collection only contains 1 level lookup
692
+ // -- we don't need Nested Query to handle it
693
+ isSimpleCollectionLookup() {
694
+ if (this.parts.length <= 1)
695
+ return false;
696
+ else {
697
+ return (this.parts.slice(1).findIndex((part) => part.type === "var") ===
698
+ this.parts.length - 2);
699
+ }
700
+ }
701
+ // --- see comment for same name method of RegoTerm
702
+ isResolveAsCollectionValue() {
703
+ const refString = this.fullRefString();
704
+ return refString.lastIndexOf("[_]") === refString.length - 3;
705
+ }
706
+ asOperator() {
707
+ if (this.isOperator())
708
+ return exports.RegoOperators[this.fullRefString()];
709
+ else
710
+ return null;
711
+ }
712
+ }
713
+ exports.RegoRef = RegoRef;
714
+ function value2String(value) {
715
+ if (lodash_1.default.isBoolean(value) || lodash_1.default.isNumber(value))
716
+ return value.toString();
717
+ else
718
+ return JSON.stringify(value);
719
+ }
720
+ exports.value2String = value2String;
721
+ /**
722
+ * OPA result Parser
723
+ *
724
+ * @export
725
+ * @class OpaCompileResponseParser
726
+ */
727
+ class OpaCompileResponseParser {
728
+ constructor() {
729
+ /**
730
+ * If a warning is produced during the parsing
731
+ *
732
+ * @type {boolean}
733
+ * @memberof OpaCompileResponseParser
734
+ */
735
+ this.hasWarns = false;
736
+ /**
737
+ * Any warnings produced during the parsing
738
+ *
739
+ * @type {string[]}
740
+ * @memberof OpaCompileResponseParser
741
+ */
742
+ this.warns = [];
743
+ this.data = null;
744
+ /**
745
+ * Inital Rules parsed from result
746
+ * Only for debug purpose
747
+ *
748
+ * @type {RegoRule[]}
749
+ * @memberof OpaCompileResponseParser
750
+ */
751
+ this.originalRules = [];
752
+ /**
753
+ * Parsed, compressed & evaluated rules
754
+ *
755
+ * @type {RegoRule[]}
756
+ * @memberof OpaCompileResponseParser
757
+ */
758
+ this.rules = [];
759
+ this.queries = [];
760
+ /**
761
+ * A cache of all resolved rule result
762
+ *
763
+ * @type {{
764
+ * [fullName: string]: CompleteRuleResult;
765
+ * }}
766
+ * @memberof OpaCompileResponseParser
767
+ */
768
+ this.completeRuleResults = {};
769
+ /**
770
+ * The pseudo query rule name
771
+ * The parser will assign a random pseudo rule name to the query expressions you submit.
772
+ *
773
+ * @type {string}
774
+ * @memberof OpaCompileResponseParser
775
+ */
776
+ this.pseudoQueryRuleName = RegoRule.randomRuleName("default_rule_");
777
+ }
778
+ setQueryRuleResult(val) {
779
+ this.completeRuleResults[this.pseudoQueryRuleName] = {
780
+ fullName: this.pseudoQueryRuleName,
781
+ name: this.pseudoQueryRuleName,
782
+ value: val,
783
+ isCompleteEvaluated: true
784
+ };
785
+ }
786
+ /**
787
+ * Parse OPA result Response
788
+ *
789
+ * @param {*} json
790
+ * @returns {RegoRule[]}
791
+ * @memberof OpaCompileResponseParser
792
+ */
793
+ parse(json) {
794
+ if (lodash_1.default.isString(json)) {
795
+ this.data = JSON.parse(json);
796
+ }
797
+ else {
798
+ this.data = json;
799
+ }
800
+ if (!this.data.result) {
801
+ // --- mean no rule matched
802
+ this.setQueryRuleResult(false);
803
+ return [];
804
+ }
805
+ this.data = this.data.result;
806
+ if ((!this.data.queries ||
807
+ !lodash_1.default.isArray(this.data.queries) ||
808
+ !this.data.queries.length) &&
809
+ (!lodash_1.default.isArray(this.data.support) || !this.data.support.length)) {
810
+ // --- mean no rule matched
811
+ this.setQueryRuleResult(false);
812
+ return [];
813
+ }
814
+ const queries = this.data.queries;
815
+ if (queries) {
816
+ if (queries.findIndex((ruleBody) => !ruleBody || !ruleBody.length) !== -1) {
817
+ // --- there is an empty array --- indicates unconditional matched
818
+ this.setQueryRuleResult(true);
819
+ return [];
820
+ }
821
+ queries.forEach((ruleBody, i) => {
822
+ const rule = new RegoRule({
823
+ name: this.pseudoQueryRuleName,
824
+ fullName: this.pseudoQueryRuleName,
825
+ expressions: RegoRule.createExpressionsFromRuleBodyData(ruleBody, this),
826
+ isDefault: false,
827
+ isCompleteEvaluated: false,
828
+ value: true,
829
+ parser: this
830
+ });
831
+ rule.evaluate();
832
+ this.originalRules.push(rule);
833
+ this.rules.push(rule);
834
+ });
835
+ }
836
+ const packages = this.data.support;
837
+ if (packages) {
838
+ packages.forEach((p) => {
839
+ if (!lodash_1.default.isArray(p.rules) || !p.rules.length)
840
+ return;
841
+ const packageName = p.package && lodash_1.default.isArray(p.package.path)
842
+ ? RegoRef.convertToFullRefString(p.package.path)
843
+ : "";
844
+ const rules = p.rules;
845
+ rules.forEach((r) => {
846
+ const regoRule = RegoRule.parseFromData(r, packageName, this);
847
+ this.originalRules.push(regoRule);
848
+ // --- only save matched rules
849
+ if (!regoRule.isCompleteEvaluated) {
850
+ this.rules.push(regoRule);
851
+ }
852
+ else {
853
+ if (regoRule.isMatched) {
854
+ this.rules.push(regoRule);
855
+ }
856
+ }
857
+ });
858
+ });
859
+ }
860
+ this.calculateCompleteRuleResult();
861
+ this.reduceDependencies();
862
+ return this.rules;
863
+ }
864
+ /**
865
+ * Tried to merge rules outcome so that the ref value can be established easier
866
+ * After this step, any rules doesn't involve unknown should be merged to one value
867
+ * This will help to generate more concise query later.
868
+ * `CompleteRule` rule involves no `unknowns`
869
+ *
870
+ * Only for internal usage
871
+ *
872
+ * @private
873
+ * @memberof OpaCompileResponseParser
874
+ */
875
+ calculateCompleteRuleResult() {
876
+ const fullNames = this.rules.map((r) => r.fullName);
877
+ fullNames.forEach((fullName) => {
878
+ const rules = this.rules.filter((r) => r.fullName === fullName);
879
+ const nonCompletedRules = rules.filter((r) => !r.isCompleteEvaluated);
880
+ const completedRules = rules.filter((r) => r.isCompleteEvaluated);
881
+ const defaultRules = completedRules.filter((r) => r.isDefault);
882
+ const nonDefaultRules = completedRules.filter((r) => !r.isDefault);
883
+ if (nonDefaultRules.length) {
884
+ // --- if a non default complete eveluated rules exist
885
+ // --- it will be the final outcome
886
+ this.completeRuleResults[fullName] = this.createCompleteRuleResult(nonDefaultRules[0]);
887
+ return;
888
+ }
889
+ if (!nonCompletedRules.length) {
890
+ // --- if no unevaluated rule left, default rule value should be used
891
+ if (defaultRules.length) {
892
+ this.completeRuleResults[fullName] = this.createCompleteRuleResult(defaultRules[0]);
893
+ return;
894
+ }
895
+ else {
896
+ // --- no matched complete non default rule left; Not possible
897
+ throw new Error(`Unexpected empty rule result for ${fullName}`);
898
+ }
899
+ }
900
+ else {
901
+ // --- do nothing
902
+ // --- Some defaultRules might be able to strip out once
903
+ // --- nonCompleteRules are determined later
904
+ return;
905
+ }
906
+ });
907
+ }
908
+ /**
909
+ * Only for internal usage
910
+ *
911
+ * @returns
912
+ * @private
913
+ * @memberof OpaCompileResponseParser
914
+ */
915
+ reduceDependencies() {
916
+ const rules = this.rules.filter((r) => !r.isCompleteEvaluated);
917
+ if (!rules.length)
918
+ return;
919
+ for (let i = 0; i < rules.length; i++) {
920
+ const rule = rules[i];
921
+ rule.expressions = rule.expressions.map((e) => e.evaluate());
922
+ rule.evaluate();
923
+ }
924
+ // --- unmatched non-default rule can be stripped out
925
+ this.rules = this.rules.filter((r) => !(r.isCompleteEvaluated && !r.isMatched && !r.isDefault));
926
+ }
927
+ /**
928
+ * Call to evaluate a rule
929
+ *
930
+ * @param {string} fullName
931
+ * @returns {CompleteRuleResult}
932
+ * @memberof OpaCompileResponseParser
933
+ */
934
+ evaluateRule(fullName) {
935
+ var _a, _b, _c;
936
+ if ((_b = (_a = this.completeRuleResults) === null || _a === void 0 ? void 0 : _a[fullName]) === null || _b === void 0 ? void 0 : _b.isCompleteEvaluated) {
937
+ // --- already evaluated during paring or dependencies removal
938
+ return (_c = this.completeRuleResults) === null || _c === void 0 ? void 0 : _c[fullName];
939
+ }
940
+ let rules = this.rules.filter((r) => r.fullName === fullName);
941
+ const originalRuleName = rules[0].name;
942
+ if (!rules.length) {
943
+ // --- no any rule matched; often (depends on your policy) it means a overall non-matched (false)
944
+ return null;
945
+ }
946
+ const defaultRule = rules.find((r) => r.isDefault);
947
+ const defaultValue = lodash_1.default.isUndefined(defaultRule)
948
+ ? undefined
949
+ : defaultRule.value;
950
+ if (rules.find((r) => r.isCompleteEvaluated))
951
+ // --- filter out default rules & unmatched
952
+ // --- isMatch is only set when r.isCompleteEvaluated = true
953
+ rules = rules.filter((r) => !(r.isDefault || (r.isCompleteEvaluated && !r.isMatched)));
954
+ if (!rules.length) {
955
+ return {
956
+ fullName,
957
+ name: defaultRule ? defaultRule.name : "",
958
+ value: defaultValue,
959
+ isCompleteEvaluated: true,
960
+ residualRules: []
961
+ };
962
+ }
963
+ else {
964
+ const matchedRule = rules.find((r) => r.isMatched);
965
+ if (matchedRule) {
966
+ return {
967
+ fullName,
968
+ name: originalRuleName,
969
+ value: matchedRule.value,
970
+ isCompleteEvaluated: true,
971
+ residualRules: []
972
+ };
973
+ }
974
+ const ruleWithEmptyExps = rules.find((r) => !r.expressions.length);
975
+ if (ruleWithEmptyExps) {
976
+ // empty exp / body means unconditional match
977
+ return {
978
+ fullName,
979
+ name: originalRuleName,
980
+ value: ruleWithEmptyExps.value,
981
+ isCompleteEvaluated: true,
982
+ residualRules: []
983
+ };
984
+ }
985
+ if (rules.length === 1 && rules[0].expressions.length === 1) {
986
+ rules[0].expressions[0].terms.length === 1;
987
+ }
988
+ // if a rules contains one expression only, we will try to resolve any possible rule ref
989
+ rules = lodash_1.default.flatMap(rules, (rule) => {
990
+ if (rules.length === 1 && rules[0].expressions.length === 1) {
991
+ const exp = rules[0].expressions[0];
992
+ if (exp.terms.length === 1 && exp.terms[0].isRef()) {
993
+ const ruleRef = exp.terms[0].fullRefString();
994
+ const result = this.evaluateRule(ruleRef);
995
+ if (result) {
996
+ if (result.isCompleteEvaluated) {
997
+ return [
998
+ RegoRule.createFromValue(result.value, this)
999
+ ];
1000
+ }
1001
+ else {
1002
+ return result.residualRules;
1003
+ }
1004
+ }
1005
+ else {
1006
+ return [rule];
1007
+ }
1008
+ }
1009
+ else if (exp.terms.length === 3) {
1010
+ const [opStr, [op1, op2]] = exp.toOperatorOperandsArray();
1011
+ if (opStr === "=" &&
1012
+ ((op1.isRef() && typeof op2.value === "boolean") ||
1013
+ (op2.isRef() && typeof op1.value === "boolean"))) {
1014
+ const ruleRef = op1.isRef()
1015
+ ? op1.fullRefString()
1016
+ : op2.fullRefString();
1017
+ const bVal = typeof op1.value === "boolean"
1018
+ ? op1.value
1019
+ : op2.value;
1020
+ const result = this.evaluateRule(ruleRef);
1021
+ if (result) {
1022
+ if (result.isCompleteEvaluated) {
1023
+ if (bVal === false) {
1024
+ return [
1025
+ RegoRule.createFromValue(!result.value, this)
1026
+ ];
1027
+ }
1028
+ else {
1029
+ return [
1030
+ RegoRule.createFromValue(result.value, this)
1031
+ ];
1032
+ }
1033
+ }
1034
+ else {
1035
+ if (bVal === false) {
1036
+ return result.residualRules.map((r) => r.clone({ value: !r.value }));
1037
+ }
1038
+ else {
1039
+ return result.residualRules;
1040
+ }
1041
+ }
1042
+ }
1043
+ else {
1044
+ return [rule];
1045
+ }
1046
+ }
1047
+ else {
1048
+ return [rule];
1049
+ }
1050
+ }
1051
+ else {
1052
+ return [rule];
1053
+ }
1054
+ }
1055
+ else {
1056
+ return [rule];
1057
+ }
1058
+ });
1059
+ return {
1060
+ fullName,
1061
+ name: rules[0].name,
1062
+ value: undefined,
1063
+ isCompleteEvaluated: false,
1064
+ residualRules: rules
1065
+ };
1066
+ }
1067
+ }
1068
+ /**
1069
+ * Shortcut to evalute query result directly
1070
+ *
1071
+ * @returns {CompleteRuleResult}
1072
+ * @memberof OpaCompileResponseParser
1073
+ */
1074
+ evaluate() {
1075
+ return this.evaluateRule(this.pseudoQueryRuleName);
1076
+ }
1077
+ /**
1078
+ * evaluate a rule and returned as human readable string
1079
+ *
1080
+ * @param {string} fullName
1081
+ * @returns {string}
1082
+ * @memberof OpaCompileResponseParser
1083
+ */
1084
+ evaluateRuleAsHumanReadableString(fullName) {
1085
+ const result = this.evaluateRule(fullName);
1086
+ if (result === null)
1087
+ return "null";
1088
+ if (result.isCompleteEvaluated) {
1089
+ return value2String(result.value);
1090
+ }
1091
+ let parts = result.residualRules.map((r) => r.toHumanReadableString());
1092
+ if (parts.length > 1) {
1093
+ parts = parts.map((p) => `( ${p} )`);
1094
+ }
1095
+ return parts.join("\nOR\n");
1096
+ }
1097
+ /**
1098
+ * Shortcut to evalute query result directly and returned as human readable string
1099
+ *
1100
+ * @returns {string}
1101
+ * @memberof OpaCompileResponseParser
1102
+ */
1103
+ evaluateAsHumanReadableString() {
1104
+ return this.evaluateRuleAsHumanReadableString(this.pseudoQueryRuleName);
1105
+ }
1106
+ /**
1107
+ * Only for internal usage
1108
+ *
1109
+ * @param {RegoRule} rule
1110
+ * @returns {CompleteRuleResult}
1111
+ * @private
1112
+ * @memberof OpaCompileResponseParser
1113
+ */
1114
+ createCompleteRuleResult(rule) {
1115
+ return {
1116
+ fullName: rule.fullName,
1117
+ name: rule.name,
1118
+ value: rule.value,
1119
+ isCompleteEvaluated: true,
1120
+ residualRules: []
1121
+ };
1122
+ }
1123
+ reportWarns(msg) {
1124
+ this.warns.push(msg);
1125
+ this.hasWarns = true;
1126
+ }
1127
+ }
1128
+ exports.default = OpaCompileResponseParser;
1129
+ function unknown2Ref(unknown) {
1130
+ const prefix = unknown.replace(/^input\./, "");
1131
+ return `data.partial.${prefix}`;
1132
+ }
1133
+ exports.unknown2Ref = unknown2Ref;
1134
+ //# sourceMappingURL=OpaCompileResponseParser.js.map