├── .gitignore ├── LICENSE ├── README.md ├── backandService.ts ├── index.ts ├── package.json ├── tsconfig.json └── tslint.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .idea 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Backand 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 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # angular2bknd-sdk 2 | ###**NOTE - This SDK is deprecated, please use our new SDK at https://github.com/backand/angular2-sdk** 3 | 4 | Backand SDK for Angular 2 5 | 6 | Compatible with AngularJS 2.0.0 7 | 8 | ## Install 9 | 10 | npm install angular2bknd-sdk --save 11 | 12 | ## Dependencies 13 | 14 | npm install @types/node --save-dev 15 | npm install @types/socket.io-client --save-dev 16 | 17 | ## Import 18 | 19 | In `src/app/app.module.ts`, 20 | 21 | import { BackandService } from 'angular2bknd-sdk'; 22 | 23 | add `BackandService` to the `providers` array. 24 | 25 | In each component where you use Backand, import it: 26 | 27 | import { BackandService } from 'angular2bknd-sdk'; 28 | 29 | and then in the constructor: 30 | 31 | constructor(private backandService:BackandService){} 32 | 33 | Use it as `this.backandService` 34 | 35 | ## Configure secure calls to Backand's REST API 36 | 37 | Backand uses OAuth2 authentication, which requires that you include the authentication token in every HTTP call. 38 | 39 | In `src/app/app.component.ts`: 40 | 41 | this.backandService.setAppName('your app name'); 42 | this.backandService.setSignUpToken('your backand signup token'); 43 | this.backandService.setAnonymousToken('your backand anonymous token'); 44 | 45 | ## Mobile 46 | 47 | In `src/app/app.component.ts`: 48 | 49 | this.backandService.setIsMobile(true); 50 | 51 | ## Do CRUD Operations on Your Database 52 | 53 | To fetch, create, and filter rows, from an object, say `todo`, the CRUD functions in BackandService, should receive `'todo'` as their first argument 54 | 55 | * Read one row 56 | 57 | ``` 58 | this.backandService.getOne('todo') 59 | .subscribe( 60 | data => { 61 | }, 62 | err => this.backandService.logError(err), 63 | () => console.log('OK') 64 | ); 65 | ``` 66 | 67 | * Create 68 | 69 | ``` 70 | this.backandService.create('todo', { name: this.name, description: this.description}) 71 | .subscribe( 72 | data => { 73 | }, 74 | err => this.backandService.logError(err), 75 | () => console.log('OK') 76 | ); 77 | ``` 78 | 79 | * Update 80 | 81 | ``` 82 | this.backandService.update('todo', this.id, { name: this.name, description: this.description}) 83 | .subscribe( 84 | data => { 85 | }, 86 | err => this.backandService.logError(err), 87 | () => console.log('OK') 88 | ); 89 | ``` 90 | 91 | * Query 92 | 93 | When `q` is set to your search pattern, define a filter: 94 | 95 | ``` 96 | let filter = [{ 97 | fieldName: 'name', 98 | operator: 'contains', 99 | value: q 100 | }]; 101 | ``` 102 | 103 | Or use NoSQL syntax: 104 | 105 | ``` 106 | let filter = { 107 | "q":{ 108 | "name" : { 109 | "$like" : q 110 | } 111 | } 112 | } 113 | 114 | ``` 115 | 116 | and call `filterItem` 117 | 118 | ``` 119 | this.backandService.getList('todo', null, null, filter) 120 | .subscribe( 121 | data => { 122 | console.log("subscribe", data); 123 | this.items = data; 124 | }, 125 | err => this.backandService.logError(err), 126 | () => console.log('OK') 127 | ); 128 | ``` 129 | 130 | ## Social Signup 131 | 132 | The app opens a dialog supplied by the social network. 133 | 134 | ``` 135 | var $obs = this.backandService.socialSignup(provider, spec); 136 | $obs.subscribe( 137 | data => { 138 | console.log('Sign up succeeded with:' + provider); 139 | }, 140 | err => { 141 | this.backandService.logError(err) 142 | }, 143 | () => console.log('Finish Auth')); 144 | ``` 145 | 146 | * `provider` is one of: facebook, twitter, googleplus, github 147 | * `spec` optionally defines the look of the social network sign in window, like: 148 | 149 | left=1, top=1, width=600, height=600 150 | 151 | 152 | ## Socket Service 153 | 154 | * Socket login and logout are done automatially as part of the login and logout calls, respectively. 155 | 156 | * To subscribe to event `items_updated` from server side via sockets, 157 | call `this.backandService.on` and in your controller, subscribe with: 158 | 159 | ``` 160 | this.backandService.on('items_updated') 161 | .subscribe( 162 | data => { 163 | console.log("items_updated", data); 164 | }, 165 | err => { 166 | console.log(err); 167 | }, 168 | () => console.log('received update from socket') 169 | ); 170 | ``` 171 | 172 | ## Get User Details 173 | 174 | Fetch: 175 | 176 | ``` 177 | this.backandService.getUserDetails(true).subscribe( 178 | data=> { 179 | console.log(data); 180 | }, 181 | err=> this.backandService.logError(err), 182 | () => console.log('Got Details') 183 | ); 184 | ``` 185 | 186 | Caches user details in the app. The `force` parameter can cause it to fetch from it from Backand as in the call above. 187 | 188 | ## Backand Storage 189 | 190 | ### Create Backand Action 191 | 192 | Create a server side action in Backand by going into the items object actions tab and clicking on the Back& Files icon. Name your action "files" 193 | 194 | ### File Upload 195 | 196 | ``` 197 | backand.uploadFile('todo', 'files', fileName, base64Data).subscribe( 198 | data => { 199 | console.log(data); 200 | //data.url is the url of the uploaded file 201 | }, 202 | err => backand.logError(err), 203 | () => console.log('OK') 204 | ); 205 | ``` 206 | 207 | ### File Delete 208 | 209 | ``` 210 | backand.deleteFile('todo', 'files', fileName).subscribe( 211 | data => { 212 | }, 213 | err => backand.logError(err), 214 | () => console.log('OK') 215 | ); 216 | ``` 217 | -------------------------------------------------------------------------------- /backandService.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by backand on 3/23/16. 3 | */ 4 | 5 | export enum EVENTS { 6 | SIGNIN, 7 | SIGNOUT, 8 | SIGNUP 9 | }; 10 | 11 | export const URLS = { 12 | signup: '/1/user/signup', 13 | token: '/token', 14 | requestResetPassword: '/1/user/requestResetPassword', 15 | resetPassword: '/1/user/resetPassword', 16 | changePassword: '/1/user/changePassword', 17 | socialLoginWithToken: '/1/user/PROVIDER/token', 18 | socketUrl: 'https://socket.backand.com', 19 | actionUrl: '/1/objects/action/' 20 | } 21 | 22 | export const ERRORS = { 23 | NO_EMAIL_SOCIAL_ERROR: 'NO_EMAIL_SOCIAL', 24 | NOT_SIGNEDIN_ERROR: 'The user is not signed up to', 25 | ALREADY_SIGNEDUP_ERROR: 'The user already signed up to' 26 | }; 27 | 28 | // get token or error message from url in social sign-in popup 29 | (function () { 30 | var dataRegex = /\?(data|error)=(.+)/; 31 | var dataMatch = dataRegex.exec(location.href); 32 | if (dataMatch && dataMatch[1] && dataMatch[2]) { 33 | var userData = {}; 34 | userData[dataMatch[1]] = JSON.parse(decodeURI(dataMatch[2].replace(/#.*/, ''))); 35 | window.opener.postMessage(JSON.stringify(userData), location.origin); 36 | } 37 | }()); 38 | 39 | import {Observable, BehaviorSubject, Subject} from 'rxjs'; 40 | import {Http, Headers, URLSearchParams, Response} from '@angular/http' 41 | import {Injectable} from '@angular/core'; 42 | // import {Facebook} from 'ionic-native'; 43 | import * as io from 'socket.io-client'; 44 | 45 | @Injectable() 46 | export class BackandService { 47 | 48 | private api_url: string = 'https://api.backand.com'; 49 | private socialProviders: any = { 50 | github: {name: 'github', label: 'Github', url: 'www.github.com', css: 'github', id: 1}, 51 | google: {name: 'google', label: 'Google', url: 'www.google.com', css: 'google-plus', id: 2}, 52 | facebook: {name: 'facebook', label: 'Facebook', url: 'www.facebook.com', css: 'facebook', id: 3}, 53 | twitter: {name: 'twitter', label: 'Twitter', url: 'www.twitter.com', css: 'twitter', id: 4} 54 | }; 55 | private dummyReturnAddress: string = 'http://www.backandkuku.com'; 56 | 57 | // configuration variables 58 | private app_name:string = 'your app name'; 59 | private signUpToken: string = 'your signup token'; 60 | private anonymousToken: string = 'your anonymousToken token'; 61 | private callSignupOnSingInSocialError: boolean = true; 62 | private isMobile: boolean = false; 63 | 64 | // authentication state 65 | private auth_status:string = ""; 66 | private auth_type:string; 67 | private is_auth_error:boolean = false; 68 | private auth_token:{ header_name : string, header_value: string}; 69 | private username: string; 70 | 71 | private socialAuthWindow: any; 72 | private statusLogin: Subject; 73 | private socket: SocketIOClient.Socket;; 74 | 75 | constructor(public http:Http) { 76 | if (this.setAuthenticationState()){ 77 | this.loginSocket(this.auth_token.header_value, 78 | this.anonymousToken, this.app_name); 79 | } 80 | else{ 81 | this.auth_token = {header_name: '', header_value: ''}; 82 | } 83 | } 84 | 85 | // configuration of SDK 86 | public setIsMobile(isMobile: boolean) { 87 | this.isMobile = isMobile; 88 | } 89 | 90 | public setRunSignupAfterErrorInSigninSocial(signUpOnSignIn: boolean) { 91 | this.callSignupOnSingInSocialError = signUpOnSignIn; 92 | } 93 | 94 | public setAppName(appName: string) { 95 | this.app_name = appName; 96 | localStorage.setItem('app_name', appName); 97 | } 98 | 99 | public setAnonymousToken(anonymousToken) { 100 | this.anonymousToken = anonymousToken; 101 | localStorage.setItem('anonymousToken', anonymousToken); 102 | } 103 | 104 | public setSignUpToken(signUpToken) { 105 | this.signUpToken = signUpToken; 106 | localStorage.setItem('signUpToken', signUpToken); 107 | } 108 | 109 | 110 | // methods 111 | public signin(username, password): Observable { 112 | let creds = `username=${username}` + 113 | `&password=${password}` + 114 | `&appName=${this.app_name}` + 115 | `&grant_type=password`; 116 | let header = new Headers(); 117 | header.append('Content-Type', 'application/x-www-form-urlencoded'); 118 | let url = this.api_url + URLS.token; 119 | var $obs = this.http.post(url, creds, { 120 | headers: header 121 | }) 122 | .map(res => res.json()); 123 | 124 | $obs.subscribe( 125 | data => { 126 | this.setTokenHeader(data.access_token) 127 | localStorage.setItem('username', username); 128 | localStorage.setItem('user', JSON.stringify(data)); 129 | this.setAuthenticationState(); 130 | this.loginSocket(data, this.anonymousToken, this.app_name); 131 | }, 132 | err => { 133 | console.log(err); 134 | }, 135 | () => console.log('Finish Auth')); 136 | 137 | return $obs; 138 | 139 | } 140 | 141 | 142 | private signinWithToken(userData): Observable { 143 | let creds = `accessToken=${userData.access_token}` + 144 | `&appName=${this.app_name}` + 145 | `&grant_type=password`; 146 | let header = new Headers(); 147 | header.append('Content-Type', 'application/x-www-form-urlencoded'); 148 | let url = this.api_url + URLS.token; 149 | var $obs = this.http.post(url, creds, { 150 | headers: header 151 | }) 152 | .map(res => res.json()); 153 | 154 | 155 | $obs.subscribe( 156 | data => { 157 | this.setTokenHeader(data.access_token); 158 | localStorage.setItem('username', data.username); 159 | localStorage.setItem('user', data); 160 | this.loginSocket(data, this.anonymousToken, this.app_name); 161 | this.statusLogin.next(EVENTS.SIGNUP); 162 | }, 163 | err => { 164 | this.statusLogin.error(err); 165 | }, 166 | () => console.log('Finish Auth')); 167 | 168 | return $obs; 169 | 170 | } 171 | 172 | public signout() { 173 | this.auth_token = {header_name: '', header_value: ''}; 174 | localStorage.removeItem('auth_token'); 175 | localStorage.removeItem('username'); 176 | localStorage.removeItem('user'); 177 | this.setAuthenticationState(); 178 | } 179 | 180 | public signup(email, password, confirmPassword, firstName, lastName): Observable { 181 | let creds = { 182 | firstName: firstName, 183 | lastName: lastName, 184 | email: email, 185 | password: password, 186 | confirmPassword: confirmPassword 187 | }; 188 | let header = new Headers(); 189 | header.append('SignUpToken', this.signUpToken); 190 | var $obs = this.http.post(this.api_url + URLS.signup, creds, 191 | { 192 | headers: header 193 | } 194 | ); 195 | 196 | $obs.subscribe( 197 | data => { 198 | console.log(data); 199 | }, 200 | err => { 201 | console.log(err); 202 | }, 203 | () => console.log('Finish Sign Up')); 204 | 205 | return $obs; 206 | } 207 | 208 | public changePassword(oldPassword, newPassword): Observable { 209 | let creds = { 210 | oldPassword: oldPassword, 211 | newPassword: newPassword 212 | }; 213 | var $obs = this.http.post(this.api_url + URLS.changePassword, creds, 214 | { 215 | headers: this.authHeader 216 | } 217 | ).map(res => { 218 | console.log(res); 219 | }); 220 | 221 | 222 | $obs.subscribe( 223 | data => { 224 | console.log(data); 225 | }, 226 | err => { 227 | console.log(err); 228 | }, 229 | () => console.log('Finish Change Password')); 230 | 231 | return $obs; 232 | } 233 | 234 | public requestResetPassword(userName: string) { 235 | let header = new Headers(); 236 | header.append('Content-Type', 'application/x-www-form-urlencoded'); 237 | let data = { 238 | appName: this.app_name, 239 | username: userName 240 | }; 241 | return this.http.post(this.api_url + URLS.requestResetPassword, data, 242 | { 243 | headers: header 244 | } 245 | ); 246 | } 247 | 248 | public resetPassword(newPassword, resetToken) { 249 | let header = new Headers(); 250 | header.append('Content-Type', 'application/x-www-form-urlencoded'); 251 | let data = { 252 | newPassword: newPassword, 253 | resetToken: resetToken 254 | }; 255 | return this.http.post(this.api_url + URLS.resetPassword, data, 256 | { 257 | headers: header 258 | } 259 | ); 260 | } 261 | 262 | private socialSigninWithToken(provider, token): Observable { 263 | 264 | let url = this.api_url + URLS.socialLoginWithToken.replace('PROVIDER', provider) + 265 | "?accessToken=" + encodeURIComponent(token) + 266 | "&appName=" + encodeURI(this.app_name) + 267 | "&signupIfNotSignedIn=" + (this.callSignupOnSingInSocialError ? "true" : "false"); 268 | 269 | this.signout(); 270 | let headers = new Headers(); 271 | headers.append('Content-Type', 'application/json'); 272 | let $obs = this.http.get(url, 273 | { 274 | headers: headers 275 | } 276 | ) 277 | .map(res => res.json()); 278 | 279 | $obs.subscribe( 280 | data => { 281 | this.setTokenHeader(data.access_token); 282 | localStorage.setItem('user', data); 283 | this.loginSocket(data.access_token, this.anonymousToken, this.app_name); 284 | this.statusLogin.next(EVENTS.SIGNUP); 285 | }, 286 | err => { 287 | this.logError(err); 288 | this.statusLogin.error(err); 289 | }, 290 | () => { } 291 | ); 292 | 293 | return $obs; 294 | 295 | } 296 | 297 | public socialSignin(provider: string, spec: any = null, email: string = null) { 298 | return this.socialAuth(provider, false, spec, email); 299 | } 300 | 301 | public socialSignup(provider: string, spec: any = null, email: string = null) { 302 | return this.socialAuth(provider, true, spec, email); 303 | } 304 | 305 | private socialAuth(provider: string, isSignUp: boolean, spec: any = null, email: string = null) 306 | { 307 | if (!this.statusLogin){ 308 | this.statusLogin = new Subject(); 309 | } 310 | 311 | if (!this.socialProviders[provider]) { 312 | throw Error('Unknown Social Provider'); 313 | } 314 | 315 | if (this.isMobile) { 316 | let windowUrl = this.api_url + '/1/' 317 | + this.getSocialUrl(provider, isSignUp) 318 | + '&appname=' + this.app_name + (email ? ("&email=" + email) : '') 319 | + '&returnAddress=' + this.dummyReturnAddress 320 | + "&signupIfNotSignedIn=" + (this.callSignupOnSingInSocialError ? "true" : "false"); 321 | this.socialAuthWindow = window.open( 322 | windowUrl, 323 | spec || 'left=1, top=1, width=600, height=600'); 324 | 325 | let source = Observable.fromEvent(this.socialAuthWindow, 'loadstart') 326 | source.subscribe((e: any) => { 327 | 328 | if (e.url.indexOf(this.dummyReturnAddress) == 0) { // mean startWith 329 | 330 | var url = e.url; 331 | // handle case of misformatted json from server 332 | if (url && url.indexOf('#_=_') > -1){ 333 | url = url.slice(0, url.lastIndexOf('#_=_')); 334 | } 335 | 336 | url = decodeURI(url); 337 | let breakdown = this.parseQueryString(url); 338 | 339 | // error return from server 340 | if (breakdown.error) { 341 | if (!isSignUp && this.callSignupOnSingInSocialError && breakdown.error.message.indexOf(ERRORS.NOT_SIGNEDIN_ERROR) > -1) { // check is right error 342 | this.socialAuth(provider, true, spec); 343 | } 344 | if (breakdown.error.message.indexOf(ERRORS.ALREADY_SIGNEDUP_ERROR) > -1) { 345 | this.statusLogin.error(breakdown.error.message + ' (signing in with ' + breakdown.error.provider + ')'); 346 | } 347 | } 348 | else{ 349 | // login is OK 350 | let userData = breakdown.data; 351 | this.signinWithToken(userData); 352 | } 353 | 354 | try { 355 | this.socialAuthWindow.close(); 356 | } 357 | catch (err) { 358 | console.log(err); 359 | } 360 | } 361 | }); 362 | 363 | 364 | } 365 | else { 366 | 367 | let windowUrl = this.api_url + '/1/' 368 | + this.getSocialUrl(provider, isSignUp) 369 | + '&appname=' + this.app_name + (email ? ("&email=" + email) : '') 370 | + '&returnAddress=' 371 | + "&signupIfNotSignedIn=" + (this.callSignupOnSingInSocialError ? "true" : "false"); 372 | this.socialAuthWindow = window.open(windowUrl, 373 | 'id1', 374 | spec || 'left=1, top=1, width=600, height=600' 375 | ); 376 | 377 | let source = Observable.fromEvent(window, 'message'); 378 | var subscription = source.subscribe((e: any) => { 379 | 380 | // where does location come from ? 381 | if (e.target.location.origin !== location.origin) { 382 | return; 383 | } 384 | let data = e.data; 385 | 386 | // handle case of misformatted json from server 387 | if (data && data.indexOf('#_=_') > -1){ 388 | data = data.slice(0, data.lastIndexOf('#_=_')); 389 | } 390 | data = JSON.parse(data); 391 | let error = data.error; 392 | data = data.data; 393 | 394 | 395 | if (error) { 396 | if (this.callSignupOnSingInSocialError && error.message.indexOf(ERRORS.NOT_SIGNEDIN_ERROR) > -1) { // check is right error 397 | this.socialAuth(provider, true, spec); 398 | } 399 | if (isSignUp && error.message.indexOf(ERRORS.ALREADY_SIGNEDUP_ERROR) > -1) { 400 | this.statusLogin.error(error.message + ' (signing in with ' + error.provider + ')'); 401 | } 402 | } 403 | else{ 404 | // login is OK 405 | let userData = data; 406 | this.signinWithToken(userData); 407 | } 408 | e.target.removeEventListener('message'); 409 | this.socialAuthWindow.close(); 410 | this.socialAuthWindow = null; 411 | }); 412 | } 413 | return this.statusLogin; 414 | } 415 | 416 | // public inAppSocial(provider: string) { 417 | // if (this.isMobile){ 418 | // let that: any = this; 419 | // if (!this.statusLogin){ 420 | // this.statusLogin = new Subject(); 421 | // } 422 | // let permissions: string[] = ['public_profile', 'email']; 423 | // Facebook.login(permissions).then( 424 | // function(data) { 425 | // console.log(data); 426 | // if (data.status.toLowerCase() == 'connected'){ 427 | // let token: string = data.authResponse.accessToken; 428 | // that.socialSigninWithToken(provider, token); 429 | // } 430 | // else{ 431 | // that.statusLogin.error(data.status); 432 | // } 433 | 434 | // }, 435 | // function(err) { 436 | // console.log(err); 437 | // that.statusLogin.error(err); 438 | // } 439 | // ); 440 | // return this.statusLogin; 441 | // } 442 | // else{ 443 | // this.socialAuth(provider, true); 444 | // } 445 | // } 446 | 447 | public create(object: string, item: any, deep: boolean = false, returnObject: boolean = false) { 448 | let data: string = JSON.stringify(item); 449 | let query: string = ''; 450 | if (returnObject){ 451 | query += 'returnObject=true'; 452 | } 453 | if (deep){ 454 | query += query ? '&deep = true' : 'deep=true'; 455 | } 456 | 457 | 458 | return this.http.post(this.api_url + '/1/objects/' + object + (query ? '?' + query : ''), data, 459 | { 460 | headers: this.authHeader 461 | }) 462 | .retry(3) 463 | .map(res => res.json()); 464 | } 465 | 466 | public update(object: string, id: string, item: any, deep: boolean = false, returnObject: boolean = false) { 467 | let data: string = JSON.stringify(item); 468 | let query: string = ''; 469 | if (returnObject){ 470 | query += 'returnObject=true'; 471 | } 472 | if (deep){ 473 | query += query ? '&deep = true' : 'deep=true'; 474 | } 475 | 476 | 477 | return this.http.put(this.api_url + '/1/objects/' + object + '/' + id + (query ? '?' + query : ''), data, 478 | { 479 | headers: this.authHeader 480 | }) 481 | .retry(3) 482 | .map(res => res.json()); 483 | } 484 | 485 | 486 | 487 | public getList(object: string, 488 | pageSize: number = null, 489 | pageNumber: number = null, 490 | filter: any = null, 491 | sort: any = null, 492 | deep: boolean = false, 493 | search: string = null, 494 | exclude: string[] = null, 495 | relatedObjects: boolean = false) { 496 | let query: string = ''; 497 | let queryParams : string[] = []; 498 | 499 | if (deep){ 500 | queryParams.push('deep=true'); 501 | } 502 | if (relatedObjects){ 503 | queryParams.push('relatedObjects=true'); 504 | } 505 | if (exclude){ 506 | queryParams.push(exclude.join(',')); 507 | } 508 | if (pageSize){ 509 | queryParams.push('pageSize=' + pageSize); 510 | } 511 | if (pageNumber){ 512 | queryParams.push('pageNumber=' + pageNumber); 513 | } 514 | if (filter){ 515 | queryParams.push('filter=' + encodeURI(JSON.stringify(filter))); 516 | } 517 | if (sort){ 518 | queryParams.push('sort=' + encodeURI(JSON.stringify(sort))); 519 | } 520 | if (search){ 521 | queryParams.push('search=' + search); 522 | } 523 | if (queryParams.length > 0){ 524 | query = '?' + queryParams.join('&'); 525 | } 526 | 527 | return this.http.get(this.api_url + '/1/objects/' + object + query, { 528 | headers: this.authHeader 529 | }) 530 | .retry(3) 531 | .map(res => res.json().data); 532 | } 533 | 534 | public getOne(object: string, id: string, deep: boolean = false, exclude: string[] = null, level: number = null) { 535 | let query: string = ''; 536 | let queryParams : string[] = []; 537 | 538 | if (deep){ 539 | queryParams.push('deep=true'); 540 | } 541 | if (exclude){ 542 | queryParams.push(exclude.join(',')); 543 | } 544 | if (level){ 545 | queryParams.push('level=' + level); 546 | } 547 | if (queryParams.length > 0){ 548 | query = '?' + queryParams.join(','); 549 | } 550 | 551 | return this.http.get(this.api_url + '/1/objects/' + object + '/' + id + query, { 552 | headers: this.authHeader 553 | }) 554 | .retry(3) 555 | .map(res => res.json()); 556 | } 557 | 558 | public delete(object: string, id: string) { 559 | let headers = this.authHeader; 560 | headers.append('Content-Type', 'application/json'); 561 | return this.http.delete( 562 | this.api_url + '/1/objects/' + object + '/' + id, 563 | { 564 | headers: headers 565 | } 566 | ); 567 | } 568 | 569 | public uploadFile(objectName: string, fileActionName: string, filename: string, filedata: string) { 570 | let headers = this.authHeader; 571 | headers.append('Content-Type', 'application/json'); 572 | let data = JSON.stringify({ 573 | 'filename': filename, 574 | 'filedata': filedata.substr(filedata.indexOf(',') + 1, filedata.length) //need to remove the file prefix type 575 | }); 576 | return this.http.post( 577 | this.api_url + URLS.actionUrl + objectName + '?name=' + fileActionName, 578 | data, 579 | { 580 | headers: headers 581 | } 582 | ); 583 | } 584 | 585 | public deleteFile(objectName: string, fileActionName: string, filename: string) { 586 | let headers = this.authHeader; 587 | headers.append('Content-Type', 'application/json'); 588 | return this.http.delete( 589 | this.api_url + URLS.actionUrl + objectName + '?name=' + fileActionName + "&filename=" + filename, 590 | { 591 | headers: headers 592 | } 593 | ); 594 | } 595 | 596 | public loginSocket(token, anonymousToken, appName) { 597 | 598 | 599 | this.socket = io.connect(URLS.socketUrl, {'forceNew':true }); 600 | 601 | this.socket.on('connect', () => { 602 | console.log('connected'); 603 | this.socket.emit("login", token, anonymousToken, appName); 604 | }); 605 | 606 | this.socket.on('disconnect', () => { 607 | console.log('disconnect'); 608 | }); 609 | 610 | this.socket.on('reconnecting', () => { 611 | console.log('reconnecting'); 612 | }); 613 | 614 | this.socket.on('error', (error: string) => { 615 | console.log('error: ${error}'); 616 | }); 617 | 618 | } 619 | 620 | public logoutSocket() { 621 | if (this.socket){ 622 | this.socket.close(); 623 | } 624 | } 625 | 626 | public on(eventName: string) { 627 | let socketStream = Observable.fromEvent(this.socket, eventName); 628 | return socketStream; 629 | } 630 | 631 | public getAuthType():string { 632 | return this.auth_type; 633 | } 634 | 635 | public getAuthStatus():string { 636 | return this.auth_status; 637 | } 638 | 639 | // user details 640 | public getUsername():string { 641 | return this.username; 642 | } 643 | 644 | public getUserDetails(force: boolean) { 645 | if (force){ 646 | let $obs = this.http.get(this.api_url + '/api/account/profile', { 647 | headers: this.authHeader 648 | }) 649 | .retry(3) 650 | .map(res => res.json()); 651 | 652 | $obs.subscribe( 653 | data => { 654 | localStorage.setItem('user', JSON.stringify(data)); 655 | }, 656 | err => { 657 | console.log(err); 658 | }, 659 | () => console.log('Got User Details')); 660 | 661 | return $obs; 662 | } 663 | else{ 664 | let userDetails = localStorage.getItem('user'); 665 | let promise = Promise.resolve(userDetails ? JSON.parse(userDetails) : null); 666 | let $obs = Observable.fromPromise(promise); 667 | return $obs; 668 | } 669 | 670 | } 671 | 672 | public getUserRole(): string { 673 | let userDetails = localStorage.getItem('user'); 674 | if (userDetails){ 675 | return userDetails.role; 676 | } 677 | else{ 678 | return null; 679 | } 680 | } 681 | 682 | private setAuthenticationState(): boolean { 683 | let storedToken = localStorage.getItem('auth_token'); 684 | if (storedToken){ 685 | this.auth_token = JSON.parse(storedToken); 686 | this.auth_type = this.auth_token.header_name == 'Anonymous' ? 'Anonymous' : 'Token'; 687 | this.auth_status = 'OK'; 688 | if (this.auth_type == 'Token'){ 689 | this.username = localStorage.getItem('username'); 690 | } 691 | this.app_name = localStorage.getItem('app_name'); 692 | this.anonymousToken = localStorage.getItem('anonymousToken'); 693 | return true; 694 | } 695 | else{ 696 | this.auth_token = {header_name: '', header_value: ''}; 697 | return false; 698 | } 699 | } 700 | 701 | private getSocialUrl(providerName: string, isSignup: boolean) { 702 | let provider = this.socialProviders[providerName]; 703 | let action = isSignup ? 'up' : 'in'; 704 | return 'user/socialSign' + action + 705 | '?provider=' + provider.label + 706 | '&response_type=token&client_id=self&redirect_uri=' + provider.url + 707 | '&state='; 708 | } 709 | 710 | private parseQueryString(queryString): any { 711 | let query = queryString.substr(queryString.indexOf('/?') + 2); 712 | let breakdown = {}; 713 | let vars = query.split('&'); 714 | for (var i = 0; i < vars.length; i++) { 715 | var pair = vars[i].split('='); 716 | breakdown[pair[0]] = JSON.parse(pair[1]); 717 | } 718 | return breakdown; 719 | } 720 | 721 | public extractErrorMessage(err) { 722 | return JSON.parse(err._body).error_description; 723 | } 724 | 725 | public useAnonymousAuth() { 726 | this.setAnonymousHeader(); 727 | } 728 | 729 | private setTokenHeader(jwt) { 730 | if (jwt) { 731 | this.auth_token.header_name = "Authorization"; 732 | this.auth_token.header_value = "Bearer " + jwt; 733 | this.storeAuthToken(this.auth_token); 734 | } 735 | } 736 | 737 | private setAnonymousHeader() { 738 | this.auth_status = "OK"; 739 | this.auth_token.header_name = "AnonymousToken"; 740 | this.auth_token.header_value = this.anonymousToken; 741 | this.storeAuthToken(this.auth_token); 742 | localStorage.setItem('username', 'Anonymous'); 743 | } 744 | 745 | private storeAuthToken(token) { 746 | localStorage.setItem('auth_token', JSON.stringify(token)); 747 | } 748 | 749 | private getToken(res) { 750 | return res.json().access_token; 751 | } 752 | 753 | private get authHeader() { 754 | var authHeader = new Headers(); 755 | if (this.auth_token && this.auth_token.header_name && this.auth_token.header_value){ 756 | authHeader.append(this.auth_token.header_name, this.auth_token.header_value); 757 | } 758 | return authHeader; 759 | } 760 | 761 | public logError(err) { 762 | console.error('Error: ' + err); 763 | } 764 | } 765 | -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | export { BackandService, URLS, EVENTS, ERRORS } from './backandService'; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular2bknd-sdk", 3 | "version": "3.2.0", 4 | "description": "Typescript SDK for Backand", 5 | "main": "src/backandService.ts", 6 | "author": "Yoram Kornatzky", 7 | "dependencies": { 8 | "@angular/common": "2.0.0", 9 | "@angular/compiler": "2.0.0", 10 | "@angular/core": "2.0.0", 11 | "@angular/http": "2.0.0", 12 | "@angular/platform-browser": "2.0.0", 13 | "@angular/platform-browser-dynamic": "2.0.0", 14 | "es6-shim": "^0.35.0", 15 | "reflect-metadata": "^0.1.3", 16 | "rxjs": "5.0.0-beta.12", 17 | "socket.io-client": "^1.7.1", 18 | "zone.js": "^0.6.12" 19 | }, 20 | "scripts": { 21 | "prestart": "npm install" 22 | }, 23 | "license": "MIT" 24 | } 25 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "sourceMap": true, 7 | "emitDecoratorMetadata": true, 8 | "experimentalDecorators": true, 9 | "removeComments": false 10 | }, 11 | "filesGlob": [ 12 | "**/*.ts", 13 | "!node_modules/**/*" 14 | ], 15 | "exclude": [ 16 | "node_modules", 17 | "typings/global", 18 | "typings/global.d.ts" 19 | ], 20 | "compileOnSave": false, 21 | "atom": { 22 | "rewriteTsconfig": false 23 | } 24 | } -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint-ionic-rules", 3 | "rules" : { 4 | 5 | } 6 | } --------------------------------------------------------------------------------