-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathSimpleClient.js
192 lines (175 loc) · 5.73 KB
/
SimpleClient.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
import defaultFetch from 'nodeify-fetch'
import mergeHeaders from './lib/mergeHeaders.js'
import RawQuery from './RawQuery.js'
/**
* A client implementation based on {@link RawQuery} that prepares URLs and headers for SPARQL queries and returns the
* raw fetch response. It does not provide a store interface.
*
* @property {RawQuery} query
* @property {string} endpointUrl
* @property {RawQuery} factory
* @property {factory} fetch
* @property {Headers} headers
* @property {string} password
* @property {string} storeUrl
* @property {string} updateUrl
* @property {string} user
* @property {string} updateUrl
*
* @example
* // read the height of the Eiffel Tower from Wikidata with a SELECT query
*
* import SparqlClient from 'sparql-http-client/SimpleClient.js'
*
* const endpointUrl = 'https://query.wikidata.org/sparql'
* const query = `
* PREFIX wd: <http://www.wikidata.org/entity/>
* PREFIX p: <http://www.wikidata.org/prop/>
* PREFIX ps: <http://www.wikidata.org/prop/statement/>
* PREFIX pq: <http://www.wikidata.org/prop/qualifier/>
*
* SELECT ?value WHERE {
* wd:Q243 p:P2048 ?height.
*
* ?height pq:P518 wd:Q24192182;
* ps:P2048 ?value .
* }`
*
* const client = new SparqlClient({ endpointUrl })
* const res = await client.query.select(query)
*
* if (!res.ok) {
* return console.error(res.statusText)
* }
*
* const content = await res.json()
*
* console.log(JSON.stringify(content, null, 2))
*/
class SimpleClient {
/**
* @param {Object} options
* @param {string} [options.endpointUrl] SPARQL query endpoint URL
* @param {factory} [options.factory] RDF/JS factory
* @param {fetch} [options.fetch=nodeify-fetch] fetch implementation
* @param {Headers} [options.headers] headers sent with every request
* @param {string} [options.password] password used for basic authentication
* @param {string} [options.storeUrl] SPARQL Graph Store URL
* @param {string} [options.updateUrl] SPARQL update endpoint URL
* @param {string} [options.user] user used for basic authentication
* @param {Query} [options.Query] Constructor of a query implementation
* @param {Store} [options.Store] Constructor of a store implementation
*/
constructor ({
endpointUrl,
factory,
fetch = defaultFetch,
headers,
password,
storeUrl,
updateUrl,
user,
Query = RawQuery,
Store
}) {
if (!endpointUrl && !storeUrl && !updateUrl) {
throw new Error('no endpointUrl, storeUrl, or updateUrl given')
}
this.endpointUrl = endpointUrl
this.factory = factory
this.fetch = fetch
this.headers = new Headers(headers)
this.password = password
this.storeUrl = storeUrl
this.updateUrl = updateUrl
this.user = user
this.query = Query ? new Query({ client: this }) : null
this.store = Store ? new Store({ client: this }) : null
if (typeof user === 'string' && typeof password === 'string') {
this.headers.set('authorization', `Basic ${btoa(`${user}:${password}`)}`)
}
}
/**
* Sends a GET request as defined in the
* {@link https://www.w3.org/TR/2013/REC-sparql11-protocol-20130321/#query-via-get SPARQL Protocol specification}.
*
* @param {string} query SPARQL query
* @param {Object} options
* @param {Headers} [options.headers] additional request headers
* @param {boolean} [options.update=false] send the request to the updateUrl
* @return {Promise<Response>}
*/
async get (query, { headers, update = false } = {}) {
let url = null
if (!update) {
url = new URL(this.endpointUrl)
url.searchParams.append('query', query)
} else {
url = new URL(this.updateUrl)
url.searchParams.append('update', query)
}
return this.fetch(url.toString().replace(/\+/g, '%20'), {
method: 'GET',
headers: mergeHeaders(this.headers, headers)
})
}
/**
* Sends a POST directly request as defined in the
* {@link https://www.w3.org/TR/2013/REC-sparql11-protocol-20130321/#query-via-post-direct SPARQL Protocol specification}.
*
*
* @param {string} query SPARQL query
* @param {Object} options
* @param {Headers} [options.headers] additional request headers
* @param {boolean} [options.update=false] send the request to the updateUrl
* @return {Promise<Response>}
*/
async postDirect (query, { headers, update = false } = {}) {
let url = null
if (!update) {
url = new URL(this.endpointUrl)
} else {
url = new URL(this.updateUrl)
}
headers = mergeHeaders(this.headers, headers)
if (!headers.has('content-type')) {
headers.set('content-type', 'application/sparql-query; charset=utf-8')
}
return this.fetch(url, {
method: 'POST',
headers,
body: query
})
}
/**
* Sends a POST URL-encoded request as defined in the
* {@link https://www.w3.org/TR/2013/REC-sparql11-protocol-20130321/#query-via-post-urlencoded SPARQL Protocol specification}.
*
* @param {string} query SPARQL query
* @param {Object} options
* @param {Headers} [options.headers] additional request headers
* @param {boolean} [options.update=false] send the request to the updateUrl
* @return {Promise<Response>}
*/
async postUrlencoded (query, { headers, update = false } = {}) {
let url = null
let body = null
if (!update) {
url = new URL(this.endpointUrl)
body = `query=${encodeURIComponent(query)}`
} else {
url = new URL(this.updateUrl)
body = `update=${encodeURIComponent(query)}`
}
headers = mergeHeaders(this.headers, headers)
if (!headers.has('content-type')) {
headers.set('content-type', 'application/x-www-form-urlencoded')
}
return this.fetch(url, {
method: 'POST',
headers,
body
})
}
}
export default SimpleClient