@qlik/api 0.0.4 → 0.0.5
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/README.md +83 -2
- package/api-keys.d.mts +1 -1
- package/api-keys.d.ts +1 -1
- package/api-keys.js +10 -10
- package/api-keys.mjs +2 -2
- package/apps.d.mts +1 -1
- package/apps.d.ts +1 -1
- package/apps.js +46 -46
- package/apps.mjs +2 -2
- package/audits.d.mts +1 -1
- package/audits.d.ts +1 -1
- package/audits.js +9 -9
- package/audits.mjs +2 -2
- package/auth-6VOJ5YIL.js +41 -0
- package/{auth-CPBNNC7S.mjs → auth-7HMK2Q6F.mjs} +1 -1
- package/auth.js +3 -3
- package/auth.mjs +2 -2
- package/automations.d.mts +1 -1
- package/automations.d.ts +1 -1
- package/automations.js +21 -21
- package/automations.mjs +2 -2
- package/brands.d.mts +1 -1
- package/brands.d.ts +1 -1
- package/brands.js +15 -15
- package/brands.mjs +2 -2
- package/{chunk-D52VW5UN.js → chunk-2II4JNZO.js} +17 -13
- package/{chunk-GV5SRHY2.js → chunk-2NSUC46G.js} +4 -4
- package/{chunk-7RY3NO6N.js → chunk-7PAVHCQG.js} +2 -2
- package/{chunk-GNWU6SP2.mjs → chunk-EDRFS7GY.mjs} +1 -1
- package/{chunk-3SVRHJII.mjs → chunk-GLWHVX4M.mjs} +3 -3
- package/{chunk-2STFUQKQ.js → chunk-JZAPZIPR.js} +3 -3
- package/{chunk-TE7XMBT5.mjs → chunk-PUBPF4EZ.mjs} +1 -1
- package/{chunk-CYRLVHU3.mjs → chunk-QLS6D4AX.mjs} +17 -13
- package/{chunk-67GJTTPV.mjs → chunk-UXRTUKY5.mjs} +1 -1
- package/{chunk-JNGURO23.js → chunk-XJKF347C.js} +3 -3
- package/collections.d.mts +1 -1
- package/collections.d.ts +1 -1
- package/collections.js +14 -14
- package/collections.mjs +2 -2
- package/csp-origins.d.mts +1 -1
- package/csp-origins.d.ts +1 -1
- package/csp-origins.js +9 -9
- package/csp-origins.mjs +2 -2
- package/data-assets.d.mts +1 -1
- package/data-assets.d.ts +1 -1
- package/data-assets.js +8 -8
- package/data-assets.mjs +2 -2
- package/data-connections.d.mts +1 -1
- package/data-connections.d.ts +1 -1
- package/data-connections.js +12 -12
- package/data-connections.mjs +2 -2
- package/data-credentials.d.mts +1 -1
- package/data-credentials.d.ts +1 -1
- package/data-credentials.js +7 -7
- package/data-credentials.mjs +2 -2
- package/data-files.d.mts +1 -1
- package/data-files.d.ts +1 -1
- package/data-files.js +15 -15
- package/data-files.mjs +2 -2
- package/docs/authentication.md +220 -0
- package/docs/examples/create-app.md +51 -0
- package/docs/examples/create-session-app.md +88 -0
- package/docs/examples/fetch-spaces.md +104 -0
- package/docs/examples/show-sheet-list.md +81 -0
- package/docs/examples.md +10 -2
- package/docs/features.md +129 -0
- package/docs/qix.md +85 -0
- package/docs/rest.md +64 -0
- package/{enigma-session-XPXDQAIV.js → enigma-session-2OXUKTWL.js} +4 -4
- package/{enigma-session-ZH6MYA54.mjs → enigma-session-L4P4IJWW.mjs} +1 -1
- package/{esm-OY4XVIJB.mjs → esm-HMY2LVVZ.mjs} +1 -1
- package/{esm-3VXX2GKL.js → esm-PXQUPJYJ.js} +1 -1
- package/extensions.d.mts +1 -1
- package/extensions.d.ts +1 -1
- package/extensions.js +10 -10
- package/extensions.mjs +2 -2
- package/{global.types-hIvp-WdX.d.ts → global.types-1rNhDH7j.d.ts} +9 -0
- package/{global.types-Eto4uYwy.d.mts → global.types-4jzAvW7R.d.mts} +9 -0
- package/glossaries.d.mts +1 -1
- package/glossaries.d.ts +1 -1
- package/glossaries.js +27 -27
- package/glossaries.mjs +2 -2
- package/groups.d.mts +1 -1
- package/groups.d.ts +1 -1
- package/groups.js +11 -11
- package/groups.mjs +2 -2
- package/identity-providers.d.mts +1 -1
- package/identity-providers.d.ts +1 -1
- package/identity-providers.js +11 -11
- package/identity-providers.mjs +2 -2
- package/index.d.mts +1 -1
- package/index.d.ts +1 -1
- package/index.js +8 -8
- package/index.mjs +4 -4
- package/{invoke-fetch-3WZWAAZH.mjs → invoke-fetch-CK5Y37H3.mjs} +1 -1
- package/invoke-fetch-D52L5Y7B.js +17 -0
- package/items.d.mts +1 -1
- package/items.d.ts +1 -1
- package/items.js +11 -11
- package/items.mjs +2 -2
- package/licenses.d.mts +1 -1
- package/licenses.d.ts +1 -1
- package/licenses.js +12 -12
- package/licenses.mjs +2 -2
- package/package.json +7 -4
- package/{qix-UNWXPGMA.js → qix-FLNSZRQX.js} +8 -8
- package/{qix-ZSJ4PIK3.mjs → qix-MP65NOTD.mjs} +2 -2
- package/qix.d.mts +1 -1
- package/qix.d.ts +1 -1
- package/qix.js +3 -3
- package/qix.mjs +2 -2
- package/quotas.d.mts +1 -1
- package/quotas.d.ts +1 -1
- package/quotas.js +5 -5
- package/quotas.mjs +2 -2
- package/reload-tasks.d.mts +1 -1
- package/reload-tasks.d.ts +1 -1
- package/reload-tasks.js +8 -8
- package/reload-tasks.mjs +2 -2
- package/reloads.d.mts +1 -1
- package/reloads.d.ts +1 -1
- package/reloads.js +7 -7
- package/reloads.mjs +2 -2
- package/roles.d.mts +1 -1
- package/roles.d.ts +1 -1
- package/roles.js +5 -5
- package/roles.mjs +2 -2
- package/spaces.d.mts +1 -1
- package/spaces.d.ts +1 -1
- package/spaces.js +15 -15
- package/spaces.mjs +2 -2
- package/temp-contents.d.mts +1 -1
- package/temp-contents.d.ts +1 -1
- package/temp-contents.js +6 -6
- package/temp-contents.mjs +2 -2
- package/tenants.d.mts +1 -1
- package/tenants.d.ts +1 -1
- package/tenants.js +9 -9
- package/tenants.mjs +2 -2
- package/themes.d.mts +1 -1
- package/themes.d.ts +1 -1
- package/themes.js +10 -10
- package/themes.mjs +2 -2
- package/transports.d.mts +1 -1
- package/transports.d.ts +1 -1
- package/transports.js +9 -9
- package/transports.mjs +2 -2
- package/users.d.mts +1 -1
- package/users.d.ts +1 -1
- package/users.js +13 -13
- package/users.mjs +2 -2
- package/web-integrations.d.mts +1 -1
- package/web-integrations.d.ts +1 -1
- package/web-integrations.js +8 -8
- package/web-integrations.mjs +2 -2
- package/web-notifications.d.mts +1 -1
- package/web-notifications.d.ts +1 -1
- package/web-notifications.js +9 -9
- package/web-notifications.mjs +2 -2
- package/webhooks.d.mts +1 -1
- package/webhooks.d.ts +1 -1
- package/webhooks.js +13 -13
- package/webhooks.mjs +2 -2
- package/auth-AWZV4DPP.js +0 -41
- package/invoke-fetch-UNTAUR7O.js +0 -17
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# Authentication
|
|
2
|
+
|
|
3
|
+
◁ [Back to overview](../README.md)
|
|
4
|
+
|
|
5
|
+
When integrating a 3rd-party solution with Qlik Cloud Services users always have to authenticate with Qlik's backend services to interact with them. Authentication with `@qlik/api` works seamlessly and uses the same authentication mechanisms as those in the `@qlik/embed` libraries. The general idea is to provide a capable yet simple-to-use authentication api that works out of the box for most users. There are a few authentication options so that users can use what suits them best.
|
|
6
|
+
|
|
7
|
+
## The Host Config
|
|
8
|
+
|
|
9
|
+
Authentication is done by setting up a host config.
|
|
10
|
+
|
|
11
|
+
The examples below uses an api-key and can be run in node.
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { auth, spaces } from "@qlik/api";
|
|
15
|
+
|
|
16
|
+
const hostConfig = {
|
|
17
|
+
authType: "apikey",
|
|
18
|
+
host: "my-org.region.qlikcloud.com", // a qlikcloud tenant
|
|
19
|
+
apiKey: "<api-key>",
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// sets a default host config for every api request
|
|
23
|
+
auth.setDefaultConfig(hostConfig);
|
|
24
|
+
|
|
25
|
+
const { data: mySpaces } = spaces.getSpaces();
|
|
26
|
+
|
|
27
|
+
console.log(mySpaces);
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
A host config can also be passed in to every single api request which then will override any default host config previously set.
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
import spaces from "@qlik/api/spaces";
|
|
34
|
+
|
|
35
|
+
const hostConfig = {
|
|
36
|
+
authType: "apikey"
|
|
37
|
+
host: "my-org.region.qlikcloud.com", // a qlikcloud tenant
|
|
38
|
+
apiKey: "<api-key>",
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const { data: mySpaces } = spaces.getSpaces({}, {
|
|
42
|
+
hostConfig,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
console.log(mySpaces);
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## The Auth Module
|
|
49
|
+
|
|
50
|
+
An auth module in `@qlik/api` is an object with a few implemented methods. When connecting to Qlik Cloud Services (or Qlik Sense Enterprise for Windows) with `@qlik/api` or with `@qlik/embed` libraries an auth module is used for configuring the communication.
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
// This is an authentication module in @qlik/api
|
|
54
|
+
|
|
55
|
+
const authModule = {
|
|
56
|
+
/** Gets the auth params needed for rest calls */
|
|
57
|
+
getRestCallAuthParams: () => Promise.resolve({
|
|
58
|
+
/** will be applied to rest requests headers */
|
|
59
|
+
headers: { ... },
|
|
60
|
+
/** will be added as query parameters to the request */
|
|
61
|
+
queryParams: { ... },
|
|
62
|
+
/** will set the credentials attribute on the rest request */
|
|
63
|
+
credentials: ("include"|"omit"|"same-origin"),
|
|
64
|
+
}),
|
|
65
|
+
|
|
66
|
+
/** Gets params for websocket connect requests */
|
|
67
|
+
getWebSocketAuthParams: () => Promise.resolve({
|
|
68
|
+
// will be added as query parameters to the websocket connect request
|
|
69
|
+
queryParams: { ... }
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
/** Will be called when there's an authentication error (e.g. user is not logged in) during an api call. */
|
|
73
|
+
handleAuthenticationError: () => {
|
|
74
|
+
// ... maybe start a login process
|
|
75
|
+
return Promise.resolve();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** optional method to get auth params when web resources are fetched (such as images etc) */
|
|
79
|
+
getWebResourceAuthParams: () => Promise.resolve();
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Optional runtime check for validating a host configs options, return false if
|
|
83
|
+
* something is missing or if there are invalid properties in the host config.
|
|
84
|
+
*/
|
|
85
|
+
validateHostConfig: (hostConfig) => true;
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## The Default Auth Modules
|
|
90
|
+
|
|
91
|
+
- [Oauth2](#oauth2)
|
|
92
|
+
- [Cookie](#cookie)
|
|
93
|
+
- [Windows Cookie](#windows-cookie)
|
|
94
|
+
- [API Key](#api-key)
|
|
95
|
+
|
|
96
|
+
### Oauth2
|
|
97
|
+
|
|
98
|
+
Uses a `clientId` from an oauth client created by a Qlik tenant admin. And optionally a `clientSecret`. A user will go through a login flow in order to get hold of an access token that is valid for 6 hours. This token will be used as query parameters for api requests. If a `clientSecret` is used a refresh token will also be received. This can be used to refresh the access token and is valid for 30 days. The user can select where these tokens should be stored (only in a browser). Either in local storate or session storage (default).
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
type Oauth2AuthConfig = {
|
|
102
|
+
/** The URL to the cloud tenant or windows server. If scheme is excluded https is used. May include a virtual proxy prefix on windows. Any trailing slashes are stripped. */
|
|
103
|
+
host?: string;
|
|
104
|
+
/** Client ID of oauth client created by tenant admin */
|
|
105
|
+
clientId: string;
|
|
106
|
+
/** Client ID of oauth client created by tenant admin */
|
|
107
|
+
clientSecret?: string;
|
|
108
|
+
/** The location where the client should be redirected after obtaining the access token */
|
|
109
|
+
redirectUri?: string;
|
|
110
|
+
/** If set, store the access token in either local or session storage, otherwise not stored */
|
|
111
|
+
accessTokenStorage?: "session" | "local";
|
|
112
|
+
/** A string with comma separated values of oauth2 scopes https://oauth.net/2/scope defaults to "user_default" */
|
|
113
|
+
scope?: string;
|
|
114
|
+
};
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
When using OAuth2 in a browser we recommend setting up a "redirect page" which is handy to use for pointing the redirection of an oauth login flow when fetching the access tokens. An oauth server will ask the client to redirect back to the hosting application after login is completed. In a single page application context this can potentially be any URL, so we recommend using this html template and use it for redirections.
|
|
118
|
+
|
|
119
|
+
```html
|
|
120
|
+
<!doctype html>
|
|
121
|
+
<html lang="en">
|
|
122
|
+
<head>
|
|
123
|
+
<meta charset="utf-8" />
|
|
124
|
+
<script
|
|
125
|
+
crossorigin="anonymous"
|
|
126
|
+
type="application/javascript"
|
|
127
|
+
data-host="<tenant-url>"
|
|
128
|
+
src="https://cdn.jsdelivr.net/npm/@qlik/embed-web-components/dist/oauth-callback.js"
|
|
129
|
+
></script>
|
|
130
|
+
</head>
|
|
131
|
+
</html>
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Cookie
|
|
135
|
+
|
|
136
|
+
Uses a `webIntegrationId` created by a tenant admin. Will get the a CSRF token and append to api calls when necessary. To use this method in an integration scenario it is required that 3d party cookies are allowed.
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
type CookieAuthConfig = {
|
|
140
|
+
/** The URL to the cloud tenant or windows server. If scheme is excluded https is used. May include a virtual proxy prefix on windows. Any trailing slashes are stripped. */
|
|
141
|
+
host?: string;
|
|
142
|
+
/** Web Integration Id created by tenant admin */
|
|
143
|
+
webIntegrationId?: string;
|
|
144
|
+
/** If set to false the `credentials` property will be set to same-origin */
|
|
145
|
+
crossSiteCookies?: boolean;
|
|
146
|
+
};
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Windows Cookie
|
|
150
|
+
|
|
151
|
+
Will do the necessary actions in order to communication with a Qlik Sense Enterprise for Windows server. This includes fetching an XrfKey from the Qlik Sense Server Repository Service. To use this method in an integration scenario it is required that 3d party cookies are allowed.
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
type WindowsCookieAuthConfig = {
|
|
155
|
+
/** The URL to the cloud tenant or windows server. If scheme is excluded https is used. May include a virtual proxy prefix on windows. Any trailing slashes are stripped. */
|
|
156
|
+
host?: string;
|
|
157
|
+
/** location of the login page, auth module will redirect to this page when an unauthenticated api call is made */
|
|
158
|
+
loginUri?: string;
|
|
159
|
+
/** If set to false the `credentials` property will be set to same-origin */
|
|
160
|
+
crossSiteCookies?: boolean;
|
|
161
|
+
};
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### API Key
|
|
165
|
+
|
|
166
|
+
Appends an `apiKey` to api requests that has been created by a user with a "developer" role on a Qlik Cloud tenant. Typically used when running integration code in a node environment.
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
type ApiKeyAuthConfig = {
|
|
170
|
+
/** api key created by a developer role on a tenant */
|
|
171
|
+
apiKey: string;
|
|
172
|
+
};
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Custom auth module
|
|
176
|
+
|
|
177
|
+
It is possible to register your own authentication module that then can be used when needed.
|
|
178
|
+
|
|
179
|
+
```ts
|
|
180
|
+
import { registerAuthModule, type AuthModule } from "@qlik/api/auth";
|
|
181
|
+
|
|
182
|
+
// declare the type of configuration needed your custom auth module
|
|
183
|
+
|
|
184
|
+
type CustomAuthConfig = {
|
|
185
|
+
configPropA: string;
|
|
186
|
+
optionalConfigPropB: boolean;
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
// Add your CustomAuthConfig to the known global auth types
|
|
190
|
+
|
|
191
|
+
declare global {
|
|
192
|
+
interface QlikAuthModules {
|
|
193
|
+
custom: { config: CustomAuthConfig };
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// implement your auth module as described above
|
|
198
|
+
|
|
199
|
+
const authModule = {
|
|
200
|
+
...
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
registerAuthModule("custom", authModule);
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
After this has been done the auth module can be used in a host config.
|
|
207
|
+
|
|
208
|
+
```ts
|
|
209
|
+
import { setDefaultHostConfig type HostConfig } from "@qlik/api/auth";
|
|
210
|
+
|
|
211
|
+
const hostConfig: HostConfig = {
|
|
212
|
+
authType: "custom",
|
|
213
|
+
configPropA: "some-config-or-a-key",
|
|
214
|
+
optionalConfigPropB: false
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
setDefaultHostConfig(hostConfig);
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
◁ [Back to overview](../README.md)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Create an app and add some data
|
|
2
|
+
|
|
3
|
+
◁ [Back to examples](../examples.md)
|
|
4
|
+
|
|
5
|
+
This examples shows how to:
|
|
6
|
+
|
|
7
|
+
- Create an app with the `apps` api
|
|
8
|
+
- Connect to the app
|
|
9
|
+
- Add a load script
|
|
10
|
+
- Reload the data
|
|
11
|
+
- Run a calculation with a qix engine expression.
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { apps, auth, qix } from "@qlik/api";
|
|
15
|
+
|
|
16
|
+
const hostConfig = {
|
|
17
|
+
host: "your-tenant.region.qlikcloud.com",
|
|
18
|
+
authType: "apikey",
|
|
19
|
+
apiKey: "<api-key>",
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
auth.setDefaultHostConfig(hostConfig);
|
|
23
|
+
|
|
24
|
+
async function main() {
|
|
25
|
+
try {
|
|
26
|
+
const { data } = await apps.createApp({ attributes: { name: "Anders App" } });
|
|
27
|
+
const appId = data.attributes?.id;
|
|
28
|
+
console.log("We have created:", appId);
|
|
29
|
+
// do stuff with the app
|
|
30
|
+
const session = qix.openAppSession({ appId });
|
|
31
|
+
const app = await session.getDoc();
|
|
32
|
+
console.log("Setting up some data");
|
|
33
|
+
await app.setScript("Load RecNo() as N autogenerate(100);");
|
|
34
|
+
await app.doReload();
|
|
35
|
+
console.log("Reloaded data done");
|
|
36
|
+
const evalResult = await app.evaluate("SUM([N])");
|
|
37
|
+
console.log(`Eval result: ${evalResult}`);
|
|
38
|
+
if (appId) {
|
|
39
|
+
await apps.deleteApp(appId);
|
|
40
|
+
console.log("We have now deleted:", appId);
|
|
41
|
+
}
|
|
42
|
+
session.close();
|
|
43
|
+
} catch (e) {
|
|
44
|
+
console.error(e);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
await main();
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
◁ [Back to examples](../examples.md)
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Create a session app
|
|
2
|
+
|
|
3
|
+
◁ [Back to examples](../examples.md)
|
|
4
|
+
|
|
5
|
+
This examples shows how to:
|
|
6
|
+
|
|
7
|
+
- create a session app by opening a qix session to an app with a random id that starts with "SessionApp\_"
|
|
8
|
+
- connect to the app and add some data to it
|
|
9
|
+
- create an object and setup an event listener to when hypercube changes
|
|
10
|
+
- reload the data to trigger the changed event.
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
import { auth, qix } from "@qlik/api";
|
|
14
|
+
|
|
15
|
+
auth.setDefaultHostConfig({
|
|
16
|
+
host: "your-tenant.region.qlikcloud.com",
|
|
17
|
+
authType: "apikey",
|
|
18
|
+
apiKey: "<api-key>",
|
|
19
|
+
|
|
20
|
+
async function main() {
|
|
21
|
+
try {
|
|
22
|
+
// Create a session app
|
|
23
|
+
const randomId = Math.random().toString(32).substring(3);
|
|
24
|
+
const appId = `SessionApp_${randomId}`;
|
|
25
|
+
// if appId starts with SessionApp_ and have a unique id it will become a session app.
|
|
26
|
+
|
|
27
|
+
// Open a websocket session with the session app id
|
|
28
|
+
const session = qix.openAppSession({ appId });
|
|
29
|
+
// Get the app object
|
|
30
|
+
const app = await session.getDoc();
|
|
31
|
+
|
|
32
|
+
// Set a script in the app
|
|
33
|
+
const script = `
|
|
34
|
+
TempTable:
|
|
35
|
+
Load
|
|
36
|
+
RecNo() as ID,
|
|
37
|
+
Rand() as Value
|
|
38
|
+
AutoGenerate 100
|
|
39
|
+
`;
|
|
40
|
+
await app.setScript(script);
|
|
41
|
+
|
|
42
|
+
// Create an object with a hypercube using fields in the data model
|
|
43
|
+
const properties = {
|
|
44
|
+
qInfo: {
|
|
45
|
+
qType: "my-straight-hypercube",
|
|
46
|
+
},
|
|
47
|
+
qHyperCubeDef: {
|
|
48
|
+
qDimensions: [
|
|
49
|
+
{
|
|
50
|
+
qDef: { qFieldDefs: ["ID"] },
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
qMeasures: [
|
|
54
|
+
{
|
|
55
|
+
qDef: { qDef: "=Sum(Value)" },
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
qInitialDataFetch: [
|
|
59
|
+
{
|
|
60
|
+
qHeight: 5,
|
|
61
|
+
qWidth: 2,
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
const hypercube = await app.createObject(properties);
|
|
67
|
+
await hypercube.getLayout();
|
|
68
|
+
|
|
69
|
+
// Register an event listener for change events
|
|
70
|
+
hypercube.on("changed", () => {
|
|
71
|
+
console.log("changed ✅");
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
console.log("performing reload, expect a change to the hypercube object to happen");
|
|
75
|
+
// Do a reload of the app
|
|
76
|
+
await app.doReload();
|
|
77
|
+
|
|
78
|
+
// Close session
|
|
79
|
+
await session.close();
|
|
80
|
+
} catch (e) {
|
|
81
|
+
console.error(e);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
main();
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
◁ [Back to examples](../examples.md)
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# Fetch Spaces in a Qlik Tenant
|
|
2
|
+
|
|
3
|
+
◁ [Back to examples](../examples.md)
|
|
4
|
+
|
|
5
|
+
## NodeJS using an API Key
|
|
6
|
+
|
|
7
|
+
This examples shows how to fetch the space list from a tenant.
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
import { auth, spaces } from "@qlik/api";
|
|
11
|
+
|
|
12
|
+
const x = {
|
|
13
|
+
host: "your-tenant.region.qlikcloud.com",
|
|
14
|
+
authType: "apikey",
|
|
15
|
+
apiKey: "<api-key>",
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
auth.setDefaultHostConfig(x);
|
|
19
|
+
|
|
20
|
+
async function main() {
|
|
21
|
+
const { data: mySpaces } = await spaces.getSpaces({});
|
|
22
|
+
console.log(mySpaces.data); // the data response (list of spaces)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
await main();
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Run the code with
|
|
29
|
+
|
|
30
|
+
```shell
|
|
31
|
+
node fetch-spaces.mjs
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## NodeJS using Oauth2
|
|
35
|
+
|
|
36
|
+
Create a file `fetch-spaces.mjs` and add the following:
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
import { auth, spaces } from "@qlik/api";
|
|
40
|
+
|
|
41
|
+
const hostConfig = {
|
|
42
|
+
host: "your-tenant.region.qlikcloud.com",
|
|
43
|
+
authType: "oauth2",
|
|
44
|
+
clientId: "<client-id>",
|
|
45
|
+
clientSecret "<client-secret>",
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
auth.setDefaultHostConfig(hostConfig);
|
|
49
|
+
|
|
50
|
+
async function main() {
|
|
51
|
+
const { data: mySpaces } = await spaces.getSpaces({});
|
|
52
|
+
console.log(mySpaces.data); // the data response (list of spaces)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
await main();
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Run the code with
|
|
59
|
+
|
|
60
|
+
```shell
|
|
61
|
+
node fetch-spaces.mjs
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Browser using cookies
|
|
65
|
+
|
|
66
|
+
When using a browser you can load the library files from a CDN provider. It is also possible to use npm and a bundler to get the code into your application. In the html below we are making an api call to fetch the spaces from a tenant and we add the names of the spaces as div elements in the dom.
|
|
67
|
+
|
|
68
|
+
```html
|
|
69
|
+
<!doctype html>
|
|
70
|
+
<html lang="en">
|
|
71
|
+
<head>
|
|
72
|
+
<title>Fetching spaces with @qlik/api</title>
|
|
73
|
+
</head>
|
|
74
|
+
<body>
|
|
75
|
+
<div id="space-container" class="container">
|
|
76
|
+
<div>Spaces:</div>
|
|
77
|
+
<!-- Spaces will be addeed here -->
|
|
78
|
+
</div>
|
|
79
|
+
<script type="module">
|
|
80
|
+
import { auth, spaces } from "https://cdn.jsdelivr.net/npm/@qlik/api/index.mjs";
|
|
81
|
+
|
|
82
|
+
auth.setDefaultHostConfig({
|
|
83
|
+
host: "your-tenant.region.qlikcloud.com",
|
|
84
|
+
authType: "cookie",
|
|
85
|
+
webIntegrationId: "<web-integration-id>",
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const { data: mySpaces } = await spaces.getSpaces();
|
|
89
|
+
const spaceContainer = document.getElementById("space-container");
|
|
90
|
+
for (const space of mySpaces.data) {
|
|
91
|
+
const div = document.createElement("div");
|
|
92
|
+
div.innerText = space.name;
|
|
93
|
+
spaceContainer.appendChild(div);
|
|
94
|
+
}
|
|
95
|
+
</script>
|
|
96
|
+
</body>
|
|
97
|
+
</html>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Browser using Oauth2
|
|
101
|
+
|
|
102
|
+
Use the same example as above but change the host config to use [Oauth2](authentication.md#oauth2)
|
|
103
|
+
|
|
104
|
+
◁ [Back to examples](../examples.md)
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Show Sheet List in a Qlik Sense Application
|
|
2
|
+
|
|
3
|
+
◁ [Back to examples](../examples.md)
|
|
4
|
+
|
|
5
|
+
For this example the id of a Qlik Sense Application that you have access to is needed. To get the id of an app, click on the "More actions" button on the app from the Qlik Cloud Hub and click on "details". There the app id is visible. Another option is to open the app and look at the URL. The app id is located as `/sense/<app-id>/` in the url.
|
|
6
|
+
|
|
7
|
+
## NodeJS using an API Key
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { auth, qix } from "@qlik/api";
|
|
11
|
+
|
|
12
|
+
const hostConfig = {
|
|
13
|
+
authType = "apikey",
|
|
14
|
+
host: "your-tenant.region.qlikcloud.com",
|
|
15
|
+
apiKey: "<api-key>",
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// to use "Oauth2" auth module switch hostConfig to oauth2 and set clientId
|
|
19
|
+
// and clientSecret instead of apiKey.
|
|
20
|
+
|
|
21
|
+
const appId = "<app-id>";
|
|
22
|
+
|
|
23
|
+
auth.setDefaultHostConfig(hostConfig);
|
|
24
|
+
|
|
25
|
+
async function main() {
|
|
26
|
+
const session = qix.openAppSession({ appId });
|
|
27
|
+
const app = await session.getDoc();
|
|
28
|
+
|
|
29
|
+
const sheetList = await app.getSheetList();
|
|
30
|
+
console.log(sheetList);
|
|
31
|
+
session.close();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
main();
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
In this example we're using the api method `app.getSheetList()` which comes from a "sense mixin" which is an extension of the official QIX API used for Qlik Sense Applications. In `@qlik/api` all "sense mixins" have been included to easily interact with Qlik Sense Applications in the same way in-house Qlik Developers do.
|
|
38
|
+
|
|
39
|
+
## Browser using Cookies
|
|
40
|
+
|
|
41
|
+
When using a browser you can load the library files from a CDN provider. It is also possible to use npm and a bundler to get the code into your application. In the html below we are making an api call to fetch the sheet list from a Qlik Sense App and we add the sheet titles as div elements in the dom.
|
|
42
|
+
|
|
43
|
+
```html
|
|
44
|
+
<!doctype html>
|
|
45
|
+
<html lang="en">
|
|
46
|
+
<head>
|
|
47
|
+
<title>Fetching sheet list from an app with @qlik/api</title>
|
|
48
|
+
</head>
|
|
49
|
+
<body>
|
|
50
|
+
<div id="sheet-container" class="container">
|
|
51
|
+
<div>Sheets:</div>
|
|
52
|
+
<!-- Sheets will be addeed here -->
|
|
53
|
+
</div>
|
|
54
|
+
<script type="module">
|
|
55
|
+
import { auth, qix } from "https://cdn.jsdelivr.net/npm/@qlik/api/index.mjs";
|
|
56
|
+
auth.setDefaultHostConfig({
|
|
57
|
+
host: "your-tenant.region.qlikcloud.com",
|
|
58
|
+
authType: "cookie",
|
|
59
|
+
webIntegrationId: "<web-ingetration-id>",
|
|
60
|
+
});
|
|
61
|
+
const appId = "<api-key>";
|
|
62
|
+
const session = qix.openAppSession({ appId });
|
|
63
|
+
const app = await session.getDoc();
|
|
64
|
+
const sheetList = await app.getSheetList();
|
|
65
|
+
const sheetContainer = document.getElementById("sheet-container");
|
|
66
|
+
for (const sheet of sheetList) {
|
|
67
|
+
const div = document.createElement("div");
|
|
68
|
+
div.innerText = sheet.qMeta.title;
|
|
69
|
+
sheetContainer.appendChild(div);
|
|
70
|
+
}
|
|
71
|
+
session.close(); // closing the app
|
|
72
|
+
</script>
|
|
73
|
+
</body>
|
|
74
|
+
</html>
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Browser using Oauth2
|
|
78
|
+
|
|
79
|
+
Use the same example as above but change the host config to use [Oauth2](authentication.md#oauth2)
|
|
80
|
+
|
|
81
|
+
◁ [Back to examples](../examples.md)
|
package/docs/examples.md
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
# Examples
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
◁ [Back to overview](../README.md)
|
|
4
|
+
|
|
5
|
+
Heres's some examples on common scenarios on how to interact with Qlik API's using `@qlik/api`
|
|
6
|
+
|
|
7
|
+
The code in the examples use ES Module Syntax. It is possible to use CommonJS format for the NodeJS examples, but we recommend using ES Modules since it's where the whole Javascript EcoSystem is moving towards. The code you write can also easily be ported between browsers and NodeJS when using ES Modules.
|
|
8
|
+
|
|
9
|
+
- [Fetching spaces](./examples/fetch-spaces.md)
|
|
10
|
+
- [Show Sheet List in an app](./examples/show-sheet-list.md)
|
|
11
|
+
- [Create an app](./examples/create-app.md)
|
|
12
|
+
- [Create session app](./examples/create-session-app.md)
|
package/docs/features.md
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# Features
|
|
2
|
+
|
|
3
|
+
◁ [Back to overview](../README.md)
|
|
4
|
+
|
|
5
|
+
- [Typed API calls](#typed-api-calls)
|
|
6
|
+
- [Caching](#caching)
|
|
7
|
+
- [Paging](#paging)
|
|
8
|
+
- [Request/response interceptors](#interceptors)
|
|
9
|
+
|
|
10
|
+
## Typed API Calls
|
|
11
|
+
|
|
12
|
+
Each API comes with full typescript support which will greatly help the developer experience. Use the types to understand how the API works by going into the type definition. In most editors (e.g. vscode) you can CMD+click (mac) or CTRL+click (windows) on an api-call to see the type definition of parameters, return structures etc etc.
|
|
13
|
+
|
|
14
|
+
## Caching
|
|
15
|
+
|
|
16
|
+
Request responses are cached in the browser so requests can be fired away without being concerned about causing unnecessary traffic. Ongoing requests are also re-used if two equal requests go out at the same time. A request can be forced by adding an option to the call `{ noCache: true }`. Clearing cache can be automatically handled as shown [below](#automatic-cache-clearing-and-auto-csrf).
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
import { getItems } from "@qlik/api/items";
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
await getItems();
|
|
23
|
+
await getItems();
|
|
24
|
+
await getItems();
|
|
25
|
+
await getItems();
|
|
26
|
+
// the 4 requests above will result in only one request over network
|
|
27
|
+
|
|
28
|
+
// empty query object as 1st parameter, 2nd parameter is the ApiCallOptions
|
|
29
|
+
await getItems({}, { noCache: true });
|
|
30
|
+
// this request above will result in a network call because of the noCache option
|
|
31
|
+
} catch (e) {
|
|
32
|
+
// something went wrong
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Automatic cache clearing and auto csrf
|
|
37
|
+
|
|
38
|
+
Requests that need parameters are all using documented types inherited from the spec files. Requests that require a valid csrf-token automatically fetch it (once if needed) and add it to the outgoing requests headers. Requests that change resources (for example POST/PUT) automatically clear the cache.
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import { getSpaces, createSpace } from "@qlik/api/spaces";
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
let { data: spaceList } = await getSpaces({});
|
|
45
|
+
// spaceList has 2 items
|
|
46
|
+
const { data: space } = await createSpace({ name: "My space", description: "description", type: "shared" });
|
|
47
|
+
// the call above automatically adds a csrf-token header. If no csrf-token has been fetched yet it will first fetch it.
|
|
48
|
+
// space now has the Space type and your editor will show the types e.g:
|
|
49
|
+
// space.id;
|
|
50
|
+
// space.createdAt;
|
|
51
|
+
|
|
52
|
+
{ data: spaceList } = await getSpaces(); // cached response has automatically been cleared because of createSpace call above
|
|
53
|
+
// spaceList has 3 items
|
|
54
|
+
} catch (e) {
|
|
55
|
+
// something went wrong
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Manual cache clearing
|
|
60
|
+
|
|
61
|
+
The cache for an api can be cleared with the `clearCache` method. This clears all cached responses for that specific api. It is recommended to use the [automatic cache clearing](#automatic-cache-clearing-and-auto-csrf).
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
import { getSpaces, clearCache: clearSpaceCache } from "@qlik/api/spaces";
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
await getSpaces();
|
|
68
|
+
clearSpaceCache(); // clears all cached responses for the space api.
|
|
69
|
+
await getSpaces();
|
|
70
|
+
// the 2 requests above will both go out on the network
|
|
71
|
+
} catch (e) {
|
|
72
|
+
// something went wrong
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**note** `clearCache` only affects one api, all other api caches are unaffected.
|
|
77
|
+
|
|
78
|
+
## Paging
|
|
79
|
+
|
|
80
|
+
Many "list" APIs return links to the next and previous page in the list GET response body. If the API follows the conventions and declares this in the OpenAPI spec next and prev functions will be available in the API response type. Note that the next and prev functions are only defined when there actually is a next and prev page.
|
|
81
|
+
|
|
82
|
+
## Interceptors
|
|
83
|
+
|
|
84
|
+
Consumer of `@qlik/api` can inject an interceptor in every api method. There are two interception points, before request and after response. The request interceptor can also be an async function.
|
|
85
|
+
|
|
86
|
+
**NOTE:** to add headers to the request, use a regular object instead of `new Headers()`. It causes a loss of all the original headers. See the example below.
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
import { getItems, interceptors } from "@qlik/api/items";
|
|
90
|
+
|
|
91
|
+
// intercept requests
|
|
92
|
+
interceptors.getItems.request.use((url, request) => {
|
|
93
|
+
const headers = {
|
|
94
|
+
...request.headers,
|
|
95
|
+
"x-qlik-custom-header": "value",
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
// modify fetch request
|
|
99
|
+
request.headers = headers;
|
|
100
|
+
|
|
101
|
+
return request;
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// intercept responses
|
|
105
|
+
interceptors.getItems.response.use((response) => {
|
|
106
|
+
response.data.YOU_ARE_INTERCEPTED = true;
|
|
107
|
+
return response;
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
const { status, data: items } = await getItems();
|
|
112
|
+
|
|
113
|
+
if (items.YOU_ARE_INTERCEPTED) {
|
|
114
|
+
// ok!
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
when adding an interceptor you get an interceptor ID that can be used to eject the interceptor later.
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
const interceptorId = interceptors.getItems.response.use(...);
|
|
123
|
+
// now interceptor will execute on response
|
|
124
|
+
|
|
125
|
+
interceptors.getItems.response.eject(interceptorId);
|
|
126
|
+
// now interceptor will NOT execute on response
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
◁ [Back to overview](../README.md)
|