From 4fb28c2c4aef8c4a3b6558cc11b6961b37fb13fd Mon Sep 17 00:00:00 2001 From: reppners Date: Wed, 25 Feb 2015 22:45:40 +0100 Subject: [PATCH 1/7] + initial commit --- js-data-angular/js-data-angular.d.ts | 19 ++ js-data/js-data.d.ts | 394 +++++++++++++++++++++++++++ 2 files changed, 413 insertions(+) create mode 100644 js-data-angular/js-data-angular.d.ts create mode 100644 js-data/js-data.d.ts diff --git a/js-data-angular/js-data-angular.d.ts b/js-data-angular/js-data-angular.d.ts new file mode 100644 index 000000000..a7c035722 --- /dev/null +++ b/js-data-angular/js-data-angular.d.ts @@ -0,0 +1,19 @@ +// Type definitions for JSDataAngular v2.1.0 +// Project: https://github.com/js-data/js-data-angular +// Definitions by: Stefan Steinhart +// Definitions: https://github.com/borisyankov/DefinitelyTyped + +/// +/// + +declare module JSData { + + class ngDS extends DS { + + // sync methods + bindAll(scope:ng.IScope, expr:string, resourceName:string, params:DSFilterParams, cb?:(err:DSError, items:Array)=>void):Function; + + bindOne(scope:ng.IScope, expr:string, resourceName:string, id:string, cb?:(err:DSError, item:T)=>void):Function; + bindOne(scope:ng.IScope, expr:string, resourceName:string, id:number, cb?:(err:DSError, item:T)=>void):Function; + } +} \ No newline at end of file diff --git a/js-data/js-data.d.ts b/js-data/js-data.d.ts new file mode 100644 index 000000000..ce06edd89 --- /dev/null +++ b/js-data/js-data.d.ts @@ -0,0 +1,394 @@ +// Type definitions for JSData v1.3.0 +// Project: https://github.com/js-data/js-data +// Definitions by: Stefan Steinhart +// Definitions: https://github.com/borisyankov/DefinitelyTyped + +/////////////////////////////////////////////////////////////////////////////// +// Promises in js-data are ES6 polyfill promises +/////////////////////////////////////////////////////////////////////////////// + +/// + +declare class JSDataPromise extends Promise { + + // enhanced with finally + finally(finallyCb?: () => U): Promise; +} + +/////////////////////////////////////////////////////////////////////////////// +// js-data module (js-data.js) +/////////////////////////////////////////////////////////////////////////////// + +// Support AMD require +declare module 'js-data' { + export = JSData; +} + +declare module JSData { + + class DS { + + constructor(config?:DSConfiguration); + + defaults:DSConfiguration; + + //TODO check if still exists + adapters:any; // Object consists of key-values pairs where the key is the name of the adapter and the value is + // the adapter itself. + //TODO check if still exists + errors:DSErrors; + + changeHistory(resourceName:string, id?:string):Array; + changeHistory(resourceName:string, id?:number):Array; + + changes(resourceName:string, id:string):Object; + changes(resourceName:string, id:number):Object; + + compute(resourceName:string, id:number):T; + compute(resourceName:string, id:string):T; + compute(resourceName:string, instance:Object):T; + + create(resourceName:string, attrs:any, options?:DSConfiguration):JSDataPromise; + + createInstance(resourceName:string, attrs?:T, options?:DSAdapterOperationConfiguration):T; + + defineResource(resourceName:string):DSResourceDefinition; + defineResource(definition:DSResourceDefinitionConfiguration):DSResourceDefinition; + + destroy(resourceName:string, id:string, options?:DSAdapterOperationConfiguration):JSDataPromise; + destroy(resourceName:string, id:number, options?:DSAdapterOperationConfiguration):JSDataPromise; + + destroyAll(resourceName:string, params?:DSFilterParams, options?:DSAdapterOperationConfiguration):JSDataPromise; + + digest():void; + + eject(resourceName:string, id:string, options?:DSConfiguration):T; + eject(resourceName:string, id:number, options?:DSConfiguration):T; + + ejectAll(resourceName:string, params:DSFilterParams, options?:DSConfiguration):Array; + + filter(resourceName:string, params:DSFilterParams, options?:DSConfiguration):Array; + + find(resourceName:string, id:string, options?:DSAdapterOperationConfiguration):JSDataPromise; + find(resourceName:string, id:number, options?:DSAdapterOperationConfiguration):JSDataPromise; + + findAll(resourceName:string, params:DSFilterParams, options?:DSAdapterOperationConfiguration):JSDataPromise>; + + get(resourceName:string, id:string, options?:DSConfiguration):T; + get(resourceName:string, id:number, options?:DSConfiguration):T; + + getAll(resourceName:string, ids?:Array):Array; + getAll(resourceName:string, ids?:Array):Array; + + hasChanges(resourceName:string, id:string):boolean; + hasChanges(resourceName:string, id:number):boolean; + + inject(resourceName:string, attrs:T, options?:DSConfiguration):T; + inject(resourceName:string, items:Array, options?:DSConfiguration):Array; + + is(resourceName:string, object:Object): boolean; + + lastModified(resourceName:string, id?:string):number; // timestamp + lastModified(resourceName:string, id?:number):number; // timestamp + + lastSaved(resourceName:string, id?:string):number; // timestamp + lastSaved(resourceName:string, id?:number):number; // timestamp + + link(resourceName:string, id:string, relations?:Array):T; + link(resourceName:string, id:number, relations?:Array):T; + + linkAll(resourceName:string, params:DSFilterParams, relations?:Array):T; + + linkInverse(resourceName:string, id:string, relations?:Array):T; + linkInverse(resourceName:string, id:number, relations?:Array):T; + + loadRelations(resourceName:string, id:string, relations:string, options?:DSAdapterOperationConfiguration):JSDataPromise; + loadRelations(resourceName:string, id:number, relations:string, options?:DSAdapterOperationConfiguration):JSDataPromise; + loadRelations(resourceName:string, instance:Object, relations:string, options?:DSAdapterOperationConfiguration):JSDataPromise; + loadRelations(resourceName:string, id:string, relations:Array, options?:DSAdapterOperationConfiguration):JSDataPromise; + loadRelations(resourceName:string, id:number, relations:Array, options?:DSAdapterOperationConfiguration):JSDataPromise; + loadRelations(resourceName:string, instance:Object, relations:Array, options?:DSAdapterOperationConfiguration):JSDataPromise; + + previous(resourceName:string, id:string):T; + previous(resourceName:string, id:number):T; + + reap(resourceName:string, options?:DSConfiguration):JSDataPromise; + + refresh(resourceName:string, id:string, options?:DSAdapterOperationConfiguration):JSDataPromise; + refresh(resourceName:string, id:number, options?:DSAdapterOperationConfiguration):JSDataPromise; + + registerAdapter(adapterId: string, adapter:IDSAdapter, options?:{default: boolean}):void; + + save(resourceName:string, id:string, options?:DSSaveConfiguration):JSDataPromise; + save(resourceName:string, id:number, options?:DSSaveConfiguration):JSDataPromise; + + unlinkInverse(resourceName:string, id:string, relations?:Array):T; + unlinkInverse(resourceName:string, id:number, relations?:Array):T; + + update(resourceName:string, id:string, attrs:any, options?:DSSaveConfiguration):JSDataPromise; + update(resourceName:string, id:number, attrs:any, options?:DSSaveConfiguration):JSDataPromise; + + updateAll(resourceName:string, attrs:any, params:DSFilterParams, options?:DSAdapterOperationConfiguration):JSDataPromise>; + } + + interface DSConfiguration extends IDSResourceLifecycleEventHandlers { + actions?: Object; + allowSimpleWhere?: boolean; + basePath?: string; + bypassCache?: boolean; + cacheResponse?: boolean; + defaultAdapter?: string; + defaultFilter?: (collection:Array, resourceName:string, params:DSFilterParams, options:DSConfiguration)=>Array; + eagerEject?: boolean; + endpoint?: string; + error?: (message?: any, ...optionalParams: any[])=> void; + fallbackAdapters?: Array; + findAllFallbackAdapters?: Array; + findAllStrategy?: string; + findBelongsTo?: boolean; + findFallbackAdapters?: Array; + findHasOne?: boolean; + findHasMany?: boolean; + findInverseLinks?: boolean; + findStrategy?: string + idAttribute?: string; + ignoredChanges?:Array; + keepChangeHistory?: boolean; + loadFromServer?: boolean; + log?: any; + // log: (message?: any, ...optionalParams: any[])=> void; + // log: boolean; + maxAge?: number; + notify?: boolean; + reapAction?: string; + reapInterval?: number; + resetHistoryOnInject?: boolean; + strategy?: string; + upsert?: boolean; + useClass?: boolean; + useFilter: boolean; + } + + interface DSAdapterOperationConfiguration extends DSConfiguration { + adapter?: string + } + + interface DSSaveConfiguration extends DSAdapterOperationConfiguration { + changesOnly?: boolean; + } + + interface DSResourceDefinitionConfiguration extends DSConfiguration { + name: string; + computed?: any; + methods?: any; + relations?: { + hasMany?: Object; + hasOne?: Object; + belongsTo?: Object; + }; + } + + interface DSResourceDefinition extends DSResourceDefinitionConfiguration { + + changeHistory(id?:string):Array; + changeHistory(id?:number):Array; + + changes(id:string):Object; + changes(id:number):Object; + + compute(id:number):T; + compute(id:string):T; + compute(instance:Object):T; + + create(attrs:any, options?:DSConfiguration):JSDataPromise; + + createInstance(attrs?:T, options?:DSAdapterOperationConfiguration):T; + + defineResource(resourceName:string):DSResourceDefinition; + defineResource(definition:DSResourceDefinitionConfiguration):DSResourceDefinition; + + destroy(id:string, options?:DSAdapterOperationConfiguration):JSDataPromise; + destroy(id:number, options?:DSAdapterOperationConfiguration):JSDataPromise; + + destroyAll(params?:DSFilterParams, options?:DSAdapterOperationConfiguration):JSDataPromise; + + digest():void; + + eject(id:string, options?:DSConfiguration):T; + eject(id:number, options?:DSConfiguration):T; + + ejectAll(params:DSFilterParams, options?:DSConfiguration):Array; + + filter(params:DSFilterParams, options?:DSConfiguration):Array; + + find(id:string, options?:DSAdapterOperationConfiguration):JSDataPromise; + find(id:number, options?:DSAdapterOperationConfiguration):JSDataPromise; + + findAll(params:DSFilterParams, options?:DSAdapterOperationConfiguration):JSDataPromise>; + + get(id:string, options?:DSConfiguration):T; + get(id:number, options?:DSConfiguration):T; + + getAll(ids?:Array):Array; + getAll(ids?:Array):Array; + + hasChanges(id:string):boolean; + hasChanges(id:number):boolean; + + inject(attrs:T, options?:DSConfiguration):T; + inject(items:Array, options?:DSConfiguration):Array; + + is(object:Object): boolean; + + lastModified(id?:string):number; // timestamp + lastModified(id?:number):number; // timestamp + + lastSaved(id?:string):number; // timestamp + lastSaved(id?:number):number; // timestamp + + link(id:string, relations?:Array):T; + link(id:number, relations?:Array):T; + + linkAll(params:DSFilterParams, relations?:Array):T; + + linkInverse(id:string, relations?:Array):T; + linkInverse(id:number, relations?:Array):T; + + loadRelations(id:string, relations:string, options?:DSAdapterOperationConfiguration):JSDataPromise; + loadRelations(id:number, relations:string, options?:DSAdapterOperationConfiguration):JSDataPromise; + loadRelations(instance:Object, relations:string, options?:DSAdapterOperationConfiguration):JSDataPromise; + loadRelations(id:string, relations:Array, options?:DSAdapterOperationConfiguration):JSDataPromise; + loadRelations(id:number, relations:Array, options?:DSAdapterOperationConfiguration):JSDataPromise; + loadRelations(instance:Object, relations:Array, options?:DSAdapterOperationConfiguration):JSDataPromise; + + previous(id:string):T; + previous(id:number):T; + + reap(options?:DSConfiguration):JSDataPromise; + + refresh(id:string, options?:DSAdapterOperationConfiguration):JSDataPromise; + refresh(id:number, options?:DSAdapterOperationConfiguration):JSDataPromise; + + save(id:string, options?:DSSaveConfiguration):JSDataPromise; + save(id:number, options?:DSSaveConfiguration):JSDataPromise; + + unlinkInverse(id:string, relations?:Array):T; + unlinkInverse(id:number, relations?:Array):T; + + update(id:string, attrs:any, options?:DSSaveConfiguration):JSDataPromise; + update(id:number, attrs:any, options?:DSSaveConfiguration):JSDataPromise; + + updateAll(attrs:any, params:DSFilterParams, options?:DSAdapterOperationConfiguration):JSDataPromise>; + } + + interface DSFilterParams { + where?: Object; + + limit?: number; + + skip?: number; + offset?: number; + + orderBy?: any; + // wait for union types to be supported + //orderBy?: Array>; + //orderBy?: Array; + //orderBy?: string; + + sort?: any; + // wait for union types to be supported + //sort?: string; + //sort?: Array; + //sort?: Array>; + } + + interface IDSResourceLifecycleValidateEventHandlers { + beforeValidate?: (resourceName:string, data:any, cb:(err:any, data?:any)=>void)=>void; + validate?: (resourceName:string, data:any, cb:(err:any, data?:any)=>void)=>void; + afterValidate?: (resourceName:string, data:any, cb:(err:any, data?:any)=>void)=>void; + } + + interface IDSResourceLifecycleCreateEventHandlers { + beforeCreate?: (resourceName:string, data:any, cb:(err:any, data?:any)=>void)=>void; + afterCreate?: (resourceName:string, data:any, cb:(err:any, data?:any)=>void)=>void; + } + + interface IDSResourceLifecycleCreateInstanceEventHandlers { + beforeCreateInstance?: (resourceName:string, data:any)=>void; + afterCreateInstance?: (resourceName:string, data:any)=>void; + } + + interface IDSResourceLifecycleUpdateEventHandlers { + beforeUpdate?: (resourceName:string, data:any, cb:(err:any, data?:any)=>void)=>void; + afterUpdate?: (resourceName:string, data:any, cb:(err:any, data?:any)=>void)=>void; + } + + interface IDSResourceLifecycleDestroyEventHandlers { + beforeDestroy?: (resourceName:string, data:any, cb:(err:any, data?:any)=>void)=>void; + afterDestroy?: (resourceName:string, data:any, cb:(err:any, data?:any)=>void)=>void; + } + + interface IDSResourceLifecycleInjectEventHandlers { + beforeInject?: (resourceName:string, data:any)=>void; + afterInject?: (resourceName:string, data:any)=>void; + } + + interface IDSResourceLifecycleEjectEventHandlers { + beforeEject?: (resourceName:string, data:any)=>void; + afterEject?: (resourceName:string, data:any)=>void; + } + + interface IDSResourceLifecycleReapEventHandlers { + beforeReap?: (resourceName:string, data:any)=>void; + afterReap?: (resourceName:string, data:any)=>void; + } + + interface IDSResourceLifecycleEventHandlers extends IDSResourceLifecycleCreateEventHandlers, + IDSResourceLifecycleCreateInstanceEventHandlers, + IDSResourceLifecycleValidateEventHandlers, + IDSResourceLifecycleUpdateEventHandlers, + IDSResourceLifecycleDestroyEventHandlers, + IDSResourceLifecycleInjectEventHandlers, + IDSResourceLifecycleEjectEventHandlers, + IDSResourceLifecycleReapEventHandlers { + + } + + //TODO check if those are still valid + // errors + interface DSErrors { + + // types + IllegalArgumentError:DSError + NonexistentResourceError:DSError + RuntimeError:DSError + } + + //TODO check if those are still valid + interface DSError { + new (message?:string):DSError; + message: string; + type: string; + } + + // DSAdapter interface + interface IDSAdapter { + create(config:DSResourceDefinition, attrs:any, options?:DSConfiguration):JSDataPromise; + + destroy(config:DSResourceDefinition, id:string, options?:DSConfiguration):JSDataPromise; + destroy(config:DSResourceDefinition, id:number, options?:DSConfiguration):JSDataPromise; + + destroyAll(config:DSResourceDefinition, params:DSFilterParams, options?:DSConfiguration):JSDataPromise; + + find(config:DSResourceDefinition, id:string, options?:DSConfiguration):JSDataPromise; + find(config:DSResourceDefinition, id:number, options?:DSConfiguration):JSDataPromise; + + findAll(config:DSResourceDefinition, params?:DSFilterParams, options?:DSConfiguration):JSDataPromise; + + update(config:DSResourceDefinition, id:string, attrs:any, options?:DSConfiguration):JSDataPromise; + update(config:DSResourceDefinition, id:number, attrs:any, options?:DSConfiguration):JSDataPromise; + + updateAll(config:DSResourceDefinition, attrs:any, params?:DSFilterParams, options?:DSConfiguration):JSDataPromise; + } +} + From 143ee58d484a6fca6f59314ed141a16d55535fc7 Mon Sep 17 00:00:00 2001 From: reppners Date: Thu, 26 Feb 2015 09:53:11 +0100 Subject: [PATCH 2/7] + added tests + changed type definition to expose definitions as module and as globally defined namespace var --- js-data-angular/js-data-angular-tests.ts | 78 ++++ js-data-angular/js-data-angular.d.ts | 23 +- js-data/js-data-node-tests.ts | 17 + js-data/js-data-tests.ts | 493 +++++++++++++++++++++++ js-data/js-data.d.ts | 84 ++-- 5 files changed, 652 insertions(+), 43 deletions(-) create mode 100644 js-data-angular/js-data-angular-tests.ts create mode 100644 js-data/js-data-node-tests.ts create mode 100644 js-data/js-data-tests.ts diff --git a/js-data-angular/js-data-angular-tests.ts b/js-data-angular/js-data-angular-tests.ts new file mode 100644 index 000000000..670857dec --- /dev/null +++ b/js-data-angular/js-data-angular-tests.ts @@ -0,0 +1,78 @@ +/// + +interface IUser { + +} + +interface CustomScope extends ng.IScope { + + comments: Array; + user: IUser; + users: Array; +} + +angular.module('myApp') + .controller('commentsCtrl', function ($scope:CustomScope, store:JSData_.DS, Comment:JSData_.DSResourceDefinition, User:JSData_.DSResourceDefinition) { + + Comment.findAll().then(function (comments) { + $scope.comments = comments; + }); + + // shortest version + User.bindOne(1, $scope, 'user'); + +// short version + store.bindOne('user', 1, $scope, 'user'); + +// long version + $scope.$watch(function () { + return store.lastModified('user', 1); + }, function () { + $scope.user = store.get('user', 1); + }); + + var params = { + where: { + age: { + '>': 30 + } + } + }; + +// shortest verions + User.bindAll(params, $scope, 'users'); + +// short version + store.bindAll('user', params, $scope, 'users'); + +// long version + $scope.$watch(function () { + return store.lastModified('user'); + }, function () { + $scope.users = store.filter('user', params); + }); + }); + +angular.module('myApp') + .run(function (DS:JSData_.DS) { + // We don't register the "User" resource + // as a service, so it can only be used + // via DS.('user', ...) + // The advantage here is that this code + // is guaranteed to be executed, and you + // only ever have to inject "DS" + DS.defineResource('user'); + }) + .factory('Comment', function (DS:JSData_.DS) { + // This code won't execute unless you actually + // inject "Comment" somewhere in your code. + // Thanks Angular... + // Some like injecting actual Resource + // definitions, instead of just "DS" + return DS.defineResource('comment'); + }); + +angular.module('myApp') + .config(function (DSProvider:JSData_.DSProvider) { + DSProvider.defaults.basePath = '/myApi'; // etc. + }); \ No newline at end of file diff --git a/js-data-angular/js-data-angular.d.ts b/js-data-angular/js-data-angular.d.ts index a7c035722..851512d15 100644 --- a/js-data-angular/js-data-angular.d.ts +++ b/js-data-angular/js-data-angular.d.ts @@ -6,14 +6,25 @@ /// /// -declare module JSData { +declare module JSData_ { - class ngDS extends DS { + interface DSProvider { + defaults:DSConfiguration; + } - // sync methods - bindAll(scope:ng.IScope, expr:string, resourceName:string, params:DSFilterParams, cb?:(err:DSError, items:Array)=>void):Function; + interface DS { - bindOne(scope:ng.IScope, expr:string, resourceName:string, id:string, cb?:(err:DSError, item:T)=>void):Function; - bindOne(scope:ng.IScope, expr:string, resourceName:string, id:number, cb?:(err:DSError, item:T)=>void):Function; + bindAll(resourceName:string, params:DSFilterParams, scope:ng.IScope, expr:string, cb?:(err:DSError, items:Array)=>void):Function; + + bindOne(resourceName:string, id:string, scope:ng.IScope, expr:string, cb?:(err:DSError, item:T)=>void):Function; + bindOne(resourceName:string, id:number, scope:ng.IScope, expr:string, cb?:(err:DSError, item:T)=>void):Function; + } + + interface DSResourceDefinition { + + bindAll(params:DSFilterParams, scope:ng.IScope, expr:string, cb?:(err:DSError, items:Array)=>void):Function; + + bindOne(id:string, scope:ng.IScope, expr:string, cb?:(err:DSError, item:T)=>void):Function; + bindOne(id:number, scope:ng.IScope, expr:string, cb?:(err:DSError, item:T)=>void):Function; } } \ No newline at end of file diff --git a/js-data/js-data-node-tests.ts b/js-data/js-data-node-tests.ts new file mode 100644 index 000000000..a0b39a161 --- /dev/null +++ b/js-data/js-data-node-tests.ts @@ -0,0 +1,17 @@ +/// + +import JSData = require('js-data'); +//TODO +//import DSRedisAdapter = require('js-data-redis') +var store = new JSData.DS(); + +// register and use http by default for async operations +//TODO +//store.registerAdapter('redis', new DSRedisAdapter(), {default: true}); + +// simplest model definition +var User = store.defineResource('user'); + +User.find(1).then(function (user: any) { + user; // { id: 1, name: 'John' } +}); \ No newline at end of file diff --git a/js-data/js-data-tests.ts b/js-data/js-data-tests.ts new file mode 100644 index 000000000..1cb3e8e72 --- /dev/null +++ b/js-data/js-data-tests.ts @@ -0,0 +1,493 @@ +/// + +interface IUser { + id?: number; + name?: string; + age?: number; + first?: string; + last?: string; + comments?:Array; + profile?:any; +} + +interface IUserWithMethod extends IUser { + fullName?: () => string; +} + +interface IUserWithComputedProperty extends IUser { + fullName?: string; +} + +var store = new JSData.DS(); + +// register and use http by default for async operations +//TODO +//store.registerAdapter('http', new DSHttpAdapter(), {default: true}); + +// simplest model definition +var User = store.defineResource('user'); + +User.find(1).then(function (user:IUser) { + user; // { id: 1, name: 'John' } +}); + +var user:IUser = User.createInstance({name: 'John'}); + +var store = new JSData.DS(); +var User = store.defineResource('user'); +var user:IUser = User.inject({id: 1, name: 'John'}); +var user2:IUser = User.inject({id: 1, age: 30}); + +user; // User { id: 1, name: 'John', age: 30 } +user2; // User { id: 1, name: 'John', age: 30 } +User.get(1); // User { id: 1, name: 'John', age: 30 } +user === user2; // true +user === User.get(1); // true +user2 === User.get(1); // true + +var store = new JSData.DS({ + // set a default lifecycle hook + afterCreate: function () { + } +}); + +var User = store.defineResource({ + name: 'user', + // override the hook for this resource + afterCreate: function () { + } +}); + +User.create({ + name: 'john' +}, { + // override the hook just for this method call + afterCreate: function () { + } +}).then(()=> { + +}); + +var store = new JSData.DS(); + +var UserWithMethod = store.defineResource({ + name: 'user', + methods: { + fullName: function () { + return this.first + ' ' + this.last; + } + } +}); + +var userWithMethod = UserWithMethod.createInstance({first: 'John', last: 'Anderson'}); + +userWithMethod.fullName(); // "John Anderson" + +var store = new JSData.DS(); + +var UserWithComputedProperty = store.defineResource({ + name: 'user', + computed: { + // each function's argument list defines the fields + // that the computed property depends on + fullName: ['first', 'last', function (first: string, last: string) { + return first + ' ' + last; + }], + // shortand, use the array syntax above if you want + // you computed properties to work after you've + // minified your code. Shorthand style won't work when minified + initials: function (first: string, last: string) { + return first.toUpperCase()[0] + '. ' + last.toUpperCase()[0] + '.'; + } + } +}); + +var userWithComputedProperty:IUserWithComputedProperty = UserWithComputedProperty.inject({ + id: 1, + first: 'John', + last: 'Anderson' +}); + +userWithComputedProperty.fullName; // "John Anderson" + +userWithComputedProperty.first = 'Fred'; + +// js-data relies on dirty-checking, so the +// computed property (probably) hasn't been updated yet +userWithComputedProperty.fullName; // "John Anderson" + +// If your browser supports Object.observe this will have no effect +// otherwise it will trigger the dirty-checking +store.digest(); + +userWithComputedProperty.fullName; // "Fred Anderson" + +interface IComment { + comments?: any; + profile?: any; +} + +var aComment:JSData_.DSResourceDefinition = store.defineResource('comment'); + +// Get all comments where comment.userId == 5 +aComment.filter({ + where: { + userId: { + '==': 5 + } + } +}); + +// Get all comments where comment.userId == 5 +aComment.filter({ + userId: 5 +}); + +// Get all comments where comment.userId === 5 +aComment.filter({ + where: { + userId: { + '===': 5 + } + } +}); + +// Get all comments where comment.userId != 5 +aComment.filter({ + where: { + userId: { + '!=': 5 + } + } +}); + +// Get all comments where comment.userId !== 5 +aComment.filter({ + where: { + userId: { + '!==': 5 + } + } +}); + +// Get all users where user.age > 30 +User.filter({ + where: { + age: { + '>': 30 + } + } +}); + +// Get all users where user.age >= 30 +User.filter({ + where: { + age: { + '>=': 30 + } + } +}); + +// Get all users where user.age < 30 +User.filter({ + where: { + age: { + '<': 30 + } + } +}); + +// Get all users where user.name is in "John Anderson" +User.filter({ + where: { + name: { + 'in': 'John Anderson' + } + } +}); + +// Get all users where user.role is in ["admin", "owner"] +User.filter({ + where: { + role: { + 'in': ['admin', 'owner'] + } + } +}); + +// Get all users where user.name is NOT in "John Anderson" +User.filter({ + where: { + name: { + 'notIn': 'John Anderson' + } + } +}); + +// Get all users where user.role is NOT in ["admin", "owner"] +User.filter({ + where: { + role: { + 'notIn': ['admin', 'owner'] + } + } +}); + +// Get all users where user.name contains "John" +User.filter({ + where: { + name: { + 'contains': 'John' + } + } +}); + +// Get all users where user.roles contains "admin" +User.filter({ + where: { + roles: { + 'contains': 'admin' + } + } +}); + +// Sorts users by age in ascending order +User.filter({ + orderBy: 'age' +}); + +// Sorts users by age in descending order +User.filter({ + orderBy: ['age', 'DESC'] +}); + +// Sorts users by age in descending order and then sort by name in ascending order to break a tie +User.filter({ + orderBy: [ + ['age', 'DESC'], + ['name', 'ASC'] + ] +}); + +var PAGE_SIZE = 20; +var currentPage = 1; + +interface IPost { + +} + +var Post:JSData_.DSResourceDefinition; + +// Grab the first "page" of posts +Post.filter({ + offset: PAGE_SIZE * (currentPage - 1), + limit: PAGE_SIZE +}); + +var User = store.defineResource({ + name: 'user', + relations: { + hasMany: { + comment: { + localField: 'comments', + foreignKey: 'userId' + } + }, + hasOne: { + profile: { + localField: 'profile', + foreignKey: 'userId' + } + }, + belongsTo: { + organization: { + localKey: 'organizationId', + localField: 'organization', + + // if you add this to a belongsTo relation + // then js-data will attempt to use + // a nested url structure, e.g. /organization/15/user/4 + parent: true + } + } + } +}); + +var Organization = store.defineResource({ + name: 'organization', + relations: { + hasMany: { + // this is an example of multiple relations + // of the same type to the same resource + user: [ + { + localField: 'users', + foreignKey: 'organizationId' + }, + { + localField: 'owners', + foreignKey: 'organizationId' + } + ] + } + } +}); + +var Profile = store.defineResource({ + name: 'profile', + relations: { + belongsTo: { + user: { + localField: 'user', + localKey: 'userId' + } + } + } +}); + +var OtherComment = store.defineResource({ + name: 'comment', + relations: { + belongsTo: { + user: { + localField: 'user', + localKey: 'userId' + } + } + } +}); + +User.find(10).then(function (user:IUser) { + // let's assume the server only returned the user + user.comments; // undefined + user.profile; // undefined + + User.loadRelations(user, ['comment', 'profile']).then(function (user:IUser) { + user.comments; // array + user.profile; // object + }); +}); + +var OtherOtherComment = store.defineResource({ + name: 'comment', + relations: { + belongsTo: { + post: { + parent: true, + localKey: 'postId', + localField: 'post' + } + } + } +}); + +// The comment isn't in the data store yet, so js-data wouldn't know +// what the id of the parent "post" would be, so we pass it in manually +OtherOtherComment.find(5, {params: {postId: 4}}); // GET /post/4/comment/5 + +// vs + +OtherOtherComment.find(5); // GET /comment/5 + +OtherOtherComment.inject({id: 1, postId: 2}); + +// We don't have to provide the parentKey here +// because js-data found it in the comment +OtherOtherComment.update(1, {content: 'stuff'}); // PUT /post/2/comment/1 + +// If you don't want the nested for just one of the calls then +// you can do the following: +OtherOtherComment.update(1, {content: 'stuff'}, {params: {postId: false}}); // PUT /comment/1 + +var store = new JSData.DS({ + // set the default + beforeCreate: function (resource, data, cb) { + // do something general + cb(null, data); + } +}); + +var User = store.defineResource({ + name: 'user', + // set just for this resource + beforeCreate: function (resource, data, cb) { + // do something more specific to "users" + cb(null, data); + } +}); + +User.create({name: 'John'}, { + // set just for this method call + beforeCreate: function (resource, data, cb) { + // do something specific for this method call + cb(null, data); + } +}); + +module CustomAdapterTest { + + class MyCustomAdapter implements JSData_.IDSAdapter { + + // All of the methods shown here must return a promise + +// "definition" is a resource defintion that would +// be returned by DS#defineResource + +// "options" would be the options argument that +// was passed into the DS method that is calling +// the adapter method + + create(definition:JSData_.DSResourceDefinition, attrs:Object, options:JSData_.DSConfiguration):JSData_.JSDataPromise { + // Must resolve the promise with the created item + + var promise:JSData_.JSDataPromise; + return promise; + } + + find(definition:JSData_.DSResourceDefinition, id:any, options:JSData_.DSConfiguration):JSData_.JSDataPromise { + // Must resolve the promise with the found item + + var promise:JSData_.JSDataPromise; + return promise; + } + + findAll(definition:JSData_.DSResourceDefinition, params:JSData_.DSFilterParams, options:JSData_.DSConfiguration):JSData_.JSDataPromise { + // Must resolve the promise with the found items + + var promise:JSData_.JSDataPromise; + return promise; + } + + update(definition:JSData_.DSResourceDefinition, id:any, attrs:Object, options:JSData_.DSConfiguration):JSData_.JSDataPromise { + // Must resolve the promise with the updated items + + var promise:JSData_.JSDataPromise; + return promise; + } + + updateAll(definition:JSData_.DSResourceDefinition, attrs:Object, params:JSData_.DSFilterParams, options:JSData_.DSConfiguration):JSData_.JSDataPromise { + // Must resolve the promise with the updated items + + var promise:JSData_.JSDataPromise; + return promise; + } + + destroy(definition:JSData_.DSResourceDefinition, id:any, options:JSData_.DSConfiguration):JSData_.JSDataPromise { + // Must return a promise + + var promise:JSData_.JSDataPromise; + return promise; + } + + destroyAll(definition:JSData_.DSResourceDefinition, params:JSData_.DSFilterParams, options:JSData_.DSConfiguration):JSData_.JSDataPromise { + // Must return a promise + + var promise:JSData_.JSDataPromise; + return promise; + } + } + + var store = new JSData.DS(); + store.registerAdapter('mca', new MyCustomAdapter(), {default: true}); + // the data store will now use your custom adapter by default +} \ No newline at end of file diff --git a/js-data/js-data.d.ts b/js-data/js-data.d.ts index ce06edd89..13fc5e691 100644 --- a/js-data/js-data.d.ts +++ b/js-data/js-data.d.ts @@ -9,35 +9,29 @@ /// -declare class JSDataPromise extends Promise { - - // enhanced with finally - finally(finallyCb?: () => U): Promise; -} - /////////////////////////////////////////////////////////////////////////////// // js-data module (js-data.js) /////////////////////////////////////////////////////////////////////////////// -// Support AMD require -declare module 'js-data' { - export = JSData; -} +// defining what exists in JSData and how it looks +declare module JSData_ { -declare module JSData { + class JSDataPromise extends Promise { - class DS { + // enhanced with finally + finally(finallyCb?:() => U):Promise; + } - constructor(config?:DSConfiguration); + //TODO switch to class again when typescript supports open ended class declaration + interface DS { + + new(config?:DSConfiguration):DS; + + // rather undocumented + errors:DSErrors; defaults:DSConfiguration; - //TODO check if still exists - adapters:any; // Object consists of key-values pairs where the key is the name of the adapter and the value is - // the adapter itself. - //TODO check if still exists - errors:DSErrors; - changeHistory(resourceName:string, id?:string):Array; changeHistory(resourceName:string, id?:number):Array; @@ -72,7 +66,7 @@ declare module JSData { find(resourceName:string, id:string, options?:DSAdapterOperationConfiguration):JSDataPromise; find(resourceName:string, id:number, options?:DSAdapterOperationConfiguration):JSDataPromise; - findAll(resourceName:string, params:DSFilterParams, options?:DSAdapterOperationConfiguration):JSDataPromise>; + findAll(resourceName:string, params?:DSFilterParams, options?:DSAdapterOperationConfiguration):JSDataPromise>; get(resourceName:string, id:string, options?:DSConfiguration):T; get(resourceName:string, id:number, options?:DSConfiguration):T; @@ -117,7 +111,7 @@ declare module JSData { refresh(resourceName:string, id:string, options?:DSAdapterOperationConfiguration):JSDataPromise; refresh(resourceName:string, id:number, options?:DSAdapterOperationConfiguration):JSDataPromise; - registerAdapter(adapterId: string, adapter:IDSAdapter, options?:{default: boolean}):void; + registerAdapter(adapterId:string, adapter:IDSAdapter, options?:{default: boolean}):void; save(resourceName:string, id:string, options?:DSSaveConfiguration):JSDataPromise; save(resourceName:string, id:number, options?:DSSaveConfiguration):JSDataPromise; @@ -128,7 +122,7 @@ declare module JSData { update(resourceName:string, id:string, attrs:any, options?:DSSaveConfiguration):JSDataPromise; update(resourceName:string, id:number, attrs:any, options?:DSSaveConfiguration):JSDataPromise; - updateAll(resourceName:string, attrs:any, params:DSFilterParams, options?:DSAdapterOperationConfiguration):JSDataPromise>; + updateAll(resourceName:string, attrs:any, params?:DSFilterParams, options?:DSAdapterOperationConfiguration):JSDataPromise>; } interface DSConfiguration extends IDSResourceLifecycleEventHandlers { @@ -140,8 +134,10 @@ declare module JSData { defaultAdapter?: string; defaultFilter?: (collection:Array, resourceName:string, params:DSFilterParams, options:DSConfiguration)=>Array; eagerEject?: boolean; + // TODO enable when eagerInject in DS#create is implemented + //eagerInject?: boolean; endpoint?: string; - error?: (message?: any, ...optionalParams: any[])=> void; + error?: (message?:any, ...optionalParams:any[])=> void; fallbackAdapters?: Array; findAllFallbackAdapters?: Array; findAllStrategy?: string; @@ -152,10 +148,13 @@ declare module JSData { findInverseLinks?: boolean; findStrategy?: string idAttribute?: string; - ignoredChanges?:Array; + ignoredChanges?: Array; + // TODO ignoreMissing is undocumented + //ignoreMissing: boolean; keepChangeHistory?: boolean; loadFromServer?: boolean; log?: any; + // TODO wait for union types to be supported // log: (message?: any, ...optionalParams: any[])=> void; // log: boolean; maxAge?: number; @@ -166,7 +165,7 @@ declare module JSData { strategy?: string; upsert?: boolean; useClass?: boolean; - useFilter: boolean; + useFilter?: boolean; } interface DSAdapterOperationConfiguration extends DSConfiguration { @@ -224,7 +223,7 @@ declare module JSData { find(id:string, options?:DSAdapterOperationConfiguration):JSDataPromise; find(id:number, options?:DSAdapterOperationConfiguration):JSDataPromise; - findAll(params:DSFilterParams, options?:DSAdapterOperationConfiguration):JSDataPromise>; + findAll(params?:DSFilterParams, options?:DSAdapterOperationConfiguration):JSDataPromise>; get(id:string, options?:DSConfiguration):T; get(id:number, options?:DSConfiguration):T; @@ -256,10 +255,10 @@ declare module JSData { loadRelations(id:string, relations:string, options?:DSAdapterOperationConfiguration):JSDataPromise; loadRelations(id:number, relations:string, options?:DSAdapterOperationConfiguration):JSDataPromise; - loadRelations(instance:Object, relations:string, options?:DSAdapterOperationConfiguration):JSDataPromise; + loadRelations(instance:T, relations:string, options?:DSAdapterOperationConfiguration):JSDataPromise; loadRelations(id:string, relations:Array, options?:DSAdapterOperationConfiguration):JSDataPromise; loadRelations(id:number, relations:Array, options?:DSAdapterOperationConfiguration):JSDataPromise; - loadRelations(instance:Object, relations:Array, options?:DSAdapterOperationConfiguration):JSDataPromise; + loadRelations(instance:T, relations:Array, options?:DSAdapterOperationConfiguration):JSDataPromise; previous(id:string):T; previous(id:number):T; @@ -278,7 +277,7 @@ declare module JSData { update(id:string, attrs:any, options?:DSSaveConfiguration):JSDataPromise; update(id:number, attrs:any, options?:DSSaveConfiguration):JSDataPromise; - updateAll(attrs:any, params:DSFilterParams, options?:DSAdapterOperationConfiguration):JSDataPromise>; + updateAll(attrs:any, params?:DSFilterParams, options?:DSAdapterOperationConfiguration):JSDataPromise>; } interface DSFilterParams { @@ -290,13 +289,13 @@ declare module JSData { offset?: number; orderBy?: any; - // wait for union types to be supported + // TODO wait for union types to be supported //orderBy?: Array>; //orderBy?: Array; //orderBy?: string; sort?: any; - // wait for union types to be supported + // TODO wait for union types to be supported //sort?: string; //sort?: Array; //sort?: Array>; @@ -354,18 +353,19 @@ declare module JSData { } - //TODO check if those are still valid // errors interface DSErrors { // types - IllegalArgumentError:DSError - NonexistentResourceError:DSError - RuntimeError:DSError + IllegalArgumentError:DSError; + IA:DSError; + RuntimeError:DSError; + R:DSError; + NonexistentResourceError:DSError; + NER:DSError; } - //TODO check if those are still valid - interface DSError { + interface DSError extends Error { new (message?:string):DSError; message: string; type: string; @@ -392,3 +392,13 @@ declare module JSData { } } +// declaring the existing global js object +declare var JSData:{ + DS: JSData_.DS +}; + +//Support node require +declare module 'js-data' { + + export = JSData; +} \ No newline at end of file From cb67739c13ed9104d4527e7ed25ec726c2361fb8 Mon Sep 17 00:00:00 2001 From: reppners Date: Thu, 26 Feb 2015 10:01:27 +0100 Subject: [PATCH 3/7] + initial commit for ds-http-adapter definition --- js-data-http/js-data-http.d.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 js-data-http/js-data-http.d.ts diff --git a/js-data-http/js-data-http.d.ts b/js-data-http/js-data-http.d.ts new file mode 100644 index 000000000..60a279103 --- /dev/null +++ b/js-data-http/js-data-http.d.ts @@ -0,0 +1,13 @@ +// Type definitions for JSData Http Adapter v1.2.0 +// Project: https://github.com/js-data/js-data-http +// Definitions by: Stefan Steinhart +// Definitions: https://github.com/borisyankov/DefinitelyTyped + +/// + +declare module JSData_ { + + interface DSHttpAdapter extends IDSAdapter { + + } +} \ No newline at end of file From 8385575c31441ffa956768161ebb653f51e7e706 Mon Sep 17 00:00:00 2001 From: reppners Date: Thu, 26 Feb 2015 10:35:21 +0100 Subject: [PATCH 4/7] + ds-http-adapter definition complete with tests --- js-data-http/js-data-http-tests.ts | 167 +++++++++++++++++++++++++++++ js-data-http/js-data-http.d.ts | 35 +++++- 2 files changed, 201 insertions(+), 1 deletion(-) create mode 100644 js-data-http/js-data-http-tests.ts diff --git a/js-data-http/js-data-http-tests.ts b/js-data-http/js-data-http-tests.ts new file mode 100644 index 000000000..41459ed81 --- /dev/null +++ b/js-data-http/js-data-http-tests.ts @@ -0,0 +1,167 @@ +/// + +var adapter = new DSHttpAdapter(); +var store = new JSData.DS(); +store.registerAdapter('http', adapter, { default: true }); + +var ADocument:JSData_.DSResourceDefinition = store.defineResource('document'); + +ADocument.inject({ id: 5, author: 'John' }); + +// bypass the data store +adapter.update(ADocument, 5, { author: 'Johnny' }).then(function (document:any) { + document; // { id: 5, author: 'Johnny' } + + // The updated document has NOT been injected into the data store because we bypassed the data store + ADocument.get(document.id); // { id: 5, author: 'John' } +}); + +// Normally you would just go through the data store +ADocument.update(5, { author: 'Johnny' }).then(function (document:any) { + document; // { id: 5, author: 'Johnny' } + + // the updated document has been injected into the data store + ADocument.get(document.id); // { id: 5, author: 'Johnny' } +}); + +ADocument.inject({ id: 5, author: 'John' }); +ADocument.inject({ id: 6, author: 'John' }); + +// bypass the data store +adapter.updateAll(ADocument, { author: 'Johnny' }, { author: 'John' }).then(function (documents) { + documents[0]; // { id: 5, author: 'Johnny' } + + // The updated documents have NOT been injected into the data store because we bypassed the data store + ADocument.filter({ author: 'John' }); // [{...}, {...}] + ADocument.filter({ author: 'Johnny' }); // [] +}); + +// Normally you would just go through the data store +ADocument.updateAll({ author: 'Johnny' }, { author: 'John' }).then(function (documents) { + documents[0]; // { id: 5, author: 'Johnny' } + + // the updated documents have been injected into the data store + ADocument.filter({ author: 'John' }); // [] + ADocument.filter({ author: 'Johnny' }); // [{...}, {...}] +}); + +adapter.PUT('/user/1', { name: 'Johnny' }).then(function (data) { + data.data; // { id: 1, name: 'Johnny', ... } + data.headers; // {...} + data.status; // 200 + data.config; //{...} +}); + +adapter.POST('/user/1', { name: 'John' }).then(function (data) { + data.data; // { id: 1, name: 'John', ... } + data.headers; // {...} + data.status; // 200 + data.config; //{...} +}); + +adapter.HTTP({ url: '/user/1', method: 'put', data: { name: 'Johnny' }}).then(function (data) { + data.data; // { id: 1, name: 'Johnny', ... } + data.headers; // {...} + data.status; // 200 + data.config; //{...} +}); + +adapter.GET('/user/1').then(function (data) { + data.data; // { id: 1, ... } + data.headers; // {...} + data.status; // 200 + data.config; //{...} +}); + +var User:JSData_.DSResourceDefinition = store.defineResource('user'); + +var params:any = { + age: { + '>': 30 + } +}; + +// bypass the data store +adapter.findAll(User, params).then(function (users) { + // users[0].age; 55 // etc., etc. + + // the users have NOT been injected into the data store because we bypassed the data store + User.filter(params); // [] +}); + +// normally you would go through the data store +User.findAll(params).then(function (users) { + // users[0].age; 55 // etc., etc. + + // the users have been injected into the data store + User.filter(params); // [{...}, {...}, ...] +}); + +// bypass the data store +adapter.find(ADocument, 5).then(function (document:any) { + document; // { id: 5, author: 'John Anderson' } + + // the document has NOT been injected into the data store because we bypassed the data store + ADocument.get(document.id); // undefined +}); + +// Normally you would just go through the data store +ADocument.find(5).then(function (document:any) { + document; // { id: 5, author: 'John Anderson' } + + // the document has been injected into the data store + ADocument.get(document.id); // { id: 5, author: 'John Anderson' } +}); + +var params:any = { + author: 'John' +}; + +// bypass the data store +adapter.destroyAll(ADocument, params).then(function () { + // the documents have NOT been ejected from the data store because we bypassed the data store + ADocument.filter(params); // [{...}, {...}, ...] +}); + +// normally you would go through the data store +ADocument.destroyAll(params).then(function () { + // the documents have been ejected from the data store + ADocument.filter(params); // [] +}); + +ADocument.inject({ id: 5, author: 'John' }); + +// bypass the data store +adapter.destroy(ADocument, 5).then(function () { + // the document is still in the data store because we bypassed the data store + //ADocument.get(document.id); // { id: 5, author: 'John' } +}); + +// Normally you would just go through the data store +ADocument.destroy(5).then(function () { + // the document has been ejected from the data store + //ADocument.get(document.id); // undefined +}); + +adapter.DEL('/user/1').then(function (data) { + data.data; // 1 + data.headers; // {...} + data.status; // 204 + data.config; //{...} +}); + +// bypass the data store +adapter.create(ADocument, { author: 'John' }).then(function (document:any) { + document; // { id: 5, author: 'John' } + + // The new document has NOT been injected into the data store because we bypassed the data store + ADocument.get(document.id); // undefined +}); + +// Normally you would just go through the data store +ADocument.create({ author: 'John' }).then(function (document:any) { + document; // { id: 5, author: 'John' } + + // the new document has been injected into the data store + ADocument.get(document.id); // { id: 5, author: 'John' } +}); \ No newline at end of file diff --git a/js-data-http/js-data-http.d.ts b/js-data-http/js-data-http.d.ts index 60a279103..3a8a8adf0 100644 --- a/js-data-http/js-data-http.d.ts +++ b/js-data-http/js-data-http.d.ts @@ -6,8 +6,41 @@ /// declare module JSData_ { + + interface DSHttpAdapterOptions { + serialize?: (resourceName:string, data:any)=>any; + deserialize?: (resourceName:string, data:any)=>any; + queryTransform?: (resourceName:string, params:DSFilterParams)=>any; + httpConfig?: any; + forceTrailingSlash?: boolean; + log?: any; + // TODO wait for union types to be supported + // log: (message?: any, ...optionalParams: any[])=> void; + // log: boolean; + error?: any; + // TODO wait for union types to be supported + // error: (message?: any, ...optionalParams: any[])=> void; + // error: boolean; + } + + interface DSHttpAdapterPromiseResolveType { + data: any; + headers: any; + status: number; + config: any; + } interface DSHttpAdapter extends IDSAdapter { + new(options?:DSHttpAdapterOptions):DSHttpAdapter; + + // DSHttpAdapter uses axios so options are axios config objects. + HTTP(options?:Object):Promise; + DEL(url:string, data?:Object, options?:Object):Promise; + GET(url:string, data?:Object, options?:Object):Promise; + POST(url:string, data?:Object, options?:Object):Promise; + PUT(url:string, data?:Object, options?:Object):Promise; } -} \ No newline at end of file +} + +declare var DSHttpAdapter:JSData_.DSHttpAdapter; \ No newline at end of file From eec429264eb620e1575dd5168fd08cb6cce8e072 Mon Sep 17 00:00:00 2001 From: reppners Date: Mon, 2 Mar 2015 21:27:20 +0100 Subject: [PATCH 5/7] + changing the JSData Promise type to interface + adding definitions object with usage example in tests for enabling custom type definitions of created resource definitions --- js-data/js-data-tests.ts | 35 ++++++++++++++++++++++++++++++++--- js-data/js-data.d.ts | 9 +++++++-- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/js-data/js-data-tests.ts b/js-data/js-data-tests.ts index 1cb3e8e72..a6ac09a42 100644 --- a/js-data/js-data-tests.ts +++ b/js-data/js-data-tests.ts @@ -90,13 +90,13 @@ var UserWithComputedProperty = store.defineResource({ computed: { // each function's argument list defines the fields // that the computed property depends on - fullName: ['first', 'last', function (first: string, last: string) { + fullName: ['first', 'last', function (first:string, last:string) { return first + ' ' + last; }], // shortand, use the array syntax above if you want // you computed properties to work after you've // minified your code. Shorthand style won't work when minified - initials: function (first: string, last: string) { + initials: function (first:string, last:string) { return first.toUpperCase()[0] + '. ' + last.toUpperCase()[0] + '.'; } } @@ -490,4 +490,33 @@ module CustomAdapterTest { var store = new JSData.DS(); store.registerAdapter('mca', new MyCustomAdapter(), {default: true}); // the data store will now use your custom adapter by default -} \ No newline at end of file +} + +/** + * showing the use of open ended interface to realize typings + * on the Datastore.definitions object where all resource definitions + * are saved. + */ + +interface MyCustomDataStore { + + myResource: JSData_.DSResourceDefinition +} + +interface MyResourceDefinition { + +} + +module JSData_ { + + interface DS { + + definitions: MyCustomDataStore; + } +} + +var store = new JSData.DS(); + +var myResourceDefinition = store.defineResource('myResource'); + +myResourceDefinition = store.definitions.myResource; \ No newline at end of file diff --git a/js-data/js-data.d.ts b/js-data/js-data.d.ts index 13fc5e691..b7271c4e2 100644 --- a/js-data/js-data.d.ts +++ b/js-data/js-data.d.ts @@ -16,7 +16,7 @@ // defining what exists in JSData and how it looks declare module JSData_ { - class JSDataPromise extends Promise { + interface JSDataPromise extends Promise { // enhanced with finally finally(finallyCb?:() => U):Promise; @@ -30,6 +30,10 @@ declare module JSData_ { // rather undocumented errors:DSErrors; + // those are objects containing the defined resources and adapters + definitions:any; + adapters:any; + defaults:DSConfiguration; changeHistory(resourceName:string, id?:string):Array; @@ -394,7 +398,8 @@ declare module JSData_ { // declaring the existing global js object declare var JSData:{ - DS: JSData_.DS + DS: JSData_.DS; + DSErrors: JSData_.DSErrors; }; //Support node require From 5f2cc5b96612afdc1583f3171c6cf4038d7368ba Mon Sep 17 00:00:00 2001 From: reppners Date: Wed, 4 Mar 2015 09:09:27 +0100 Subject: [PATCH 6/7] + renaming module JSData_ to JSData since it is possible to declare var and module with the same name --- js-data-angular/js-data-angular-tests.ts | 8 ++--- js-data-angular/js-data-angular.d.ts | 2 +- js-data-http/js-data-http-tests.ts | 4 +-- js-data-http/js-data-http.d.ts | 4 +-- js-data/js-data-tests.ts | 38 ++++++++++++------------ js-data/js-data.d.ts | 6 ++-- 6 files changed, 31 insertions(+), 31 deletions(-) diff --git a/js-data-angular/js-data-angular-tests.ts b/js-data-angular/js-data-angular-tests.ts index 670857dec..8e036212f 100644 --- a/js-data-angular/js-data-angular-tests.ts +++ b/js-data-angular/js-data-angular-tests.ts @@ -12,7 +12,7 @@ interface CustomScope extends ng.IScope { } angular.module('myApp') - .controller('commentsCtrl', function ($scope:CustomScope, store:JSData_.DS, Comment:JSData_.DSResourceDefinition, User:JSData_.DSResourceDefinition) { + .controller('commentsCtrl', function ($scope:CustomScope, store:JSData.DS, Comment:JSData.DSResourceDefinition, User:JSData.DSResourceDefinition) { Comment.findAll().then(function (comments) { $scope.comments = comments; @@ -54,7 +54,7 @@ angular.module('myApp') }); angular.module('myApp') - .run(function (DS:JSData_.DS) { + .run(function (DS:JSData.DS) { // We don't register the "User" resource // as a service, so it can only be used // via DS.('user', ...) @@ -63,7 +63,7 @@ angular.module('myApp') // only ever have to inject "DS" DS.defineResource('user'); }) - .factory('Comment', function (DS:JSData_.DS) { + .factory('Comment', function (DS:JSData.DS) { // This code won't execute unless you actually // inject "Comment" somewhere in your code. // Thanks Angular... @@ -73,6 +73,6 @@ angular.module('myApp') }); angular.module('myApp') - .config(function (DSProvider:JSData_.DSProvider) { + .config(function (DSProvider:JSData.DSProvider) { DSProvider.defaults.basePath = '/myApi'; // etc. }); \ No newline at end of file diff --git a/js-data-angular/js-data-angular.d.ts b/js-data-angular/js-data-angular.d.ts index 851512d15..11a295562 100644 --- a/js-data-angular/js-data-angular.d.ts +++ b/js-data-angular/js-data-angular.d.ts @@ -6,7 +6,7 @@ /// /// -declare module JSData_ { +declare module JSData { interface DSProvider { defaults:DSConfiguration; diff --git a/js-data-http/js-data-http-tests.ts b/js-data-http/js-data-http-tests.ts index 41459ed81..d7c07d654 100644 --- a/js-data-http/js-data-http-tests.ts +++ b/js-data-http/js-data-http-tests.ts @@ -4,7 +4,7 @@ var adapter = new DSHttpAdapter(); var store = new JSData.DS(); store.registerAdapter('http', adapter, { default: true }); -var ADocument:JSData_.DSResourceDefinition = store.defineResource('document'); +var ADocument:JSData.DSResourceDefinition = store.defineResource('document'); ADocument.inject({ id: 5, author: 'John' }); @@ -73,7 +73,7 @@ adapter.GET('/user/1').then(function (data) { data.config; //{...} }); -var User:JSData_.DSResourceDefinition = store.defineResource('user'); +var User:JSData.DSResourceDefinition = store.defineResource('user'); var params:any = { age: { diff --git a/js-data-http/js-data-http.d.ts b/js-data-http/js-data-http.d.ts index 3a8a8adf0..626f55648 100644 --- a/js-data-http/js-data-http.d.ts +++ b/js-data-http/js-data-http.d.ts @@ -5,7 +5,7 @@ /// -declare module JSData_ { +declare module JSData { interface DSHttpAdapterOptions { serialize?: (resourceName:string, data:any)=>any; @@ -43,4 +43,4 @@ declare module JSData_ { } } -declare var DSHttpAdapter:JSData_.DSHttpAdapter; \ No newline at end of file +declare var DSHttpAdapter:JSData.DSHttpAdapter; \ No newline at end of file diff --git a/js-data/js-data-tests.ts b/js-data/js-data-tests.ts index a6ac09a42..35fdbf00f 100644 --- a/js-data/js-data-tests.ts +++ b/js-data/js-data-tests.ts @@ -127,7 +127,7 @@ interface IComment { profile?: any; } -var aComment:JSData_.DSResourceDefinition = store.defineResource('comment'); +var aComment:JSData.DSResourceDefinition = store.defineResource('comment'); // Get all comments where comment.userId == 5 aComment.filter({ @@ -276,7 +276,7 @@ interface IPost { } -var Post:JSData_.DSResourceDefinition; +var Post:JSData.DSResourceDefinition; // Grab the first "page" of posts Post.filter({ @@ -426,7 +426,7 @@ User.create({name: 'John'}, { module CustomAdapterTest { - class MyCustomAdapter implements JSData_.IDSAdapter { + class MyCustomAdapter implements JSData.IDSAdapter { // All of the methods shown here must return a promise @@ -437,52 +437,52 @@ module CustomAdapterTest { // was passed into the DS method that is calling // the adapter method - create(definition:JSData_.DSResourceDefinition, attrs:Object, options:JSData_.DSConfiguration):JSData_.JSDataPromise { + create(definition:JSData.DSResourceDefinition, attrs:Object, options:JSData.DSConfiguration):JSData.JSDataPromise { // Must resolve the promise with the created item - var promise:JSData_.JSDataPromise; + var promise:JSData.JSDataPromise; return promise; } - find(definition:JSData_.DSResourceDefinition, id:any, options:JSData_.DSConfiguration):JSData_.JSDataPromise { + find(definition:JSData.DSResourceDefinition, id:any, options:JSData.DSConfiguration):JSData.JSDataPromise { // Must resolve the promise with the found item - var promise:JSData_.JSDataPromise; + var promise:JSData.JSDataPromise; return promise; } - findAll(definition:JSData_.DSResourceDefinition, params:JSData_.DSFilterParams, options:JSData_.DSConfiguration):JSData_.JSDataPromise { + findAll(definition:JSData.DSResourceDefinition, params:JSData.DSFilterParams, options:JSData.DSConfiguration):JSData.JSDataPromise { // Must resolve the promise with the found items - var promise:JSData_.JSDataPromise; + var promise:JSData.JSDataPromise; return promise; } - update(definition:JSData_.DSResourceDefinition, id:any, attrs:Object, options:JSData_.DSConfiguration):JSData_.JSDataPromise { + update(definition:JSData.DSResourceDefinition, id:any, attrs:Object, options:JSData.DSConfiguration):JSData.JSDataPromise { // Must resolve the promise with the updated items - var promise:JSData_.JSDataPromise; + var promise:JSData.JSDataPromise; return promise; } - updateAll(definition:JSData_.DSResourceDefinition, attrs:Object, params:JSData_.DSFilterParams, options:JSData_.DSConfiguration):JSData_.JSDataPromise { + updateAll(definition:JSData.DSResourceDefinition, attrs:Object, params:JSData.DSFilterParams, options:JSData.DSConfiguration):JSData.JSDataPromise { // Must resolve the promise with the updated items - var promise:JSData_.JSDataPromise; + var promise:JSData.JSDataPromise; return promise; } - destroy(definition:JSData_.DSResourceDefinition, id:any, options:JSData_.DSConfiguration):JSData_.JSDataPromise { + destroy(definition:JSData.DSResourceDefinition, id:any, options:JSData.DSConfiguration):JSData.JSDataPromise { // Must return a promise - var promise:JSData_.JSDataPromise; + var promise:JSData.JSDataPromise; return promise; } - destroyAll(definition:JSData_.DSResourceDefinition, params:JSData_.DSFilterParams, options:JSData_.DSConfiguration):JSData_.JSDataPromise { + destroyAll(definition:JSData.DSResourceDefinition, params:JSData.DSFilterParams, options:JSData.DSConfiguration):JSData.JSDataPromise { // Must return a promise - var promise:JSData_.JSDataPromise; + var promise:JSData.JSDataPromise; return promise; } } @@ -500,14 +500,14 @@ module CustomAdapterTest { interface MyCustomDataStore { - myResource: JSData_.DSResourceDefinition + myResource: JSData.DSResourceDefinition } interface MyResourceDefinition { } -module JSData_ { +module JSData { interface DS { diff --git a/js-data/js-data.d.ts b/js-data/js-data.d.ts index b7271c4e2..cf74b6b79 100644 --- a/js-data/js-data.d.ts +++ b/js-data/js-data.d.ts @@ -14,7 +14,7 @@ /////////////////////////////////////////////////////////////////////////////// // defining what exists in JSData and how it looks -declare module JSData_ { +declare module JSData { interface JSDataPromise extends Promise { @@ -398,8 +398,8 @@ declare module JSData_ { // declaring the existing global js object declare var JSData:{ - DS: JSData_.DS; - DSErrors: JSData_.DSErrors; + DS: JSData.DS; + DSErrors: JSData.DSErrors; }; //Support node require From a6cbfba663b3ec69923b7cf85cef84ad27e640b0 Mon Sep 17 00:00:00 2001 From: reppners Date: Thu, 5 Mar 2015 08:42:15 +0100 Subject: [PATCH 7/7] + changed promise declaration to own custom since it is not possible to reuse ES6 polyfill promise class declaration --- js-data-http/js-data-http.d.ts | 10 +++++----- js-data/js-data-tests.ts | 4 +++- js-data/js-data.d.ts | 14 ++++++-------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/js-data-http/js-data-http.d.ts b/js-data-http/js-data-http.d.ts index 626f55648..416aa3c8c 100644 --- a/js-data-http/js-data-http.d.ts +++ b/js-data-http/js-data-http.d.ts @@ -35,11 +35,11 @@ declare module JSData { new(options?:DSHttpAdapterOptions):DSHttpAdapter; // DSHttpAdapter uses axios so options are axios config objects. - HTTP(options?:Object):Promise; - DEL(url:string, data?:Object, options?:Object):Promise; - GET(url:string, data?:Object, options?:Object):Promise; - POST(url:string, data?:Object, options?:Object):Promise; - PUT(url:string, data?:Object, options?:Object):Promise; + HTTP(options?:Object):JSDataPromise; + DEL(url:string, data?:Object, options?:Object):JSDataPromise; + GET(url:string, data?:Object, options?:Object):JSDataPromise; + POST(url:string, data?:Object, options?:Object):JSDataPromise; + PUT(url:string, data?:Object, options?:Object):JSDataPromise; } } diff --git a/js-data/js-data-tests.ts b/js-data/js-data-tests.ts index 35fdbf00f..c6fe6d13b 100644 --- a/js-data/js-data-tests.ts +++ b/js-data/js-data-tests.ts @@ -387,7 +387,9 @@ OtherOtherComment.find(5, {params: {postId: 4}}); // GET /post/4/comment/5 // vs -OtherOtherComment.find(5); // GET /comment/5 +var promise = OtherOtherComment.find(5); // GET /comment/5 + +promise.then().catch().finally(); OtherOtherComment.inject({id: 1, postId: 2}); diff --git a/js-data/js-data.d.ts b/js-data/js-data.d.ts index cf74b6b79..7017481ee 100644 --- a/js-data/js-data.d.ts +++ b/js-data/js-data.d.ts @@ -3,12 +3,6 @@ // Definitions by: Stefan Steinhart // Definitions: https://github.com/borisyankov/DefinitelyTyped -/////////////////////////////////////////////////////////////////////////////// -// Promises in js-data are ES6 polyfill promises -/////////////////////////////////////////////////////////////////////////////// - -/// - /////////////////////////////////////////////////////////////////////////////// // js-data module (js-data.js) /////////////////////////////////////////////////////////////////////////////// @@ -16,10 +10,14 @@ // defining what exists in JSData and how it looks declare module JSData { - interface JSDataPromise extends Promise { + interface JSDataPromise { + + then(onFulfilled?: (value: R) => U | JSDataPromise, onRejected?: (error: any) => U | JSDataPromise): JSDataPromise; + + catch(onRejected?: (error: any) => U | JSDataPromise): JSDataPromise; // enhanced with finally - finally(finallyCb?:() => U):Promise; + finally(finallyCb?:() => U):JSDataPromise; } //TODO switch to class again when typescript supports open ended class declaration