Skip to content

Commit 2269775

Browse files
committed
feat: Define and load policy definition for HTTP request
1 parent 5119b42 commit 2269775

6 files changed

Lines changed: 31136 additions & 22208 deletions

File tree

app/controllers/index.js

Lines changed: 103 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { inject as service } from '@ember/service';
44
import { tracked } from '@glimmer/tracking';
55
import { A } from '@ember/array';
66
import { namedNode } from 'rdflib';
7+
import { n3reasoner } from 'eyereasoner';
78

89
export default class IndexController extends Controller {
910
queryParams = ['form'];
@@ -26,6 +27,11 @@ export default class IndexController extends Controller {
2627
@tracked
2728
fields = this.loadFields();
2829

30+
@tracked policyTypeError = '';
31+
@tracked policyURLError = '';
32+
@tracked policyMethodError = '';
33+
@tracked policyContentTypeError = '';
34+
2935
loadFields() {
3036
let fields;
3137
if (
@@ -156,7 +162,7 @@ export default class IndexController extends Controller {
156162
}
157163

158164
// Remove all N3 rules from the resource.
159-
const matches = await this.model.removeN3RulesFromResource();
165+
let matches = await this.model.removeN3RulesFromResource();
160166

161167
this.fields.forEach((field, i) => {
162168
field.order = i;
@@ -185,6 +191,38 @@ export default class IndexController extends Controller {
185191

186192
await this.store.persist();
187193

194+
// Apply the policy configuration to the rules.
195+
matches.rules = await matches.rules.filter(
196+
(rule) => !this.isEventSubmitRule(rule, matches.prefixes)
197+
);
198+
matches.rules.push(`
199+
{
200+
_:id ex:event ex:Submit.
201+
} => {
202+
ex:HttpPolicy pol:policy ex:Pol .
203+
ex:Pol a fno:Execution ;
204+
fno:executes ex:httpRequest ;
205+
ex:method "${this.model.policyMethod}" ;
206+
ex:url <${this.model.policyURL}> ;
207+
ex:contentType "${this.model.policyContentType}" .
208+
} .
209+
`);
210+
matches.prefixes = this.addIfNotIncluded(
211+
matches.prefixes,
212+
'ex',
213+
'http://example.org/'
214+
);
215+
matches.prefixes = this.addIfNotIncluded(
216+
matches.prefixes,
217+
'fno',
218+
'https://w3id.org/function/ontology#'
219+
);
220+
matches.prefixes = this.addIfNotIncluded(
221+
matches.prefixes,
222+
'pol',
223+
'https://www.example.org/ns/policy#'
224+
);
225+
188226
// Re-add the N3 rules to the resource.
189227
await this.model.addN3RulesToResource(matches);
190228

@@ -220,6 +258,27 @@ export default class IndexController extends Controller {
220258
}
221259
}
222260
});
261+
262+
if (!this.model.policyURL.trim()) {
263+
this.policyURLError = 'Please fill in a URL.';
264+
valid = false;
265+
}
266+
if (!this.model.policyContentType.trim()) {
267+
this.policyContentTypeError = 'Please fill in a Content-Type.';
268+
valid = false;
269+
}
270+
if (
271+
!['GET', 'POST', 'PUT', 'DELETE', 'PATCH'].includes(
272+
this.model.policyMethod
273+
)
274+
) {
275+
this.policyMethodError = 'Please choose a valid HTTP method.';
276+
valid = false;
277+
}
278+
if (!['HTTP'].includes(this.model.policyType)) {
279+
this.policyTypeError = 'Please choose a valid policy type.';
280+
valid = false;
281+
}
223282
return valid;
224283
}
225284

@@ -481,4 +540,47 @@ export default class IndexController extends Controller {
481540
await this.solidAuth.ensureLogout();
482541
this.model.refresh();
483542
}
543+
544+
async isEventSubmitRule(rule, prefixes) {
545+
const options = { blogic: false, outputType: 'string' };
546+
const query = `${prefixes ? prefixes.join('\n') : ''}\n${rule}`;
547+
const reasonerResult = await n3reasoner(
548+
'_:id <http://example.org/event> <http://example.org/Submit> .',
549+
query,
550+
options
551+
);
552+
return reasonerResult.length > 0;
553+
}
554+
addIfNotIncluded(prefixes, prefix, url) {
555+
let alreadyIncluded = false;
556+
prefixes.forEach((p) => {
557+
if (p.includes(prefix) && p.includes(url)) {
558+
alreadyIncluded = true;
559+
}
560+
});
561+
if (!alreadyIncluded) {
562+
prefixes.push(`@prefix ${prefix}: <${url}> .`);
563+
}
564+
return prefixes;
565+
}
566+
567+
@action
568+
updatePolicyType(event) {
569+
this.model.policyType = event.target.value?.trim();
570+
}
571+
572+
@action
573+
updatePolicyURL(event) {
574+
this.model.policyURL = event.target.value?.trim();
575+
}
576+
577+
@action
578+
updatePolicyMethod(event) {
579+
this.model.policyMethod = event.target.value?.trim();
580+
}
581+
582+
@action
583+
updatePolicyContentType(event) {
584+
this.model.policyContentType = event.target.value?.trim();
585+
}
484586
}

app/routes/index.js

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { inject as service } from '@ember/service';
33
import { v4 as uuid } from 'uuid';
44
import { namedNode } from 'rdflib';
55
import { tracked } from '@glimmer/tracking';
6+
import { n3reasoner } from 'eyereasoner';
7+
import { QueryEngine } from '@comunica/query-sparql-solid';
68

79
export default class IndexRoute extends Route {
810
queryParams = {
@@ -21,6 +23,13 @@ export default class IndexRoute extends Route {
2123

2224
@tracked vocabulary = 'http://www.w3.org/ns/shacl#';
2325

26+
engine = new QueryEngine();
27+
28+
@tracked policyType = '';
29+
@tracked policyURL = '';
30+
@tracked policyMethod = '';
31+
@tracked policyContentType = '';
32+
2433
async model({ form }) {
2534
await this.solidAuth.ensureLogin();
2635

@@ -87,6 +96,9 @@ export default class IndexRoute extends Route {
8796
if (removeN3Rules) {
8897
await this.addN3RulesToResource(matches);
8998
}
99+
100+
// Fill in the form with submit event policy values.
101+
await this.fillInFormWithSubmitEventPolicy(matches);
90102
}
91103

92104
initiateNewRdfForm() {
@@ -140,7 +152,6 @@ export default class IndexRoute extends Route {
140152

141153
// Get content.
142154
let text = await response.text();
143-
console.log(text);
144155

145156
// Match prefixes.
146157
const prefixRegex = /(@prefix|PREFIX)\s+[^:]*:\s*<[^>]*>\s*\.?\n/g;
@@ -150,7 +161,6 @@ export default class IndexRoute extends Route {
150161
const rulesRegex =
151162
/\{[^{}]*}\s*(=>|[^\s{}]*implies[^\s{}]*)\s*{[^{}]*}\s*\./g;
152163
const rules = text.match(rulesRegex);
153-
console.log(rules);
154164

155165
// Remove N3 rules.
156166
rules?.forEach((match) => {
@@ -208,4 +218,63 @@ export default class IndexRoute extends Route {
208218
body: text,
209219
});
210220
}
221+
222+
async fillInFormWithSubmitEventPolicy(matches) {
223+
for (const rule of matches.rules) {
224+
const options = { blogic: false, outputType: 'string' };
225+
const query = `${
226+
matches.prefixes ? matches.prefixes.join('\n') : ''
227+
}\n${rule}`;
228+
const reasonerResult = await n3reasoner(
229+
'_:id <http://example.org/event> <http://example.org/Submit> .',
230+
query,
231+
options
232+
);
233+
234+
const queryPolicy = `
235+
PREFIX ex: <http://example.org/>
236+
PREFIX pol: <https://www.example.org/ns/policy#>
237+
PREFIX fno: <https://w3id.org/function/ontology#>
238+
239+
SELECT ?executionTarget ?method ?url ?contentType WHERE {
240+
?id pol:policy ?policy .
241+
?policy a fno:Execution .
242+
?policy fno:executes ?executionTarget .
243+
?policy ex:method ?method .
244+
?policy ex:url ?url .
245+
OPTIONAL { ?policy ex:contentType ?contentType } .
246+
}
247+
`;
248+
const bindings = await (
249+
await this.engine.queryBindings(queryPolicy, {
250+
sources: [
251+
{
252+
type: 'stringSource',
253+
value: reasonerResult,
254+
mediaType: 'text/n3',
255+
baseIRI: this.loadedFormUri,
256+
},
257+
],
258+
})
259+
).toArray();
260+
261+
const policies = bindings.map((row) => {
262+
return {
263+
executionTarget: row.get('executionTarget').value,
264+
method: row.get('method').value,
265+
url: row.get('url').value,
266+
contentType: row.get('contentType')?.value,
267+
};
268+
});
269+
270+
for (const policy of policies) {
271+
if (policy.executionTarget === 'http://example.org/httpRequest') {
272+
this.policyType = 'HTTP';
273+
this.policyURL = policy.url;
274+
this.policyMethod = policy.method;
275+
this.policyContentType = policy.contentType;
276+
}
277+
}
278+
}
279+
}
211280
}

app/templates/index.hbs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,71 @@
114114
</div>
115115
</div>
116116

117+
<hr>
118+
<h6 class="card-subtitle mb-2 text-muted">Fill in the details below about where the data should be stored when someone fill in the form and click submit.</h6>
119+
120+
<div class="mb-3 row">
121+
<label for="policy-type-{{this.model.form.uuid}}" id="label-policy-type-{{this.model.form.uuid}}"
122+
class="col-sm-3 col-form-label">Policy Type</label>
123+
<div class="col-sm-9">
124+
<select class="form-select" id="policy-type-{{this.model.form.uuid}}"
125+
{{ on "change" this.updatePolicyType }}
126+
aria-describedby="label-policy-type-{{this.model.form.uuid}}">
127+
<option value="HTTP" selected={{this.isEqual this.model.policyType "HTTP"}}>HTTP Request</option>
128+
</select>
129+
{{#if this.policyTypeError }}
130+
<small class="text-danger">{{ this.policyTypeError }}</small>
131+
{{/if}}
132+
</div>
133+
</div>
134+
135+
<div class="mb-3 row">
136+
<label for="policy-url-{{this.model.form.uuid}}" id="label-policy-url-{{this.model.form.uuid}}"
137+
class="col-sm-3 col-form-label">Policy URL</label>
138+
<div class="col-sm-9">
139+
<input type="text" class="form-control" id="policy-url-{{this.model.form.uuid}}" placeholder="https://httpbin.org/post"
140+
value={{this.model.policyURL}} {{ on "change" this.updatePolicyURL }}
141+
aria-describedby="label-policy-url-{{this.model.form.uuid}}" />
142+
{{#if this.policyURLError }}
143+
<small class="text-danger">{{ this.policyTypeError }}</small>
144+
{{/if}}
145+
</div>
146+
</div>
147+
148+
<div class="mb-3 row">
149+
<label for="policy-method-{{this.model.form.uuid}}" id="label-policy-method-{{this.model.form.uuid}}"
150+
class="col-sm-3 col-form-label">Policy HTTP Method</label>
151+
<div class="col-sm-9">
152+
<select class="form-select" id="policy-method-{{this.model.form.uuid}}"
153+
{{ on "change" this.updatePolicyMethod }}
154+
aria-describedby="label-policy-method-{{this.model.form.uuid}}">
155+
<option value="POST" selected={{this.isEqual this.model.policyMethod "POST"}}>POST</option>
156+
<option value="PUT" selected={{this.isEqual this.model.policyMethod "PUT"}}>PUT</option>
157+
<option value="GET" selected={{this.isEqual this.model.policyMethod "GET"}}>GET</option>
158+
<option value="PATCH" selected={{this.isEqual this.model.policyMethod "PATCH"}}>PATCH</option>
159+
<option value="DELETE" selected={{this.isEqual this.model.policyMethod "DELETE"}}>DELETE</option>
160+
</select>
161+
{{#if this.policyMethodError }}
162+
<small class="text-danger">{{ this.policyMethodError }}</small>
163+
{{/if}}
164+
</div>
165+
</div>
166+
167+
<div class="mb-3 row">
168+
<label for="policy-content-type-{{this.model.form.uuid}}" id="label-policy-content-type-{{this.model.form.uuid}}"
169+
class="col-sm-3 col-form-label">Policy Content-Type</label>
170+
<div class="col-sm-9">
171+
<input type="text" class="form-control" id="policy-content-type-{{this.model.form.uuid}}" placeholder="application/ld+json"
172+
value={{this.model.policyContentType}} {{ on "change" this.updatePolicyContentType }}
173+
aria-describedby="label-policy-content-type-{{this.model.form.uuid}}" />
174+
{{#if this.policyContentTypeError }}
175+
<small class="text-danger">{{ this.policyContentTypeError }}</small>
176+
{{/if}}
177+
</div>
178+
</div>
179+
180+
<hr>
181+
117182
<SortableObjects @sortableObjectList={{this.fields}} @enableSort={{true}} @useSwap={{false}}
118183
@inPlace={{false}} @sortingScope={{"formFields"}}>
119184
{{#each this.fields as |field|}}

ember-cli-build.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,20 @@ module.exports = function (defaults) {
88
bootstrapVersion: 5,
99
importBootstrapCSS: true,
1010
},
11+
autoImport: {
12+
webpack: {
13+
node: {
14+
global: true,
15+
},
16+
resolve: {
17+
fallback: {
18+
fs: false,
19+
crypto: false,
20+
path: false,
21+
},
22+
},
23+
},
24+
},
1125
});
1226

1327
// Use `app.import` to add additional libraries to the generated

0 commit comments

Comments
 (0)