redux-flute
Advanced tools
Comparing version 0.1.1 to 0.2.0
@@ -62,10 +62,10 @@ "use strict"; | ||
value: function setAPI(_ref) { | ||
var _ref$prefix = _ref.prefix, | ||
prefix = _ref$prefix === undefined ? this.apiPrefix : _ref$prefix, | ||
_ref$delimiter = _ref.delimiter, | ||
delimiter = _ref$delimiter === undefined ? this.apiDelimiter : _ref$delimiter, | ||
_ref$headers = _ref.headers, | ||
headers = _ref$headers === undefined ? this.apiHeaders : _ref$headers, | ||
_ref$credentials = _ref.credentials, | ||
credentials = _ref$credentials === undefined ? this.apiCredentials : _ref$credentials; | ||
var _ref$prefix = _ref.prefix; | ||
var prefix = _ref$prefix === undefined ? this.apiPrefix : _ref$prefix; | ||
var _ref$delimiter = _ref.delimiter; | ||
var delimiter = _ref$delimiter === undefined ? this.apiDelimiter : _ref$delimiter; | ||
var _ref$headers = _ref.headers; | ||
var headers = _ref$headers === undefined ? this.apiHeaders : _ref$headers; | ||
var _ref$credentials = _ref.credentials; | ||
var credentials = _ref$credentials === undefined ? this.apiCredentials : _ref$credentials; | ||
@@ -79,5 +79,6 @@ this.apiPrefix = prefix; | ||
key: "getRoute", | ||
value: function getRoute(_ref2, method, record) { | ||
var routes = _ref2.routes, | ||
name = _ref2.name; | ||
value: function getRoute(_ref2, method) { | ||
var routes = _ref2.routes; | ||
var name = _ref2.name; | ||
var record = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; | ||
@@ -91,3 +92,3 @@ /* | ||
if (!(0, _utils.routePermitted)(routes, method)) throw new TypeError("Method " + method + " is not permitted for model #<" + name + ">. Check the #<" + name + "> route configuration."); | ||
var route = routes[method] || (0, _utils.generateRoute)(name, method, this.apiDelimiter, this.apiPrefix); | ||
var route = routes[method] || (0, _utils.generateRoute)(name, method, this.apiDelimiter, this.apiPrefix, !!!record.id); | ||
return (0, _utils.interpolateRoute)(route, record); | ||
@@ -103,15 +104,14 @@ } | ||
(function () { | ||
var modelType = modelInstance.constructor.name, | ||
modelTypeForAction = Sugar.String.underscore(modelType).toUpperCase(), | ||
model = _this.models[modelType], | ||
record = (0, _utils.pruneDeep)(modelInstance.record), | ||
recordForAction = (0, _utils.isEmptyObject)(record) ? null : record, | ||
version = modelInstance._version, | ||
method = record.id ? "PUT" : "POST", | ||
route = _this.getRoute(model, method, record), | ||
body = JSON.stringify(record), | ||
headers = _this.apiHeaders, | ||
credentials = _this.apiCredentials; | ||
var modelType = modelInstance.constructor.name; | ||
var modelTypeForAction = Sugar.String.underscore(modelType).toUpperCase(); | ||
var model = _this.models[modelType]; | ||
var record = (0, _utils.pruneDeep)(modelInstance.record); | ||
var recordForAction = (0, _utils.isEmptyObject)(record) ? null : record; | ||
var version = modelInstance._version; | ||
var method = record.id ? "PUT" : "POST"; | ||
var route = _this.getRoute(model, method, record); | ||
var body = JSON.stringify(record); | ||
var headers = _this.apiHeaders; | ||
var credentials = _this.apiCredentials; | ||
if (_this.checkForDispatch()) _this.dispatch({ type: "@FLUTE_" + method + "_" + modelTypeForAction, record: recordForAction }); | ||
@@ -126,10 +126,10 @@ fetch(route, { method: method, body: body, headers: headers, credentials: credentials }).then(_utils.checkResponseStatus).then(function (res) { | ||
}).catch(function (_ref3) { | ||
var status = _ref3.status, | ||
response = _ref3.response; | ||
var status = _ref3.status; | ||
var response = _ref3.response; | ||
response.json().then(function (_ref4) { | ||
var _ref4$body = _ref4.body, | ||
body = _ref4$body === undefined ? "" : _ref4$body, | ||
_ref4$errors = _ref4.errors, | ||
errors = _ref4$errors === undefined ? {} : _ref4$errors; | ||
var _ref4$body = _ref4.body; | ||
var body = _ref4$body === undefined ? "" : _ref4$body; | ||
var _ref4$errors = _ref4.errors; | ||
var errors = _ref4$errors === undefined ? {} : _ref4$errors; | ||
@@ -174,5 +174,43 @@ var requestInfo = { _request: { version: version, status: status, body: body }, errors: errors }, | ||
}, { | ||
key: "getModel", | ||
value: function getModel(model) { | ||
var _this2 = this; | ||
var id = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; | ||
return new Promise(function (resolve, error) { | ||
try { | ||
(function () { | ||
var modelType = model.name, | ||
modelTypeForAction = Sugar.String.underscore(modelType).toUpperCase(), | ||
method = "GET", | ||
route = _this2.getRoute(model, method, { id: id }), | ||
headers = _this2.apiHeaders, | ||
credentials = _this2.apiCredentials; | ||
if (_this2.checkForDispatch()) _this2.dispatch({ type: "@FLUTE_" + method + "_" + modelTypeForAction }); | ||
fetch(route, { method: method, headers: headers, credentials: credentials }).then(_utils.checkResponseStatus).then(function (res) { | ||
return res.json(); | ||
}).then(function (data) { | ||
_this2.dispatch({ type: "@FLUTE_" + method + "_SUCCESS_" + modelTypeForAction, record: data }); | ||
var instantiatedModels = [].concat(data).map(function (recordRetrieved) { | ||
return new model(recordRetrieved); | ||
}); | ||
if (id) resolve(instantiatedModels[0]);else resolve(instantiatedModels); | ||
}).catch(function (e) { | ||
var action = { type: "@FLUTE_REQUEST_INFO_" + modelTypeForAction }; | ||
if (id) action["record"] = { id: id }; | ||
_this2.dispatch(action); | ||
error(e); | ||
}); | ||
})(); | ||
} catch (e) { | ||
error(e); | ||
} | ||
}); | ||
} | ||
}, { | ||
key: "destroyModel", | ||
value: function destroyModel(modelType, record) { | ||
var _this2 = this; | ||
var _this3 = this; | ||
@@ -182,13 +220,13 @@ return new Promise(function (resolve, error) { | ||
(function () { | ||
var model = _this2.models[modelType], | ||
var model = _this3.models[modelType], | ||
method = "DELETE", | ||
route = _this2.getRoute(model, method, record), | ||
headers = _this2.apiHeaders, | ||
credentials = _this2.apiCredentials, | ||
route = _this3.getRoute(model, method, record), | ||
headers = _this3.apiHeaders, | ||
credentials = _this3.apiCredentials, | ||
recordForAction = record.id ? { id: record.id } : undefined, | ||
modelTypeForAction = Sugar.String.underscore(modelType).toUpperCase(); | ||
if (!recordForAction) throw new Error("Cannot destroy unsaved #<" + modelType + ">."); | ||
if (_this2.checkForDispatch()) _this2.dispatch({ type: "@FLUTE_DELETE_" + modelTypeForAction, record: recordForAction }); | ||
if (_this3.checkForDispatch()) _this3.dispatch({ type: "@FLUTE_DELETE_" + modelTypeForAction, record: recordForAction }); | ||
fetch(route, { method: method, headers: headers, credentials: credentials }).then(_utils.checkResponseStatus).then(function () { | ||
_this2.dispatch({ type: "@FLUTE_DELETE_SUCCESS_" + modelTypeForAction, record: recordForAction }); | ||
_this3.dispatch({ type: "@FLUTE_DELETE_SUCCESS_" + modelTypeForAction, record: recordForAction }); | ||
resolve(); | ||
@@ -273,7 +311,7 @@ }) | ||
// Extract the timestamps and key declaration from the schema | ||
var _model$schema = model.schema; | ||
var _timestamps = _model$schema._timestamps; | ||
var _key = _model$schema._key; | ||
var _model$schema = model.schema, | ||
_timestamps = _model$schema._timestamps, | ||
_key = _model$schema._key, | ||
schema = _objectWithoutProperties(_model$schema, ["_timestamps", "_key"]); | ||
var schema = _objectWithoutProperties(_model$schema, ["_timestamps", "_key"]); | ||
@@ -287,3 +325,3 @@ (0, _utils.setReadOnlyProps)(params, _timestamps, modelName, this); | ||
get: function get() { | ||
var _this3 = this; | ||
var _this4 = this; | ||
@@ -294,4 +332,4 @@ return function () { | ||
// Includes validations | ||
Object.assign(_this3, attributes); | ||
return _this3.save(); | ||
Object.assign(_this4, attributes); | ||
return _this4.save(); | ||
}; | ||
@@ -302,8 +340,8 @@ } | ||
get: function get() { | ||
var _this4 = this; | ||
var _this5 = this; | ||
return function (name, value) { | ||
// Excludes validations | ||
_this4[name] = value; | ||
return _this4.save({ validate: false }); | ||
_this5[name] = value; | ||
return _this5.save({ validate: false }); | ||
}; | ||
@@ -314,3 +352,3 @@ } | ||
get: function get() { | ||
var _this5 = this; | ||
var _this6 = this; | ||
@@ -321,16 +359,16 @@ return function () { | ||
return new Promise(function (resolve, error) { | ||
flute.saveModel(_this5).then(function (savedRecord) { | ||
flute.saveModel(_this6).then(function (savedRecord) { | ||
// Copy the new properties to this instance | ||
Object.assign(_this5.record, savedRecord.record); | ||
_this5._version = 0; | ||
_this5._request.clear(); | ||
_this5.errors.clear(); | ||
if (savedRecord.timestamps) Object.assign(_this5.timestamps, savedRecord.timestamps); | ||
resolve(_this5); | ||
Object.assign(_this6.record, savedRecord.record); | ||
_this6._version = 0; | ||
_this6._request.clear(); | ||
_this6.errors.clear(); | ||
if (savedRecord.timestamps) Object.assign(_this6.timestamps, savedRecord.timestamps); | ||
resolve(_this6); | ||
}).catch(function (e) { | ||
if (e instanceof Model) { | ||
_this5._request.clear(); | ||
Object.assign(_this5._request, e._request); | ||
_this5.errors.clear(); | ||
Object.assign(_this5.errors, e.errors); | ||
_this6._request.clear(); | ||
Object.assign(_this6._request, e._request); | ||
_this6.errors.clear(); | ||
Object.assign(_this6.errors, e.errors); | ||
} | ||
@@ -342,18 +380,11 @@ error(e); | ||
} | ||
// get validate(){ | ||
// return (includingServerValidations)=>{ | ||
// return new Promise((res,err)=>{ | ||
// }) | ||
// } | ||
// } | ||
}, { | ||
key: "destroy", | ||
get: function get() { | ||
var _this6 = this; | ||
var _this7 = this; | ||
return function () { | ||
return new Promise(function (resolve, error) { | ||
var modelType = _this6.constructor.name; | ||
flute.destroyModel(modelType, { id: _this6.id }).then(resolve()).catch(function (e) { | ||
var modelType = _this7.constructor.name; | ||
flute.destroyModel(modelType, { id: _this7.id }).then(resolve()).catch(function (e) { | ||
return error(e); | ||
@@ -369,5 +400,16 @@ }); | ||
} | ||
// Will retrieve an index from the API as an array | ||
}, { | ||
key: "all", | ||
value: function all(callback) {} | ||
value: function all(options) { | ||
return flute.getModel(this); | ||
} | ||
// Will retrieve a single record by the model's key | ||
}, { | ||
key: "find", | ||
value: function find(keyStr) { | ||
return flute.getModel(this, keyStr); | ||
} | ||
}]); | ||
@@ -383,11 +425,12 @@ | ||
var _ref5 = arguments[1]; | ||
var type = _ref5.type; | ||
var _ref5$record = _ref5.record; | ||
var record = _ref5$record === undefined ? null : _ref5$record; | ||
var _ref5$_request = _ref5._request; | ||
var type = _ref5.type, | ||
_ref5$record = _ref5.record, | ||
record = _ref5$record === undefined ? null : _ref5$record, | ||
_ref5$_request = _ref5._request, | ||
_request = _ref5$_request === undefined ? {} : _ref5$_request, | ||
_ref5$errors = _ref5.errors, | ||
errors = _ref5$errors === undefined ? {} : _ref5$errors; | ||
var _request = _ref5$_request === undefined ? _extends({}, _constants.versioningProps._request) : _ref5$_request; | ||
var _ref5$errors = _ref5.errors; | ||
var errors = _ref5$errors === undefined ? {} : _ref5$errors; | ||
// If this is a flute action | ||
@@ -398,14 +441,16 @@ if ((0, _utils.regexIndexOf)(_constants.actionMatch, type) === -1) return state; | ||
var _type$match = type.match(_constants.actionMatch), | ||
_type$match2 = _slicedToArray(_type$match, 4), | ||
internalAction = _type$match2[1], | ||
_type$match2$ = _type$match2[2], | ||
isSuccessful = _type$match2$ === undefined ? false : _type$match2$, | ||
actionModelName = _type$match2[3], | ||
modelName = Sugar.String.camelize(actionModelName), | ||
model = flute.models[modelName], | ||
singleton = model.store.singleton, | ||
keyStr = model.schema._key || "id", | ||
newState = _extends({}, state); | ||
var _type$match = type.match(_constants.actionMatch); | ||
var _type$match2 = _slicedToArray(_type$match, 4); | ||
var internalAction = _type$match2[1]; | ||
var _type$match2$ = _type$match2[2]; | ||
var isSuccessful = _type$match2$ === undefined ? false : _type$match2$; | ||
var actionModelName = _type$match2[3]; | ||
var modelName = Sugar.String.camelize(actionModelName); | ||
var model = flute.models[modelName]; | ||
var singleton = model.store.singleton; | ||
var keyStr = model.schema._key || "id"; | ||
var newState = _extends({}, state); | ||
switch (internalAction) { | ||
@@ -434,3 +479,3 @@ case "SET": | ||
if (singleton) { | ||
newState[modelName].record = record; | ||
newState[modelName].record = record instanceof Array ? record[0] : record; | ||
newState[modelName].version = 0; | ||
@@ -440,3 +485,3 @@ } else { | ||
} | ||
} else if (!singleton && record[keyStr]) { | ||
} else if (!singleton && record && record[keyStr]) { | ||
// It is the start of a get request, so if there is record | ||
@@ -443,0 +488,0 @@ // Set that record's getting prop to true |
@@ -85,4 +85,4 @@ "use strict"; | ||
function checkResponseStatus(response) { | ||
var status = response.status, | ||
error = new Error(); | ||
var status = response.status; | ||
var error = new Error(); | ||
// If no error, great, return the response. | ||
@@ -98,4 +98,4 @@ if (status >= 200 && status < 300) return response; | ||
function routePermitted(_ref, method) { | ||
var only = _ref.only, | ||
except = _ref.except; | ||
var only = _ref.only; | ||
var except = _ref.except; | ||
@@ -137,4 +137,4 @@ if (only instanceof Array && only.indexOf(method) === -1 || typeof only === "string" && only !== method) return false; | ||
function setReadOnlyProps(params, _timestamps, modelName, _obj) { | ||
var id = params.id, | ||
_id = params._id; | ||
var id = params.id; | ||
var _id = params._id; | ||
// Establish the ID, also _id | ||
@@ -145,2 +145,3 @@ | ||
Object.defineProperty(_obj, "id", { | ||
enumerable: true, | ||
get: function get() { | ||
@@ -155,3 +156,14 @@ return _obj.record.id || null; | ||
_obj._version = params._version || 0; | ||
var defineVersion = function defineVersion(newValue) { | ||
delete this._version; | ||
Object.defineProperty(this, "_version", { | ||
enumerable: false, | ||
configurable: true, | ||
get: function get() { | ||
return newValue || 0; | ||
}, | ||
set: defineVersion.bind(this) | ||
}); | ||
}; | ||
defineVersion.call(_obj, params._version); | ||
@@ -199,2 +211,3 @@ Object.defineProperty(_obj, "_request", { | ||
Object.defineProperty(_obj, "createdAt", { | ||
enumerable: true, | ||
get: function get() { | ||
@@ -213,2 +226,3 @@ return _obj.timestamps.createdAt ? new Date(_obj.timestamps.createdAt) : null; | ||
Object.defineProperty(_obj, "updatedAt", { | ||
enumerable: true, | ||
get: function get() { | ||
@@ -256,3 +270,3 @@ return _obj.timestamps.updatedAt ? new Date(_obj.timestamps.updatedAt) : null; | ||
Object.defineProperty(_obj, prop, { get: get, set: set }); | ||
Object.defineProperty(_obj, prop, { get: get, set: set, enumerable: true }); | ||
}; | ||
@@ -259,0 +273,0 @@ |
{ | ||
"name": "redux-flute", | ||
"version": "0.1.01", | ||
"version": "0.2.0", | ||
"description": "Smart front-end data cache manager", | ||
@@ -15,3 +15,3 @@ "main": "dist/index.js", | ||
"type": "git", | ||
"url": "git+https://github.com/kyleramirez/flute.git" | ||
"url": "git+https://github.com/kyleramirez/redux-flute.git" | ||
}, | ||
@@ -25,5 +25,5 @@ "keywords": [ | ||
"bugs": { | ||
"url": "https://github.com/kyleramirez/flute/issues" | ||
"url": "https://github.com/kyleramirez/redux-flute/issues" | ||
}, | ||
"homepage": "https://github.com/kyleramirez/flute#readme", | ||
"homepage": "https://github.com/kyleramirez/redux-flute#readme", | ||
"devDependencies": { | ||
@@ -30,0 +30,0 @@ "babel-cli": "^6.18.0", |
172
README.md
@@ -5,36 +5,38 @@ # redux-flute | ||
### What does it do? | ||
Flute is an object-relational mapping (ORM) implementation that lets you interact with RESTful APIs. By defining models on the front end, integrating closely with the popular state container, Redux, Flute offers a Ruby-on-Rails-esque, ActiveRecord-ey syntax. Think ActiveRecord for JavaScript. Flute allows you to write syntax like this: | ||
Flute is a front-end-only Object Data-store Mapping (ODM) implementation that lets you interact with RESTful APIs. By defining models on the front end and integrating closely with the popular state container, Redux, Flute offers a Ruby-on-Rails-esque, ActiveRecord-ey syntax. Think ActiveRecord for JavaScript. Flute is agnostic to back-end architecture. It's built for APIs which respond to GET/POST/POST/DELETE requests for resources identified by predefined keys, so Rails, Express, Sinatra, CouchDB UNAMEIT! Flute allows you to write syntax like this: | ||
const userAddress = new Address | ||
```js | ||
const userAddress = new Address | ||
userAddress.address1 = "1100 Congress Ave" | ||
userAddress.city = "Austin" | ||
userAddress.state = "TX" | ||
userAddress.address1 = "1100 Congress Ave" | ||
userAddress.city = "Austin" | ||
userAddress.state = "TX" | ||
userAddress.save() | ||
/* | ||
REQUEST: | ||
Method: POST | ||
URL: /api/addresses | ||
Data: { | ||
"address1": "1100 Congress Ave", | ||
"city": "Austin", | ||
"state": "TX" | ||
} | ||
RESPONSE: | ||
Status: 201 Created | ||
Body: { | ||
"_id": "583132c8edc3b79a853b8d69", | ||
"createdAt": "2016-11-20T05:21:12.988Z", | ||
"updatedAt": "2016-11-20T05:21:12.988Z", | ||
"userId": "580432279153ea2679095acd", | ||
"address1": "1100 Congress Ave", | ||
"city": "Austin", | ||
"state": "TX" | ||
} | ||
*/ | ||
userAddress.id | ||
// Returns 583132c8edc3b79a853b8d69 | ||
userAddress.destroy() | ||
// Does what you would expect (A DELETE request to the same resource) | ||
userAddress.save() | ||
/* | ||
REQUEST: | ||
Method: POST | ||
URL: /api/addresses | ||
Data: { | ||
"address1": "1100 Congress Ave", | ||
"city": "Austin", | ||
"state": "TX" | ||
} | ||
RESPONSE: | ||
Status: 201 Created | ||
Body: { | ||
"_id": "583132c8edc3b79a853b8d69", | ||
"createdAt": "2016-11-20T05:21:12.988Z", | ||
"updatedAt": "2016-11-20T05:21:12.988Z", | ||
"userId": "580432279153ea2679095acd", | ||
"address1": "1100 Congress Ave", | ||
"city": "Austin", | ||
"state": "TX" | ||
} | ||
*/ | ||
userAddress.id | ||
// Returns 583132c8edc3b79a853b8d69 | ||
userAddress.destroy() | ||
// Does what you would expect (A DELETE request to the same resource) | ||
``` | ||
@@ -45,1 +47,109 @@ ### Installation | ||
### Prerequisites | ||
This library was made to solve problems in my current project stacks (insert buzz words: React/Redux/Webpack/ES6). The only real assumptions are: | ||
- You are using NPM as a package manager | ||
- You are using Redux | ||
The nice-to-haves are: | ||
- React | ||
- React Redux | ||
I'm open to suggestion on making this library more widely supported. | ||
# Minimum Setup | ||
### In your app | ||
```js | ||
import flute from "redux-flute"; | ||
const Story = flute.model("Story"), | ||
const newStory = new Story; | ||
newStory.save().then(savedRecord=>(this.setState({ ...savedRecord })); | ||
// Also works ... | ||
Story.create({ title: "A working title", body: "Once upon a time..."}) // Makes an API request | ||
.then(savedRecord=>(this.setState({ ...savedRecord })); // Returns a promise ... as do the following methods | ||
newStory.updateAttribute("title", "A working title") // Makes an API request | ||
newStory.updateAttributes({ title: "A working title", body: "Once upon a time..."}) // Makes an API request | ||
newStory.destroy() // Makes an API request | ||
Story.all() // Makes an API request to the model's index, returns array of records | ||
Story.find("583132c8edc3b79a853b8d69") // Makes an API request to this resource, returns single record | ||
``` | ||
### Defining Models | ||
```js | ||
// In a file like /models/Story.js | ||
import flute, { Model } from "redux-flute"; | ||
class Story extends Model { | ||
static schema = { | ||
title: String, | ||
body: String, | ||
isActive: Boolean, | ||
userId: String, | ||
_timestamps: true | ||
} | ||
} | ||
export default flute.model(Story); | ||
``` | ||
### In your reducers setup | ||
```js | ||
// In a file like /reducers.js | ||
import { combineReducers } from "redux" | ||
import flute, { reducer as models } from "redux-flute" | ||
flute.setAPI({ prefix: "/api" }) | ||
import "models" | ||
export default combineReducers({ | ||
models, | ||
// your other reducers | ||
}); | ||
``` | ||
### In your store setup | ||
```js | ||
// In a file like /store.js | ||
import { createStore, applyMiddleware, compose } from "redux"; | ||
import reducer from "./reducers"; | ||
import { middleware as fluteMiddleware } from "redux-flute"; | ||
export default createStore(reducer, compose( applyMiddleware(fluteMiddleware /* , ...your other middlewares*/))); | ||
``` | ||
# Coming Soon! | ||
- **Model associations** syntax a-la `userAddress.user` with declarations like: | ||
```js | ||
const User = flute.model("User") | ||
class Address extends Model { | ||
static associations = [ | ||
{belongsTo: User} | ||
] | ||
} | ||
``` | ||
- **Validations**, with the ability to exclude local validation like `userAddress.save({validate: false})`, with declarations a-la Mongoose like: | ||
```js | ||
class Address extends Model { | ||
static schema = { | ||
address1: { | ||
type: String, | ||
required: [true, "A street address is required."] | ||
} | ||
zip: { | ||
type: String, | ||
length: { | ||
min: 5, | ||
message: "ZIP codes must be at least 5 numbers." | ||
} | ||
} | ||
// ... other schema | ||
} | ||
} | ||
``` | ||
Also planned is the creation of a flute validations API, with the ability to include local and remote validations. | ||
- **Scopes** and default scope syntax like `orders.completed` | ||
- **Order** and default ordering `cards.orderBy("price", "ASC")` | ||
- **Custom schema types** such as automatic conversions to U.S. dollar |
@@ -45,6 +45,3 @@ import "./sugar" | ||
setAPI({ prefix=this.apiPrefix, | ||
delimiter=this.apiDelimiter, | ||
headers=this.apiHeaders, | ||
credentials=this.apiCredentials }){ | ||
setAPI({ prefix=this.apiPrefix, delimiter=this.apiDelimiter, headers=this.apiHeaders, credentials=this.apiCredentials }){ | ||
this.apiPrefix = prefix; | ||
@@ -56,3 +53,3 @@ this.apiDelimiter = delimiter; | ||
getRoute({ routes, name }, method, record){ | ||
getRoute({ routes, name }, method, record={}){ | ||
/* | ||
@@ -65,3 +62,3 @@ Get route should now lookup on the model itself for a path | ||
if (!routePermitted(routes, method)) throw new TypeError(`Method ${method} is not permitted for model #<${name}>. Check the #<${name}> route configuration.`) | ||
const route = routes[method] || generateRoute(name, method, this.apiDelimiter, this.apiPrefix) | ||
const route = routes[method] || generateRoute(name, method, this.apiDelimiter, this.apiPrefix, !!!record.id) | ||
return interpolateRoute(route, record) | ||
@@ -133,2 +130,34 @@ } | ||
getModel(model, id=false){ | ||
return new Promise((resolve, error)=>{ | ||
try { | ||
const modelType = model.name, | ||
modelTypeForAction = Sugar.String.underscore(modelType).toUpperCase(), | ||
method = "GET", | ||
route = this.getRoute(model, method, { id }), | ||
headers = this.apiHeaders, | ||
credentials = this.apiCredentials; | ||
if (this.checkForDispatch()) | ||
this.dispatch({ type: `@FLUTE_${method}_${modelTypeForAction}`}) | ||
fetch(route, { method, headers, credentials }) | ||
.then(checkResponseStatus) | ||
.then(res=>(res.json())) | ||
.then(data=>{ | ||
this.dispatch({ type: `@FLUTE_${method}_SUCCESS_${modelTypeForAction}`, record:data }) | ||
const instantiatedModels = [].concat(data).map(recordRetrieved=>(new model(recordRetrieved))) | ||
if (id) resolve(instantiatedModels[0]) | ||
else resolve(instantiatedModels) | ||
}) | ||
.catch(e=>{ | ||
const action = { type: `@FLUTE_REQUEST_INFO_${modelTypeForAction}` } | ||
if (id) action["record"] = { id } | ||
this.dispatch(action) | ||
error(e) | ||
}) | ||
} | ||
catch(e) { error(e) } | ||
}) | ||
} | ||
destroyModel(modelType, record){ | ||
@@ -258,8 +287,2 @@ return new Promise((resolve, error)=>{ | ||
} | ||
// get validate(){ | ||
// return (includingServerValidations)=>{ | ||
// return new Promise((res,err)=>{ | ||
// }) | ||
// } | ||
// } | ||
get destroy(){ | ||
@@ -278,3 +301,6 @@ return ()=>{ | ||
} | ||
static all(callback){} | ||
// Will retrieve an index from the API as an array | ||
static all(options){ return flute.getModel(this) } | ||
// Will retrieve a single record by the model's key | ||
static find(keyStr){ return flute.getModel(this,keyStr) } | ||
static routes = {} | ||
@@ -284,3 +310,3 @@ static store = { singleton: false } | ||
export const reducer = (state = flute.buildInitialState(), { type, record=null, _request={}, errors={} })=>{ | ||
export const reducer = (state = flute.buildInitialState(), { type, record=null, _request={...versioningProps._request}, errors={} })=>{ | ||
// If this is a flute action | ||
@@ -320,3 +346,3 @@ if (regexIndexOf(actionMatch, type) === -1) return state | ||
if (singleton) { | ||
newState[modelName].record = record | ||
newState[modelName].record = record instanceof Array ? record[0] : record | ||
newState[modelName].version = 0 | ||
@@ -327,3 +353,3 @@ } else { | ||
} | ||
else if(!singleton && record[keyStr]) { | ||
else if(!singleton && record && record[keyStr]) { | ||
// It is the start of a get request, so if there is record | ||
@@ -330,0 +356,0 @@ // Set that record's getting prop to true |
@@ -114,2 +114,3 @@ import "./sugar" | ||
Object.defineProperty(_obj, "id", { | ||
enumerable: true, | ||
get: ()=>(_obj.record.id || null), | ||
@@ -120,3 +121,12 @@ // ID can only be set on instantiation, otherwise it stays undefined | ||
_obj._version = params._version || 0; | ||
const defineVersion = function(newValue){ | ||
delete this._version; | ||
Object.defineProperty(this, "_version", { | ||
enumerable: false, | ||
configurable: true, | ||
get:()=>(newValue || 0), | ||
set:defineVersion.bind(this) | ||
}) | ||
} | ||
defineVersion.call(_obj, params._version) | ||
@@ -163,2 +173,3 @@ Object.defineProperty(_obj, "_request", { | ||
Object.defineProperty(_obj, "createdAt", { | ||
enumerable: true, | ||
get: ()=>(_obj.timestamps.createdAt ? new Date(_obj.timestamps.createdAt) : null), | ||
@@ -173,2 +184,3 @@ // createdAt can only be set on instantiation, otherwise it stays undefined | ||
Object.defineProperty(_obj, "updatedAt", { | ||
enumerable: true, | ||
get: ()=>(_obj.timestamps.updatedAt ? new Date(_obj.timestamps.updatedAt) : null), | ||
@@ -208,3 +220,3 @@ // updatedAt can only be set on instantiation, otherwise it stays undefined | ||
Object.defineProperty(_obj, prop, { get, set }) | ||
Object.defineProperty(_obj, prop, { get, set, enumerable: true }) | ||
} | ||
@@ -211,0 +223,0 @@ } |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
109460
2212
154
6