Javascript client to interact with mondrian-rest and tesseract-olap servers.
This package is the sucessor of the mondrian-rest-client
and the @datawheel/tesseract-client
packages.
npm install @datawheel/olap-client
import {Client, MondrianDataSource, TesseractDataSource} from "@datawheel/olap-client"
// Create an instance of the DataSource according to the type of server
const datasource = new MondrianDataSource("https://your.mondrian-rest.server/");
// ...and pass it as the parameter to initialize your client
const client = new Client(datasource);
The constructor parameter is optional. You can also set/reset the datasource afterwards:
const client = new Client();
// ...
const datasource = new TesseractDataSource("https://another.tesseract-olap.server/olap/");
client.setDataSource(datasource);
If you don't know beforehand, use the Client.fromURL(url)
static method.
It will do some requests to try and guess the type of datasource.
// Using Promise chaining
let unknownClient;
Client.fromURL("https://unknown.server/olap/").then(client => {
unknownClient = client;
});
// Using async/await
const unknownClient = await Client.fromURL("https://unknown.server/olap/");
If you really need the client object directly, you can initialize the instance and add the DataSource later:
const client = new Client();
// ...
Client.dataSourceFromURL("https://unknown.server/olap/").then(datasource => {
client.setDataSource(datasource);
});
Notice if you try to interact with a method of an IClient
instance without a configured DataSource, it will throw an error.
Both Client.fromURL
and Client.dataSourceFromURL
also accept an AxiosRequestConfig object, so you can customize the headers and other properties of the testing request.
These additional properties will be stored in the DataSource instance, and will be reused in future requests through the client. You can change them at any time using the IClient#setRequestConfig
method.
import {MultiClient, TesseractDataSource} from "@datawheel/olap-client"
// MultiClient instances can also be initialized with or without a datasource
const client = new MultiClient();
// To add a datasource, use the #addDataSource() method instead of #setDataSource()
const datasource = new TesseractDataSource("https://your.mondrian-rest.server/");
client.addDataSource(datasource);
You can pass multiple datasources to the MultiClient#addDataSource()
method:
client.addDataSource(datasourceA, datasourceB, datasourceC);
And likewise Client, if you don't know beforehand, you can let the client guess the type of datasource:
const unknownClient = MultiClient.fromURL("https://unknown.server/olap/", "https://another.server/", ...);
Both Client
and MultiClient
instances follow the IClient
interface. Client
and MultiClient
-specific methods are described at the end.
getCubes(): Promise<Cube[]>
Returns a promise that resolves to a Cube
array.
In a MultiClient
instance, the cubes from all the subscribed datasources are concatenated.
getCube(cubeName: string, selectorFn?: (cubes: Cube[]) => Cube): Promise<Cube>
Returns a promise that resolves to a single Cube
instance, whose .name
is equal to the cubeName
parameter.
In a MultiClient
instance, if there's more than one cube with the same cubeName
, a selectorFn
function can be used to pick the right cube.
getMembers(levelRef: Level | LevelDescriptor, options?: any): Promise<Member[]>
Returns a promise that resolves to a list of the members available for the level referenced.
levelRef
can be a Level
instance, or an object describing how to find a Level
in a DataSource. For more information on this object, see the specification of the LevelDescriptor interface.
the properties options
getMember(levelRef: Level | LevelDescriptor, key: string | number, options?: any): Promise<Member>
Returns a promise that resolves to a member for the level referenced, specified by its key
.
execQuery(query: Query, endpoint?: string): Promise<Aggregation>
Execute a query in all the available datasources. The returned object implements the Aggregation interface.
The query
parameter must be a Query
instance, usually obtained from a Cube
instance.
parseQueryURL(url: string, options?: Partial<ParseURLOptions>): Promise<Query>;
Allows to parse a query URL for the server (or one of them) associated to the IClient
instance, and get a Query
instance with its associated data.
Since the IClient
instance needs a Cube
to populate the Query
, this method returns a Promise that fullfills to a Query
.
All invalid parameters for the current state of the Cube are ignored.
Additionally, the options
parameter allows the user to include
/exclude
/filter
certain parameters from the resulting query.
setRequestConfig(config: AxiosRequestConfig): void;
Sets the default configuration parameters for the requests done on all the associated DataSources.
This method is also available on the IDataSource
interface, so configurations can be different on each datasource, on the same client.
checkStatus(): Promise<ServerStatus>
Returns an object with information about the server. For now, only supports detailed info about Tesseract OLAP, but tries its best with Mondrian REST anyway.
setDataSource(datasource: IDataSource): void
Sets the datasource the client instance will work with.
The datasource
parameter must be an object compatible with the IDataSource
interface.
static dataSourceFromURL(url: string | AxiosRequestConfig): Promise<IDataSource>
Tries to guess the type of server from a request to the serverUrl
. The parameter must be a string.
Since a request must be done beforehand, this static method returns a Promise
that resolves to a object compatible with the IDataSource
interface.
static fromURL(url: string | AxiosRequestConfig): Promise<Client>
Using the result from Client.dataSourceFromURL(serverUrl)
, generates a new Client(datasource)
instance.
addDataSource(...datasources: IDataSource[]): void
Adds datasources to the client internal directory.
The datasources
must be objects compatible with the IDataSource
interface.
checkStatus(): Promise<ServerStatus[]>
Returns an array of objects with information about each datasource server. These objects have the same structure of response as Client#checkStatus
.
static dataSourcesFromURL(...serverUrls: Array<string | AxiosRequestConfig>): Promise<IDataSource[]>
Does a request to each url in serverUrls
, tries to guess the type of server, and returns the respective datasource.
This method returns a Promise
that resolves to an array of IDataSource
.
static fromURL(...serverUrls: Array<string | AxiosRequestConfig>): Promise<MultiClient>
From the result from MultiClient.dataSourcesFromURL(...serverUrls)
, generates a single new MultiClient(...datasources)
instance.
This method returns a Promise
that resolves to a MultiClient
instance.
interface Aggregation<T = any> {
data: T;
headers: Record<string, string>; // Response headers
query: Query;
status?: number;
url?: string;
}
The result of executing a Query is represented by an object implementing the Aggregation
interface.
The type of the data
property depends on the format
set on the Query:
Format.jsonrecords
returns an array of tidy data objects- All other
Format
s return the raw data returned by the server
interface LevelDescriptor {
server?: string; // Server URL
cube?: string; // Cube name
dimension?: string; // Dimension name
hierarchy?: string; // Hierarchy name
level: string; // Level name, required
}
A LevelDescriptor is an ordinary object with enough info to differentiate a Level in a list of DataSources. Depending on the circumstances (e.g. some name is shared in more than one object) some Levels might need more information on a LevelDescriptor to be differentiated. All the properties are the name
s of the parents, except for server
that maps to the URL of the DataSource. Level is the only required at all times.
It is suggested to fill the properties with as much information as possible to prevent getting a different level.
interface ParseURLOptions {
anyServer: boolean;
exclude: string[];
include: string[];
filter: (key: string, value: string | boolean | string[]) => boolean;
}
Gives the directions for the parseQueryURL method to include
explicitly only some properties, or exclude
properties from the ones available, or to filter
on a parameter by parameter basis.
Setting the anyServer
property to true
bypasses the check for the URL to the server associated to the query object, so only the query params are used to populate query properties. The include
and exclude
directives supersede the filter
function.
interface ServerStatus {
software: string;
online: boolean;
url: string;
version: string;
}
Contains information about the current state of the server.
Due to server implementation, version
isn't available from MondrianDataSource
.
This package integrates the TesseractDataSource
and MondrianDataSource
classes, but the clients can work with any object that implements the IDataSource
interface correctly:
interface IDataSource {
serverOnline: boolean;
serverSoftware: string;
serverUrl: string;
serverVersion: string;
checkStatus(): Promise<ServerStatus>;
execQuery(query: Query, endpoint?: string): Promise<Aggregation>;
fetchCube(cubeName: string): Promise<PlainCube>;
fetchCubes(): Promise<PlainCube[]>;
fetchMember(parent: Level, key: string | number, options?: any): Promise<PlainMember>;
fetchMembers(parent: Level, options?: any): Promise<PlainMember[]>;
parseQueryURL(query: Query, url: string, options: Partial<ParseURLOptions>): Query;
setRequestConfig(config: AxiosRequestConfig): void;
stringifyQueryURL(query: Query, kind: string): string;
}
Check the source code to see the requirements of the PlainObject
interfaces and how the current data sources are implemented.
Client.fromURL("https://chilecube.datachile.io")
.then(client => {
return client.getCube("tax_data").then(cube => {
const query = cube.query;
query
.addMeasure("Labour")
.addDrilldown("Year")
.setOption("debug", false)
.setOption("distinct", false)
.setOption("nonempty", true);
return client.execQuery(query);
});
})
.then(aggregation => {
console.log(aggregation);
});
MIT © 2019 Datawheel