From 7ef0e16df59aa5934ab27f40cee53f51ed82bc4d Mon Sep 17 00:00:00 2001 From: Rene Vergara Date: Wed, 10 Aug 2022 14:12:28 -0500 Subject: [PATCH] Implement Xero service --- src/app/invoice/invoice.component.ts | 2 + src/app/order/order.component.ts | 2 + src/app/order/order.model.ts | 4 +- src/app/order/order.service.ts | 8 +++ src/app/receipt.service.ts | 2 + src/app/receipt/receipt.component.ts | 2 + src/app/settings/settings.component.ts | 23 ++++---- src/app/xero.service.spec.ts | 16 ++++++ src/app/xero.service.ts | 73 ++++++++++++++++++++++++++ src/app/xeroreg/xeroreg.component.ts | 53 +++++++++++-------- 10 files changed, 152 insertions(+), 33 deletions(-) create mode 100644 src/app/xero.service.spec.ts create mode 100644 src/app/xero.service.ts diff --git a/src/app/invoice/invoice.component.ts b/src/app/invoice/invoice.component.ts index 5aeef5c..75e91ed 100644 --- a/src/app/invoice/invoice.component.ts +++ b/src/app/invoice/invoice.component.ts @@ -34,6 +34,8 @@ export class InvoiceComponent implements OnInit { total: 0, totalZec: 0, paid: false, + externalInvoice: '', + shortCode: '', lines: [ { qty: 1, diff --git a/src/app/order/order.component.ts b/src/app/order/order.component.ts index 6f74da7..e924f2d 100644 --- a/src/app/order/order.component.ts +++ b/src/app/order/order.component.ts @@ -41,6 +41,8 @@ export class OrderComponent implements OnInit{ total:0, totalZec: 0, paid: false, + externalInvoice: '', + shortCode: '', lines: [ { qty: 1, diff --git a/src/app/order/order.model.ts b/src/app/order/order.model.ts index 3b7e66f..57ce3d9 100644 --- a/src/app/order/order.model.ts +++ b/src/app/order/order.model.ts @@ -11,5 +11,7 @@ export interface Order { total: number, totalZec: number, lines: LineItem[], - paid: boolean + paid: boolean, + externalInvoice: string, + shortCode: string } diff --git a/src/app/order/order.service.ts b/src/app/order/order.service.ts index e620219..229e0b6 100644 --- a/src/app/order/order.service.ts +++ b/src/app/order/order.service.ts @@ -62,6 +62,8 @@ export class OrderService { total: 0, totalZec: 0, paid: false, + externalInvoice: '', + shortCode: '', lines: [ { qty: 1, @@ -163,6 +165,8 @@ export class OrderService { price: 0, total: 0, paid: false, + externalInvoice: '', + shortCode: '', lines: [lineItem] }; let obs = this.http.post<{message: string, order: Order}>(this.beUrl+'api/order', {payload: order}, { headers: this.reqHeaders }); @@ -190,6 +194,8 @@ export class OrderService { totalZec: 0, price: 0, paid: false, + externalInvoice: '', + shortCode: '', lines: [ { qty: 1, @@ -223,6 +229,8 @@ export class OrderService { total: 0, totalZec: 0, paid: false, + externalInvoice: '', + shortCode: '', lines: [ { qty: 1, diff --git a/src/app/receipt.service.ts b/src/app/receipt.service.ts index cf17190..a140ea5 100644 --- a/src/app/receipt.service.ts +++ b/src/app/receipt.service.ts @@ -51,6 +51,8 @@ export class ReceiptService { total: 0, totalZec: 0, paid: false, + externalInvoice: '', + shortCode: '', lines: [ { qty: 1, diff --git a/src/app/receipt/receipt.component.ts b/src/app/receipt/receipt.component.ts index f0981e4..6f701ad 100644 --- a/src/app/receipt/receipt.component.ts +++ b/src/app/receipt/receipt.component.ts @@ -25,6 +25,8 @@ export class ReceiptComponent implements OnInit { total: 0, totalZec: 0, paid: false, + externalInvoice: '', + shortCode: '', lines: [ { qty: 1, diff --git a/src/app/settings/settings.component.ts b/src/app/settings/settings.component.ts index 96fa4c3..37c365f 100644 --- a/src/app/settings/settings.component.ts +++ b/src/app/settings/settings.component.ts @@ -2,12 +2,9 @@ import { Inject, Component, OnInit, OnDestroy, ViewEncapsulation } from '@angula import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; import { MatSlideToggleChange } from '@angular/material/slide-toggle'; import { UntypedFormBuilder, Validators, UntypedFormGroup, FormControl } from '@angular/forms'; -import { User } from '../user.model'; +import { Observable } from 'rxjs'; import { Owner } from '../owner.model'; - -var crypto = require('sha.js'); -var URLSafeBase64 = require('urlsafe-base64'); -var Buffer = require('buffer/').Buffer; +import { XeroService } from '../xero.service'; @Component({ selector: 'app-settings', @@ -41,9 +38,12 @@ export class SettingsComponent implements OnInit { ]; xeroLink: string = ''; localToken: string = ''; + clientId: string = ''; + clientIdUpdate: Observable; constructor( private fb: UntypedFormBuilder, + public xeroService: XeroService, private dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) public data: Owner) { this.useZats = data.zats; @@ -59,15 +59,16 @@ export class SettingsComponent implements OnInit { this.settingsForm.get('vKey')!.enable(); } this.owner = data; + + this.clientIdUpdate = xeroService.clientIdUpdate; + xeroService.getXeroConfig(); + this.clientIdUpdate.subscribe(clientId => { + this.clientId = clientId; + this.xeroLink = `https://login.xero.com/identity/connect/authorize?response_type=code&client_id=${this.clientId}&redirect_uri=http%3A%2F%2Flocalhost%3A4200%2Ftest&scope=accounting.transactions offline_access&state=1234` + }); } ngOnInit() { - this.localToken = localStorage.getItem('s4z_token')!; - const hash = crypto('sha256').update(this.owner.address).digest('base64'); - //const hash2 = URLSafeBase64.encode(crypto('sha256').update(this.localToken)); - console.log(hash); - console.log(this.safeURL(hash)); - this.xeroLink = `https://login.xero.com/identity/connect/authorize?response_type=code&client_id=CA4A8C8DA0AE462186B34DBD42074D2D&redirect_uri=http%3A%2F%2Flocalhost%3A4200%2Ftest&scope=accounting.transactions offline_access&state=1234` } safeURL(s: string){ diff --git a/src/app/xero.service.spec.ts b/src/app/xero.service.spec.ts new file mode 100644 index 0000000..4119cc1 --- /dev/null +++ b/src/app/xero.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { XeroService } from './xero.service'; + +describe('XeroService', () => { + let service: XeroService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(XeroService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/xero.service.ts b/src/app/xero.service.ts new file mode 100644 index 0000000..0da9818 --- /dev/null +++ b/src/app/xero.service.ts @@ -0,0 +1,73 @@ +import { Injectable } from '@angular/core'; +import { BehaviorSubject, Observable } from 'rxjs'; +import { HttpClient, HttpParams, HttpHeaders } from '@angular/common/http'; + +import { ConfigData } from './configdata'; + +var Buffer = require('buffer/').Buffer; + +@Injectable({ + providedIn: 'root' +}) +export class XeroService { + beUrl = ConfigData.Be_URL; + clientId: string = ''; + clientSecret: string = ''; + xeroToken: any = { + accessToken: '', + refreshToken: '', + expiresIn: 0, + scope: '', + tokenType: '' + }; + private _clientIdUpdated: BehaviorSubject = new BehaviorSubject(this.clientId); + private _clientSecretUpdated: BehaviorSubject = new BehaviorSubject(this.clientSecret); + private _tokenUpdated: BehaviorSubject = new BehaviorSubject(this.xeroToken); + public readonly clientIdUpdate: Observable = this._clientIdUpdated.asObservable(); + public readonly clientSecretUpdate: Observable = this._clientSecretUpdated.asObservable(); + public readonly tokenUpdate: Observable = this._tokenUpdated.asObservable(); + private reqHeaders: HttpHeaders; + + constructor( + private http: HttpClient + ) { + var auth = 'Basic ' + Buffer.from(ConfigData.UsrPwd).toString('base64'); + this.reqHeaders = new HttpHeaders().set('Authorization', auth); + this.getXeroConfig(); + } + + getXeroConfig(){ + let obs = this.http.get<{message: string, xeroConfig: any}>(this.beUrl+'api/xero', { headers:this.reqHeaders, observe: 'response'}); + + obs.subscribe(xeroDataResponse => { + if (xeroDataResponse.status == 200) { + this.clientId = xeroDataResponse.body!.xeroConfig.clientId; + this.clientSecret = xeroDataResponse.body!.xeroConfig.clientSecret; + this._clientIdUpdated.next(Object.assign({}, this).clientId); + this._clientSecretUpdated.next(Object.assign({}, this).clientSecret); + } else { + console.log('No config in DB!'); + } + }); + + return obs; + } + + getXeroAccessToken(code: string){ + this.getXeroConfig().subscribe(xeroDataResponse => { + console.log('XAT: '+this.clientId); + var xeroAuth = 'Basic ' + Buffer.from(`${this.clientId}:${this.clientSecret}`).toString('base64'); + var xeroHeaders = new HttpHeaders().set('Authorization', xeroAuth).append('Content-Type', 'application/x-www-form-urlencoded'); + let obs = this.http.post('https://identity.xero.com/connect/token', `grant_type=authorization_code&code=${code}&redirect_uri=http://localhost:4200/test` , {headers: xeroHeaders, observe: 'response'}) + obs.subscribe(tokenData => { + if (tokenData.status == 200) { + console.log(tokenData.body!); + this.xeroToken = tokenData.body!; + this._tokenUpdated.next(Object.assign({}, this).xeroToken); + } else { + console.log('Error: '+tokenData.status); + } + }); + }); + } +} diff --git a/src/app/xeroreg/xeroreg.component.ts b/src/app/xeroreg/xeroreg.component.ts index 4496bc8..b87c0f8 100644 --- a/src/app/xeroreg/xeroreg.component.ts +++ b/src/app/xeroreg/xeroreg.component.ts @@ -3,7 +3,8 @@ import { ActivatedRoute } from '@angular/router'; import { HttpClient, HttpParams, HttpHeaders } from '@angular/common/http'; import { UserService } from '../user.service'; import { Owner } from '../owner.model'; -//import { XeroClient } from 'xero-node'; +import { Observable } from 'rxjs'; +import { XeroService } from '../xero.service'; var Buffer = require('buffer/').Buffer; @@ -16,28 +17,38 @@ var Buffer = require('buffer/').Buffer; export class XeroRegComponent implements OnInit { - reqHeaders: HttpHeaders; - constructor(private http: HttpClient, - private activatedRoute: ActivatedRoute - ) { - //const secret = 'J8Ft70Tk6theIFNYpuDRPvzuc271_dyjq7EJlzPFh8lpXXvV'; - var auth = 'Basic ' + Buffer.from('CA4A8C8DA0AE462186B34DBD42074D2D:J8Ft70Tk6theIFNYpuDRPvzuc271_dyjq7EJlzPFh8lpXXvV').toString('base64'); - this.reqHeaders = new HttpHeaders().set('Authorization', auth).append('Content-Type', 'application/x-www-form-urlencoded'); - //this.session = localStorage.getItem('s4z_token')!; - //this.ownerUpdate = userService.ownerUpdate; - //this.ownerUpdate.subscribe(ownerData => { - //this.owner = ownerData; - //}); + clientId: string = ''; + clientSecret: string = ''; + xeroToken: any; + clientIdUpdate: Observable; + clientSecretUpdate: Observable; + tokenUpdate: Observable; + + constructor( + public xeroService: XeroService, + private activatedRoute: ActivatedRoute + ) { + this.tokenUpdate = xeroService.tokenUpdate; + this.clientIdUpdate = xeroService.clientIdUpdate; + xeroService.getXeroConfig(); + this.clientIdUpdate.subscribe(clientId => { + this.clientId = clientId; + }); + this.clientSecretUpdate = xeroService.clientSecretUpdate; + this.clientSecretUpdate.subscribe(clientSecret => { + this.clientSecret = clientSecret; + }); } - ngOnInit(): void { - this.activatedRoute.queryParams.subscribe((params) => { - console.log(params); - this.http.post('https://identity.xero.com/connect/token', `grant_type=authorization_code&code=${params.code}&redirect_uri=http://localhost:4200/test` , {headers: this.reqHeaders, observe: 'response'}) - .subscribe(responseData => { - console.log(responseData); + ngOnInit(): void { + this.activatedRoute.queryParams.subscribe((params) => { + console.log(params); + this.xeroService.getXeroAccessToken(params.code) + this.tokenUpdate.subscribe(token => { + console.log(token); + this.xeroToken = token; }); - }); - } + }); + } }