Mapcreator Javascript API
The Mapcreator API is a powerful mapping service built for our front-end applications. This library is released to provide a painless way of talking to the api using Javascript. See the LICENSE file for licensing information. Api tokens can be granted through support requests.
Documentation
The documentation can be found here. Please refer to the api documentation for resource information. Job structure documentation will be released soon.
Installation
Please refer to the installation docs here.
Download
The library is built to be ran in either the browser or using nodejs.
The source code can be found on the GitLab repo.
Building
Please refer to the build docs here
Authenticating
Authentication can be done using OAuth. Examples of how to use authentication can be found in the documentation. The following authentication methods are supported:
Examples
Examples can be found in the documentation under the "Manual" section.
License
See the LICENSE file for license information. This project is licensed under a BSD-3-Clause license.
Basics
These examples assume that an instance of the api exists and is authenticated. See the node and web authentication examples for more information on authenticating.
Getting a resource
Resources are bound to the base api class by default. Resources can be fetched in
two ways; by selecting them (.select
) or by fetching them (.get
). Selecting them will only set the
object's id to it's properties. Fetching a resource
Fetch resource and all it's properties:
api.colors.get(1).then(function(color) {
console.log(color.id + " " + color.name + ": " + color.hex);
})
Select the current user to quickly obtain related mapstyle sets:
api.users.select('me').mapstyleSets().then(function(sets) {
for(var i = 0; i < sets.data.length; i++) {
console.log(sets.data[i].name);
}
});
Selection is only usefull as a stepping stone to related resources that can be easily obtained using the id of the parent. Please refer to the api documentation for further reference.
Create a new resource
Create a new color and dump the new resource to the console after saving
var data = {name: 'Smurf', hex: '88CCFF'};
api.colors.new(data).save().then(console.dir);
Modify a resource
Change profession of the current user and save it.
api.users.get('me').then(me => {
me.profession = 'Developer';
me.save(); // Optional chaining to get the updated resource
});
Clone a resource
Setting the id to null forces the creation of a new object upon saving.
api.colors.get(1).then(color => {
color.id = null;
color.save();
});
Pagination
Listing resources with pagination. First page with 5 items per page
api.colors.list(1, 5).then(page => {
console.log('Got resources:');
for (var i = 0; i < page.data.length; i++) {
console.log(page.data[i].toString());
}
});
Loop over every page and print the result to the console.
function parsePages(page) {
for (var i = 0; i < page.data.length; i++) {
console.log(page.data[i].toString());
}
if (page.hasNext) {
console.log('Grabbing page ' + (page.page + 1));
page.next().then(parsePage);
}
}
api.colors
.list(1, 50)
.then(parsePages);
Loop over all pages and return the data in a promise
function parsePages(page) {
var data = [];
function parse(page) {
data = data.concat(page.data);
if(page.hasNext) {
return page.next().then(parse);
} else {
return data;
}
}
return parse(page);
}
api.colors
.list(1, 50)
.then(parsePages)
.then(d => console.log('Total rows: ' + d.length));
Select current user but do not fetch any info to make fetching resources easier.
api.users.select('me').colors.list().then(page => {
console.dir(page.data);
});
Searching
Resource lists can be queried to search for specific records as follows:
var query = {
name: '^:test',
scale_min: ['>:1', '<:10'],
}
api.layers.search(query).then(console.dir);
The search
method is an extension of list
. This means that .search({})
is the same as
list()
. More information about search query formatting can be found in the api documentation.
Canceling a requests
Most methods that return a promise will have a method called cancel
. This method can be called
to cancel the request. If the request is running or about to be ran the promise will throw an error
once canceled. If the request has been completed before the promise has been canceled it will not
throw an error and instead complete successfully.
// Fetch a preview
const promise = api.jobs.select(123456).downloadPreview();
// Turns out we don't need it anyways
promise.cancel();
Authentication
Authentication is done through OAuth. This library provides multiple OAuth flow
implementations for authentication. A client id can be obtained through a support
ticket but this is planned to change in the near future. The client will first
check if any tokens can be found in the cache before requiring authentication.
If one can be found the api.authenticate()
method will instantly resolve without
any side-effects. The variable api.authenticated
will be set to true if a token
has been found and is still valid.
Tokens are stored in HTTPS cookies if possible and using localStorage
when the
browser is not using a HTTPS connection. Nodejs uses a file named .m4n_token
to store the token.
Web
Multiple flows are supported for web browsers. All the web examples assume the web build of the library has been included in the target page.
Implicit Flow
A client id is required to use the implicit flow. The redirect url must be the same as the one linked to the client id. The callback url is automatically guessed if none is provided.
// Obtained client id
var clientId = 1;
// Callback url is set to the current url by default
var auth = new ImplicitFlow(clientId);
var api = new Mapcreator(auth);
// This will hijack the page if no authentication cache can
// be found. Smartest thing to do is to just let it happen
// and initialize any other code afterwards.
api.authenticate().then(function() {
// Save the token
api.saveToken();
// Get the current user and dump the result to the console.
api.users.get('me').then(console.dir);
});
Implicit flow pop-up
Just like the Implicit Flow a client id is required.
// Obtained client id
var clientId = 1;
// Callback url is set to the current url by default. The
// script is smart enough close the page if it detects that
// it's a child after authentication. This means that either
// the current page can be set as the callback (default) or
// a custom page that just contains `api.authenticate()`
// that uses ImplicitFlowPopup as the auth parameter.
var auth = new ImplicitFlowPopup(clientId);
var api = new Mapcreator(auth);
// This will create a pop-up window containing the log in
// page. Once the pop-up redirects back to the callback it
// will resolve the promise. The callback page should contain
api.authenticate().then(function() {
// Save the token
api.saveToken();
// Get the current user and dump the result to the console.
api.users.get('me').then(console.dir);
});
Implicit flow pop-up (advanced)
Due to the nature of the implicit flow pop-up (referred to as IFP from now on) method the callback page can be set to a blank page that just grabs the token and then closes. This can be done in the following way.
index.html:
var clientId = 1;
var callbackUrl = 'https://example.com/callback.html';
var auth = new ImplicitFlowPopup(clientId);
var api = new Mapcreator(auth);
// This will resolve once the callback page has been loaded
api.authenticate().then(function() {
// Save the token
api.saveToken();
// Get the current user and dump the result to the console.
api.users.get('me').then(console.dir);
});
callback.html:
var clientId = 1;
// This will instantly detect the token and close the page
new ImplicitFlowPopup(clientId);
Password flow (dangerous)
The password flow is NOT intended to be used in the browser. If you do decide to use the password flow then it is recommended to make sure that the site is NOT public facing and using HTTPS. Leaking the secret is a very bad idea.
var clientId = 1; // client id
var secret = ''; // secret
var username = '[email protected]'; // email is used for authentication
var password = 'Password1!'; // password
// Secret will be leaked if this is used on a webpage. Please only use
// this for non-web applications.
var auth = new PasswordFlow(clientId, secret, username, password);
var api = new Mapcreator(auth);
// This will resolve once the authentication has completed
api.authenticate().then(function() {
// Get the current user and dump the result to the console.
api.users.get('me').then(console.dir);
});
Dummy flow
The dummy flow can be used when a token should be present in the cache.
var auth = new DummyFlow();
var api = new Mapcreator(auth);
// Manually check if we're logged in
if (api.authenticated) {
console.log('Found authentication token in cache!');
}
api.authenticate().then(function() {
// Will only resolve if a token was found
console.log("We're authenticated");
}).catch(function(err) {
// This will be called if `api.authenticated` is false
console.log(err.toString());
});
Nodejs
The library currently only supports the password flow and the dummy flow for nodejs. Other flows might be added in the future.
Password Flow
Make sure to store your secret somewhere safe and to only store the token and never the unencrypted user password.
var clientId = 1; // client id
var secret = ''; // secret
var username = '[email protected]'; // email is used for authentication
var password = 'Password1!'; // password
var auth = new PasswordFlow(clientId, secret, username, password);
var api = new Mapcreator(auth);
// This will resolve once the authentication has completed
api.authenticate().then(function() {
// Get the current user and dump the result to the console.
api.users.get('me').then(console.dir);
});
Dummy flow
The dummy flow can also be used when a token is known.
var auth = new DummyFlow();
var api = new Mapcreator(auth);
var token = {
token: "eyJ0eXAiOiJKV1...",
type: "Bearer",
expires: "Thu, 18 May 2017 14:14:38 GMT"
};
// Set the token
api.auth.token = OAuthToken.fromResponseObject(token);
// Manually check if we're logged in
if (api.authenticated) {
console.log('Found authentication token in cache!');
}
api.authenticate().then(function() {
// Will only resolve if a token was found
console.log("We're authenticated");
}).catch(function(err) {
// This will be called if `api.authenticated` is false
console.log(err.toString());
});
Building
Building the api can be done using [yarn] and [webpack]. The result will be placed in the /dist
folder. Both the web
and nodejs releases are built this way.
yarn install
yarn run build
Building docs
The docs can be built using [esdoc] after which they can be found in the /docs
folder.
yarn install
yarn run docs
Development docs can be built by adding "private"
to the esdoc.access
variable in package.json
before building the
docs.
Installation
Installation can be done through either npm or yarn.
// Using npm
npm install --save @mapcreator/api
// Using yarn
yarn add @mapcreator/api
After installation the package can be imported as follows:
var m4n = require('@mapcreator/api');
// Do stuff
var auth = new m4n.ImplicitFlow(1);
var api = new m4n.Mapcreator(auth);
or using ES6 import statements:
import {Mapcreator, DummyFlow} from '@mapcreator/api';
// Do stuff
var auth = new DummyFlow();
var api = new Mapcreator(auth);