README.md 9.72 KB
Newer Older
Élodie Thiéblin's avatar
Élodie Thiéblin committed
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
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
# cwclientlibjs - client library for CubicWeb's rqlcontroller

Javascript clone of the cwclientlib python library that wraps the rqlcontroller API offered by the cubicweb-rqlcontroller cube.

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).

## Client.ts

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).

[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 user has at least an enabled token linked to his/her account.

[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 a Authorization header with the connection token and its hashed value.

```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']}]
});
```

## Providers.ts

The [provider](src/providers.ts) namespace provides two main objects: [EntitySchemaProvider](#entityschemaprovider) and [EntityProvider](#entityprovider).

### EntitySchemaProvider

A [RqlEntitySchemaProvider](src/providers.ts#L593) provides an interface to load a CubicWeb instance Schema.

A EntitySchemaProvider provides the following functions:

-   **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
-   **load()**: Loads the Schema

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.

-   **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.

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.

```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:

-   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.

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:

-   [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

Each EntityProvider provides the following functions:

-   **getEntity(eid:number)**: Gets an entity given its eid
-   **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.

```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);
});
```

An entity will be a json object, whose keys are defined by its EntitySchema (CWEType)

```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

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.