Skip to content

Commit

Permalink
Merge pull request #8 from alechenninger/http
Browse files Browse the repository at this point in the history
Actually make http requests (closes #4)
  • Loading branch information
alechenninger committed Apr 26, 2015
2 parents 6776648 + 4a70664 commit 147af88
Show file tree
Hide file tree
Showing 18 changed files with 648 additions and 176 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ node_modules/
*.sublime*
sketch
test.html
.c9/
*.log
98 changes: 47 additions & 51 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,44 +2,73 @@

A [lightblue](https://github.com/lightblue-platform) client written in Javascript.

Conceivably one day useful for:
Write...
- Node.JS apps talking to a Lightblue REST service
- Client side apps communicating with a server that forwards requests to a Lightblue REST service

At the moment this is really just a rough sketch of an idea and will change drastically.

# Install
## Install

`bower install lightblue.js --save`

`npm install lightblue.js --save`

`git clone https://github.com/alechenninger/lightblue.js.git`

# Usage

It does still sort of work! It won't actually make any requests, but gives you an API for building the key components of the request: the HTTP method, the URL, and the request body. The idea is this information could then be used easily with either XMLHttpRequest, jQuery.ajax, Angular's $http service, or a Node.JS HTTP client. All it takes is a little glue code to tie the necessary components with one of the aforementioned common AJAX mechanisms.

Use browserify `require` or commonjs `define`, or just include dist/lightblue.min.js and use the namespace `lightblue`.
## Imports

## Imports:
### Vanilla.js

```javascript
// Plain old HTML
<script src="lightblue.min.js"></script>
// No module framework (use window.lightblue)
<script src="lightblue.min.js" type="text/javascript"></script>
```

// NodeJS or Browserify
var lightblue = require("./lightblue.min.js");
### Browserify (CommonJS) or RequireJS (AMD)

// CommonJS and RequireJS work too but I don't have an example
```js
// commonjs
var lightblue = require("lightblue");

// asynchronous module definition (amd)
require(["lightblue"], function(lightblue) {
...
});
```

## Construct a find request:
Once you have a `lightblue` object, you can get a client:

```javascript
```js
// Assumes /data and /metadata for data and metadata services respectively,
// but you can override.
var client = lightblue.getClient("http://my.lightblue.host.com/rest");
```

### AngularJS
If angular is detected, a "lightblue" module will be registered with a
"lightblue" service as the client.

```js
var app = angular.module("app", ["lightblue"]);

app.config(["lightblueProvider", function(lightblueProvider) {
lightblueProvider.setHost("http://my.lightblue.com");
}]);

app.controller("ctrl", ["lightblue", function(lightblueClient) {
lightblueClient.data.find(...)
.then(...);
}]);
```

**At the moment you will also need to use the global "lightblue" namespace if
you want query builder API. So don't name your client variable `lightblue`
just yet. See
[issue #9](https://github.com/alechenninger/lightblue.js/issues/9).**

## Construct a find request

```javascript
var field = lightblue.field;

var find = client.data.find({
Expand All @@ -51,39 +80,6 @@ var find = client.data.find({
.and(field("age").greaterThan(4))),
// No projection builder yet but it would be something like this:
projection: include("*").recursively()
});

assertEquals("http://my.lightblue.host.com/rest/data/find/User/1.0.0", find.url);
assertEquals("post", find.method);
assertEquals({
objectType: "User",
version: "1.0.0",
query: {
$or: [
{
field: "username",
op: "$eq",
rvalue: "bob"
},
{
$and: [
{
field: "firstName",
op: "$eq",
rfield: "username"
},
{
field: "age",
op: "$gt",
rvalue: 4
}
]
}
]
},
projection: {
field: "*",
recursive: true
}
}, find.body);
})
.then(console.log);
```
6 changes: 6 additions & 0 deletions lib/client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = LightblueClient;

function LightblueClient(dataClient, metadataClient) {
this.data = dataClient;
this.metadata = metadataClient;
}
51 changes: 49 additions & 2 deletions lib/clientutil.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,55 @@ exports.resolve = function() {
.filter(notEmpty)
.map(trimSlashes)
.join("/");
}
};

exports.isEmpty = function(s) {
return typeof s === "undefined" || s === "";
}
};

function ifDefined(it, otherwise) {
return isDefined(it) ? it : otherwise;
}

function isDefined(it) {
return typeof it !== "undefined";
}

function isObject(it) {
return typeof it === "object";
}

var assertArg = {
isInstance: function(arg, ctor, name) {
if (!(arg instanceof ctor)) {
throw new Error("Expected instanceof " + ctor + " but " + name + " was "
+ isObject(arg) ? arg.constructor : "undefined");
}

return arg;
},
isNotBlankString: function(arg, name) {
if (typeof arg !== "string") {
throw new Error("Expected a string but " + name + " was " + typeof arg);
}

if (arg.trim().length === 0) {
throw new Error("Expected non-blank string but " + name + " was empty.");
}

return arg;
},
isTypeOf: function(arg, type, name) {
if (typeof arg !== type) {
throw new Error("Expected a typeof " + type + " but " + name + " was " +
typeof arg);
}

return arg;
}
};

exports.assertArg = assertArg;
exports.ifDefined = ifDefined;
exports.isDefined = isDefined;
exports.isObject = isObject;
56 changes: 35 additions & 21 deletions lib/data.js
Original file line number Diff line number Diff line change
@@ -1,45 +1,55 @@
var resolve = require("./clientutil").resolve;
var RestRequest = require("./rest").RestRequest;
// TODO: Consider using url.resolve?
var resolve = require("./clientutil").resolve;
var http = require("./http");

var HttpRequest = http.HttpRequest;

module.exports = LightblueDataClient;

/**
* Client for making requests against a Lightblue data endpoint.
* @constructor
* @param {String} host - The URL where the rest data server is deployed. This
* @param {HttpClient} httpClient
* @param {String} host The URL where the rest data server is deployed. This
* path will be the base for requests, like ${host}/find, so if the app is
* deployed under a particular context (like /rest/data), this must be
* included in the host.
*/
function LightblueDataClient(host) {
this.host = host;
function LightblueDataClient(httpClient, host) {
this._httpClient = httpClient;
this._host = host;
}

LightblueDataClient.prototype._execute = function(request) {
return this._httpClient.execute(request);
};

LightblueDataClient.prototype.find = function(config) {
return new FindRequest(this.host, config);
return this._execute(new FindRequest(this._host, config));
};

LightblueDataClient.prototype.insert = function(config) {
return new InsertRequest(this.host, config);
return this._execute(new InsertRequest(this._host, config));
};

LightblueDataClient.prototype.update = function(config) {
return new UpdateRequest(this.host, config);
return this._execute(new UpdateRequest(this._host, config));
};

LightblueDataClient.prototype.save = function(config) {
return new SaveRequest(this.host, config);
return this._execute(new SaveRequest(this._host, config));
};

LightblueDataClient.prototype.delete = function(config) {
return new DeleteRequest(this.host, config);
return this._execute(new DeleteRequest(this._host, config));
};

/**
* @param {Object} config The config object for the request.
*/
function FindRequest(host, config) {
var url = resolve(host, "find", config.entity, config.version);

var body = {
objectType: config.entity,
version: config.version,
Expand All @@ -57,54 +67,57 @@ function FindRequest(host, config) {
body.range[1] = config.range.to || config.range[1];
}

RestRequest.call(this, "post", url, body);
HttpRequest.call(this, "post", url, body);
}

FindRequest.prototype = Object.create(RestRequest.prototype);
FindRequest.prototype = Object.create(HttpRequest.prototype);
FindRequest.prototype.constructor = FindRequest;

/**
* @param {Object} config The config object for the request.
*/
function InsertRequest(host, config) {
var url = resolve(host, "insert", config.entity, config.version);

var body = {
objectType: config.entity,
version: config.version,
data: config.data,
projection: config.projection
};

RestRequest.call(this, "put", url, body);
HttpRequest.call(this, "put", url, body);
}

InsertRequest.prototype = Object.create(RestRequest.prototype);
InsertRequest.prototype = Object.create(HttpRequest.prototype);
InsertRequest.prototype.constructor = InsertRequest;

/**
* @param {Object} config The config object for the request.
*/
function SaveRequest(host, config) {
var url = resolve(host, "save", config.entity, config.version);

var body = {
objectType: config.entity,
version: config.version,
data: config.data,
upsert: config.upsert,
projection: config.projection
}
};

RestRequest.call(this, "post", url, body);
HttpRequest.call(this, "post", url, body);
}

SaveRequest.prototype = Object.create(RestRequest.prototype);
SaveRequest.prototype = Object.create(HttpRequest.prototype);
SaveRequest.prototype.constructor = SaveRequest;

/**
* @param {Object} config The config object for the request.
*/
function UpdateRequest(host, config) {
var url = resolve(host, "update", config.entity, config.version);

var body = {
objectType: config.entity,
version: config.version,
Expand All @@ -113,25 +126,26 @@ function UpdateRequest(host, config) {
projection: config.projection
};

RestRequest.call(this, "post", url, body);
HttpRequest.call(this, "post", url, body);
}

UpdateRequest.prototype = Object.create(RestRequest.prototype);
UpdateRequest.prototype = Object.create(HttpRequest.prototype);
UpdateRequest.prototype.constructor = UpdateRequest;

/**
* @param {Object} config The config object for the request.
*/
function DeleteRequest(host, config) {
var url = resolve(host, "delete", config.entity, config.version);

var body = {
objectType: config.entity,
version: config.version,
query: config.query
};

RestRequest.call(this, "post", url, body);
HttpRequest.call(this, "post", url, body);
}

DeleteRequest.prototype = Object.create(RestRequest.prototype);
DeleteRequest.prototype = Object.create(HttpRequest.prototype);
DeleteRequest.prototype.constructor = DeleteRequest;
25 changes: 25 additions & 0 deletions lib/http.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
var assertArg = require("./clientutil.js").assertArg;

/**
* @constructor
* @param {String} method Http method, case insensitive.
* @param {Url} url
* @param {String=} body The request body.
*/
exports.HttpRequest = function(method, url, body) {
this.method = assertArg.isNotBlankString(method, "method");
this.url = assertArg.isTypeOf(url, "string", "url");
this.body = body;

this.METHOD = this.method.toUpperCase();
};

/**
* @interface HttpClient
*/

/**
* @function
* @name HttpClient#execute
* @return {Promise}
*/

0 comments on commit 147af88

Please sign in to comment.