-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathrepo.go
148 lines (130 loc) · 3.58 KB
/
repo.go
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
package sparql
import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strconv"
"strings"
"time"
"github.com/knakk/digest"
"github.com/knakk/rdf"
)
// Repo represent a RDF repository, assumed to be
// queryable via the SPARQL protocol over HTTP.
type Repo struct {
endpoint string
client *http.Client
}
// NewRepo creates a new representation of a RDF repository. It takes a
// variadic list of functional options which can alter the configuration
// of the repository.
func NewRepo(addr string, options ...func(*Repo) error) (*Repo, error) {
r := Repo{
endpoint: addr,
client: http.DefaultClient,
}
return &r, r.SetOption(options...)
}
// SetOption takes one or more option function and applies them in order to Repo.
func (r *Repo) SetOption(options ...func(*Repo) error) error {
for _, opt := range options {
if err := opt(r); err != nil {
return err
}
}
return nil
}
// DigestAuth configures Repo to use digest authentication on HTTP requests.
func DigestAuth(username, password string) func(*Repo) error {
return func(r *Repo) error {
r.client.Transport = digest.NewTransport(username, password)
return nil
}
}
// Timeout instructs the underlying HTTP transport to timeout after given duration.
func Timeout(t time.Duration) func(*Repo) error {
return func(r *Repo) error {
r.client.Timeout = t
return nil
}
}
// Query performs a SPARQL HTTP request to the Repo, and returns the
// parsed application/sparql-results+json response.
func (r *Repo) Query(q string) (*Results, error) {
form := url.Values{}
form.Set("query", q)
b := form.Encode()
// TODO make optional GET or Post, Query() should default GET (idempotent, cacheable)
// maybe new for updates: func (r *Repo) Update(q string) using POST?
req, err := http.NewRequest(
"POST",
r.endpoint,
bytes.NewBufferString(b))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("Content-Length", strconv.Itoa(len(b)))
req.Header.Set("Accept", "application/sparql-results+json")
resp, err := r.client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
b, err := ioutil.ReadAll(resp.Body)
var msg string
if err != nil {
msg = "Failed to read response body"
} else {
if strings.TrimSpace(string(b)) != "" {
msg = "Response body: \n" + string(b)
}
}
return nil, fmt.Errorf("Query: SPARQL request failed: %s. "+msg, resp.Status)
}
results, err := ParseJSON(resp.Body)
if err != nil {
return nil, err
}
return results, nil
}
// Construct performs a SPARQL HTTP request to the Repo, and returns the
// result triples.
func (r *Repo) Construct(q string) ([]rdf.Triple, error) {
form := url.Values{}
form.Set("query", q)
form.Set("format", "text/turtle")
b := form.Encode()
req, err := http.NewRequest(
"POST",
r.endpoint,
bytes.NewBufferString(b))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("Content-Length", strconv.Itoa(len(b)))
req.Header.Set("Accept", "text/turtle")
resp, err := r.client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
b, err := ioutil.ReadAll(resp.Body)
var msg string
if err != nil {
msg = "Failed to read response body"
} else {
if strings.TrimSpace(string(b)) != "" {
msg = "Response body: \n" + string(b)
}
}
return nil, fmt.Errorf("Construct: SPARQL request failed: %s. "+msg, resp.Status)
}
dec := rdf.NewTripleDecoder(resp.Body, rdf.Turtle)
return dec.DecodeAll()
}