README.md 10.7 KB
Newer Older
Élodie Thiéblin's avatar
Élodie Thiéblin committed
1
2
# cwclientlibjs - client library for CubicWeb's rqlcontroller

3
4
Javascript clone of the cwclientlib python library that wraps the rqlcontroller
API offered by the cubicweb-rqlcontroller cube.
Élodie Thiéblin's avatar
Élodie Thiéblin committed
5

6
7
To read about the rql client, go to [Section Client.ts](#client.ts). To read
about the providers, go to [Section Providers.ts](#providers.ts).
Élodie Thiéblin's avatar
Élodie Thiéblin committed
8

Arthur Lutz's avatar
Arthur Lutz committed
9
10
For internal use : [sonarqube analysis](https://sonarqube.k.intra.logilab.fr/dashboard?id=open-source-cwclientlibjs)

Élodie Thiéblin's avatar
Élodie Thiéblin committed
11
12
## Client.ts

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
The [client](src/client.ts) namespace provides helpers to run RQL queries on a
cubicweb instance with the rqlcontroller cube.

The class [CwRqlClient](src/client.ts#L690) implements the interface
[RqlClient](src/client.ts#L644) and provides the following functions:

-   **queryRows(query:string, params?:any)**: runs query on the cubicweb
    instance and returns the results as [RqlRows](src/client.ts#L553) in a
    Promise. This function calls transactionV1
-   **queryBindings(query:string, params?)**: runs query on the cubicweb
    instance with and returns the results as [RqlSolutions](src/client.ts#L586)
    in a Promise. This function calls transactionV2
-   **queryAndTransform(query:string, params?,viewId)**: Build the url
    corresponding to the query with a given viewId and returns the content in a
    Promise
-   **transactionV1(queries)**: runs a set of queries with the rqlio/1.0
    interface and returns the query results or throws an error if something went
    wrong
-   **transactionV2(queries)**: runs a set of queries with the rqlio/2.0
    interface and returns the query results as
    [RqlIoV2TransactionResponse](src/client.ts#L576)

A RqlClient relies on an HttpClient. Two implementations are available
[CwSimpleHttpClient](src/client.ts#L332) and
[CwSigningHttpClient](src/client.ts#L462).

39
40
41

###  CwSimpleHttpClient: anonymous request

42
43
44
45
46
47
[CwSimpleHttpClient](src/client.ts#L332) requires a baseUrl (the base url of a
cubicweb instance). A boolean can be added to allow cross origin requests. The
CwSimpleHttpClient can perform a doLogin(login,password) operation on the
cubicweb instance and perform the queries by using cookies (see [Known
issues](#known-issues)). It can also be transformed into a CwSigningHttpClient
(toSigningClient()) after a doLogin() operation. It requires that the CubicWeb
48
49
user has at least an enabled token linked to his/her account. The CSRF token
will be included if `allowsCrossOrigin` is false (the default).
50

Élodie Thiéblin's avatar
Élodie Thiéblin committed
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
```javascript
import {providers, client} from '@logilab/cwclientlibjs';

// url is the base url of your cubicweb instance
const url = 'http://my-cubicweb-instance-base-url/';

const rqlClient = new client.CwRqlClient(
    new client.CwSimpleHttpClient(url, true)
);

const query = 'Any X, Y WHERE X is BlogEntry, X entry_of Y';

rqlClient.queryRows(query).then(res => {
    console.log(res); // [[123,1], [234, 2]]
});

rqlClient.queryBindings(query).then(res => {
    console.log(res); // [{'X': 123, 'Y': 1}, {'X':234, 'Y':2}]
});

rqlClient.queryAndTransform(query, 'rdf').then(res => {
    console.log(res); // equivalent to http://my-cubicweb-instance-base-url/view?rql=Any X, Y WHERE X is BlogEntry, X entry_of Y &vid=rdf
});

rqlClient.transactionV1([query]).then(res => {
    console.log(res); // [[[123,1], [234, 2]]]
});

rqlClient.transactionV2([query]).then(res => {
    console.log(res); // [{rows: [[123,1], [234, 2]], variables : ['X','Y']}]
});
```

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
### CwSigningHttpClient: authenticated requests.

[CwSigningHttpClient](src/client.ts#L462) requires a baseUrl (the base url of a
cubicweb instance), a tokenName, a tokenValue and a hashMethod code. Each
request will contain an Authorization header with the connection token and its
hashed value.

These tokens are managed on your CubicWeb instance under Profile > Actions > Add
> Authentication Token.

```javascript
import {providers, client} from '@logilab/cwclientlibjs';

// url is the base url of your cubicweb instance
const url = 'http://my-cubicweb-instance-base-url/';

const rqlClient = new client.CwRqlClient(
    new client.CwSimpleHttpClient(url, "my-token-name", "1a2b3c4d5e6f...")
);

const query = 'Any X, Y WHERE X is BlogEntry, X entry_of Y';

rqlClient.queryRows(query).then(res => {
    console.log(res); // [[123,1], [234, 2]]
});
```

Élodie Thiéblin's avatar
Élodie Thiéblin committed
111
112
## Providers.ts

113
114
115
The [provider](src/providers.ts) namespace provides two main objects:
[EntitySchemaProvider](#entityschemaprovider) and
[EntityProvider](#entityprovider).
Élodie Thiéblin's avatar
Élodie Thiéblin committed
116
117
118

### EntitySchemaProvider

119
120
A [RqlEntitySchemaProvider](src/providers.ts#L593) provides an interface to load
a CubicWeb instance Schema.
Élodie Thiéblin's avatar
Élodie Thiéblin committed
121
122
123

A EntitySchemaProvider provides the following functions:

124
125
126
127
128
129
130
131
-   **getAllRelationsFor(typeEid:number)**: Outputs two lists: relationsFrom and
    relationsTo relative to the CWEType referenced by typeEid
-   **getEntitySchemaByName(typeName: string)**: Retrieves a CWEType (as
    EntitySchema) from its name
-   **getEntitySchemaById(typeEid: number)** : Retrieves a CWEType (as
    EntitySchema) from its eid
-   **getEntitySchemaFor(eid:number)** : Retrieves a CWEType (as EntitySchema)
    for a given instance number
Élodie Thiéblin's avatar
Élodie Thiéblin committed
132
133
-   **load()**: Loads the Schema

134
135
136
137
138
An RqlEntitySchemaProvider can instantiate a
[LocalStorageEntitySchemasLoader](src/providers.ts#L259) which will store the
schema in the browser's window.localStorage, if given a 'storageKey' value.
Additionnaly, two functions: **isCacheUsable** and **onRetrieved** can be given
to the RqlEntitySchemaProvider, they can be used to deal with schema versioning.
Élodie Thiéblin's avatar
Élodie Thiéblin committed
139

140
141
142
143
144
145
146
-   **isCacheUsable** is called in a LocalStorageEntitySchemasLoader, it takes a
    `providers.Schema` as input and outputs a boolean Promise. If the promise is
    resolved with the value `true`, the schema will be loaded from the
    localStorage. Otherwise, if will be queried and loaded from the cubicweb
    instance again.
-   **onRetrieved** is called after the schema has been queried and loaded from
    the cubicweb instance.
Élodie Thiéblin's avatar
Élodie Thiéblin committed
147

148
149
150
If no 'storageKey' value is given, a
[RqlEntitySchemasLoader](src/providers.ts#L340) will be instantiated and the
schema will be queried at every call of the `load` function.
Élodie Thiéblin's avatar
Élodie Thiéblin committed
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

```javascript
import {providers, client} from '@logilab/cwclientlibjs';

// url is the base url of your cubicweb instance
const url = 'http://my-cubicweb-instance-base-url/';

const rqlClient = new client.CwRqlClient(new client.CwSimpleHttpClient(url));

// Schema will not be cached
const schemaProvider = new providers.RqlEntitySchemaProvider(rqlClient);

// Schema will be cached
const schemaProviderWithLocalStorage = new providers.RqlEntitySchemaProvider(
    rqlClient,
    'mySchemaStorageKey'
);

// load a schema
schemaProvider.load().then((schema: providers.Schema) => {
    console.log(schema);
});

// load a schema
schemaProviderWithLocalStorage.load().then((schema: providers.Schema) => {
    console.log(schema);
});
```

A Schema is a json object with two entries:

182
183
184
185
-   a **metadata** object which is computed on the schema load. At the moment,
    the metadata contains the schema loading timestamp.
-   **entities**: a list of EntitySchema, each EntitySchema describes a CWEType,
    its attributes and to and from relations.
Élodie Thiéblin's avatar
Élodie Thiéblin committed
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237

Example instanciation of a schema object:

```json
{
    "metadata": {
        "timestamp": "2020-04-20T09:35:49.183Z"
    },
    "entities": [
        {
            "eid": 30,
            "name": "BlogEntry",
            "modificationDate": "2009/09/01 12:15:04",
            "specializes": null,
            "attributes": [
                {"eid": 78, "name": "title", "type": "String", "cardinality": 2}
            ],
            "relationsTo": [
                {
                    "eid": 6055,
                    "name": "entry_of",
                    "from": 30,
                    "fromName": "BlogEntry",
                    "fromCardinality": 1,
                    "to": 6044,
                    "toName": "Blog",
                    "toCardinality": 1,
                    "description": ""
                }
            ],
            "relationsFrom": [
                {
                    "eid": 711,
                    "name": "tags",
                    "from": 61,
                    "fromName": "Tag",
                    "fromCardinality": 1,
                    "to": 30,
                    "toName": "BlogEntry",
                    "toCardinality": 1,
                    "description": "indicates that an entity is classified by a given tag"
                }
            ]
        }
    ]
}
```

### EntityProvider

Three entity Providers:

238
239
240
241
242
243
244
-   [RqlEntityProvider](src/providers.ts#L682): Provides access to entities
    through a RQL enpoint. An EntitySchemaProvider can be specified on
    implementation, for example to cache the Schema in the localStorage.
-   [EJsonExportEntityProvider](src/providers.ts#L812): Provides access to
    entities through the ejsonexport view of a RQL endpoint
-   [CachedEntityProvider](src/providers.ts#L859): Caches the access to entities
    provided by another provider
Élodie Thiéblin's avatar
Élodie Thiéblin committed
245
246
247
248

Each EntityProvider provides the following functions:

-   **getEntity(eid:number)**: Gets an entity given its eid
249
250
251
252
253
254
-   **getEntityGivenSchema(eid:number, entitySchema: EntitySchema)**: Gets an
    entity given its expected entitySchema (CWEType, list of attributes,
    relations to and from)
-   **getEntityTitleGivenSchema(eid: number, entitySchema EntitySchema)** : Gets
    the title for an entity, i.e., the value of the first attribute in the list
    of the entitySchema attributes.
Élodie Thiéblin's avatar
Élodie Thiéblin committed
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283

```javascript
import {providers, client} from '@logilab/cwclientlibjs';

// url is the base url of your cubicweb instance
const url = 'http://my-cubicweb-instance-base-url/';

const rqlClient = new client.CwRqlClient(new client.CwSimpleHttpClient(url));

// Schema will not be cached
const schemaProvider = new providers.RqlEntitySchemaProvider(
    rqlClient,
    'storageKey'
);

const rqlEntityProvider = new providers.RqlEntityProvider(
    rqlClient,
    schemaProvider
);
const eJsonEntityProvider = new providers.EJsonExportEntityProvider(rqlClient);
const cachedEntityProvider = new providers.CachedEntityProvider(
    rqlEntityProvider
);

cachedEntityProvider.getEntity(123456).then(entity => {
    console.log(entity);
});
```

284
285
An entity will be a json object, whose keys are defined by its EntitySchema
(CWEType)
Élodie Thiéblin's avatar
Élodie Thiéblin committed
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303

```json
{
    "@eid": 123456,
    "@type": "BlogEntry",
    "title": "Super Blog Post",
    "entry_of_BlogEntry_Blog": [11111],
    "tags_Tag_BlogEntry": [765432, 99999],
    "modification_date": "2010/03/01 18:45:52"
}
```

## Tests

Running the test suite requires nodejs, mocha and xhr2.

## Known issues

304
305
306
307
There seems to be a problem with Cookie handling: in the [doRequestFetch
function](src/client.ts#L107). The headers of the request seem to be empty for
every response. Therefore, the login functionality is broken for the moment as
it requires to access the `Set-Cookie` fields of HTTP Responses.