-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2 from CartoDB/feat/add-core-module
Add core module in Web SDK
- Loading branch information
Showing
9 changed files
with
290 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
const DEFAULT_USER_NAME = 'username'; | ||
const DEFAULT_PUBLIC_API_KEY = 'default_public'; | ||
const DEFAULT_SERVER_URL_TEMPLATE = 'https://{user}.carto.com'; | ||
const DEFAULT_USER_COMPONENT_IN_URL = '{user}'; | ||
|
||
/** | ||
* Build a generic instance of credentials, eg to interact with APIs such as Windshaft or SQL | ||
* @param username | ||
* @param apiKey | ||
* @param serverURL A url pattern with {user}, like default 'https://{user}.carto.com' | ||
* | ||
*/ | ||
export class Credentials { | ||
private _username: string; | ||
private _apiKey: string; | ||
private _serverUrlTemplate: string; | ||
|
||
constructor( | ||
username: string, | ||
apiKey: string, | ||
serverUrlTemplate: string = DEFAULT_SERVER_URL_TEMPLATE | ||
) { | ||
if (!username) { | ||
throw new Error('Username is required'); | ||
} | ||
|
||
if (!apiKey) { | ||
throw new Error('Api key is required'); | ||
} | ||
|
||
this._username = username; | ||
this._apiKey = apiKey; | ||
this._serverUrlTemplate = serverUrlTemplate; | ||
} | ||
|
||
public static get DEFAULT_SERVER_URL_TEMPLATE() { | ||
return DEFAULT_SERVER_URL_TEMPLATE; | ||
} | ||
|
||
public static get DEFAULT_PUBLIC_API_KEY() { | ||
return DEFAULT_PUBLIC_API_KEY; | ||
} | ||
|
||
public get username(): string { | ||
return this._username; | ||
} | ||
|
||
public set username(value: string) { | ||
this._username = value; | ||
} | ||
|
||
public get apiKey(): string { | ||
return this._apiKey; | ||
} | ||
|
||
public set apiKey(value: string) { | ||
this._apiKey = value; | ||
} | ||
|
||
public get serverUrlTemplate(): string { | ||
return this._serverUrlTemplate; | ||
} | ||
|
||
public set serverUrlTemplate(value: string) { | ||
this._serverUrlTemplate = value; | ||
} | ||
|
||
public get serverURL(): string { | ||
let url = this._serverUrlTemplate.replace(DEFAULT_USER_COMPONENT_IN_URL, this._username); | ||
|
||
if (!url.endsWith('/')) { | ||
url += '/'; | ||
} | ||
|
||
return url; | ||
} | ||
} | ||
|
||
export const defaultCredentials = new Credentials(DEFAULT_USER_NAME, DEFAULT_PUBLIC_API_KEY); | ||
|
||
export function setDefaultCredentials(credentials: { | ||
username: string; | ||
apiKey: string; | ||
serverUrlTemplate: string; | ||
}) { | ||
defaultCredentials.username = credentials.username; | ||
defaultCredentials.apiKey = credentials.apiKey || DEFAULT_PUBLIC_API_KEY; | ||
defaultCredentials.serverUrlTemplate = | ||
credentials.serverUrlTemplate || DEFAULT_SERVER_URL_TEMPLATE; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { uuidv4 } from './utils'; | ||
|
||
// Custom CARTO headers, for metrics at API level | ||
// Double check they are valid for the API (eg. allowed for CORS requests) | ||
const CUSTOM_HEADER_EVENT_SOURCE = 'Carto-Event-Source'; | ||
const CUSTOM_HEADER_EVENT = 'Carto-Event'; | ||
const CUSTOM_HEADER_EVENT_GROUP_ID = 'Carto-Event-Group-Id'; | ||
|
||
/** | ||
* Class to represent a relevant event, identifying several relevant properties | ||
* of it: source, name and group-id | ||
*/ | ||
class MetricsEvent { | ||
public source: string; | ||
public name: string; | ||
public groupId: string; | ||
|
||
constructor(source: string, name: string, groupId: string = uuidv4()) { | ||
this.source = source; | ||
this.name = name; | ||
this.groupId = groupId; | ||
} | ||
|
||
public getHeaders() { | ||
return [ | ||
[CUSTOM_HEADER_EVENT_SOURCE, this.source], | ||
[CUSTOM_HEADER_EVENT, this.name], | ||
[CUSTOM_HEADER_EVENT_GROUP_ID, this.groupId] | ||
]; | ||
} | ||
} | ||
|
||
export default MetricsEvent; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import { Credentials } from '../Credentials'; | ||
|
||
describe('auth/Credentials', () => { | ||
it('should require a username and an API key', () => { | ||
const creds = new Credentials('aUserName', 'anApiKey'); | ||
|
||
expect(creds.username).toBe('aUserName'); | ||
expect(creds.apiKey).toBe('anApiKey'); | ||
}); | ||
|
||
it('should fail if api key or username are not present', () => { | ||
expect(() => { | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
const creds = new Credentials('aUserName', undefined as any); | ||
expect(creds.username).toBe('aUserName'); | ||
}).toThrow(); | ||
|
||
expect(() => { | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
const creds = new Credentials(undefined as any, 'anApiKey'); | ||
expect(creds.username).toBe(undefined); | ||
}).toThrow(); | ||
}); | ||
|
||
it('has a default server', () => { | ||
const creds = new Credentials('aUser', 'anApiKey'); | ||
expect(creds.serverURL).toBe('https://aUser.carto.com/'); | ||
}); | ||
|
||
it('can manage different servers', () => { | ||
const customServer = 'http://127.0.0.1:8181/user/{user}'; | ||
const creds = new Credentials('aUser', 'anApiKey', customServer); | ||
expect(creds.serverURL).toBe('http://127.0.0.1:8181/user/aUser/'); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import MetricsEvent from '../MetricsEvent'; | ||
|
||
describe('core/MetricsEvent', () => { | ||
it('can be created easily', () => { | ||
const event = new MetricsEvent('mytestlib', 'name of this test'); | ||
|
||
expect(event.source).toBe('mytestlib'); | ||
expect(event.name).toBe('name of this test'); | ||
expect(event.groupId).toBeTruthy(); | ||
}); | ||
|
||
it('can be created with a custom id', () => { | ||
const event = new MetricsEvent('mytestlib', 'name of this test', 'id'); | ||
|
||
expect(event.source).toBe('mytestlib'); | ||
expect(event.name).toBe('name of this test'); | ||
expect(event.groupId).toBe('id'); | ||
}); | ||
|
||
it('can generate proper headers', () => { | ||
const event = new MetricsEvent('mytestlib', 'name of this test', 'id'); | ||
|
||
const headers = event.getHeaders(); | ||
expect(headers).toBeTruthy(); | ||
expect(headers[0]).toEqual(['Carto-Event-Source', 'mytestlib']); | ||
expect(headers[1]).toEqual(['Carto-Event', 'name of this test']); | ||
expect(headers[2]).toEqual(['Carto-Event-Group-Id', 'id']); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
/** | ||
* Represents an error in the carto library. | ||
* | ||
* @typedef {Object} CartoError | ||
* @property {String} message - A short error description | ||
* @property {String} name - The name of the error "CartoError" | ||
* @property {String} type - The type of the error "CartoError" | ||
* @property {Object} originalError - An object containing the internal/original error | ||
* | ||
* @event CartoError | ||
* @api | ||
*/ | ||
export class CartoError extends Error { | ||
/** | ||
* Build a cartoError from a generic error. | ||
* @constructor | ||
* | ||
* @return {CartoError} A well formed object representing the error. | ||
*/ | ||
|
||
constructor(error: { message: string; type: string }) { | ||
if (!error) { | ||
throw Error('Invalid CartoError, a message is mandatory'); | ||
} | ||
|
||
if (!error.message) { | ||
throw Error('Invalid CartoError, a message is mandatory'); | ||
} | ||
|
||
if (!error.type) { | ||
throw Error('Invalid CartoError, a type is mandatory'); | ||
} | ||
|
||
super(`${error.type} ${error.message}`); | ||
this.name = 'CartoError'; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import mitt from 'mitt'; | ||
import { CartoError } from '../errors/CartoError'; | ||
|
||
export abstract class WithEvents { | ||
protected emitter = mitt(); | ||
protected availableEvents = ['*']; | ||
|
||
protected registerAvailableEvents(eventArray: string[]) { | ||
this.availableEvents = ['*', ...eventArray]; | ||
} | ||
|
||
public emit(type: string, event?: unknown) { | ||
if (!this.availableEvents.includes(type)) { | ||
throw new CartoError({ | ||
type: '[Events]', | ||
message: `Trying to emit an unknown event type: ${type}. Available events: ${this.availableEvents.join( | ||
', ' | ||
)}.` | ||
}); | ||
} | ||
|
||
this.emitter.emit(type, event); | ||
} | ||
|
||
on(type: string, handler: mitt.Handler) { | ||
if (!this.availableEvents.includes(type)) { | ||
this.throwEventNotFoundError(type); | ||
} | ||
|
||
this.emitter.on(type, handler); | ||
} | ||
|
||
off(type: string, handler: mitt.Handler) { | ||
if (!this.availableEvents.includes(type)) { | ||
this.throwEventNotFoundError(type); | ||
} | ||
|
||
this.emitter.off(type, handler); | ||
} | ||
|
||
private throwEventNotFoundError(eventType: string) { | ||
throw new CartoError({ | ||
type: '[Events]', | ||
message: `Trying to listen an unknown event type: ${eventType}. Available events: ${this.availableEvents.join( | ||
', ' | ||
)}.` | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
/** | ||
* Unique Identifier v4 | ||
* Adapted from https://stackoverflow.com/a/2117523/251834 | ||
*/ | ||
/* eslint-disable no-bitwise */ | ||
export function uuidv4() { | ||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => { | ||
// tslint:disable-next-line: no-bitwise | ||
const r = (Math.random() * 16) | 0; | ||
// tslint:disable-next-line: no-bitwise | ||
const v = c === 'x' ? r : (r & 0x3) | 0x8; | ||
return v.toString(16); | ||
}); | ||
} |
58c0ea0
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to following URLs: