├── examples ├── backend │ ├── myproject │ │ ├── myapp │ │ │ ├── __init__.py │ │ │ ├── migrations │ │ │ │ ├── __init__.py │ │ │ │ ├── __pycache__ │ │ │ │ │ ├── __init__.cpython-35.pyc │ │ │ │ │ └── 0001_initial.cpython-35.pyc │ │ │ │ └── 0001_initial.py │ │ │ ├── tests.py │ │ │ ├── views.py │ │ │ ├── apps.py │ │ │ ├── __pycache__ │ │ │ │ ├── admin.cpython-35.pyc │ │ │ │ ├── models.cpython-35.pyc │ │ │ │ ├── __init__.cpython-35.pyc │ │ │ │ └── resource.cpython-35.pyc │ │ │ ├── admin.py │ │ │ ├── models.py │ │ │ └── resource.py │ │ ├── myproject │ │ │ ├── __init__.py │ │ │ ├── __pycache__ │ │ │ │ ├── urls.cpython-35.pyc │ │ │ │ ├── wsgi.cpython-35.pyc │ │ │ │ ├── __init__.cpython-35.pyc │ │ │ │ └── settings.cpython-35.pyc │ │ │ ├── wsgi.py │ │ │ ├── urls.py │ │ │ └── settings.py │ │ ├── db.sqlite3 │ │ ├── manage.py │ │ └── topsongs.sql │ └── requirements.txt ├── frontend │ ├── simple_app │ │ ├── lib │ │ │ ├── osi_logo_100X133_90ppi.png │ │ │ ├── angular │ │ │ │ ├── angular-resource.min.js │ │ │ │ └── angular-resource.min.js.map │ │ │ └── angular-resource-tastypie.min.js │ │ ├── controller.js │ │ └── index.html │ ├── usability_app │ │ ├── osi_keyhole_100X100_90ppi.png │ │ ├── README │ │ ├── bower.json │ │ ├── controller.js │ │ └── index.html │ └── index.html └── README.md ├── dev ├── art.zargo ├── art.zargo~ ├── ClassDiagram.png └── arq_rest_angular_django.jpg ├── Gruntfile.js ├── package.json ├── LICENSE ├── bower.json ├── README.md └── src ├── angular-resource-tastypie.min.js └── angular-resource-tastypie.js /examples/backend/myproject/myapp/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/backend/myproject/myproject/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/backend/myproject/myapp/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dev/art.zargo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mw-ferretti/angular-resource-tastypie/HEAD/dev/art.zargo -------------------------------------------------------------------------------- /dev/art.zargo~: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mw-ferretti/angular-resource-tastypie/HEAD/dev/art.zargo~ -------------------------------------------------------------------------------- /dev/ClassDiagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mw-ferretti/angular-resource-tastypie/HEAD/dev/ClassDiagram.png -------------------------------------------------------------------------------- /examples/backend/myproject/myapp/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /examples/backend/myproject/myapp/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /dev/arq_rest_angular_django.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mw-ferretti/angular-resource-tastypie/HEAD/dev/arq_rest_angular_django.jpg -------------------------------------------------------------------------------- /examples/backend/myproject/db.sqlite3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mw-ferretti/angular-resource-tastypie/HEAD/examples/backend/myproject/db.sqlite3 -------------------------------------------------------------------------------- /examples/backend/myproject/myapp/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class MyappConfig(AppConfig): 5 | name = 'myapp' 6 | -------------------------------------------------------------------------------- /examples/backend/requirements.txt: -------------------------------------------------------------------------------- 1 | Django==1.9.5 2 | django-cors-headers==1.1.0 3 | django-tastypie==0.13.3 4 | python-dateutil==2.5.2 5 | python-mimeparse==1.5.1 6 | six==1.10.0 7 | -------------------------------------------------------------------------------- /examples/frontend/simple_app/lib/osi_logo_100X133_90ppi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mw-ferretti/angular-resource-tastypie/HEAD/examples/frontend/simple_app/lib/osi_logo_100X133_90ppi.png -------------------------------------------------------------------------------- /examples/frontend/usability_app/osi_keyhole_100X100_90ppi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mw-ferretti/angular-resource-tastypie/HEAD/examples/frontend/usability_app/osi_keyhole_100X100_90ppi.png -------------------------------------------------------------------------------- /examples/backend/myproject/myapp/__pycache__/admin.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mw-ferretti/angular-resource-tastypie/HEAD/examples/backend/myproject/myapp/__pycache__/admin.cpython-35.pyc -------------------------------------------------------------------------------- /examples/backend/myproject/myapp/__pycache__/models.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mw-ferretti/angular-resource-tastypie/HEAD/examples/backend/myproject/myapp/__pycache__/models.cpython-35.pyc -------------------------------------------------------------------------------- /examples/backend/myproject/myapp/__pycache__/__init__.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mw-ferretti/angular-resource-tastypie/HEAD/examples/backend/myproject/myapp/__pycache__/__init__.cpython-35.pyc -------------------------------------------------------------------------------- /examples/backend/myproject/myapp/__pycache__/resource.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mw-ferretti/angular-resource-tastypie/HEAD/examples/backend/myproject/myapp/__pycache__/resource.cpython-35.pyc -------------------------------------------------------------------------------- /examples/backend/myproject/myproject/__pycache__/urls.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mw-ferretti/angular-resource-tastypie/HEAD/examples/backend/myproject/myproject/__pycache__/urls.cpython-35.pyc -------------------------------------------------------------------------------- /examples/backend/myproject/myproject/__pycache__/wsgi.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mw-ferretti/angular-resource-tastypie/HEAD/examples/backend/myproject/myproject/__pycache__/wsgi.cpython-35.pyc -------------------------------------------------------------------------------- /examples/backend/myproject/myproject/__pycache__/__init__.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mw-ferretti/angular-resource-tastypie/HEAD/examples/backend/myproject/myproject/__pycache__/__init__.cpython-35.pyc -------------------------------------------------------------------------------- /examples/backend/myproject/myproject/__pycache__/settings.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mw-ferretti/angular-resource-tastypie/HEAD/examples/backend/myproject/myproject/__pycache__/settings.cpython-35.pyc -------------------------------------------------------------------------------- /examples/backend/myproject/myapp/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from myapp.models import Song 3 | 4 | class SongAdmin(admin.ModelAdmin): 5 | pass 6 | 7 | admin.site.register(Song, SongAdmin) 8 | 9 | -------------------------------------------------------------------------------- /examples/backend/myproject/myapp/migrations/__pycache__/__init__.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mw-ferretti/angular-resource-tastypie/HEAD/examples/backend/myproject/myapp/migrations/__pycache__/__init__.cpython-35.pyc -------------------------------------------------------------------------------- /examples/frontend/usability_app/README: -------------------------------------------------------------------------------- 1 | IMPORTANT 2 | 3 | If you downloaded the project with bower: 4 | EX: "bower install angular-resource-tastypie" 5 | 6 | Then install dependence for usability_app: 7 | bower install bower.json -------------------------------------------------------------------------------- /examples/backend/myproject/myapp/migrations/__pycache__/0001_initial.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mw-ferretti/angular-resource-tastypie/HEAD/examples/backend/myproject/myapp/migrations/__pycache__/0001_initial.cpython-35.pyc -------------------------------------------------------------------------------- /examples/backend/myproject/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myproject.settings") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | grunt.initConfig({ 3 | uglify: { 4 | compile: { 5 | files: { 6 | 'src/angular-resource-tastypie.min.js': ['src/angular-resource-tastypie.js'] 7 | } 8 | } 9 | } 10 | }); 11 | 12 | grunt.loadNpmTasks('grunt-contrib-uglify'); // load the given tasks 13 | grunt.registerTask('default', ['uglify']); // Default grunt tasks maps to grunt 14 | }; -------------------------------------------------------------------------------- /examples/backend/myproject/myapp/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | class Song(models.Model): 4 | rank = models.IntegerField(blank=True, null=True) 5 | song = models.CharField(max_length=200, blank=True, null=True) 6 | artist = models.CharField(max_length=100, blank=True, null=True) 7 | 8 | def __str__(self): 9 | return '%s - %s, by %s.' % (self.rank, self.song, self.artist) 10 | 11 | class Meta: 12 | ordering = ['rank',] 13 | -------------------------------------------------------------------------------- /examples/backend/myproject/myproject/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for myproject project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.9/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myproject.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /examples/frontend/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | start examples 6 | 7 | 8 | 9 |

Examples

10 |

11 |

23 |

24 | 25 | -------------------------------------------------------------------------------- /examples/frontend/simple_app/controller.js: -------------------------------------------------------------------------------- 1 | angular.module('myApp', ['ngResourceTastypie']) 2 | 3 | .config(function($tastypieProvider){ 4 | 5 | $tastypieProvider 6 | .add('provider1', { 7 | url: 'http://127.0.0.1:8001/api/v1/', 8 | username: 'admin', 9 | apikey: '320c4e7da6ed93946f97f51e6f4c8354a098bb6e' 10 | }); 11 | 12 | }) 13 | 14 | .controller('MyCtrl', ['$scope', '$tastypieResource', '$tastypie', function($scope, $tastypieResource, $tastypie){ 15 | 16 | $scope.Song = new $tastypieResource('song', {limit:5}); 17 | $scope.Song.objects.$find(); 18 | $scope.Api = $tastypie; 19 | $scope.song = $scope.Song.objects.$create(); 20 | 21 | }]); 22 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | #Instructions 2 | 3 | ``` 4 | IMPORTANT - USE PYTHON3 5 | EX: 6 | 1. virtualenv env --no-site-packages --python=/usr/bin/python3 7 | 2. source env/bin/activate 8 | ``` 9 | 10 | ##Backend: 11 | 1. cd backend/ 12 | 2. pip install -r requirements.txt 13 | 3. cd myproject 14 | 4. ./manage.py makemigrations 15 | 5. ./manage.py migrate 16 | 6. ./manage.py runserver 0.0.0.0:8001 17 | 18 | ``` 19 | Django admin superuser "admin", password "abcd8585" 20 | ``` 21 | 22 | ##Frontend: 23 | 1. cd frontend/ 24 | 2. python3 -m http.server 25 | ``` 26 | Now, open your browser and go to 127.0.0.1:8000 27 | ``` 28 | 29 | ``` 30 | IMPORTANT 31 | 32 | If you downloaded the project with bower: 33 | 34 | EX: "bower install angular-resource-tastypie" 35 | 36 | Then install dependence for usability_app: 37 | 1. cd examples/frontend/usability_app 38 | 2. bower install bower.json 39 | ``` 40 | -------------------------------------------------------------------------------- /examples/backend/myproject/myapp/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.5 on 2016-04-04 02:15 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | initial = True 11 | 12 | dependencies = [ 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='Song', 18 | fields=[ 19 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 20 | ('rank', models.IntegerField(blank=True, null=True)), 21 | ('song', models.CharField(blank=True, max_length=200, null=True)), 22 | ('artist', models.CharField(blank=True, max_length=100, null=True)), 23 | ], 24 | options={ 25 | 'ordering': ['rank'], 26 | }, 27 | ), 28 | ] 29 | -------------------------------------------------------------------------------- /examples/frontend/usability_app/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "usability_app", 3 | "version": "1.0.0", 4 | "homepage": "https://github.com/mw-ferretti/angular-resource-tastypie", 5 | "authors": [ 6 | "Marcos William Ferretti " 7 | ], 8 | "description": "Example with google material design", 9 | "main": [ 10 | "examples/frontend/usability_app/controller.js" 11 | ], 12 | "keywords": [ 13 | "tastypie", 14 | "example", 15 | "usability" 16 | ], 17 | "license": "MIT", 18 | "ignore": [ 19 | "**/.*", 20 | "node_modules", 21 | "bower_components", 22 | "test", 23 | "tests" 24 | ], 25 | "dependencies": { 26 | "angular-animate": "1.3.16", 27 | "angular-aria": "1.3.16", 28 | "angular-bootstrap": "~0.13.0", 29 | "angular-material-icons": "~0.4.0", 30 | "angular-material": "0.9.4", 31 | "angular-resource-tastypie": "~1.0.0", 32 | "angular-resource": "1.3.16", 33 | "bootstrap": "~3.3.5", 34 | "json-formatter": "~0.3.0", 35 | "bower.json": "~0.6.9" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /examples/backend/myproject/myproject/urls.py: -------------------------------------------------------------------------------- 1 | """myproject URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/1.9/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.conf.urls import url, include 14 | 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) 15 | """ 16 | from django.conf.urls import url, include 17 | from django.contrib import admin 18 | from tastypie.api import Api 19 | from myapp.resource import SongResource 20 | 21 | v1_api = Api(api_name='v1') 22 | v1_api.register(SongResource()) 23 | 24 | urlpatterns = [ 25 | url(r'^admin/', admin.site.urls), 26 | url(r'^api/', include(v1_api.urls)), 27 | ] 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-resource-tastypie", 3 | "version": "1.0.5", 4 | "description": "RESTful AngularJs client for Django-Tastypie or equivalent schema.", 5 | "main": "src/angular-resource-tastypie.js", 6 | "directories": { 7 | "example": "examples" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/mw-ferretti/angular-resource-tastypie.git" 15 | }, 16 | "keywords": [ 17 | "angular", 18 | "resource", 19 | "tastypie", 20 | "restful", 21 | "django" 22 | ], 23 | "author": "Marcos William Ferretti", 24 | "license": "MIT", 25 | "bugs": { 26 | "url": "https://github.com/mw-ferretti/angular-resource-tastypie/issues" 27 | }, 28 | "homepage": "https://github.com/mw-ferretti/angular-resource-tastypie#readme", 29 | "dependencies": { 30 | "angular": ">=1.3.15", 31 | "angular-resource": ">=1.3.15" 32 | }, 33 | "devDependencies": { 34 | "grunt": "^1.0.1", 35 | "grunt-cli": "^1.2.0", 36 | "grunt-contrib-uglify": "^2.0.0" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Marcos William Ferretti 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-resource-tastypie", 3 | "version": "1.0.5", 4 | "homepage": "https://github.com/mw-ferretti/angular-resource-tastypie", 5 | "authors": [ 6 | "Marcos William Ferretti " 7 | ], 8 | "description": "RESTful AngularJs client for Django-Tastypie or equivalent schema.", 9 | "main": [ 10 | "src/angular-resource-tastypie.min.js" 11 | ], 12 | "moduleType": [ 13 | "amd", 14 | "globals", 15 | "node" 16 | ], 17 | "keywords": [ 18 | "django", 19 | "tastypie", 20 | "restful", 21 | "rest", 22 | "webservices", 23 | "ws", 24 | "client", 25 | "angularjs", 26 | "resource" 27 | ], 28 | "license": "MIT", 29 | "dependencies": { 30 | "angular": ">=1.3.15", 31 | "angular-resource": ">=1.3.15" 32 | }, 33 | "devDependencies": { 34 | "grunt": "^1.0.1", 35 | "grunt-cli": "^1.2.0", 36 | "grunt-contrib-uglify": "^2.0.0" 37 | }, 38 | "repository": { 39 | "type": "git", 40 | "url": "https://github.com/mw-ferretti/angular-resource-tastypie.git" 41 | }, 42 | "ignore": [ 43 | "node_modules", 44 | "bower_components", 45 | "test", 46 | "tests" 47 | ] 48 | } 49 | -------------------------------------------------------------------------------- /examples/backend/myproject/myapp/resource.py: -------------------------------------------------------------------------------- 1 | from tastypie.authorization import DjangoAuthorization 2 | from tastypie.authentication import ApiKeyAuthentication 3 | from django.db.models import Q 4 | from tastypie.resources import ModelResource 5 | from myapp.models import Song 6 | 7 | 8 | class SongResource(ModelResource): 9 | class Meta: 10 | queryset = Song.objects.all() 11 | resource_name = 'song' 12 | authentication = ApiKeyAuthentication() 13 | authorization = DjangoAuthorization() 14 | always_return_data = True 15 | 16 | filtering = { 17 | 'id': ['exact'], 18 | 'rank': ['exact', 'range', 'gt', 'gte', 'lt', 'lte'], 19 | 'song': ['exact', 'icontains'], 20 | 'artist': ['exact', 'icontains'], 21 | } 22 | 23 | def build_filters(self, filters=None, ignore_bad_filters=True): 24 | if filters is None: 25 | filters = {} 26 | 27 | orm_filters = super(SongResource, self).build_filters(filters) 28 | 29 | if('search' in filters): 30 | query = filters['search'] 31 | qset = ( 32 | Q(artist__icontains=query) | 33 | Q(song__icontains=query) 34 | ) 35 | orm_filters.update({'custom': qset}) 36 | 37 | return orm_filters 38 | 39 | def apply_filters(self, request, applicable_filters): 40 | if 'custom' in applicable_filters: 41 | custom = applicable_filters.pop('custom') 42 | else: 43 | custom = None 44 | 45 | semi_filtered = super(SongResource, self).apply_filters(request, applicable_filters) 46 | 47 | return semi_filtered.filter(custom) if custom else semi_filtered 48 | 49 | -------------------------------------------------------------------------------- /examples/frontend/usability_app/controller.js: -------------------------------------------------------------------------------- 1 | angular.module('myApp', ['ngResourceTastypie', 'ngMaterial', 'ui.bootstrap', 'ngMdIcons', 'jsonFormatter']) 2 | 3 | .config(function($tastypieProvider, $mdThemingProvider){ 4 | $tastypieProvider.setResourceUrl('http://127.0.0.1:8001/api/v1/'); 5 | $tastypieProvider.setAuth('admin','320c4e7da6ed93946f97f51e6f4c8354a098bb6e'); 6 | }) 7 | 8 | .service('Api', ['$tastypie', '$tastypieResource', function($tastypie, $tastypieResource){ 9 | this.Provider = $tastypie; 10 | this.Song = new $tastypieResource('song', {limit:4}); 11 | this.Song.objects.$find(); 12 | }]) 13 | 14 | .controller('ProjectCtrl', ['$scope', '$mdSidenav', 'Api', function($scope, $mdSidenav, Api){ 15 | $scope.toggleSidenav = function(menuId){ 16 | $mdSidenav(menuId).toggle(); 17 | }; 18 | $scope.Api = Api; 19 | }]) 20 | 21 | .controller('SongCtrl', function ($scope, $timeout, $mdSidenav, $mdUtil, $log, Api){ 22 | $scope.Song = Api.Song; 23 | 24 | $scope.select = function(obj){ 25 | if(typeof(obj) == 'undefined') return; 26 | if(!obj) return; 27 | $scope.rightTitle = ((obj.id > 0) ? 'Edit Song' : 'New Song'); 28 | $scope.song = obj; 29 | $scope.toggleRight(); 30 | }; 31 | 32 | $scope.new = function(){ 33 | var song = Api.Song.objects.$create(); 34 | $scope.select(song); 35 | }; 36 | 37 | $scope.toggleRight = buildToggler('right'); 38 | function buildToggler(navID){ 39 | var debounceFn = $mdUtil.debounce(function(){ 40 | $mdSidenav(navID) 41 | .toggle() 42 | .then(function(){ 43 | $log.debug("toggle " + navID + " is done"); 44 | }); 45 | },300); 46 | return debounceFn; 47 | } 48 | }) 49 | 50 | .controller('RightCtrl', function ($scope, $timeout, $mdSidenav, $log) { 51 | $scope.close = function(){ 52 | $mdSidenav('right').close().then(function(){ 53 | $log.debug("close RIGHT is done"); 54 | }); 55 | }; 56 | 57 | $scope.save = function(obj){ 58 | obj.$save().then( 59 | function(){ 60 | $scope.close(); 61 | } 62 | ); 63 | }; 64 | 65 | $scope.delete = function(obj){ 66 | obj.$delete().then( 67 | function(){ 68 | $scope.close(); 69 | } 70 | ); 71 | }; 72 | }); -------------------------------------------------------------------------------- /examples/backend/myproject/myproject/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for myproject project. 3 | 4 | Generated by 'django-admin startproject' using Django 1.9.5. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.9/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/1.9/ref/settings/ 11 | """ 12 | 13 | import os 14 | 15 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = 'rvg*skuxh#vx9+%t4=42l6o7s&)tc5fdib(t4a2vzk2mj+zn&0' 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = True 27 | 28 | ALLOWED_HOSTS = [] 29 | 30 | 31 | # Application definition 32 | 33 | INSTALLED_APPS = [ 34 | 'django.contrib.admin', 35 | 'django.contrib.auth', 36 | 'django.contrib.contenttypes', 37 | 'django.contrib.sessions', 38 | 'django.contrib.messages', 39 | 'django.contrib.staticfiles', 40 | 'tastypie', 41 | 'corsheaders', 42 | 'myapp', 43 | ] 44 | 45 | MIDDLEWARE_CLASSES = [ 46 | 'django.middleware.security.SecurityMiddleware', 47 | 'django.contrib.sessions.middleware.SessionMiddleware', 48 | 'django.middleware.common.CommonMiddleware', 49 | 'django.middleware.csrf.CsrfViewMiddleware', 50 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 51 | 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 52 | 'django.contrib.messages.middleware.MessageMiddleware', 53 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 54 | 'corsheaders.middleware.CorsMiddleware', 55 | ] 56 | 57 | CORS_ORIGIN_ALLOW_ALL = True 58 | ROOT_URLCONF = 'myproject.urls' 59 | 60 | TEMPLATES = [ 61 | { 62 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 63 | 'DIRS': [], 64 | 'APP_DIRS': True, 65 | 'OPTIONS': { 66 | 'context_processors': [ 67 | 'django.template.context_processors.debug', 68 | 'django.template.context_processors.request', 69 | 'django.contrib.auth.context_processors.auth', 70 | 'django.contrib.messages.context_processors.messages', 71 | ], 72 | }, 73 | }, 74 | ] 75 | 76 | WSGI_APPLICATION = 'myproject.wsgi.application' 77 | 78 | 79 | # Database 80 | # https://docs.djangoproject.com/en/1.9/ref/settings/#databases 81 | 82 | DATABASES = { 83 | 'default': { 84 | 'ENGINE': 'django.db.backends.sqlite3', 85 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 86 | } 87 | } 88 | 89 | 90 | # Password validation 91 | # https://docs.djangoproject.com/en/1.9/ref/settings/#auth-password-validators 92 | 93 | AUTH_PASSWORD_VALIDATORS = [ 94 | { 95 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 96 | }, 97 | { 98 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 99 | }, 100 | { 101 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 102 | }, 103 | { 104 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 105 | }, 106 | ] 107 | 108 | 109 | # Internationalization 110 | # https://docs.djangoproject.com/en/1.9/topics/i18n/ 111 | 112 | LANGUAGE_CODE = 'en-us' 113 | 114 | TIME_ZONE = 'UTC' 115 | 116 | USE_I18N = True 117 | 118 | USE_L10N = True 119 | 120 | USE_TZ = True 121 | 122 | 123 | # Static files (CSS, JavaScript, Images) 124 | # https://docs.djangoproject.com/en/1.9/howto/static-files/ 125 | 126 | STATIC_URL = '/static/' 127 | -------------------------------------------------------------------------------- /examples/frontend/simple_app/lib/angular/angular-resource.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | AngularJS v1.5.8 3 | (c) 2010-2016 Google, Inc. http://angularjs.org 4 | License: MIT 5 | */ 6 | (function(P,d){'use strict';function G(t,g){g=g||{};d.forEach(g,function(d,q){delete g[q]});for(var q in t)!t.hasOwnProperty(q)||"$"===q.charAt(0)&&"$"===q.charAt(1)||(g[q]=t[q]);return g}var z=d.$$minErr("$resource"),M=/^(\.[a-zA-Z_$@][0-9a-zA-Z_$@]*)+$/;d.module("ngResource",["ng"]).provider("$resource",function(){var t=/^https?:\/\/[^\/]*/,g=this;this.defaults={stripTrailingSlashes:!0,cancellable:!1,actions:{get:{method:"GET"},save:{method:"POST"},query:{method:"GET",isArray:!0},remove:{method:"DELETE"}, 7 | "delete":{method:"DELETE"}}};this.$get=["$http","$log","$q","$timeout",function(q,L,H,I){function A(d,h){return encodeURIComponent(d).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,h?"%20":"+")}function B(d,h){this.template=d;this.defaults=v({},g.defaults,h);this.urlParams={}}function J(e,h,n,k){function b(a,c){var b={};c=v({},h,c);u(c,function(c,h){x(c)&&(c=c(a));var f;if(c&&c.charAt&&"@"==c.charAt(0)){f=a;var l=c.substr(1);if(null==l||""===l||"hasOwnProperty"=== 8 | l||!M.test("."+l))throw z("badmember",l);for(var l=l.split("."),m=0,k=l.length;m 2 | 3 | 4 | 5 | Simple View 6 | 7 | 8 | 9 | 10 | 11 | 16 | 17 | 18 | 19 | 20 | Fork me on GitHub 23 | 24 | 25 |

Angular Resource Tastypie   >>> Api working ...

26 | 27 |
28 |

Meta

29 |
    30 |
  • Endpoint: {{ Song.endpoint }}
  • 31 |
  • Previous: {{ Song.page.meta.previous || 'NULL' }}
  • 32 |
  • Next: {{ Song.page.meta.next || 'NULL' }}
  • 33 |
  • Limit: {{Song.page.meta.limit}}
  • 34 |
  • Offset: {{Song.page.meta.offset}}
  • 35 |
  • Total_count: {{Song.page.meta.total_count}}
  • 36 |
37 |
38 | 39 |
40 |

Form

41 |
42 | ID: {{song.id}}
43 | RANK:
44 | SONG:
45 | ARTIST:
46 | 47 | 48 |
49 |
50 | 51 |
52 | 53 |
54 |

Pagination control server-side

55 | 56 | 57 | {{Song.page.index}}/{{Song.page.length}} 58 | 59 | 60 |
61 | 62 |
63 |

Search server-side

64 | 65 |
66 | 67 |
68 |

Limit server-side

69 | 75 |
76 | 77 |
78 | 79 |
80 |

Objects   >>> Service working ...

81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 |
IdRankSongArtist
{{obj.id}}
100 |

No result

101 |
102 | 103 |

104 |

All with just a single object:

105 | $scope.Song = new $tastypieResource('song', {limit:5}); 106 |

107 | Have a good time!

108 | 109 | 110 | 111 | 122 | 132 | 133 |
112 | 113 | View Marcos W. Ferretti profile on LinkedIn 120 | 121 | 123 |
124 | 125 | 126 | 129 | 130 |
131 |
134 | 135 | -------------------------------------------------------------------------------- /examples/backend/myproject/topsongs.sql: -------------------------------------------------------------------------------- 1 | delete from myapp_song; 2 | VACUUM; 3 | insert into myapp_song(rank,artist,song) values 4 | (100,"Sammy Hagar","I Can’t Drive 55" ), 5 | (99,"Kansas","Carry on Wayward Son" ), 6 | (98,"Scorpions","Rock You Like a Hurricane" ), 7 | (97,"Electric Light Orchestra","Don’t Bring Me Down" ), 8 | (96,"Molly Hatchet","Flirtin’ with Disaster" ), 9 | (95,"Styx","Renegade" ), 10 | (94,"Blind Faith","Can’t Find My Way Home" ), 11 | (93,"George Thorogood","Bad to the Bone" ), 12 | (92,"Grand Funk Railroad","We’re an American Band" ), 13 | (91,".38 Special","Hold On Loosely" ), 14 | (90,"Ram Jam","Black Betty" ), 15 | (89,"John Cougar","Jack & Diane" ), 16 | (88,"Yes","Roundabout" ), 17 | (87,"War","Low Rider" ), 18 | (86,"Steppenwolf","Born to be Wild" ), 19 | (85,"Marshall Tucker Band","Can’t You See" ), 20 | (84,"The James Gang","Funk #49" ), 21 | (83,"The Pretenders","Brass in Pocket" ), 22 | (82,"The Animals","House of the Rising Sun" ), 23 | (81,"T.Rex","Bang a Gong (Get it On)" ), 24 | (80,"Jefferson Airplane","Somebody to Love" ), 25 | (79,"Pete Townshend","Let My Love Open the Door" ), 26 | (78,"Horse With No Name","America" ), 27 | (77,"Mississippi Queen","Mountain" ), 28 | (76,"Nazareth","Hair of the Dog" ), 29 | (75,"Buffalo Springfield","For What It’s Worth" ), 30 | (74,"Manfred Mann’s Earth Band","Blinded by the Light" ), 31 | (73,"Lou Reed","Walk on the Wild Side" ), 32 | (72,"Foreigner","Juke Box Hero" ), 33 | (71,"Janis Joplin","Piece of My Heart" ), 34 | (70,"Talking Heads","Burning Down the House" ), 35 | (69,"Rod Stewart","Maggie May" ), 36 | (68,"Don Henley","The Boys of Summer" ), 37 | (67,"Motley Crue","Home Sweet Home" ), 38 | (66,"Phil Collins","In the Air Tonight" ), 39 | (65,"Bachman-Turner Overdrive","You Ain’t Seen Nothin’ Yet" ), 40 | (64,"Stevie Nicks","Edge of Seventeen" ), 41 | (63,"Billy Joel","Piano Man" ), 42 | (62,"Golden Earring","Radar Love" ), 43 | (61,"Stevie Ray Vaughan","Pride and Joy" ), 44 | (60,"Foghat","Slow Ride" ), 45 | (59,"The Kinks","You Really Got Me" ), 46 | (58,"Steve Miller Band","The Joker" ), 47 | (57,"Joe Walsh","Rocky Mountain Way" ), 48 | (56,"George Harrison","My Sweet Lord" ), 49 | (55,"Def Leppard","Photograph" ), 50 | (54,"Free","All Right Now" ), 51 | (53,"Blue Oyster Cult","Don’t Fear the Reaper" ), 52 | (52,"The Guess Who","American Woman" ), 53 | (51,"The Police","Roxanne" ), 54 | (50,"Peter Frampton","Show Me the Way" ), 55 | (49,"The Doobie Brothers","Black Water" ), 56 | (48,"Rush","Tom Sawyer" ), 57 | (47,"Thin Lizzy","The Boys Are Back in Town" ), 58 | (46,"Judas Priest","Livin’ After Midnight" ), 59 | (45,"Alice Cooper","School’s Out" ), 60 | (44,"Cheap Trick","I Want You to Want Me" ), 61 | (43,"Eric Clapton","Cocaine" ), 62 | (42,"Bad Company","Feel Like Makin’ Love" ), 63 | (41,"Heart","Barracuda" ), 64 | (40,"Fleetwood Mac","Go Your Own Way" ), 65 | (39,"Dire Straits","Sultans of Swing" ), 66 | (38,"Paul McCartney","Live and Let Die" ), 67 | (37,"Elton John","Rocket Man" ), 68 | (36,"Allman Brothers Band","Ramblin’ Man" ), 69 | (35,"The Cars","Just What I Needed" ), 70 | (34,"Neil Young","Rockin’ in the Free World" ), 71 | (33,"Cream","Sunshine of Your Love" ), 72 | (32,"Deep Purple","Smoke on the Water" ), 73 | (31,"Metallica","Enter Sandman" ), 74 | (30,"Crosby, Stills & Nash","Suite: Judy Blue Eyes" ), 75 | (29,"David Bowie","Space Oddity" ), 76 | (28,"Boston","More Than a Feeling" ), 77 | (27,"Derek and the Dominos","Layla" ), 78 | (26,"Kiss","Rock and Roll All Nite" ), 79 | (25,"Tom Petty","Free Fallin’" ), 80 | (24,"Ted Nugent","Stranglehold" ), 81 | (23,"John Lennon","Imagine" ), 82 | (22,"Bob Seger","Turn the Page" ), 83 | (21,"Bob Dylan","Like a Rolling Stone" ), 84 | (20,"The Eagles","Hotel California" ), 85 | (19,"ZZ Top","La Grange" ), 86 | (18,"The Doors","L.A Woman" ), 87 | (17,"Ozzy Osbourne","Crazy Train" ), 88 | (16,"Bruce Springsteen","Born to Run" ), 89 | (15,"Creedence Clearwater Revival","Fortunate Son" ), 90 | (14,"Lynyrd Skynyrd","Sweet Home Alabama" ), 91 | (13,"Journey","Don’t Stop Believin" ), 92 | (12,"Guns N’ Roses","Sweet Child O’ Mine" ), 93 | (11,"The Who","Baba O’ Riley" ), 94 | (10,"Black Sabbath","Paranoid" ), 95 | (9,"Pink Floyd","Comfortably Numb" ), 96 | (8,"Van Halen","Everybody Wants Some!!" ), 97 | (7,"Queen","Bohemian Rhapsody" ), 98 | (6,"The Jimi Hendrix Experience","All Along the Watchtower" ), 99 | (5,"The Beatles","A Day in the Life" ), 100 | (4,"AC/DC","Back in Black" ), 101 | (3,"The Rolling Stones","Gimme Shelter" ), 102 | (2,"Led Zeppelin","Kashmir" ), 103 | (1,"Aerosmith","Sweet Emotion" ); 104 | -------------------------------------------------------------------------------- /examples/frontend/simple_app/lib/angular/angular-resource.min.js.map: -------------------------------------------------------------------------------- 1 | { 2 | "version":3, 3 | "file":"angular-resource.min.js", 4 | "lineCount":14, 5 | "mappings":"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", 6 | "sources":["angular-resource.js"], 7 | "names":["window","angular","shallowClearAndCopy","src","dst","forEach","value","key","hasOwnProperty","charAt","$resourceMinErr","$$minErr","MEMBER_NAME_REGEX","module","provider","PROTOCOL_AND_DOMAIN_REGEX","defaults","stripTrailingSlashes","cancellable","actions","method","isArray","$get","$http","$log","$q","$timeout","encodeUriQuery","val","pctEncodeSpaces","encodeURIComponent","replace","Route","template","extend","urlParams","resourceFactory","url","paramDefaults","options","extractParams","data","actionParams","ids","isFunction","path","test","keys","split","i","ii","length","isDefined","obj","undefined","defaultResponseInterceptor","response","resource","Resource","route","prototype","toJSON","Resource.prototype.toJSON","$promise","$resolved","action","name","hasBody","numericTimeout","timeout","isNumber","debug","a1","a2","a3","a4","params","success","error","arguments","isInstanceCall","httpConfig","responseInterceptor","interceptor","responseErrorInterceptor","responseError","timeoutDeferred","numericTimeoutPromise","copy","defer","promise","resolve","setUrlParams","then","item","push","noop","reject","$cancelRequest","cancel","headers","result","call","bind","Resource.bind","additionalParamDefaults","config","actionUrl","self","encodedVal","protocolAndDomain","param","RegExp","isQueryParamValue","match","paramInfo","urlParam","p1","leadingSlashes","tail"] 8 | } 9 | -------------------------------------------------------------------------------- /examples/frontend/usability_app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | Fork me on GitHub 47 | 48 |
49 | 50 |

51 | Angular Resource Tastypie 52 |

53 | 54 | 55 |
56 |
57 | 58 | 59 | 62 | 63 |
64 |
65 | 66 | View Marcos W. Ferretti profile on LinkedIn 72 | 73 |
74 |
75 | 76 | 77 | 78 |
79 | 86 | 87 | 88 | 89 |
90 | 91 |
92 | 99 | 100 | 101 |
102 | 109 | 110 | 111 | 112 | 113 | 114 |
115 |
116 | 117 | 118 |
119 | 120 | 121 |
{{obj.rank}}
122 |
123 |

{{obj.song}}

124 |

125 | {{obj.artist}} 126 |

127 |
128 |
129 | 130 | 131 | 132 |
133 | 134 |
135 |
136 |
137 |
138 | 139 | 140 |
141 | 149 | 150 | 151 | 160 |
161 |
162 | 163 | 164 | 165 |

{{rightTitle}}

166 |
167 | 168 |
169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 |
182 | 183 |
184 | Save 185 | Delete 186 |
Raised
187 |
188 |
189 |
190 |
191 | 192 |
193 |
194 | 195 | 196 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Angular Resource Tastypie 2 | [RESTful](http://www.ibm.com/developerworks/library/ws-restful/) AngularJs client for [Django-Tastypie](https://django-tastypie.readthedocs.org/en/latest/) or equivalent schema. 3 | 4 | > [For angular2+ use the version in TypeScript (ts-resource-tastypie)](https://github.com/mw-ferretti/ts-resource-tastypie) 5 | 6 | Features: 7 | 1. Pagination 8 | 2. Complete CRUD 9 | 3. Abstract AJAX(J) providing operations which are similar to the [Django Model API](https://docs.djangoproject.com/en/dev/topics/db/queries/) 10 | 11 | ## Context 12 | [RESTful](http://www.ibm.com/developerworks/library/ws-restful/) architecture with [AngularJs](https://angularjs.org/) and [Django](https://www.djangoproject.com/). 13 | ![Architecture](/dev/arq_rest_angular_django.jpg) 14 | 15 | IMPORTANT: 16 | - Backend: Security rules for data persistence and access. 17 | - Frontend: [Usability](https://en.wikipedia.org/wiki/Usability) rules, only! 18 | 19 | BENEFITS: 20 | - Asynchronous development between frontend and backend developers. 21 | - Reuse of web developers team to create mobile applications. 22 | - The frontend is isolated, we can distribute it as an application by using [Apache Cordova](https://cordova.apache.org/). 23 | - Independent layers between business rules and usability rules of user interface. 24 | - Business rules are the same for different types of [UI](https://en.wikipedia.org/wiki/User_interface). We can create different [UIs](https://en.wikipedia.org/wiki/User_interface) with any other programming language, passing through the same business rules on the backend. 25 | - And more ... 26 | 27 | ## Requirements 28 | - Frontend: [AngularJs 1.3+](https://angularjs.org/) 29 | - Backend: [Django-Tastypie](https://django-tastypie.readthedocs.org/en/latest/) or equivalent schema. 30 | 31 | Note 32 | > Requirements for the backend: 33 | > - [django-cors-headers](https://github.com/ottoyiu/django-cors-headers) 34 | > - [always_return_data](http://django-tastypie.readthedocs.org/en/latest/resources.html#always-return-data) 35 | 36 | [See how to use.](https://github.com/mw-ferretti/angular-resource-tastypie/tree/master/examples) 37 | 38 | ## Install 39 | > bower install angular-resource-tastypie - [See how to use.](https://github.com/mw-ferretti/angular-resource-tastypie/tree/master/examples/frontend/usability_app) 40 | 41 | ## Usage 42 | ```javascript 43 | angular.module('myApp', ['ngResourceTastypie']) 44 | 45 | .config(function($tastypieProvider){ 46 | $tastypieProvider 47 | .add('provider1', { 48 | url: 'http://address1/api/v1/', 49 | username: 'username', 50 | apikey: 'apikey' 51 | }); 52 | }) 53 | 54 | .controller('MyCtrl', ['$scope', '$tastypieResource', function($scope, $tastypieResource){ 55 | 56 | $scope.ServiceEndpoint = new $tastypieResource('ServiceEndpoint', {limit:5}); 57 | $scope.ServiceEndpoint.objects.$find(); 58 | 59 | }]); 60 | ``` 61 | 62 | - Add the module dependency: 63 | ```javascript 64 | angular.module('myApp', ['ngResourceTastypie']) 65 | ``` 66 | 67 | - Add your web services provider configuration: 68 | ```javascript 69 | .config(function($tastypieProvider){ 70 | $tastypieProvider 71 | .add('provider1', { 72 | url: 'http://address1/api/v1/', 73 | username: 'username', 74 | apikey: 'apikey' 75 | }); 76 | }) 77 | ``` 78 | 79 | - Add dependency in the scope: 80 | ```javascript 81 | .controller('MyCtrl', ['$scope', '$tastypieResource', function($scope, $tastypieResource){ 82 | ... 83 | }]); 84 | ``` 85 | 86 | IMPORTANT: 87 | ```javascript 88 | $tastypieProvider 89 | .add('provider1', { 90 | apikey: 'apikey' 91 | }); 92 | ``` 93 |
94 |

95 | This apikey was fixed only for demo purposes.
96 | You must generate a dynamic api_key after the user login, on backend authorization system, and then configure this attribute.
97 | With django-tastypie this task is quite simple:
98 | http://django-tastypie.readthedocs.org/en/latest/authentication.html 99 |

100 |
101 | 102 | ```javascript 103 | //Access $tastypieProvider in the controller 104 | //Login and Logout sample: 105 | .controller('AuthCtrl', ['$scope', '$tastypie', '$http', function($scope, $tastypie, $http){ 106 | $scope.login = function(){ 107 | var data = { 108 | userName: $scope.userName, 109 | password: $scope.password 110 | }; 111 | $http.post('/loginUrl', data).success(function(response){ 112 | $tastypie.setProviderAuth('providerName', response.username, response.apikey) 113 | }); 114 | } 115 | 116 | $scope.logout = function(){ 117 | var provider = $tastypie.getProvider('providerName'); 118 | $http.post('/logoutUrl', {username: provider.username, apikey: provider.apikey}); 119 | $tastypie.clearAuthSession('providerName'); //clean auth session data 120 | } 121 | }]); 122 | ``` 123 | 124 | MULTI "PROVIDER" SAMPLE: 125 | ```javascript 126 | angular.module('myApp', ['ngResourceTastypie']) 127 | 128 | .config(function($tastypieProvider){ 129 | $tastypieProvider 130 | .add('provider1', { 131 | url: 'http://address1/api/v1/', 132 | username: 'username', 133 | apikey: 'apikey' 134 | }) 135 | .add('provider2', { 136 | url: 'http://address2/api/v1/', 137 | username: 'username', 138 | apikey: 'apikey' 139 | }); 140 | 141 | $tastypieProvider.setDefault('provider1'); 142 | }) 143 | 144 | .controller('MyCtrl', ['$scope', '$tastypieResource', function($scope, $tastypieResource){ 145 | 146 | $scope.ServiceEndpoint = new $tastypieResource('ServiceEndpoint', {limit:5}); //using default provider - "provider1". 147 | $scope.ServiceEndpoint.objects.$find(); 148 | 149 | $scope.ServiceEndpoint = new $tastypieResource('ServiceEndpoint', {limit:5}, 'provider2'); //using selected provider - "provider2". 150 | $scope.ServiceEndpoint.objects.$find(); 151 | 152 | }]); 153 | ``` 154 | 155 | 156 | ## Making queries 157 | The $tastypieResource class is held responsible for connecting on the specific "list endpoint". 158 | 159 | Consider the following example: 160 | We have a service called "song", which is responsible for providing the "TOP 100 SONGS CLASSIC ROCK": 161 | ``` 162 | http://127.0.0.1:8001/api/v1/song/ 163 | ``` 164 | Then: 165 | ```javascript 166 | $scope.Song = new $tastypieResource('song'); 167 | 168 | //or with default filters 169 | $scope.Song = new $tastypieResource('song',{limit:5}); 170 | 171 | ``` 172 | 173 | - Creating objects 174 | The "$create()" method will return a "$tastypieObjects" object.
175 | The $tastypieObjects class is held responsible for providing the "(C)reate, (R)ead, (U)pdate, (D)elete" methods. 176 | 177 | ```javascript 178 | $scope.song = $scope.Song.objects.$create(); 179 | $scope.song.rank = 1 180 | $scope.song.song = "Sweet Emotion" 181 | $scope.song.artist = "Aerosmith" 182 | $scope.song.$save(); 183 | ``` 184 | 185 | ```javascript 186 | //or 187 | $scope.Song.objects.$create({ 188 | rank: 1, 189 | song: "Sweet Emotion", 190 | artist: "Aerosmith" 191 | }).$save(); 192 | ``` 193 | 194 | ```javascript 195 | //or with callback 196 | $scope.Song.objects.$create({ 197 | rank: 1, 198 | song: "Sweet Emotion", 199 | artist: "Aerosmith" 200 | }).$save().then( 201 | function(result){ 202 | console.log(result); 203 | }, 204 | function(error){ 205 | console.log(error); 206 | } 207 | ); 208 | ``` 209 | 210 |
211 |

212 | After saving, your obj will be updated. For example, your obj now has an "id".. wow!! 213 |

214 |
215 | 216 | 217 | - Updating objects 218 | 219 | ```javascript 220 | $scope.Song.objects.$update({ 221 | id:100, 222 | song:'Sweet Emotion ...' 223 | }); 224 | ``` 225 | 226 | ```javascript 227 | //or with callback 228 | $scope.Song.objects.$update({ 229 | id:100, 230 | song:'Sweet Emotion' 231 | }).then( 232 | function(result){ 233 | console.log(result); 234 | }, 235 | function(error){ 236 | console.log(error); 237 | } 238 | ); 239 | ``` 240 | 241 | ```javascript 242 | //or from get 243 | $scope.Song.objects.$get({id:100}).then( 244 | function(result){ 245 | result.rank += 1; 246 | result.$save(); 247 | } 248 | ); 249 | ``` 250 | 251 | ```javascript 252 | //or from local object 253 | //creating 254 | var song = $scope.Song.objects.$create(); 255 | song.rank = 1; 256 | song.song = "Sweet Emotion"; 257 | song.artist = "Aerosmith"; 258 | song.$save(); 259 | 260 | //updating 261 | song.rank = 2 262 | song.$save(); 263 | ``` 264 | 265 | 266 | - Deleting objects 267 | 268 | ```javascript 269 | $scope.Song.objects.$delete({id:100}); 270 | ``` 271 | 272 | ```javascript 273 | //or with callback 274 | $scope.Song.objects.$delete({id:100}).then( 275 | function(result){ 276 | console.log(result); 277 | }, 278 | function(error){ 279 | console.log(error); 280 | } 281 | ); 282 | ``` 283 | 284 | ```javascript 285 | //or from local object 286 | //creating 287 | var song = $scope.Song.objects.$create(); 288 | song.rank = 1 289 | song.song = "Sweet Emotion" 290 | song.artist = "Aerosmith" 291 | song.$save(); 292 | 293 | //deleting 294 | song.$delete() 295 | ``` 296 | 297 | - Retrieving objects 298 | The "$find()" method will return a "$tastypiePaginator" object.
299 | The $tastypiePaginator class is responsible for providing the "pagination control" methods, and the objects list. 300 | 301 | ```javascript 302 | //all objects 303 | $scope.Song.objects.$find(); 304 | ``` 305 | 306 | ```javascript 307 | //or with filters 308 | $scope.Song.objects.$find({rank__lte:10}); 309 | ``` 310 | 311 | ```javascript 312 | //or with callback 313 | $scope.Song.objects.$find({rank__lte:10}).then( 314 | function(result){ 315 | console.log(result); //The "result" is a "$tastypiePaginator" object. 316 | }, 317 | function(error){ 318 | console.log(error); 319 | } 320 | ); 321 | ``` 322 |
323 | NOTE 324 |

325 | 1. After running the "$find" method for the first time, you have inside your instance "$tastypieResource" ($scope.Song), a "$tastypiePaginator" object ($scope.Song.page). 326 |

327 |

328 | 2. For each item of "$scope.Song.page.objects" you retrieve a "$tastypieObjects" object. ;) 329 |

330 |
331 | 332 | ```javascript 333 | //All page attributes: 334 | $scope.Song.page.meta.previous; // URL of previous page 335 | $scope.Song.page.meta.next; // URL of next page 336 | $scope.Song.page.meta.limit; // Limit of records by page 337 | $scope.Song.page.meta.offset; // Current displacement records 338 | $scope.Song.page.meta.total_count; // Total count of found records. 339 | $scope.Song.page.objects; // Objects ($tastypieObjects) list of current page 340 | $scope.Song.page.index; // Current number page 341 | $scope.Song.page.length; // Pages quantity 342 | $scope.Song.page.range; // Numbers list of pages 343 | 344 | //All page methods: 345 | $scope.Song.page.change(index); 346 | $scope.Song.page.next(); 347 | $scope.Song.page.previous(); 348 | $scope.Song.page.refresh(); 349 | $scope.Song.page.first(); 350 | $scope.Song.page.last(); 351 | 352 | //All methods has promise. EX: 353 | $scope.Song.page.next().then( 354 | function(result){ 355 | console.log(result); //The "result" is a "$tastypiePaginator" object. 356 | }, 357 | function(error){ 358 | console.log(error); 359 | } 360 | ); 361 | ``` 362 | 363 | ```javascript 364 | //get object from resource_uri 365 | //In this case, there is no paging. An "$tastypieObjects" is returned. 366 | $scope.Song.objects.$get({id:100}).then( 367 | function(result){ 368 | console.log(result); 369 | }, 370 | function(error){ 371 | console.log(error); 372 | } 373 | ); 374 | ``` 375 | 376 | ## Working status 377 | 1. $tastypie.working: Global requests (return true or false) 378 | 2. YourService.working: Individual service requests (return true or false) 379 | 380 | It is usual for when you need to show an animation of "waiting", "working", "loading", etc ... 381 | at the time the user makes a request. Ex: save, update, delete, search, etc ... 382 | 383 | Informe the request status for user. EX: 384 | 385 | ```javascript 386 | .controller('ProjectCtrl', ['$scope', '$tastypie', '$tastypieResource', function($scope, $tastypie, $tastypieResource){ 387 | $scope.Api = $tastypie; 388 | $scope.Song = new $tastypieResource('song', {limit:4}); 389 | }]) 390 | ``` 391 | 392 | ```html 393 | >>> Api working ... please wait... 394 | >>> Service Song working ... please wait ... 395 | ``` 396 | [See how to use.](https://github.com/mw-ferretti/angular-resource-tastypie/tree/master/examples) 397 | 398 | ## Class Diagram 399 | ![Class Diagram](/dev/ClassDiagram.png) 400 | 401 | ## Contribute 402 | If you found it useful, please consider paying me a coffee ; 403 | [![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=RGQ8NSYPA59FL) 404 | 405 | ## License 406 | angular-resource-tastypie is released under the [MIT License](https://github.com/mw-ferretti/angular-resource-tastypie/blob/master/LICENSE). 407 | -------------------------------------------------------------------------------- /src/angular-resource-tastypie.min.js: -------------------------------------------------------------------------------- 1 | var ngResourceTastypie={name:"Angular Resource Tastypie",description:"RESTful AngularJs client for Django-Tastypie or equivalent schema.",version:{full:"1.0.5",major:1,minor:0,dot:5,codeName:"Cappuccino"},author:{name:"Marcos William Ferretti",email:"ferretti.spo@gmail.com",github:"https://github.com/mw-ferretti/",linkedin:"https://www.linkedin.com/in/mwferretti"},license:"MIT, (c) 2014-2016 Marcos William Ferretti",source:"https://github.com/mw-ferretti/angular-resource-tastypie"};if("undefined"==typeof angular)throw"[ngResourceTastypie v".concat(ngResourceTastypie.version.full,"] Requires AngularJs 1.3+");if(angular.version.major<1||1==angular.version.major&&angular.version.minor<3)throw"[ngResourceTastypie v".concat(ngResourceTastypie.version.full,"] Requires AngularJs 1.3+, your version is ",angular.version.full);angular.module("ngResourceTastypie",["ngResource"]).constant("ngResourceTastypie",ngResourceTastypie).config(["$resourceProvider",function(a){a.defaults.stripTrailingSlashes=!1}]).provider("$tastypie",["$httpProvider",function(a){a.defaults.useXDomain=!0,delete a.defaults.headers.common["X-Requested-With"],a.defaults.headers.common["Content-Type"]="application/json";var b=this,c="",d="",e={username:"",api_key:""};b.providers={},b.default={},b.getProvider=function(a){var c={};if(c="default"==a?b.default||{}:b.providers[a]||{},!c.hasOwnProperty("url"))throw'[$tastypieProvider][GetProvider] provider "'.concat(a,'" not found.');return c},b.setDefault=function(a){if(!b.providers.hasOwnProperty(a))throw"[$tastypieProvider][ProviderSetDefault] Provider ".concat(a," not found.");b.default=b.providers[a],c=b.default.url,d=b.default.domain,e.username=b.default.username,e.api_key=b.default.apikey,"undefined"!=typeof Storage&&(sessionStorage.$tastypieDefaultProvider=angular.toJson(b.default))},b.add=function(a,c){if(!a||!("string"==typeof a||a instanceof String))throw'[$tastypieProvider][ProviderAdd] Invalid providerName string. Usage: add("providerName", {url:"address"})';if(!c||!angular.isObject(c)||!c.hasOwnProperty("url"))throw'[$tastypieProvider][ProviderAdd] Invalid providerObj object. Usage: add("providerName", {url:"address"})';if(b.providers[a])return b;c.name=a;var d=document.createElement("a");d.href=c.url;var e=d.protocol.concat("//",d.hostname);return""!=d.port&&(e=e.concat(":",d.port)),c.domain=e,c.hasOwnProperty("headers")||(c.headers={}),c.headers.hasOwnProperty("Content-Type")||(c.headers["Content-Type"]="application/json"),c.hasOwnProperty("username")&&c.hasOwnProperty("apikey")&&(c.headers.Authorization="ApiKey ".concat(c.username,":",c.apikey)),b.providers[a]=c,b.default.hasOwnProperty("name")||b.setDefault(a),"undefined"!=typeof Storage&&(sessionStorage.$tastypieProviders=angular.toJson(b.providers)),b},b.httpExceptions={},b.httpExceptionsAdd=function(a,c){if(!a)throw"[$tastypieProvider][httpExceptionsAdd] Invalid httpCode integer. Usage: httpExceptionsAdd(httpCode, callback)";if(!c||!angular.isFunction(c))throw"[$tastypieProvider][httpExceptionsAdd] Invalid callback function. Usage: httpExceptionsAdd(httpCode, callback)";return b.httpExceptions["c".concat(a)]=c,b},b.setProviderAuth=function(a,c,d){var e=b.getProvider(a);e.username=c,e.apikey=d,e.hasOwnProperty("headers")||(e.headers={}),e.headers.Authorization="ApiKey ".concat(e.username,":",e.apikey),b.providers[a]=e,b.default.name==a&&(b.default=e,"undefined"!=typeof Storage&&(sessionStorage.$tastypieDefaultProvider=angular.toJson(b.default))),"undefined"!=typeof Storage&&(sessionStorage.$tastypieProviders=angular.toJson(b.providers))},b.setResourceUrl=function(a){if(!a||!("string"==typeof a||a instanceof String))throw"[$tastypieProvider][SetResourceUrl] Invalid URL.";c=a;var e=document.createElement("a");e.href=c,d=e.protocol.concat("//",e.hostname),""!=e.port&&(d=d.concat(":",e.port)),b.default.name="default",b.default.hasOwnProperty("headers")||(b.default.headers={}),b.default.headers.hasOwnProperty("Content-Type")||(b.default.headers["Content-Type"]="application/json"),b.default.url=a,b.default.domain=d,b.providers[b.default.name]=b.default,"undefined"!=typeof Storage&&(sessionStorage.$tastypieDefaultProvider=angular.toJson(b.default),sessionStorage.$tastypieProviders=angular.toJson(b.providers))},b.setAuth=function(a,c){var d=a&&("string"==typeof a||a instanceof String);c&&("string"==typeof c||String);if(!d)throw"[$tastypieProvider][SetResourceUrl] Invalid username.";if(!c)throw"[$tastypieProvider][SetResourceUrl] Invalid apikey.";e.username=a,e.api_key=c,b.default.name="default",b.default.hasOwnProperty("headers")||(b.default.headers={}),b.default.headers.hasOwnProperty("Content-Type")||(b.default.headers["Content-Type"]="application/json"),b.default.username=a,b.default.apikey=c,b.default.headers.Authorization="ApiKey ".concat(a,":",c),b.providers[b.default.name]=b.default,"undefined"!=typeof Storage&&(sessionStorage.$tastypieDefaultProvider=angular.toJson(b.default),sessionStorage.$tastypieProviders=angular.toJson(b.providers))};var f=function(a){if(!angular.isObject(a)||!a.hasOwnProperty("url"))throw"[$tastypieProvider][clearAuthProvider] Invalid provider.";a.hasOwnProperty("username")&&(a.username=""),a.hasOwnProperty("apikey")&&(a.apikey=""),a.hasOwnProperty("headers")&&a.headers.hasOwnProperty("Authorization")&&delete a.headers.Authorization,b.providers[a.name]=a,"undefined"!=typeof Storage&&(sessionStorage.$tastypieProviders=angular.toJson(b.providers)),a.name==b.default.name&&(e.username="",e.api_key="",b.default=a,"undefined"!=typeof Storage&&(sessionStorage.$tastypieDefaultProvider=angular.toJson(b.default)))};b.clearAuthSession=function(a){if(!a||!("string"==typeof a||a instanceof String))throw"[$tastypieProvider][ClearAuthSession] Invalid providerName.";if("all"==a)for(var c=Object.keys(b.providers),d=0;d0},set:function(a){void 0===a&&(a=!1),a?g.push(1):g.splice(-1,1)}}}),b.$get=function(){return{providers:b.providers,add:b.add,getProvider:b.getProvider,setDefault:b.setDefault,setProviderAuth:b.setProviderAuth,default:b.default,clearAuthSession:b.clearAuthSession,resource_url:b.getResourceUrl(),resource_domain:b.getResourceDomain(),auth:b.getAuth(),working:b.working,setAuth:b.setAuth,setResourceUrl:b.setResourceUrl,close:b.close,httpExceptionsAdd:b.httpExceptionsAdd,httpExceptions:b.httpExceptions}}}]).factory("$tastypiePaginator",["$resource","$tastypie","$q",function(a,b,c){function d(a,b,c){this.resource=a,this.filters=b||{},this.meta={},this.objects=[],this.index=0,this.length=0,this.range=[],f(this,c)}function e(a){var b=c.defer();return"object"==typeof console&&console.log(a),b.reject({statusText:a}),b.promise}function f(a,b){if(!angular.isObject(b.meta))throw"[$tastypiePaginator] Invalid django-tastypie object.";a.meta=b.meta;for(var c=0;c0&&d<=c.length){c.resource.working=!0;var j=angular.copy(c.filters);j.offset=(d-1)*c.meta.limit;return a(c.resource.endpoint,c.resource.defaults,{get:{method:"GET",headers:c.resource.provider.headers}}).get(j).$promise.then(function(a){return a.meta.offset==a.meta.total_count?d-1==0?(f(c,a),c.resource.working=!1,c):(c.resource.working=!1,h(c,d-1,!0)):(f(c,a),c.resource.working=!1,c)},function(a){a=a||{},a.statusText="[$tastypiePaginator][$get] ".concat(a.statusText||"Server Not Responding."),c.resource.working=!1;var d=b.httpExceptions["c".concat(a.status)];throw d&&angular.isFunction(d)&&d(a),a})}var i="[$tastypiePaginator][$get] ".concat("Index ",d," not exist.");return e(i)}return d.prototype.change=function(a){return a?h(this,a,!1):e("[$tastypiePaginator][change] ".concat('Parameter "index" not informed.'))},d.prototype.next=function(){return this.meta.next?g(this,b.resource_domain.concat(this.meta.next)):e("[$tastypiePaginator][next] ".concat("Not exist next pages."))},d.prototype.previous=function(){return this.meta.previous?g(this,b.resource_domain.concat(this.meta.previous)):e("[$tastypiePaginator][previous] ".concat("Not exist previous pages."))},d.prototype.refresh=function(){return h(this,this.index,!0)},d.prototype.first=function(){return h(this,1,!1)},d.prototype.last=function(){return h(this,this.length,!1)},d}]).factory("$tastypieObjects",["$resource","$tastypiePaginator","$q","$tastypie",function(a,b,c,d){function e(a){this.resource=a}function f(a){var b=c.defer();return"object"==typeof console&&console.log(a),b.reject({statusText:a}),b.promise}function g(b,c){var e={post:{method:"POST",headers:b.resource.provider.headers},save:{method:"POST",headers:b.resource.provider.headers},get:{method:"GET",headers:b.resource.provider.headers,url:b.resource.endpoint.concat(":id/")},get_uri:{method:"GET",headers:b.resource.provider.headers,url:b.resource.endpoint.concat(":id/")},update:{method:"PATCH",headers:b.resource.provider.headers,url:b.resource.endpoint.concat(":id/")},put:{method:"PUT",headers:b.resource.provider.headers,url:b.resource.endpoint.concat(":id/")},patch:{method:"PATCH",headers:b.resource.provider.headers,url:b.resource.endpoint.concat(":id/")},delete:{method:"DELETE",headers:b.resource.provider.headers,url:b.resource.endpoint.concat(":id/")},remove:{method:"DELETE",headers:b.resource.provider.headers,url:b.resource.endpoint.concat(":id/")}},g=a(b.resource.endpoint,{id:"@id"},e);return delete g.prototype.$query,g.prototype.$domain=b.resource.provider.domain,g.prototype.$get=function(a){var c=this;if(angular.extend(c,a||{}),!c.hasOwnProperty("id")){return f("[$tastypieObjects][$get] ".concat("Attribute [id] is required."))}return b.resource.working=!0,c.$get_uri(c).then(function(a){return b.resource.working=!1,a},function(a){a=a||{},a.statusText="[$tastypieObjects][$get] ".concat(a.statusText||"Server Not Responding."),b.resource.working=!1;var c=d.httpExceptions["c".concat(a.status)];throw c&&angular.isFunction(c)&&c(a),a})},g.prototype.$save=function(a){var c=this;angular.extend(c,a||{});var e=c.hasOwnProperty("id")?c.$put():c.$post();return b.resource.working=!0,e.then(function(a){return b.resource.working=!1,typeof b.resource.page.refresh==typeof Function&&b.resource.page.refresh(),a},function(a){a=a||{},a.statusText="[$tastypieObjects][$save] ".concat(a.statusText||"Server Not Responding."),b.resource.working=!1;var c=d.httpExceptions["c".concat(a.status)];throw c&&angular.isFunction(c)&&c(a),a})},g.prototype.$update=function(a){var c=this;if(angular.extend(c,a||{}),!c.hasOwnProperty("id")||!c.id){return f("[$tastypieObjects][$update] ".concat("Attribute [id] is required."))}return b.resource.working=!0,c.$patch().then(function(a){return b.resource.working=!1,typeof b.resource.page.refresh==typeof Function&&b.resource.page.refresh(),a},function(a){a=a||{},a.statusText="[$tastypieObjects][$update] ".concat(a.statusText||"Server Not Responding."),b.resource.working=!1;var c=d.httpExceptions["c".concat(a.status)];throw c&&angular.isFunction(c)&&c(a),a})},g.prototype.$delete=function(a){var c=this;if(angular.extend(c,a||{}),!c.hasOwnProperty("id")||!c.id){return f("[$tastypieObjects][$delete] ".concat("Attribute [id] is required."))}return b.resource.working=!0,c.$remove().then(function(a){return b.resource.working=!1,angular.forEach(c,function(a,b){delete c[b]}),typeof b.resource.page.refresh==typeof Function&&b.resource.page.refresh(),a},function(a){a=a||{},a.statusText="[$tastypieObjects][$delete] ".concat(a.statusText||"Server Not Responding."),b.resource.working=!1;var c=d.httpExceptions["c".concat(a.status)];throw c&&angular.isFunction(c)&&c(a),a})},g.prototype.$clear=function(){var a=this;angular.forEach(a,function(b,c){delete a[c]})},new g(c)}function h(c){var e=a(c.resource.endpoint,c.resource.defaults,{get:{method:"GET",headers:c.resource.provider.headers},find:{method:"GET",headers:c.resource.provider.headers}});return e.prototype.$find=function(a){return c.resource.working=!0,this.$get(a).then(function(d){return c.resource.working=!1,c.resource.page=new b(c.resource,a,d),c.resource.page},function(a){a=a||{},a.statusText="[$tastypieObjects][$find] ".concat(a.statusText||"Server Not Responding."),c.resource.working=!1;var b=d.httpExceptions["c".concat(a.status)];throw b&&angular.isFunction(b)&&b(a),a})},new e}return e.prototype.$create=function(a){return g(this,a)},e.prototype.$get=function(a){return g(this).$get(a)},e.prototype.$delete=function(a){return g(this,a).$delete()},e.prototype.$find=function(a){return h(this).$find(a)},e.prototype.$update=function(a){return g(this,a).$update()},e}]).factory("$tastypieResource",["$resource","$tastypie","$tastypiePaginator","$tastypieObjects",function(a,b,c,d){function e(a,c,e){if(!a||!("string"==typeof a||a instanceof String))throw"[$tastypieResource] Unknown service name.";this.provider=b.getProvider(e||"default"),this.endpoint=this.provider.url.concat(a,"/"),this.defaults=c||{},this.page={},this.objects=new d(this);var f=[];Object.defineProperties(this,{working:{get:function(){return f.length>0},set:function(a){void 0===a&&(a=!1),a?f.push(1):f.splice(-1,1),b.working=a}}})}return e}]); -------------------------------------------------------------------------------- /examples/frontend/simple_app/lib/angular-resource-tastypie.min.js: -------------------------------------------------------------------------------- 1 | var ngResourceTastypie={name:"Angular Resource Tastypie",description:"RESTful AngularJs client for Django-Tastypie or equivalent schema.",version:{full:"1.0.4",major:1,minor:0,dot:4,codeName:"Cappuccino"},author:{name:"Marcos William Ferretti",email:"ferretti.spo@gmail.com",github:"https://github.com/mw-ferretti/",linkedin:"https://www.linkedin.com/in/mwferretti"},license:"MIT, (c) 2014-2016 Marcos William Ferretti",source:"https://github.com/mw-ferretti/angular-resource-tastypie"};if("undefined"==typeof angular)throw"[ngResourceTastypie v".concat(ngResourceTastypie.version.full,"] Requires AngularJs 1.3+");if(angular.version.major<1||1==angular.version.major&&angular.version.minor<3)throw"[ngResourceTastypie v".concat(ngResourceTastypie.version.full,"] Requires AngularJs 1.3+, your version is ",angular.version.full);angular.module("ngResourceTastypie",["ngResource"]).constant("ngResourceTastypie",ngResourceTastypie).config(["$resourceProvider",function(a){a.defaults.stripTrailingSlashes=!1}]).provider("$tastypie",["$httpProvider",function(a){a.defaults.useXDomain=!0,delete a.defaults.headers.common["X-Requested-With"],a.defaults.headers.common["Content-Type"]="application/json";var b=this,c="",d="",e={username:"",api_key:""};b.providers={},b.default={},b.getProvider=function(a){var c={};if(c="default"==a?b.default||{}:b.providers[a]||{},!c.hasOwnProperty("url"))throw'[$tastypieProvider][GetProvider] provider "'.concat(a,'" not found.');return c},b.setDefault=function(a){if(!b.providers.hasOwnProperty(a))throw"[$tastypieProvider][ProviderSetDefault] Provider ".concat(a," not found.");b.default=b.providers[a],c=b.default.url,d=b.default.domain,e.username=b.default.username,e.api_key=b.default.apikey,"undefined"!=typeof Storage&&(sessionStorage.$tastypieDefaultProvider=angular.toJson(b.default))},b.add=function(a,c){var d=a&&("string"==typeof a||a instanceof String);if(!d)throw'[$tastypieProvider][ProviderAdd] Invalid providerName string. Usage: add("providerName", {url:"address"})';if(!c||!angular.isObject(c)||!c.hasOwnProperty("url"))throw'[$tastypieProvider][ProviderAdd] Invalid providerObj object. Usage: add("providerName", {url:"address"})';if(b.providers[a])return b;c.name=a;var e=document.createElement("a");e.href=c.url;var f=e.protocol.concat("//",e.hostname);return""!=e.port&&(f=f.concat(":",e.port)),c.domain=f,c.hasOwnProperty("headers")||(c.headers={}),c.headers.hasOwnProperty("Content-Type")||(c.headers["Content-Type"]="application/json"),c.hasOwnProperty("username")&&c.hasOwnProperty("apikey")&&(c.headers.Authorization="ApiKey ".concat(c.username,":",c.apikey)),b.providers[a]=c,b.default.hasOwnProperty("name")||b.setDefault(a),"undefined"!=typeof Storage&&(sessionStorage.$tastypieProviders=angular.toJson(b.providers)),b},b.httpExceptions={},b.httpExceptionsAdd=function(a,c){if(!a)throw"[$tastypieProvider][httpExceptionsAdd] Invalid httpCode integer. Usage: httpExceptionsAdd(httpCode, callback)";if(!c||!angular.isFunction(c))throw"[$tastypieProvider][httpExceptionsAdd] Invalid callback function. Usage: httpExceptionsAdd(httpCode, callback)";return b.httpExceptions["c".concat(a)]=c,b},b.setProviderAuth=function(a,c,d){var e=b.getProvider(a);e.username=c,e.apikey=d,e.hasOwnProperty("headers")||(e.headers={}),e.headers.Authorization="ApiKey ".concat(e.username,":",e.apikey),b.providers[a]=e,b.default.name==a&&(b.default=e,"undefined"!=typeof Storage&&(sessionStorage.$tastypieDefaultProvider=angular.toJson(b.default))),"undefined"!=typeof Storage&&(sessionStorage.$tastypieProviders=angular.toJson(b.providers))},b.setResourceUrl=function(a){var e=a&&("string"==typeof a||a instanceof String);if(!e)throw"[$tastypieProvider][SetResourceUrl] Invalid URL.";c=a;var f=document.createElement("a");f.href=c,d=f.protocol.concat("//",f.hostname),""!=f.port&&(d=d.concat(":",f.port)),b.default.name="default",b.default.hasOwnProperty("headers")||(b.default.headers={}),b.default.headers.hasOwnProperty("Content-Type")||(b.default.headers["Content-Type"]="application/json"),b.default.url=a,b.default.domain=d,b.providers[b.default.name]=b.default,"undefined"!=typeof Storage&&(sessionStorage.$tastypieDefaultProvider=angular.toJson(b.default),sessionStorage.$tastypieProviders=angular.toJson(b.providers))},b.setAuth=function(a,c){var d=a&&("string"==typeof a||a instanceof String);c&&("string"==typeof c||c instanceof String);if(!d)throw"[$tastypieProvider][SetResourceUrl] Invalid username.";if(!c)throw"[$tastypieProvider][SetResourceUrl] Invalid apikey.";e.username=a,e.api_key=c,b.default.name="default",b.default.hasOwnProperty("headers")||(b.default.headers={}),b.default.headers.hasOwnProperty("Content-Type")||(b.default.headers["Content-Type"]="application/json"),b.default.username=a,b.default.apikey=c,b.default.headers.Authorization="ApiKey ".concat(a,":",c),b.providers[b.default.name]=b.default,"undefined"!=typeof Storage&&(sessionStorage.$tastypieDefaultProvider=angular.toJson(b.default),sessionStorage.$tastypieProviders=angular.toJson(b.providers))};var f=function(a){if(!angular.isObject(a)||!a.hasOwnProperty("url"))throw"[$tastypieProvider][clearAuthProvider] Invalid provider.";a.hasOwnProperty("username")&&(a.username=""),a.hasOwnProperty("apikey")&&(a.apikey=""),a.hasOwnProperty("headers")&&a.headers.hasOwnProperty("Authorization")&&delete a.headers.Authorization,b.providers[a.name]=a,"undefined"!=typeof Storage&&(sessionStorage.$tastypieProviders=angular.toJson(b.providers)),a.name==b.default.name&&(e.username="",e.api_key="",b.default=a,"undefined"!=typeof Storage&&(sessionStorage.$tastypieDefaultProvider=angular.toJson(b.default)))};b.clearAuthSession=function(a){var c=a&&("string"==typeof a||a instanceof String);if(!c)throw"[$tastypieProvider][ClearAuthSession] Invalid providerName.";if("all"==a)for(var d=Object.keys(b.providers),e=0;e0},set:function(a){"undefined"==typeof a&&(a=!1),a?g.push(1):g.splice(-1,1)}}}),b.$get=function(){return{providers:b.providers,add:b.add,getProvider:b.getProvider,setDefault:b.setDefault,setProviderAuth:b.setProviderAuth,default:b.default,clearAuthSession:b.clearAuthSession,resource_url:b.getResourceUrl(),resource_domain:b.getResourceDomain(),auth:b.getAuth(),working:b.working,setAuth:b.setAuth,setResourceUrl:b.setResourceUrl,close:b.close,httpExceptionsAdd:b.httpExceptionsAdd,httpExceptions:b.httpExceptions}}}]).factory("$tastypiePaginator",["$resource","$tastypie","$q",function(a,b,c){function d(a,b,c){this.resource=a,this.filters=b||{},this.meta={},this.objects=[],this.index=0,this.length=0,this.range=[],f(this,c)}function e(a){var b=c.defer();return"object"==typeof console&&console.log(a),b.reject({statusText:a}),b.promise}function f(a,b){if(!angular.isObject(b.meta))throw"[$tastypiePaginator] Invalid django-tastypie object.";a.meta=b.meta;for(var c=0;c0&&d<=c.length){c.resource.working=!0;var j=angular.copy(c.filters);j.offset=(d-1)*c.meta.limit;var k=a(c.resource.endpoint,c.resource.defaults,{get:{method:"GET",headers:c.resource.provider.headers}}).get(j).$promise.then(function(a){return a.meta.offset==a.meta.total_count?d-1==0?(f(c,a),c.resource.working=!1,c):(c.resource.working=!1,h(c,d-1,!0)):(f(c,a),c.resource.working=!1,c)},function(a){a=a||{},a.statusText="[$tastypiePaginator][$get] ".concat(a.statusText||"Server Not Responding."),c.resource.working=!1;var d=b.httpExceptions["c".concat(a.status)];throw d&&angular.isFunction(d)&&d(a),a});return k}var i="[$tastypiePaginator][$get] ".concat("Index ",d," not exist.");return e(i)}return d.prototype.change=function(a){if(a)return h(this,a,!1);var b="[$tastypiePaginator][change] ".concat('Parameter "index" not informed.');return e(b)},d.prototype.next=function(){if(this.meta.next)return g(this,b.resource_domain.concat(this.meta.next));var a="[$tastypiePaginator][next] ".concat("Not exist next pages.");return e(a)},d.prototype.previous=function(){if(this.meta.previous)return g(this,b.resource_domain.concat(this.meta.previous));var a="[$tastypiePaginator][previous] ".concat("Not exist previous pages.");return e(a)},d.prototype.refresh=function(){return h(this,this.index,!0)},d.prototype.first=function(){return h(this,1,!1)},d.prototype.last=function(){return h(this,this.length,!1)},d}]).factory("$tastypieObjects",["$resource","$tastypiePaginator","$q","$tastypie",function(a,b,c,d){function e(a){this.resource=a}function f(a){var b=c.defer();return"object"==typeof console&&console.log(a),b.reject({statusText:a}),b.promise}function g(b,c){var e={post:{method:"POST",headers:b.resource.provider.headers},save:{method:"POST",headers:b.resource.provider.headers},get:{method:"GET",headers:b.resource.provider.headers,url:b.resource.endpoint.concat(":id/")},get_uri:{method:"GET",headers:b.resource.provider.headers,url:b.resource.endpoint.concat(":id/")},update:{method:"PATCH",headers:b.resource.provider.headers,url:b.resource.endpoint.concat(":id/")},put:{method:"PUT",headers:b.resource.provider.headers,url:b.resource.endpoint.concat(":id/")},patch:{method:"PATCH",headers:b.resource.provider.headers,url:b.resource.endpoint.concat(":id/")},delete:{method:"DELETE",headers:b.resource.provider.headers,url:b.resource.endpoint.concat(":id/")},remove:{method:"DELETE",headers:b.resource.provider.headers,url:b.resource.endpoint.concat(":id/")}},g=a(b.resource.endpoint,{id:"@id"},e);return delete g.prototype.$query,g.prototype.$domain=b.resource.provider.domain,g.prototype.$get=function(a){var c=this;if(angular.extend(c,a||{}),!c.hasOwnProperty("id")){var e="[$tastypieObjects][$get] ".concat("Attribute [id] is required.");return f(e)}b.resource.working=!0;var g=c.$get_uri(c).then(function(a){return b.resource.working=!1,a},function(a){a=a||{},a.statusText="[$tastypieObjects][$get] ".concat(a.statusText||"Server Not Responding."),b.resource.working=!1;var c=d.httpExceptions["c".concat(a.status)];throw c&&angular.isFunction(c)&&c(a),a});return g},g.prototype.$save=function(a){var c=this;angular.extend(c,a||{});var e=c.hasOwnProperty("id")?c.$put():c.$post();b.resource.working=!0;var f=e.then(function(a){return b.resource.working=!1,typeof b.resource.page.refresh==typeof Function&&b.resource.page.refresh(),a},function(a){a=a||{},a.statusText="[$tastypieObjects][$save] ".concat(a.statusText||"Server Not Responding."),b.resource.working=!1;var c=d.httpExceptions["c".concat(a.status)];throw c&&angular.isFunction(c)&&c(a),a});return f},g.prototype.$update=function(a){var c=this;if(angular.extend(c,a||{}),!c.hasOwnProperty("id")||!c.id){var e="[$tastypieObjects][$update] ".concat("Attribute [id] is required.");return f(e)}b.resource.working=!0;var g=c.$patch().then(function(a){return b.resource.working=!1,typeof b.resource.page.refresh==typeof Function&&b.resource.page.refresh(),a},function(a){a=a||{},a.statusText="[$tastypieObjects][$update] ".concat(a.statusText||"Server Not Responding."),b.resource.working=!1;var c=d.httpExceptions["c".concat(a.status)];throw c&&angular.isFunction(c)&&c(a),a});return g},g.prototype.$delete=function(a){var c=this;if(angular.extend(c,a||{}),!c.hasOwnProperty("id")||!c.id){var e="[$tastypieObjects][$delete] ".concat("Attribute [id] is required.");return f(e)}b.resource.working=!0;var g=c.$remove().then(function(a){return b.resource.working=!1,angular.forEach(c,function(a,b){delete c[b]}),typeof b.resource.page.refresh==typeof Function&&b.resource.page.refresh(),a},function(a){a=a||{},a.statusText="[$tastypieObjects][$delete] ".concat(a.statusText||"Server Not Responding."),b.resource.working=!1;var c=d.httpExceptions["c".concat(a.status)];throw c&&angular.isFunction(c)&&c(a),a});return g},g.prototype.$clear=function(){var a=this;angular.forEach(a,function(b,c){delete a[c]})},new g(c)}function h(c){var e=a(c.resource.endpoint,c.resource.defaults,{get:{method:"GET",headers:c.resource.provider.headers},find:{method:"GET",headers:c.resource.provider.headers}});return e.prototype.$find=function(a){c.resource.working=!0;var e=this.$get(a).then(function(d){return c.resource.working=!1,c.resource.page=new b(c.resource,a,d),c.resource.page},function(a){a=a||{},a.statusText="[$tastypieObjects][$find] ".concat(a.statusText||"Server Not Responding."),c.resource.working=!1;var b=d.httpExceptions["c".concat(a.status)];throw b&&angular.isFunction(b)&&b(a),a});return e},new e}return e.prototype.$create=function(a){return g(this,a)},e.prototype.$get=function(a){return g(this).$get(a)},e.prototype.$delete=function(a){return g(this,a).$delete()},e.prototype.$find=function(a){return h(this).$find(a)},e.prototype.$update=function(a){return g(this,a).$update()},e}]).factory("$tastypieResource",["$resource","$tastypie","$tastypiePaginator","$tastypieObjects",function(a,b,c,d){function e(a,c,e){var f=a&&("string"==typeof a||a instanceof String);if(!f)throw"[$tastypieResource] Unknown service name.";this.provider=b.getProvider(e||"default"),this.endpoint=this.provider.url.concat(a,"/"),this.defaults=c||{},this.page={},this.objects=new d(this);var g=[];Object.defineProperties(this,{working:{get:function(){return g.length>0},set:function(a){"undefined"==typeof a&&(a=!1),a?g.push(1):g.splice(-1,1),b.working=a}}})}return e}]); -------------------------------------------------------------------------------- /src/angular-resource-tastypie.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license Angular Resource Tastypie v1.0.5 3 | * (c) 2014-2016 Marcos William Ferretti, https://github.com/mw-ferretti/angular-resource-tastypie 4 | * License: MIT 5 | */ 6 | 7 | var ngResourceTastypie = { 8 | name: 'Angular Resource Tastypie', 9 | description: 'RESTful AngularJs client for Django-Tastypie or equivalent schema.', 10 | version: { 11 | full: '1.0.5', 12 | major: 1, 13 | minor: 0, 14 | dot: 5, 15 | codeName: 'Cappuccino' 16 | }, 17 | author: { 18 | name: 'Marcos William Ferretti', 19 | email: 'ferretti.spo@gmail.com', 20 | github: 'https://github.com/mw-ferretti/', 21 | linkedin: 'https://www.linkedin.com/in/mwferretti' 22 | }, 23 | license: 'MIT, (c) 2014-2016 Marcos William Ferretti', 24 | source: 'https://github.com/mw-ferretti/angular-resource-tastypie' 25 | }; 26 | 27 | if(typeof angular == 'undefined') 28 | throw '[ngResourceTastypie v'.concat(ngResourceTastypie.version.full,'] Requires AngularJs 1.3+'); 29 | 30 | if(angular.version.major < 1 || (angular.version.major == 1 && angular.version.minor < 3)) 31 | throw '[ngResourceTastypie v'.concat(ngResourceTastypie.version.full,'] Requires AngularJs 1.3+, your version is ', angular.version.full); 32 | 33 | 34 | angular.module('ngResourceTastypie', ['ngResource']) 35 | .constant('ngResourceTastypie', ngResourceTastypie) 36 | 37 | .config(['$resourceProvider', function($resourceProvider){ 38 | $resourceProvider.defaults.stripTrailingSlashes = false; 39 | }]) 40 | 41 | .provider('$tastypie', ['$httpProvider', function($httpProvider){ 42 | 43 | $httpProvider.defaults.useXDomain = true; 44 | delete $httpProvider.defaults.headers.common['X-Requested-With']; 45 | $httpProvider.defaults.headers.common['Content-Type'] = 'application/json'; 46 | 47 | var self = this; 48 | var resource_url = ''; 49 | var resource_domain = ''; 50 | 51 | var auth = { 52 | username : '', 53 | api_key : '' 54 | }; 55 | 56 | self.providers = {}; 57 | self.default = {}; 58 | 59 | self.getProvider = function(providerName){ 60 | var provider = {}; 61 | if(providerName == 'default'){ 62 | provider = self.default || {}; 63 | }else{ 64 | provider = self.providers[providerName] || {}; 65 | } 66 | if(!provider.hasOwnProperty('url')){ 67 | throw '[$tastypieProvider][GetProvider] provider "'.concat(providerName,'" not found.'); 68 | } 69 | return provider; 70 | }; 71 | 72 | self.setDefault = function(providerName){ 73 | if(!self.providers.hasOwnProperty(providerName)){ 74 | throw '[$tastypieProvider][ProviderSetDefault] Provider '.concat(providerName, ' not found.'); 75 | } 76 | self.default = self.providers[providerName]; 77 | resource_url = self.default['url']; 78 | resource_domain = self.default['domain']; 79 | auth.username = self.default['username']; 80 | auth.api_key = self.default['apikey']; 81 | 82 | if(typeof(Storage) !== "undefined"){ 83 | sessionStorage.$tastypieDefaultProvider = angular.toJson(self.default); 84 | } 85 | }; 86 | 87 | self.add = function(providerName, providerObj){ 88 | var validProviderName = (providerName && (typeof(providerName) === 'string' || providerName instanceof String)); 89 | if(!validProviderName){ 90 | throw '[$tastypieProvider][ProviderAdd] Invalid providerName string. Usage: add("providerName", {url:"address"})'; 91 | } 92 | if(!providerObj || !angular.isObject(providerObj) || !providerObj.hasOwnProperty('url')){ 93 | throw '[$tastypieProvider][ProviderAdd] Invalid providerObj object. Usage: add("providerName", {url:"address"})'; 94 | } 95 | if(self.providers[providerName]){ 96 | return self; 97 | } 98 | 99 | providerObj.name = providerName; 100 | 101 | var el_href = document.createElement('a'); 102 | el_href.href = providerObj.url; 103 | var domain = el_href.protocol.concat('//', el_href.hostname); 104 | if (el_href.port != '') domain = domain.concat(':', el_href.port); 105 | providerObj.domain = domain; 106 | 107 | if(!providerObj.hasOwnProperty('headers')){ 108 | providerObj.headers = {}; 109 | } 110 | 111 | if(!providerObj.headers.hasOwnProperty('Content-Type')){ 112 | providerObj.headers['Content-Type'] = 'application/json'; 113 | } 114 | 115 | if(providerObj.hasOwnProperty('username') && providerObj.hasOwnProperty('apikey')){ 116 | providerObj.headers.Authorization = 'ApiKey '.concat( 117 | providerObj.username, ':', providerObj.apikey 118 | ); 119 | } 120 | 121 | self.providers[providerName] = providerObj; 122 | 123 | if(!self.default.hasOwnProperty('name')){ 124 | self.setDefault(providerName); 125 | } 126 | 127 | if(typeof(Storage) !== "undefined"){ 128 | sessionStorage.$tastypieProviders = angular.toJson(self.providers); 129 | } 130 | 131 | return self; 132 | }; 133 | 134 | self.httpExceptions = {}; 135 | self.httpExceptionsAdd = function(httpCode, callback){ 136 | if(!httpCode){ 137 | throw '[$tastypieProvider][httpExceptionsAdd] Invalid httpCode integer. Usage: httpExceptionsAdd(httpCode, callback)'; 138 | } 139 | if(!callback || !angular.isFunction(callback)){ 140 | throw '[$tastypieProvider][httpExceptionsAdd] Invalid callback function. Usage: httpExceptionsAdd(httpCode, callback)'; 141 | } 142 | self.httpExceptions['c'.concat(httpCode)] = callback; 143 | return self; 144 | }; 145 | 146 | self.setProviderAuth = function(providerName, username, apikey){ 147 | var providerObj = self.getProvider(providerName); 148 | providerObj.username = username; 149 | providerObj.apikey = apikey; 150 | 151 | if(!providerObj.hasOwnProperty('headers')){ 152 | providerObj.headers = {}; 153 | } 154 | 155 | providerObj.headers.Authorization = 'ApiKey '.concat( 156 | providerObj.username, ':', providerObj.apikey 157 | ); 158 | 159 | self.providers[providerName] = providerObj; 160 | 161 | if(self.default.name == providerName){ 162 | self.default = providerObj; 163 | if(typeof(Storage) !== "undefined"){ 164 | sessionStorage.$tastypieDefaultProvider = angular.toJson(self.default); 165 | } 166 | } 167 | 168 | if(typeof(Storage) !== "undefined"){ 169 | sessionStorage.$tastypieProviders = angular.toJson(self.providers); 170 | } 171 | }; 172 | 173 | self.setResourceUrl = function(url){ 174 | var validUrl = (url && (typeof(url) === 'string' || url instanceof String)); 175 | if(!validUrl){ 176 | throw '[$tastypieProvider][SetResourceUrl] Invalid URL.'; 177 | } 178 | 179 | resource_url = url; 180 | 181 | var dominio = document.createElement('a'); 182 | dominio.href = resource_url; 183 | resource_domain = dominio.protocol.concat('//', dominio.hostname); 184 | if (dominio.port != '') resource_domain = resource_domain.concat(':', dominio.port); 185 | 186 | self.default.name = 'default'; 187 | if(!self.default.hasOwnProperty('headers')){ 188 | self.default.headers = {}; 189 | } 190 | if(!self.default.headers.hasOwnProperty('Content-Type')){ 191 | self.default.headers['Content-Type'] = 'application/json'; 192 | } 193 | self.default.url = url; 194 | self.default.domain = resource_domain; 195 | 196 | self.providers[self.default.name] = self.default; 197 | 198 | if(typeof(Storage) !== "undefined"){ 199 | sessionStorage.$tastypieDefaultProvider = angular.toJson(self.default); 200 | sessionStorage.$tastypieProviders = angular.toJson(self.providers); 201 | } 202 | }; 203 | 204 | self.setAuth = function(username, apikey){ 205 | var validUsername = (username && (typeof(username) === 'string' || username instanceof String)); 206 | var validApikey = (apikey && (typeof(apikey) === 'string' || apikey instanceof String)); 207 | if(!validUsername){ 208 | throw '[$tastypieProvider][SetResourceUrl] Invalid username.'; 209 | } 210 | if(!apikey){ 211 | throw '[$tastypieProvider][SetResourceUrl] Invalid apikey.'; 212 | } 213 | 214 | auth.username = username; 215 | auth.api_key = apikey; 216 | 217 | self.default.name = 'default'; 218 | if(!self.default.hasOwnProperty('headers')){ 219 | self.default.headers = {}; 220 | } 221 | if(!self.default.headers.hasOwnProperty('Content-Type')){ 222 | self.default.headers['Content-Type'] = 'application/json'; 223 | } 224 | 225 | self.default.username = username; 226 | self.default.apikey = apikey; 227 | self.default.headers.Authorization = 'ApiKey '.concat(username, ':', apikey); 228 | 229 | self.providers[self.default.name] = self.default; 230 | 231 | if(typeof(Storage) !== "undefined"){ 232 | sessionStorage.$tastypieDefaultProvider = angular.toJson(self.default); 233 | sessionStorage.$tastypieProviders = angular.toJson(self.providers); 234 | } 235 | }; 236 | 237 | var clearAuthSessionAux = function(providerObj){ 238 | if(!angular.isObject(providerObj) || !providerObj.hasOwnProperty('url')){ 239 | throw '[$tastypieProvider][clearAuthProvider] Invalid provider.'; 240 | } 241 | 242 | if(providerObj.hasOwnProperty('username')){ 243 | providerObj.username = ''; 244 | } 245 | 246 | if(providerObj.hasOwnProperty('apikey')){ 247 | providerObj.apikey = ''; 248 | } 249 | 250 | if(providerObj.hasOwnProperty('headers')){ 251 | if(providerObj.headers.hasOwnProperty('Authorization')){ 252 | delete providerObj.headers["Authorization"]; 253 | } 254 | } 255 | 256 | self.providers[providerObj.name] = providerObj; 257 | 258 | if(typeof(Storage) !== "undefined"){ 259 | sessionStorage.$tastypieProviders = angular.toJson(self.providers); 260 | } 261 | 262 | if(providerObj.name == self.default.name){ 263 | auth.username = ''; 264 | auth.api_key = ''; 265 | self.default = providerObj; 266 | if(typeof(Storage) !== "undefined"){ 267 | sessionStorage.$tastypieDefaultProvider = angular.toJson(self.default); 268 | } 269 | } 270 | }; 271 | 272 | self.clearAuthSession = function(providerName){ 273 | var validProviderName = (providerName && (typeof(providerName) === 'string' || providerName instanceof String)); 274 | 275 | if(!validProviderName){ 276 | throw '[$tastypieProvider][ClearAuthSession] Invalid providerName.'; 277 | } 278 | 279 | if(providerName == 'all'){ 280 | var providerList = Object.keys(self.providers); 281 | for(var i=0; i 0); 319 | }, 320 | "set": function(b){ 321 | if(typeof(b) == 'undefined') b = false; 322 | if(b) working_list.push(1); 323 | else working_list.splice(-1,1); 324 | } 325 | } 326 | }); 327 | 328 | self.$get = function(){ 329 | return { 330 | providers:self.providers, 331 | add:self.add, 332 | getProvider:self.getProvider, 333 | setDefault:self.setDefault, 334 | setProviderAuth:self.setProviderAuth, 335 | default:self.default, 336 | clearAuthSession:self.clearAuthSession, 337 | resource_url:self.getResourceUrl(), 338 | resource_domain:self.getResourceDomain(), 339 | auth:self.getAuth(), 340 | working:self.working, 341 | setAuth:self.setAuth, 342 | setResourceUrl:self.setResourceUrl, 343 | close:self.close, 344 | httpExceptionsAdd:self.httpExceptionsAdd, 345 | httpExceptions:self.httpExceptions 346 | } 347 | }; 348 | }]) 349 | 350 | .factory('$tastypiePaginator', ['$resource', '$tastypie', '$q', function($resource, $tastypie, $q){ 351 | 352 | function $tastypiePaginator(tastypieResource, filters, result){ 353 | 354 | this.resource = tastypieResource; 355 | this.filters = filters || {}; 356 | this.meta = {}; 357 | this.objects = []; 358 | this.index = 0; 359 | this.length = 0; 360 | this.range = []; 361 | 362 | setPage(this, result); 363 | } 364 | 365 | function promise_except_data_invalid(msg){ 366 | var deferred = $q.defer(); 367 | if (typeof(console) == "object") console.log(msg); 368 | deferred.reject({statusText:msg}); 369 | return deferred.promise; 370 | } 371 | 372 | function setPage(self, result){ 373 | if (!angular.isObject(result.meta)) throw '[$tastypiePaginator] Invalid django-tastypie object.'; 374 | 375 | self.meta = result.meta; 376 | 377 | for (var x=0; x 0) && (index <= self.length)){ 435 | self.resource.working = true; 436 | var filters = angular.copy(self.filters); 437 | filters.offset = ((index-1)*self.meta.limit); 438 | 439 | var promise = $resource(self.resource.endpoint, self.resource.defaults, { 440 | 'get':{ 441 | method:'GET', 442 | headers: self.resource.provider.headers 443 | } 444 | }).get(filters).$promise.then( 445 | function(result){ 446 | if(result.meta.offset == result.meta.total_count){ 447 | if((index - 1) == 0){ 448 | setPage(self, result); 449 | self.resource.working = false; 450 | return self; 451 | }else{ 452 | self.resource.working = false; 453 | return changePage(self, (index - 1), true); 454 | } 455 | }else{ 456 | setPage(self, result); 457 | self.resource.working = false; 458 | return self; 459 | } 460 | }, 461 | function(error){ 462 | error = error || {}; 463 | error.statusText = '[$tastypiePaginator][$get] '.concat(error.statusText || 'Server Not Responding.'); 464 | self.resource.working = false; 465 | var fn = $tastypie.httpExceptions['c'.concat(error.status)]; 466 | if(fn && angular.isFunction(fn)){ 467 | fn(error); 468 | } 469 | throw error; 470 | } 471 | ); 472 | return promise; 473 | }else{ 474 | var msg = '[$tastypiePaginator][$get] '.concat('Index ', index, ' not exist.'); 475 | return promise_except_data_invalid(msg); 476 | } 477 | } 478 | 479 | $tastypiePaginator.prototype.change = function(index){ 480 | if (index) 481 | return changePage(this,index,false); 482 | else{ 483 | var msg = '[$tastypiePaginator][change] '.concat('Parameter "index" not informed.'); 484 | return promise_except_data_invalid(msg); 485 | } 486 | }; 487 | 488 | $tastypiePaginator.prototype.next = function(){ 489 | if (this.meta.next) 490 | return getPage(this, $tastypie.resource_domain.concat(this.meta.next)); 491 | else{ 492 | var msg = '[$tastypiePaginator][next] '.concat('Not exist next pages.'); 493 | return promise_except_data_invalid(msg); 494 | } 495 | }; 496 | 497 | $tastypiePaginator.prototype.previous = function(){ 498 | if (this.meta.previous) 499 | return getPage(this, $tastypie.resource_domain.concat(this.meta.previous)); 500 | else{ 501 | var msg = '[$tastypiePaginator][previous] '.concat('Not exist previous pages.'); 502 | return promise_except_data_invalid(msg); 503 | } 504 | }; 505 | 506 | $tastypiePaginator.prototype.refresh = function(){ 507 | return changePage(this,this.index,true); 508 | }; 509 | 510 | $tastypiePaginator.prototype.first = function(){ 511 | return changePage(this,1,false); 512 | }; 513 | 514 | $tastypiePaginator.prototype.last = function(){ 515 | return changePage(this,this.length,false); 516 | }; 517 | 518 | return $tastypiePaginator; 519 | }]) 520 | 521 | 522 | .factory('$tastypieObjects', ['$resource', '$tastypiePaginator', '$q', '$tastypie', function($resource, $tastypiePaginator, $q, $tastypie){ 523 | 524 | function $tastypieObjects(tastypieResource){ 525 | this.resource = tastypieResource; 526 | } 527 | 528 | function promise_except_data_invalid(msg){ 529 | var deferred = $q.defer(); 530 | if (typeof(console) == "object") console.log(msg); 531 | deferred.reject({statusText:msg}); 532 | return deferred.promise; 533 | } 534 | 535 | function create(self, data){ 536 | 537 | var custom_method = { 538 | 'post':{ 539 | method:'POST', 540 | headers: self.resource.provider.headers 541 | }, 542 | 'save':{ 543 | method:'POST', 544 | headers: self.resource.provider.headers 545 | }, 546 | 'get':{ 547 | method:'GET', 548 | headers: self.resource.provider.headers, 549 | url:self.resource.endpoint.concat(":id/") 550 | }, 551 | 'get_uri':{ 552 | method:'GET', 553 | headers: self.resource.provider.headers, 554 | url:self.resource.endpoint.concat(":id/") 555 | }, 556 | 'update':{ 557 | method:'PATCH', 558 | headers: self.resource.provider.headers, 559 | url:self.resource.endpoint.concat(":id/") 560 | }, 561 | 'put':{ 562 | method:'PUT', 563 | headers: self.resource.provider.headers, 564 | url:self.resource.endpoint.concat(":id/") 565 | }, 566 | 'patch':{ 567 | method:'PATCH', 568 | headers: self.resource.provider.headers, 569 | url:self.resource.endpoint.concat(":id/") 570 | }, 571 | 'delete':{ 572 | method:'DELETE', 573 | headers: self.resource.provider.headers, 574 | url:self.resource.endpoint.concat(":id/") 575 | }, 576 | 'remove':{ 577 | method:'DELETE', 578 | headers: self.resource.provider.headers, 579 | url:self.resource.endpoint.concat(":id/") 580 | } 581 | }; 582 | 583 | var obj = $resource(self.resource.endpoint, {id:'@id'}, custom_method); 584 | delete obj.prototype['$query']; 585 | obj.prototype.$domain = self.resource.provider.domain; 586 | obj.prototype.$get = function(data){ 587 | var fields = this; 588 | angular.extend(fields, (data || {})); 589 | 590 | if(!fields.hasOwnProperty('id')){ 591 | var msg = '[$tastypieObjects][$get] '.concat('Attribute [id] is required.'); 592 | return promise_except_data_invalid(msg); 593 | } 594 | 595 | self.resource.working = true; 596 | var promise = fields.$get_uri(fields).then( 597 | function(result){ 598 | self.resource.working = false; 599 | return result; 600 | }, 601 | function(error){ 602 | error = error || {}; 603 | error.statusText = '[$tastypieObjects][$get] '.concat(error.statusText || 'Server Not Responding.'); 604 | self.resource.working = false; 605 | var fn = $tastypie.httpExceptions['c'.concat(error.status)]; 606 | if(fn && angular.isFunction(fn)){ 607 | fn(error); 608 | } 609 | throw error; 610 | } 611 | ); 612 | return promise; 613 | }; 614 | 615 | obj.prototype.$save = function(data){ 616 | var fields = this; 617 | angular.extend(fields, (data || {})); 618 | var ws = fields.hasOwnProperty('id') ? fields.$put() : fields.$post(); 619 | 620 | self.resource.working = true; 621 | var promise = ws.then( 622 | function(result){ 623 | self.resource.working = false; 624 | if (typeof(self.resource.page.refresh) == typeof(Function)) 625 | self.resource.page.refresh(); 626 | return result; 627 | }, 628 | function(error){ 629 | error = error || {}; 630 | error.statusText = '[$tastypieObjects][$save] '.concat(error.statusText || 'Server Not Responding.'); 631 | self.resource.working = false; 632 | var fn = $tastypie.httpExceptions['c'.concat(error.status)]; 633 | if(fn && angular.isFunction(fn)){ 634 | fn(error); 635 | } 636 | throw error; 637 | } 638 | ); 639 | return promise; 640 | }; 641 | 642 | obj.prototype.$update = function(data){ 643 | var fields = this; 644 | angular.extend(fields, (data || {})); 645 | 646 | if(!fields.hasOwnProperty('id') || !fields.id){ 647 | var msg = '[$tastypieObjects][$update] '.concat('Attribute [id] is required.'); 648 | return promise_except_data_invalid(msg); 649 | } 650 | 651 | self.resource.working = true; 652 | var promise = fields.$patch().then( 653 | function(result){ 654 | self.resource.working = false; 655 | if (typeof(self.resource.page.refresh) == typeof(Function)) 656 | self.resource.page.refresh(); 657 | return result; 658 | }, 659 | function(error){ 660 | error = error || {}; 661 | error.statusText = '[$tastypieObjects][$update] '.concat(error.statusText || 'Server Not Responding.'); 662 | self.resource.working = false; 663 | var fn = $tastypie.httpExceptions['c'.concat(error.status)]; 664 | if(fn && angular.isFunction(fn)){ 665 | fn(error); 666 | } 667 | throw error; 668 | } 669 | ); 670 | return promise; 671 | }; 672 | 673 | obj.prototype.$delete = function(data){ 674 | var fields = this; 675 | angular.extend(fields, (data || {})); 676 | 677 | if(!fields.hasOwnProperty('id') || !fields.id){ 678 | var msg = '[$tastypieObjects][$delete] '.concat('Attribute [id] is required.'); 679 | return promise_except_data_invalid(msg); 680 | } 681 | 682 | self.resource.working = true; 683 | var promise = fields.$remove().then( 684 | function(result){ 685 | self.resource.working = false; 686 | angular.forEach(fields, function(value, key){delete fields[key]}); 687 | if (typeof(self.resource.page.refresh) == typeof(Function)) 688 | self.resource.page.refresh(); 689 | return result; 690 | }, 691 | function(error){ 692 | error = error || {}; 693 | error.statusText = '[$tastypieObjects][$delete] '.concat(error.statusText || 'Server Not Responding.'); 694 | self.resource.working = false; 695 | var fn = $tastypie.httpExceptions['c'.concat(error.status)]; 696 | if(fn && angular.isFunction(fn)){ 697 | fn(error); 698 | } 699 | throw error; 700 | } 701 | ); 702 | return promise; 703 | }; 704 | 705 | obj.prototype.$clear = function(){ 706 | var fields = this; 707 | angular.forEach(fields, function(value, key){delete fields[key]}); 708 | }; 709 | 710 | return new obj(data); 711 | }; 712 | 713 | function find(self){ 714 | 715 | var obj = $resource(self.resource.endpoint, self.resource.defaults, { 716 | 'get':{method:'GET', headers: self.resource.provider.headers}, 717 | 'find':{method:'GET', headers: self.resource.provider.headers} 718 | }); 719 | 720 | obj.prototype.$find = function(filter){ 721 | self.resource.working = true; 722 | var promise = this.$get(filter).then( 723 | function(result){ 724 | self.resource.working = false; 725 | self.resource.page = new $tastypiePaginator(self.resource, filter, result); 726 | return self.resource.page; 727 | }, 728 | function(error){ 729 | error = error || {}; 730 | error.statusText = '[$tastypieObjects][$find] '.concat(error.statusText || 'Server Not Responding.'); 731 | self.resource.working = false; 732 | var fn = $tastypie.httpExceptions['c'.concat(error.status)]; 733 | if(fn && angular.isFunction(fn)){ 734 | fn(error); 735 | } 736 | throw error; 737 | } 738 | ); 739 | return promise; 740 | }; 741 | 742 | return new obj(); 743 | } 744 | 745 | $tastypieObjects.prototype.$create = function(data){ 746 | return create(this, data); 747 | }; 748 | 749 | $tastypieObjects.prototype.$get = function(data){ 750 | return create(this).$get(data); 751 | }; 752 | 753 | $tastypieObjects.prototype.$delete = function(data){ 754 | return create(this, data).$delete(); 755 | }; 756 | 757 | $tastypieObjects.prototype.$find = function(data){ 758 | return find(this).$find(data); 759 | }; 760 | 761 | $tastypieObjects.prototype.$update = function(data){ 762 | return create(this, data).$update(); 763 | }; 764 | 765 | return $tastypieObjects; 766 | }]) 767 | 768 | .factory('$tastypieResource', ['$resource', '$tastypie', '$tastypiePaginator', '$tastypieObjects', function($resource, $tastypie, $tastypiePaginator, $tastypieObjects){ 769 | 770 | function $tastypieResource(service, default_filters, provider_name) { 771 | 772 | var validService = (service && (typeof(service) === 'string' || service instanceof String)); 773 | 774 | if (!validService){ 775 | throw '[$tastypieResource] Unknown service name.'; 776 | } 777 | 778 | this.provider = $tastypie.getProvider(provider_name || 'default'); 779 | this.endpoint = this.provider.url.concat(service, '/'); 780 | this.defaults = default_filters || {}; 781 | this.page = {}; 782 | this.objects = new $tastypieObjects(this); 783 | 784 | var working_list = []; 785 | Object.defineProperties(this, { 786 | "working": { 787 | "get": function(){ 788 | return (working_list.length > 0); 789 | }, 790 | "set": function(b){ 791 | if(typeof(b) == 'undefined') b = false; 792 | if(b) working_list.push(1); 793 | else working_list.splice(-1,1); 794 | $tastypie.working = b; 795 | } 796 | } 797 | }); 798 | } 799 | 800 | return $tastypieResource; 801 | }]); 802 | --------------------------------------------------------------------------------