11 Commits

Author SHA1 Message Date
bc2fbe14e3 Adding SSL support, release 0.2.3 2018-03-11 15:25:44 -07:00
9fc5d64ceb Adding start/end as static Position
Add the streamPosition constants
Move Projections into the expose classes section
Version 0.2.2
2018-02-19 10:06:33 -08:00
2bdd74e041 Merge pull request #53 from paullucas/master
Upgrade jsdoc to version 3.5.5
2018-02-18 17:03:07 -08:00
fde6ad2e77 Use jsdoc version 3.5.5 2018-02-18 16:50:59 -08:00
4a2065c0a7 Release npm package version to 0.2.1 2018-01-17 11:31:25 -08:00
67dd275b5d Fix potential error handling bug due to missing EventEmitter.call(this) in EventStoreNodeConnection and EventStoreConnectionLogicHandler 2018-01-17 11:29:24 -08:00
8997a8c398 Update README 2017-11-08 13:06:16 -08:00
a5807483a2 Merge branch 'v4_proto' 2017-11-08 12:47:30 -08:00
bf89354564 Merge pull request #50 from sledorze/master
fix noopLogger typo fix #49
2017-10-26 10:25:24 -07:00
254ddda8a2 fix noopLogger typo fix #49 2017-10-23 22:11:11 +02:00
16c081020a Add ProjectionsManager 2017-10-18 14:39:25 -07:00
17 changed files with 635 additions and 2866 deletions

6
.idea/misc.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptSettings">
<option name="languageLevel" value="ES5" />
</component>
</project>

View File

@ -5,7 +5,6 @@ A port of the EventStore .Net ClientAPI to Node.js
### Missing features: ### Missing features:
- Ssl connection
- Set system settings - Set system settings
### Areas to improve ### Areas to improve
@ -23,9 +22,16 @@ Install using `npm install node-eventstore-client`
### Dependencies ### Dependencies
- Node.js >= 0.12 - Node.js >= 4.0
- Modules: [long](https://www.npmjs.org/package/long), [protobufjs](https://www.npmjs.org/package/protobufjs), [uuid](https://www.npmjs.org/package/uuid) (installed via `npm install`) - Modules: [long](https://www.npmjs.org/package/long), [protobufjs](https://www.npmjs.org/package/protobufjs), [uuid](https://www.npmjs.org/package/uuid) (installed via `npm install`)
### Install and run an Eventstore on localhost
See https://eventstore.org/docs/introduction/4.1.0/
*Note: If you are using a version of EventStore prior to 3.9.4, you need to use version 0.1.x of this package `npm install node-eventstore-client@^0.1`.*
### API Documentation ### API Documentation
#### Offline #### Offline
@ -35,11 +41,7 @@ The offline documentation can be found in the module folder `./node_modules/node
#### Online #### Online
The online documentation can be found at [https://dev.nicdex.com/node-eventstore-client/docs/](https://dev.nicdex.com/node-eventstore-client/docs/) The online documentation can be found at [https://dev.nicdex.com/node-eventstore-client/docs/](https://dev.nicdex.com/node-eventstore-client/docs/)
### Install & run an Eventstore on localhost
See http://docs.geteventstore.com/introduction/3.9.0/ .
### Example: Storing an event ### Example: Storing an event
Save to ```app.js:``` Save to ```app.js:```
@ -112,7 +114,15 @@ To generate a test event, open a separate console and run:
To run the tests it is recommended that you use an in-memory instance of the eventstore so you don't pollute your dev instance. To run the tests it is recommended that you use an in-memory instance of the eventstore so you don't pollute your dev instance.
EventStore.ClusterNode.exe --memdb EventStore.ClusterNode.exe --run-projections=all --memdb certificate-file=yourcert.pfx
or
./run-node.sh --run-projections=all --memdb certificate-file=yourcert.p12
For SSL setup see:
https://eventstore.org/docs/server/setting_up_ssl/
or
https://eventstore.org/docs/server/setting_up_ssl_linux/
To execute the tests suites simply run To execute the tests suites simply run

87
index.d.ts vendored
View File

@ -1,14 +1,15 @@
/// <reference types="node" /> /// <reference types="node" />
/// <reference types="Long" /> /// <reference types="Long" />
// Expose classes
export class Position { export class Position {
constructor(commitPosition: number|Long, preparePosition: number|Long); constructor(commitPosition: number|Long, preparePosition: number|Long);
readonly commitPosition: Long; readonly commitPosition: Long;
readonly preparePosition: Long; readonly preparePosition: Long;
static readonly start: number;
static readonly end: number;
} }
// Expose classes
export class UserCredentials { export class UserCredentials {
constructor(username: string, password: string); constructor(username: string, password: string);
readonly username: string; readonly username: string;
@ -26,7 +27,7 @@ export class PersistentSubscriptionSettings {
export namespace SystemConsumerStrategies { export namespace SystemConsumerStrategies {
const DispatchToSingle: string; const DispatchToSingle: string;
const RoundRobin: string; const RoundRobin: string;
const Pinned: string const Pinned: string;
} }
export class GossipSeed { export class GossipSeed {
@ -35,6 +36,57 @@ export class GossipSeed {
readonly hostHeader: string; readonly hostHeader: string;
} }
export interface ProjectionDetails {
readonly coreProcessingTime: number,
readonly version: number,
readonly epoch: number,
readonly effectiveName: string,
readonly writesInProgress: number,
readonly readsInProgress: number,
readonly partitionsCached: number,
readonly status: string,
readonly stateReason: string,
readonly name: string,
readonly mode: string,
readonly position: string,
readonly progress: number,
readonly lastCheckpoint: string,
readonly eventsProcessedAfterRestart: number,
readonly statusUrl: string,
readonly stateUrl: string,
readonly resultUrl: string,
readonly queryUrl: string,
readonly enableCommandUrl: string,
readonly disableCommandUrl: string,
readonly checkpointStatus: string,
readonly bufferedEvents: number,
readonly writePendingEventsBeforeCheckpoint: number,
readonly writePendingEventsAfterCheckpoint: number
}
export class ProjectionsManager {
constructor(log: Logger, httpEndPoint: string, operationTimeout: number);
enable(name: string, userCredentials: UserCredentials): Promise<void>;
disable(name: string, userCredentials: UserCredentials): Promise<void>;
abort(name: string, userCredentials: UserCredentials): Promise<void>;
createOneTime(query: string, userCredentials: UserCredentials): Promise<void>;
createTransient(name: string, query: string, userCredentials: UserCredentials): Promise<void>;
createContinuous(name: string, query: string, trackEmittedStreams: boolean, userCredentials: UserCredentials): Promise<void>;
listAll(userCredentials: UserCredentials): Promise<ProjectionDetails[]>;
listOneTime(userCredentials: UserCredentials): Promise<ProjectionDetails[]>;
listContinuous(userCredentials: UserCredentials): Promise<ProjectionDetails[]>;
getStatus(name: string, userCredentials: UserCredentials): Promise<string>;
getState(name: string, userCredentials: UserCredentials): Promise<string>;
getPartitionState(name: string, partitionId: string, userCredentials: UserCredentials): Promise<string>;
getResult(name: string, userCredentials: UserCredentials): Promise<string>;
getPartitionResult(name: string, partitionId: string, userCredentials: UserCredentials): Promise<string>;
getStatistics(name: string, userCredentials: UserCredentials): Promise<string>;
getQuery(name: string, userCredentials: UserCredentials): Promise<string>;
getState(name: string, userCredentials: UserCredentials): Promise<string>;
updateQuery(name: string, query: string, userCredentials: UserCredentials): Promise<void>;
deleteQuery(name: string, deleteEmittedStreams: boolean, userCredentials: UserCredentials): Promise<void>;
}
// Expose errors // Expose errors
export class WrongExpectedVersionError { export class WrongExpectedVersionError {
readonly name: string; readonly name: string;
@ -59,8 +111,12 @@ export class AccessDeniedError {
readonly transactionId?: Long; readonly transactionId?: Long;
} }
// Expose enums/constants export class ProjectionCommandFailedError {
readonly httpStatusCode: number;
readonly message: string;
}
// Expose enums/constants
export namespace expectedVersion { export namespace expectedVersion {
const any: number; const any: number;
const noStream: number; const noStream: number;
@ -72,19 +128,24 @@ export namespace positions {
const end: Position; const end: Position;
} }
export namespace streamPosition {
const start: number;
const end: number;
}
//TODO
// systemMetadata // systemMetadata
// eventReadStatus // eventReadStatus
// sliceReadStatus // sliceReadStatus
// Expose loggers // Expose loggers
export interface Logger { export interface Logger {
debug(fmt: string, ...args: any[]): void; debug(fmt: string, ...args: any[]): void;
info(fmt: string, ...args: any[]): void; info(fmt: string, ...args: any[]): void;
error(fmt: string, ...args: any[]): void; error(fmt: string, ...args: any[]): void;
} }
export class NoOpLogger implements Logger { export class NoopLogger implements Logger {
constructor() constructor()
debug(fmt: string, ...args: any[]): void; debug(fmt: string, ...args: any[]): void;
info(fmt: string, ...args: any[]): void; info(fmt: string, ...args: any[]): void;
@ -228,12 +289,12 @@ export interface TcpEndPoint {
} }
export interface HeartbeatInfo { export interface HeartbeatInfo {
connectionId: string; readonly connectionId: string;
remoteEndPoint: TcpEndPoint; readonly remoteEndPoint: TcpEndPoint;
requestSentAt: number; readonly requestSentAt: number;
requestPkgNumber: number; readonly requestPkgNumber: number;
responseReceivedAt: number; readonly responseReceivedAt: number;
responsePkgNumber: number; readonly responsePkgNumber: number;
} }
export interface EventData { export interface EventData {
@ -277,7 +338,6 @@ export interface EventStoreNodeConnection {
} }
// Expose helper functions // Expose helper functions
export interface ConnectionSettings { export interface ConnectionSettings {
log?: Logger, log?: Logger,
verboseLogging?: boolean, verboseLogging?: boolean,
@ -310,6 +370,7 @@ export interface ConnectionSettings {
gossipTimeout?: number gossipTimeout?: number
} }
// Expose Helper functions
export function createConnection(settings: ConnectionSettings, endPointOrGossipSeed: string | TcpEndPoint | GossipSeed[], connectionName?: string): EventStoreNodeConnection; export function createConnection(settings: ConnectionSettings, endPointOrGossipSeed: string | TcpEndPoint | GossipSeed[], connectionName?: string): EventStoreNodeConnection;
export function createJsonEventData(eventId: string, event: any, metadata?: any, type?: string): EventData; export function createJsonEventData(eventId: string, event: any, metadata?: any, type?: string): EventData;
export function createEventData(eventId: string, type: string, isJson: boolean, data: Buffer, metadata?: Buffer): EventData; export function createEventData(eventId: string, type: string, isJson: boolean, data: Buffer, metadata?: Buffer): EventData;

View File

@ -1,9 +1,13 @@
{ {
"name": "node-eventstore-client", "name": "node-eventstore-client",
"version": "0.2.0", "version": "0.2.3",
"description": "A port of the EventStore .Net ClientAPI to Node.js", "description": "A port of the EventStore .Net ClientAPI to Node.js",
"main": "index.js", "main": "index.js",
"types": "index.d.ts", "types": "index.d.ts",
"engines": {
"node": ">=4.0"
},
"engineStrict": true,
"scripts": { "scripts": {
"clean": "rm lib/dist.js", "clean": "rm lib/dist.js",
"build": "webpack", "build": "webpack",
@ -48,7 +52,7 @@
"uuid": "^3.0.1" "uuid": "^3.0.1"
}, },
"devDependencies": { "devDependencies": {
"jsdoc": "^3.5.3", "jsdoc": "^3.5.5",
"nodeunit": "^0.11.1", "nodeunit": "^0.11.1",
"webpack": "^3.3.0" "webpack": "^3.3.0"
} }

View File

@ -4,15 +4,23 @@ var results = require('./results');
const expectedVersion = { const expectedVersion = {
any: -2, any: -2,
noStream: -1, noStream: -1,
emptyStream: -1 emptyStream: -1,
streamExists: -4
}; };
Object.freeze(expectedVersion); Object.freeze(expectedVersion);
const positions = { const positions = {
start: new results.Position(0, 0), start: new results.Position(0, 0),
end: new results.Position(-1, -1) end: new results.Position(-1, -1)
}; };
Object.freeze(positions); Object.freeze(positions);
const streamPosition = {
start: 0,
end: -1
};
Object.freeze(streamPosition);
/** /**
* Create an EventData object from JavaScript event/metadata that will be serialized as json * Create an EventData object from JavaScript event/metadata that will be serialized as json
* @public * @public
@ -51,13 +59,16 @@ module.exports.PersistentSubscriptionSettings = require('./persistentSubscriptio
module.exports.SystemConsumerStrategies = require('./systemConsumerStrategies'); module.exports.SystemConsumerStrategies = require('./systemConsumerStrategies');
module.exports.GossipSeed = require('./gossipSeed'); module.exports.GossipSeed = require('./gossipSeed');
module.exports.EventStoreConnection = require('./eventStoreConnection'); module.exports.EventStoreConnection = require('./eventStoreConnection');
module.exports.ProjectionsManager = require('./projections/projectionsManager');
// Expose errors // Expose errors
module.exports.WrongExpectedVersionError = require('./errors/wrongExpectedVersionError'); module.exports.WrongExpectedVersionError = require('./errors/wrongExpectedVersionError');
module.exports.StreamDeletedError = require('./errors/streamDeletedError'); module.exports.StreamDeletedError = require('./errors/streamDeletedError');
module.exports.AccessDeniedError = require('./errors/accessDeniedError'); module.exports.AccessDeniedError = require('./errors/accessDeniedError');
module.exports.ProjectionCommandFailedError = require('./errors/projectionCommandFailedError');
// Expose enums/constants // Expose enums/constants
module.exports.expectedVersion = expectedVersion; module.exports.expectedVersion = expectedVersion;
module.exports.positions = positions; module.exports.positions = positions;
module.exports.streamPosition = streamPosition;
module.exports.systemMetadata = require('./common/systemMetadata'); module.exports.systemMetadata = require('./common/systemMetadata');
module.exports.eventReadStatus = results.EventReadStatus; module.exports.eventReadStatus = results.EventReadStatus;
module.exports.sliceReadStatus = require('./sliceReadStatus'); module.exports.sliceReadStatus = require('./sliceReadStatus');

View File

@ -47,6 +47,7 @@ const ClientVersion = 1;
* @property {Number} totalOperationCount * @property {Number} totalOperationCount
*/ */
function EventStoreConnectionLogicHandler(esConnection, settings) { function EventStoreConnectionLogicHandler(esConnection, settings) {
EventEmitter.call(this);
this._esConnection = esConnection; this._esConnection = esConnection;
this._settings = settings; this._settings = settings;
this._queue = new SimpleQueuedHandler(); this._queue = new SimpleQueuedHandler();

View File

@ -0,0 +1,10 @@
const util = require('util');
function ProjectionCommandFailedError(httpStatusCode, message) {
Error.captureStackTrace(this, this.constructor);
this.httpStatusCode = httpStatusCode;
this.message = message;
}
util.inherits(ProjectionCommandFailedError, Error);
module.exports = ProjectionCommandFailedError;

View File

@ -38,6 +38,7 @@ const MaxReadSize = 4096;
* @constructor * @constructor
*/ */
function EventStoreNodeConnection(settings, clusterSettings, endpointDiscoverer, connectionName) { function EventStoreNodeConnection(settings, clusterSettings, endpointDiscoverer, connectionName) {
EventEmitter.call(this);
this._connectionName = connectionName || ['ES-', uuid.v4()].join(''); this._connectionName = connectionName || ['ES-', uuid.v4()].join('');
this._settings = settings; this._settings = settings;
this._clusterSettings = clusterSettings; this._clusterSettings = clusterSettings;

View File

@ -0,0 +1,56 @@
function ProjectionDetails(
coreProcessingTime,
version,
epoch,
effectiveName,
writesInProgress,
readsInProgress,
partitionsCached,
status,
stateReason,
name,
mode,
position,
progress,
lastCheckpoint,
eventsProcessedAfterRestart,
statusUrl,
stateUrl,
resultUrl,
queryUrl,
enableCommandUrl,
disableCommandUrl,
checkpointStatus,
bufferedEvents,
writePendingEventsBeforeCheckpoint,
writePendingEventsAfterCheckpoint
) {
this.coreProcessingTime = coreProcessingTime;
this.version = version;
this.epoch = epoch;
this.effectiveName = effectiveName;
this.writesInProgress = writesInProgress;
this.readsInProgress = readsInProgress;
this.partitionsCached = partitionsCached;
this.status = status;
this.stateReason = stateReason;
this.name = name;
this.mode = mode;
this.position = position;
this.progress = progress;
this.lastCheckpoint = lastCheckpoint;
this.eventsProcessedAfterRestart = eventsProcessedAfterRestart;
this.statusUrl = statusUrl;
this.stateUrl = stateUrl;
this.resultUrl = resultUrl;
this.queryUrl = queryUrl;
this.enableCommandUrl = enableCommandUrl;
this.disableCommandUrl = disableCommandUrl;
this.checkpointStatus = checkpointStatus;
this.bufferedEvents = bufferedEvents;
this.writePendingEventsBeforeCheckpoint = writePendingEventsBeforeCheckpoint;
this.writePendingEventsAfterCheckpoint = writePendingEventsAfterCheckpoint;
Object.freeze(this);
}
module.exports = ProjectionDetails;

View File

@ -0,0 +1,170 @@
const http = require('http');
const url = require('url');
const util = require('util');
const ProjectionCommandFailedError = require('../errors/projectionCommandFailedError');
const HTTP_OK = 200;
const HTTP_CREATED = 201;
function safeParseJson(json) {
try {
return JSON.parse(json);
} catch(e) {
return null;
}
}
function ProjectionsClient(log, operationTimeout) {
this._log = log;
this._operationTimeout = operationTimeout;
}
ProjectionsClient.prototype.enable = function(httpEndPoint, name, userCredentials) {
return this.sendPost(httpEndPoint + '/projection/' + name + '/command/enable', '', userCredentials, HTTP_OK);
};
ProjectionsClient.prototype.disable = function(httpEndPoint, name, userCredentials) {
return this.sendPost(httpEndPoint + '/projection/' + name + '/command/disable', '', userCredentials, HTTP_OK);
};
ProjectionsClient.prototype.abort = function(httpEndPoint, name, userCredentials) {
return this.sendPost(httpEndPoint + '/projection/' + name + '/command/abort', '', userCredentials, HTTP_OK);
};
ProjectionsClient.prototype.createOneTime = function(httpEndPoint, query, userCredentials) {
return this.sendPost(httpEndPoint + '/projections/onetime?type=JS', query, userCredentials, HTTP_CREATED);
};
ProjectionsClient.prototype.createTransient = function(httpEndPoint, name, query, userCredentials) {
return this.sendPost(httpEndPoint + '/projections/transient?name=' + name + '&type=JS', query, userCredentials, HTTP_CREATED);
};
ProjectionsClient.prototype.createContinuous = function(httpEndPoint, name, query, trackEmittedStreams, userCredentials) {
return this.sendPost(httpEndPoint + '/projections/continuous?name=' + name + '&type=JS&emit=1&trackEmittedStreams=' + trackEmittedStreams, query, userCredentials, HTTP_CREATED);
};
ProjectionsClient.prototype.listAll = function(httpEndPoint, userCredentials) {
return this.sendGet(httpEndPoint + '/projections/any', userCredentials, HTTP_OK)
.then(function (json) {
var r = safeParseJson(json);
if (r && r.projections) return r.projections;
return null;
});
};
ProjectionsClient.prototype.listOneTime = function(httpEndPoint, userCredentials) {
return this.sendGet(httpEndPoint + '/projections/onetime', userCredentials, HTTP_OK)
.then(function (json) {
var r = safeParseJson(json);
if (r && r.projections) return r.projections;
return null;
});
};
ProjectionsClient.prototype.listContinuous = function(httpEndPoint, userCredentials) {
return this.sendGet(httpEndPoint + '/projections/continuous', userCredentials, HTTP_OK)
.then(function (json) {
var r = safeParseJson(json);
if (r && r.projections) return r.projections;
return null;
});
};
ProjectionsClient.prototype.getStatus = function(httpEndPoint, name, userCredentials) {
return this.sendGet(httpEndPoint + '/projection/' + name, userCredentials, HTTP_OK);
};
ProjectionsClient.prototype.getState = function(httpEndPoint, name, userCredentials) {
return this.sendGet(httpEndPoint + '/projection/' + name + '/state', userCredentials, HTTP_OK);
};
ProjectionsClient.prototype.getPartitionState = function(httpEndPoint, name, partitionId, userCredentials) {
return this.sendGet(httpEndPoint + '/projection/' + name + '/state?partition=' + partitionId, userCredentials, HTTP_OK);
};
ProjectionsClient.prototype.getResult = function(httpEndPoint, name, userCredentials) {
return this.sendGet(httpEndPoint + '/projection/' + name + '/result', userCredentials, HTTP_OK);
};
ProjectionsClient.prototype.getPartitionResult = function(httpEndPoint, name, partitionId, userCredentials) {
return this.sendGet(httpEndPoint + '/projection/' + name + '/result?partition=' + partitionId, userCredentials, HTTP_OK);
};
ProjectionsClient.prototype.getStatistics = function(httpEndPoint, name, userCredentials) {
return this.sendGet(httpEndPoint + '/projection/' + name + '/statistics', userCredentials, HTTP_OK);
};
ProjectionsClient.prototype.getQuery = function(httpEndPoint, name, userCredentials) {
return this.sendGet(httpEndPoint + '/projection/' + name + '/query', userCredentials, HTTP_OK);
};
ProjectionsClient.prototype.updateQuery = function(httpEndPoint, name, query, userCredentials) {
return this.sendPut(httpEndPoint + '/projection/' + name + '/query?type=JS', query, userCredentials, HTTP_OK);
};
ProjectionsClient.prototype.delete = function(httpEndPoint, name, deleteEmittedStreams, userCredentials) {
return this.sendDelete(httpEndPoint + '/projection/' + name + '?deleteEmittedStreams=' + deleteEmittedStreams, userCredentials, HTTP_OK);
};
ProjectionsClient.prototype.request = function(method, _url, data, userCredentials, expectedCode) {
const options = url.parse(_url);
options.method = method;
if (userCredentials) {
options.auth = [userCredentials.username, userCredentials.password].join(':');
}
var self = this;
return new Promise(function (resolve, reject) {
const timeout = setTimeout(function () {
reject(new Error(util.format('Request timed out for %s on %s', method, _url)))
}, self._operationTimeout);
const req = http.request(options, function (res) {
const hasExpectedCode = res.statusCode === expectedCode;
var result = '';
res.setEncoding('utf8');
res.on('data', function (chunk) {
result += chunk;
});
res.on('end', function () {
if (hasExpectedCode) {
clearTimeout(timeout);
resolve(result);
} else {
clearTimeout(timeout);
reject(new ProjectionCommandFailedError(
res.statusCode,
util.format('Server returned %d (%s) for %s on %s', res.statusCode, res.statusMessage, method, _url)
));
}
});
});
req.on('error', reject);
if (data) {
req.setHeader('Content-Type', 'application/json');
req.write(data);
}
req.end();
});
};
function voidResult() {}
ProjectionsClient.prototype.sendGet = function(_url, userCredentials, expectedCode) {
return this.request('GET', _url, null, userCredentials, expectedCode);
};
ProjectionsClient.prototype.sendPost = function(_url, data, userCredentials, expectedCode) {
return this.request('POST', _url, data, userCredentials, expectedCode)
.then(voidResult);
};
ProjectionsClient.prototype.sendPut = function(_url, data, userCredentials, expectedCode) {
return this.request('PUT', _url, data, userCredentials, expectedCode)
.then(voidResult);
};
ProjectionsClient.prototype.sendDelete = function(_url, data, userCredentials, expectedCode) {
return this.request('DELETE', _url, data, userCredentials, expectedCode)
.then(voidResult);
};
module.exports = ProjectionsClient;

View File

@ -0,0 +1,202 @@
const ensure = require('../common/utils/ensure');
const ProjectionsClient = require('./projectionsClient');
/**
* Creates a new instance of ProjectionsManager.
* @param {Logger} log Instance of Logger to use for logging.
* @param {string} httpEndPoint HTTP endpoint of an Event Store server.
* @param {number} operationTimeout Operation timeout in milliseconds.
* @constructor
*/
function ProjectionsManager(log, httpEndPoint, operationTimeout) {
ensure.notNull(log, "log");
ensure.notNull(httpEndPoint, "httpEndPoint");
this._client = new ProjectionsClient(log, operationTimeout);
this._httpEndPoint = httpEndPoint;
}
/**
* Enables a projection.
* @param name The name of the projection.
* @param userCredentials Credentials for a user with permission to enable a projection.
* @returns {Promise<void>}
*/
ProjectionsManager.prototype.enable = function(name, userCredentials) {
return this._client.enable(this._httpEndPoint, name, userCredentials);
};
/**
* Aborts and disables a projection without writing a checkpoint.
* @param name The name of the projection.
* @param userCredentials Credentials for a user with permission to disable a projection.
* @returns {Promise<void>}
*/
ProjectionsManager.prototype.disable = function(name, userCredentials) {
return this._client.disable(this._httpEndPoint, name, userCredentials);
};
/**
* Disables a projection.
* @param name The name of the projection.
* @param userCredentials Credentials for a user with permission to disable a projection.
* @returns {Promise<void>}
*/
ProjectionsManager.prototype.abort = function(name, userCredentials) {
return this._client.abort(this._httpEndPoint, name, userCredentials);
};
/**
* Creates a one-time query.
* @param query The JavaScript source code for the query.
* @param userCredentials Credentials for a user with permission to create a query.
* @returns {Promise<void>}
*/
ProjectionsManager.prototype.createOneTime = function(query, userCredentials) {
return this._client.createOneTime(this._httpEndPoint, query, userCredentials);
};
/**
* Creates a one-time query.
* @param name A name for the query.
* @param query The JavaScript source code for the query.
* @param userCredentials Credentials for a user with permission to create a query.
* @returns {Promise<void>}
*/
ProjectionsManager.prototype.createTransient = function(name, query, userCredentials) {
return this._client.createTransient(this._httpEndPoint, query, userCredentials);
};
/**
* Creates a one-time query.
* @param name The name of the projection.
* @param query The JavaScript source code for the query.
* @param trackEmittedStreams Whether the streams emitted by this projection should be tracked.
* @param userCredentials Credentials for a user with permission to create a query.
* @returns {Promise<void>}
*/
ProjectionsManager.prototype.createContinuous = function(name, query, trackEmittedStreams, userCredentials) {
return this._client.createContinuous(this._httpEndPoint, name, query, trackEmittedStreams, userCredentials);
};
/**
* Lists the status of all projections.
* @param userCredentials Credentials for the operation.
* @returns {Promise<ProjectionDetails[]>}
*/
ProjectionsManager.prototype.listAll = function(userCredentials) {
return this._client.listAll(this._httpEndPoint, userCredentials);
};
/**
* Lists the status of all one-time projections.
* @param userCredentials Credentials for the operation.
* @returns {Promise<ProjectionDetails[]>}
*/
ProjectionsManager.prototype.listOneTime = function(userCredentials) {
return this._client.listOneTime(this._httpEndPoint, userCredentials);
};
/**
* Lists the status of all continuous projections.
* @param userCredentials Credentials for the operation.
* @returns {Promise<ProjectionDetails[]>}
*/
ProjectionsManager.prototype.listContinuous = function(userCredentials) {
return this._client.listContinuous(this._httpEndPoint, userCredentials);
};
/**
* Gets the status of a projection.
* @param name The name of the projection.
* @param userCredentials Credentials for the operation.
* @returns {Promise<string>} String of JSON containing projection status.
*/
ProjectionsManager.prototype.getStatus = function(name, userCredentials) {
return this._client.getStatus(this._httpEndPoint, name, userCredentials);
};
/**
* Gets the state of a projection.
* @param name The name of the projection.
* @param userCredentials Credentials for the operation.
* @returns {Promise<string>} String of JSON containing projection state.
*/
ProjectionsManager.prototype.getState = function(name, userCredentials) {
return this._client.getState(this._httpEndPoint, name, userCredentials);
};
/**
* Gets the state of a projection for a specified partition.
* @param name The name of the projection.
* @param partitionId The id of the partition.
* @param userCredentials Credentials for the operation.
* @returns {Promise<string>} String of JSON containing projection state.
*/
ProjectionsManager.prototype.getPartitionState = function(name, partitionId, userCredentials) {
return this._client.getPartitionState(this._httpEndPoint, name, partitionId, userCredentials);
};
/**
* Gets the state of a projection.
* @param name The name of the projection.
* @param userCredentials Credentials for the operation.
* @returns {Promise<string>} String of JSON containing projection state.
*/
ProjectionsManager.prototype.getResult = function(name, userCredentials) {
return this._client.getResult(this._httpEndPoint, name, userCredentials);
};
/**
* Gets the state of a projection for a specified partition.
* @param name The name of the projection.
* @param partitionId The id of the partition.
* @param userCredentials Credentials for the operation.
* @returns {Promise<string>} String of JSON containing projection state.
*/
ProjectionsManager.prototype.getPartitionResult = function(name, partitionId, userCredentials) {
return this._client.getPartitionResult(this._httpEndPoint, name, partitionId, userCredentials);
};
/**
* Gets the statistics of a projection.
* @param name The name of the projection.
* @param userCredentials Credentials for the operation.
* @returns {Promise<string>} String of JSON containing projection statistics.
*/
ProjectionsManager.prototype.getStatistics = function(name, userCredentials) {
return this._client.getStatistics(this._httpEndPoint, name, userCredentials);
};
/**
* Gets the status of a query.
* @param name The name of the query.
* @param userCredentials Credentials for the operation.
* @returns {Promise<string>} String of JSON containing query status.
*/
ProjectionsManager.prototype.getQuery = function(name, userCredentials) {
return this._client.getQuery(this._httpEndPoint, name, userCredentials);
};
/**
* Updates the definition of a query.
* @param name The name of the query.
* @param query The JavaScript source code for the query.
* @param userCredentials Credentials for the operation.
* @returns {Promise<void>}
*/
ProjectionsManager.prototype.updateQuery = function(name, query, userCredentials) {
return this._client.updateQuery(this._httpEndPoint, name, query, userCredentials);
};
/**
* Updates the definition of a query.
* @param name The name of the projection.
* @param deleteEmittedStreams Whether to delete the streams that were emitted by this projection.
* @param userCredentials Credentials for a user with permission to delete a projection.
* @returns {Promise<void>}
*/
ProjectionsManager.prototype.delete = function(name, deleteEmittedStreams, userCredentials) {
return this._client.delete(this._httpEndPoint, name, deleteEmittedStreams, userCredentials);
};
module.exports = ProjectionsManager;

View File

@ -30,6 +30,8 @@ Position.prototype.toString = function() {
return [this.commitPosition.toString(), this.preparePosition.toString()].join("/"); return [this.commitPosition.toString(), this.preparePosition.toString()].join("/");
}; };
Position.start = new Position(0,0);
Position.end = new Position(-1,-1);
const EventReadStatus = { const EventReadStatus = {
Success: 'success', Success: 'success',

View File

@ -1,4 +1,5 @@
var net = require('net'); var net = require('net');
var tls = require('tls');
var createBufferSegment = require('../../common/bufferSegment'); var createBufferSegment = require('../../common/bufferSegment');
const MaxSendPacketSize = 64 * 1024; const MaxSendPacketSize = 64 * 1024;
@ -136,11 +137,16 @@ TcpConnection.prototype._closeInternal = function(err, reason) {
}; };
TcpConnection.createConnectingConnection = function( TcpConnection.createConnectingConnection = function(
log, connectionId, remoteEndPoint, connectionTimeout, log, connectionId, remoteEndPoint, ssl, targetHost, validateServer,
onConnectionEstablished, onConnectionFailed, onConnectionClosed connectionTimeout, onConnectionEstablished, onConnectionFailed, onConnectionClosed
) { ) {
var connection = new TcpConnection(log, connectionId, remoteEndPoint, onConnectionClosed); var connection = new TcpConnection(log, connectionId, remoteEndPoint, onConnectionClosed);
var socket = net.connect(remoteEndPoint.port, remoteEndPoint.host); var provider = ssl ? tls : net;
var options = {
servername: targetHost,
rejectUnauthorized: validateServer
};
var socket = provider.connect(remoteEndPoint.port, remoteEndPoint.host, options);
function onError(err) { function onError(err) {
if (onConnectionFailed) if (onConnectionFailed)
onConnectionFailed(connection, err); onConnectionFailed(connection, err);

View File

@ -38,15 +38,14 @@ function TcpPackageConnection(
this._framer = new LengthPrefixMessageFramer(); this._framer = new LengthPrefixMessageFramer();
this._framer.registerMessageArrivedCallback(this._incomingMessageArrived.bind(this)); this._framer.registerMessageArrivedCallback(this._incomingMessageArrived.bind(this));
//TODO ssl
var self = this; var self = this;
this._connection = TcpConnection.createConnectingConnection( this._connection = TcpConnection.createConnectingConnection(
log, log,
connectionId, connectionId,
remoteEndPoint, remoteEndPoint,
//ssl, ssl,
//targetHost, targetHost,
//validateServer, validateServer,
timeout, timeout,
function(tcpConnection) { function(tcpConnection) {
log.debug("TcpPackageConnection: connected to [%j, L%j, %s].", tcpConnection.remoteEndPoint, tcpConnection.localEndPoint, connectionId); log.debug("TcpPackageConnection: connected to [%j, L%j, %s].", tcpConnection.remoteEndPoint, tcpConnection.localEndPoint, connectionId);

View File

@ -72,7 +72,24 @@ module.exports = {
if (err) return test.done(err); if (err) return test.done(err);
test.done(); test.done();
} }
}*/ }*/,
'Connect to secure tcp endpoint': function(test) {
var conn = client.createConnection({
useSslConnection: true,
targetHost: 'localhost',
validateServer: false
}, 'tcp://localhost:1115');
conn.on('error', function (err) {
test.done(err);
});
conn.connect()
.catch(function (err) {
test.done(err);
});
conn.on('connected', function () {
test.done();
});
}
}; };
testBase.init(module.exports, false); testBase.init(module.exports, false);

47
test/projections_test.js Normal file
View File

@ -0,0 +1,47 @@
const client = require('../src/client');
const userCredentials = new client.UserCredentials('admin', 'changeit');
const log = new client.NoopLogger();
const httpEndpoint = 'http://127.0.0.1:2113';
const operationTimeout = 5000;
const simpleProjection = "\
fromStream('$stats-127.0.0.1:2113')\
.when({\
$init: function(){\
return {\
count: 0\
}\
},\
$any: function(s,e){\
s.count += 1;\
}\
})\
";
module.exports = {
setUp: function(cb) {
this.projectionsManager = new client.ProjectionsManager(log, httpEndpoint, operationTimeout);
cb();
},
'Create One Time Projection Happy Path': function(test) {
test.expect(1);
this.projectionsManager.createOneTime(simpleProjection, userCredentials)
.then(function (result) {
test.equal(result, undefined);
test.done();
})
.catch(test.done);
},
'List All Happy Path': function(test) {
test.expect(1);
this.projectionsManager.listAll(userCredentials)
.then(function (projections) {
test.ok(projections.length > 0, "no projections");
test.done();
})
.catch(test.done);
}
//TODO: other tests
};

2834
yarn.lock

File diff suppressed because it is too large Load Diff