kibi-core 0.6.0 → 0.6.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kibi-core",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "private": false,
5
5
  "description": "Core Prolog modules and RDF graph logic for Kibi",
6
6
  "type": "module",
@@ -0,0 +1,65 @@
1
+ % Module: derived_chr
2
+ % Internal CHR pilot for bounded derived validation facts.
3
+
4
+ :- module(derived_chr, [
5
+ derive_chr_facts/0,
6
+ clear_chr_facts/0,
7
+ derived_coverage_gap/2,
8
+ derived_symbol_gap/2
9
+ ]).
10
+
11
+ :- use_module(library(chr)).
12
+ :- use_module('kb.pl').
13
+
14
+ :- chr_constraint seen_must_req/1, seen_scenario/1, seen_test/1,
15
+ seen_production_symbol/1, seen_symbol_req_coverage/1.
16
+
17
+ :- dynamic derived_coverage_gap/2.
18
+ :- dynamic derived_symbol_gap/2.
19
+ :- dynamic snapshot_must_req/1.
20
+ :- dynamic snapshot_scenario/1.
21
+ :- dynamic snapshot_test/1.
22
+ :- dynamic snapshot_production_symbol/1.
23
+ :- dynamic snapshot_symbol_req_coverage/1.
24
+
25
+ seen_must_req(Req) <=> assertz(snapshot_must_req(Req)).
26
+ seen_scenario(Req) <=> assertz(snapshot_scenario(Req)).
27
+ seen_test(Req) <=> assertz(snapshot_test(Req)).
28
+ seen_production_symbol(Symbol) <=> assertz(snapshot_production_symbol(Symbol)).
29
+ seen_symbol_req_coverage(Symbol) <=> assertz(snapshot_symbol_req_coverage(Symbol)).
30
+
31
+ clear_chr_facts :-
32
+ retractall(derived_coverage_gap(_, _)),
33
+ retractall(derived_symbol_gap(_, _)),
34
+ retractall(snapshot_must_req(_)),
35
+ retractall(snapshot_scenario(_)),
36
+ retractall(snapshot_test(_)),
37
+ retractall(snapshot_production_symbol(_)),
38
+ retractall(snapshot_symbol_req_coverage(_)).
39
+
40
+ derive_chr_facts :-
41
+ clear_chr_facts,
42
+ forall(kb:must_requirement(Req), seen_must_req(Req)),
43
+ forall(kb:has_scenario(Req), seen_scenario(Req)),
44
+ forall(kb:has_test(Req), seen_test(Req)),
45
+ forall(kb:production_symbol(Symbol), seen_production_symbol(Symbol)),
46
+ forall(kb:production_symbol_covered_for_requirement(Symbol, _), seen_symbol_req_coverage(Symbol)),
47
+ derive_coverage_gaps,
48
+ derive_symbol_gaps.
49
+
50
+ derive_coverage_gaps :-
51
+ forall(
52
+ ( snapshot_must_req(Req),
53
+ \+ snapshot_scenario(Req),
54
+ \+ snapshot_test(Req)
55
+ ),
56
+ assertz(derived_coverage_gap(Req, missing_scenario_and_test))
57
+ ).
58
+
59
+ derive_symbol_gaps :-
60
+ forall(
61
+ ( snapshot_production_symbol(Symbol),
62
+ \+ snapshot_symbol_req_coverage(Symbol)
63
+ ),
64
+ assertz(derived_symbol_gap(Symbol, no_qualifying_production_coverage))
65
+ ).
package/src/discovery.pl CHANGED
@@ -8,6 +8,7 @@
8
8
  ]).
9
9
 
10
10
  :- use_module(library(http/json)).
11
+ :- use_module(library(aggregate)).
11
12
  :- use_module('kb.pl').
12
13
  :- use_module('status.pl', [status_meta_dict/1]).
13
14
  :- use_module('../schema/relationships.pl', [relationship_type/1]).
@@ -90,10 +91,9 @@ relationship_counts(Id, CountsDict) :-
90
91
  dict_pairs(CountsDict, relationship_counts, Pairs).
91
92
 
92
93
  relationship_count(Id, Relationship, Count) :-
93
- findall(1,
94
+ aggregate_all(count,
94
95
  (kb_relationship(Relationship, Id, _); kb_relationship(Relationship, _, Id)),
95
- Matches),
96
- length(Matches, Count).
96
+ Count).
97
97
 
98
98
  coverage_rows(req, Tags, IncludePassing, IncludeTransitive, Rows, Summary) :-
99
99
  !,
@@ -0,0 +1,150 @@
1
+ % Module: kibi_sparql_client
2
+ % Explicit remote SPARQL client wrapper.
3
+
4
+ :- module(kibi_sparql_client, [
5
+ remote_sparql_select_json/4
6
+ ]).
7
+
8
+ :- use_module(library(error)).
9
+ :- use_module(library(http/json)).
10
+ :- use_module(library(semweb/sparql_client)).
11
+
12
+ remote_sparql_select_json(Endpoint, Query, Options, JsonString) :-
13
+ validate_non_empty_atom(endpoint, Endpoint),
14
+ validate_non_empty_atom(query, Query),
15
+ validate_remote_endpoint(Endpoint),
16
+ validate_select_query(Query),
17
+ validate_sparql_options(Options, SparqlOptions),
18
+ sparql_query(Query, Rows, [endpoint(Endpoint), variable_names(Variables)|SparqlOptions]),
19
+ maplist(row_json(Variables), Rows, JsonRows),
20
+ with_output_to(string(JsonString), json_write_dict(current_output, _{rows: JsonRows}, [])).
21
+
22
+ validate_non_empty_atom(Name, Value) :-
23
+ must_be(atom, Value),
24
+ ( Value == ''
25
+ -> throw(error(domain_error(non_empty_atom, Name), _))
26
+ ; true
27
+ ).
28
+
29
+ validate_remote_endpoint(Endpoint) :-
30
+ ( sub_atom(Endpoint, 0, _, _, 'http://')
31
+ ; sub_atom(Endpoint, 0, _, _, 'https://')
32
+ ),
33
+ endpoint_host(Endpoint, Host),
34
+ \+ local_or_private_host(Host),
35
+ !.
36
+ validate_remote_endpoint(Endpoint) :-
37
+ ( sub_atom(Endpoint, 0, _, _, 'http://')
38
+ ; sub_atom(Endpoint, 0, _, _, 'https://')
39
+ ),
40
+ !,
41
+ throw(error(domain_error(public_remote_endpoint, Endpoint), _)).
42
+ validate_remote_endpoint(Endpoint) :-
43
+ throw(error(domain_error(remote_http_endpoint, Endpoint), _)).
44
+
45
+ validate_select_query(Query) :-
46
+ normalize_space(atom(Normalized), Query),
47
+ downcase_atom(Normalized, Lower),
48
+ sub_atom(Lower, 0, 6, After, 'select'),
49
+ ( After =:= 0
50
+ ; sub_atom(Lower, 6, 1, _, ' ')
51
+ ; sub_atom(Lower, 6, 1, _, '*')
52
+ ),
53
+ !.
54
+ validate_select_query(Query) :-
55
+ throw(error(domain_error(sparql_select_query, Query), _)).
56
+
57
+ endpoint_host(Endpoint, Host) :-
58
+ strip_endpoint_scheme(Endpoint, WithoutScheme),
59
+ segment_before(WithoutScheme, '/', WithoutPath),
60
+ segment_before(WithoutPath, '?', WithoutQuery),
61
+ segment_before(WithoutQuery, '#', Authority),
62
+ strip_userinfo(Authority, HostWithPort),
63
+ strip_port(HostWithPort, Host0),
64
+ downcase_atom(Host0, Host).
65
+
66
+ strip_endpoint_scheme(Endpoint, WithoutScheme) :-
67
+ sub_atom(Endpoint, 0, 7, _, 'http://'),
68
+ !,
69
+ sub_atom(Endpoint, 7, _, 0, WithoutScheme).
70
+ strip_endpoint_scheme(Endpoint, WithoutScheme) :-
71
+ sub_atom(Endpoint, 0, 8, _, 'https://'),
72
+ sub_atom(Endpoint, 8, _, 0, WithoutScheme).
73
+
74
+ segment_before(Atom, Delimiter, Segment) :-
75
+ sub_atom(Atom, Before, _, _, Delimiter),
76
+ !,
77
+ sub_atom(Atom, 0, Before, _, Segment).
78
+ segment_before(Atom, _Delimiter, Atom).
79
+
80
+ strip_userinfo(Authority, HostWithPort) :-
81
+ sub_atom(Authority, _, _, After, '@'),
82
+ !,
83
+ sub_atom(Authority, _, After, 0, HostWithPort).
84
+ strip_userinfo(Authority, Authority).
85
+
86
+ strip_port(HostWithPort, Host) :-
87
+ sub_atom(HostWithPort, 0, 1, _, '['),
88
+ sub_atom(HostWithPort, 1, Length, _, ']'),
89
+ !,
90
+ sub_atom(HostWithPort, 1, Length, _, Host).
91
+ strip_port(HostWithPort, Host) :-
92
+ segment_before(HostWithPort, ':', Host).
93
+
94
+ local_or_private_host(localhost).
95
+ local_or_private_host('0.0.0.0').
96
+ local_or_private_host('::1').
97
+ local_or_private_host(Host) :-
98
+ sub_atom(Host, _, _, 0, '.localhost').
99
+ local_or_private_host(Host) :-
100
+ sub_atom(Host, 0, _, _, '127.').
101
+ local_or_private_host(Host) :-
102
+ sub_atom(Host, 0, _, _, '10.').
103
+ local_or_private_host(Host) :-
104
+ sub_atom(Host, 0, _, _, '192.168.').
105
+ local_or_private_host(Host) :-
106
+ sub_atom(Host, 0, _, _, '169.254.').
107
+ local_or_private_host(Host) :-
108
+ sub_atom(Host, 0, _, _, 'fe80:').
109
+ local_or_private_host(Host) :-
110
+ sub_atom(Host, 0, 2, _, Prefix),
111
+ memberchk(Prefix, ['fc', 'fd']).
112
+ local_or_private_host(Host) :-
113
+ sub_atom(Host, 0, _, _, '172.'),
114
+ atomic_list_concat([_, SecondAtom|_], '.', Host),
115
+ atom_number(SecondAtom, Second),
116
+ Second >= 16,
117
+ Second =< 31.
118
+
119
+ validate_sparql_options([], []).
120
+ validate_sparql_options([result_format(json)|Rest], SparqlOptions) :-
121
+ !,
122
+ validate_sparql_options(Rest, SparqlOptions).
123
+ validate_sparql_options([timeout(Timeout)|Rest], [timeout(Timeout)|SparqlOptions]) :-
124
+ !,
125
+ validate_sparql_options(Rest, SparqlOptions).
126
+ validate_sparql_options([Option|_], _) :-
127
+ throw(error(domain_error(sparql_client_option, Option), _)).
128
+
129
+ row_json(Variables, Row, Dict) :-
130
+ Row =.. [row|Values],
131
+ pairs_json(Variables, Values, Pairs),
132
+ dict_create(Dict, row, Pairs).
133
+
134
+ pairs_json([], [], []).
135
+ pairs_json([Variable|Variables], [Value|Values], [Variable-JsonValue|Pairs]) :-
136
+ sparql_value_json(Value, JsonValue),
137
+ pairs_json(Variables, Values, Pairs).
138
+
139
+ sparql_value_json(Value, JsonValue) :-
140
+ atomic(Value),
141
+ !,
142
+ JsonValue = Value.
143
+ sparql_value_json(literal(Value), JsonValue) :-
144
+ !,
145
+ sparql_value_json(Value, JsonValue).
146
+ sparql_value_json(type(_Type, Value), JsonValue) :-
147
+ !,
148
+ sparql_value_json(Value, JsonValue).
149
+ sparql_value_json(Value, JsonValue) :-
150
+ term_string(Value, JsonValue).