/**
 * Copyright 2018 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */ import { AppOptions , AppHandler , ServiceBaseApp , attach } from '../../assistant' ; import { JsonObject } from '../../common' ; import * as common from '../../common' ; import { Headers , BuiltinFrameworkMetadata } from '../../framework' ; import * as Api from './api/v1' ; import { google } from 'googleapis' ; const encoding = 'utf8' ; const homegraphWrapperDeprecationNotice = method => `SmartHomeApp.${ method } Home Graph wrapper method is deprecated. Use Google APIs Node.js Client for Home Graph: https://www.npmjs.com/package/@googleapis/homegraph` ; public deprecated export interface SmartHomeJwt { type : 'service_account' ; project_id : string ; private_key_id : string ; private_key : string ; client_email : string ; client_id : string ; auth_uri : string ; token_uri : string ; auth_provider_x509_cert_url : string ; client_x509_cert_url : string ; } public export interface SmartHomeOptions extends AppOptions { public deprecated key ? : string ; public deprecated jwt ? : SmartHomeJwt ; } public export interface SmartHomeHandler < TRequest extends Api . SmartHomeV1Request , TResponse extends Api . SmartHomeV1Response > { ( body , headers , framework ) : TResponse | Promise < TResponse > ; } hidden export interface SmartHomeHandlers { [ intent ] : SmartHomeHandler < Api . SmartHomeV1Request , Api . SmartHomeV1Response > ; } public export interface SmartHomeApp extends ServiceBaseApp { hidden _intents : SmartHomeHandlers ; hidden _intent ( intent , handler ) : this ; example param handler public onSync ( handler ) : this ; example param handler public onQuery ( handler ) : this ; example param handler public onExecute ( handler ) : this ; example param handler public onDisconnect ( handler ) : this ; example param agentUserId public deprecated requestSync ( agentUserId ) : Promise < string > ; example param reportedState public deprecated reportState ( reportedState ) : Promise < string > ; public deprecated key ? : string ; public deprecated jwt ? : SmartHomeJwt ; } public export interface SmartHome { ( options ? ) : AppHandler & SmartHomeApp ; } const makeApiCall = ( url , data , jwt ? ) : Promise < string > => { const options = { hostname : 'homegraph.googleapis.com' , port : 443 , path : url , method : 'POST' , headers : { } , } ; const apiCall = ( options ) => { if ( jwt && ! options . headers . Authorization ) { throw new Error ( 'JWT is defined but Authorization header is not defined ' + JSON . stringify ( options ) ) ; } return new Promise < string > ( ( resolve , reject ) => { const buffers = [ ] ; const req = common . request ( options , res => { res . on ( 'data' , d => { buffers . push ( typeof d === 'string' ? Buffer . from ( d , encoding ) : d ) ; } ) ; res . on ( 'end' , ( ) => { const apiResponse = Buffer . concat ( buffers ) . toString ( encoding ) ; const apiResponseJson = JSON . parse ( apiResponse ) ; if ( apiResponseJson . error && apiResponseJson . error . code >= 400 ) { reject ( apiResponse ) ; return ; } resolve ( apiResponse ) ; } ) ; } ) ; req . on ( 'error' , e => { reject ( e ) ; } ) ; req . write ( JSON . stringify ( data ) ) ; req . end ( ) ; } ) ; } ; if ( jwt ) { return new Promise < JsonObject > ( ( resolve , reject ) => { if ( jwt . client_id === 'sample-client-id' ) { options . headers = { Authorization : ' Bearer 1234' , } ; resolve ( options ) ; return ; } const jwtClient = new google . auth . JWT ( jwt . client_email , undefined , jwt . private_key , [ 'https://www.googleapis.com/auth/homegraph' ] , undefined ) ; jwtClient . authorize ( ( err , tokens ) => { if ( err ) { return reject ( err ) ; } options . headers = { Authorization : ` Bearer ${ tokens . access_token }` , } ; resolve ( options ) ; } ) ; } ) . then ( options => { return apiCall ( options ) ; } ) ; } else { return apiCall ( options ) ; } } ; example public export const smarthome = ( options = { } ) => attach < SmartHomeApp > ( { _intents : { } , _intent ( this , intent , handler ) { this . _intents [ intent ] = handler ; return this ; } , onSync ( this , handler ) { return this . _intent ( 'action.devices.SYNC' , handler ) ; } , onQuery ( this , handler ) { return this . _intent ( 'action.devices.QUERY' , handler ) ; } , onExecute ( this , handler ) { return this . _intent ( 'action.devices.EXECUTE' , handler ) ; } , onDisconnect ( this , handler ) { return this . _intent ( 'action.devices.DISCONNECT' , handler ) ; } , async requestSync ( this , agentUserId ) { common . warn . log ( homegraphWrapperDeprecationNotice ( 'requestSync' ) ) ; if ( this . jwt ) { return await makeApiCall ( '/v1/devices:requestSync' , { agent_user_id : agentUserId , } , this . jwt ) ; } if ( this . key ) { return await makeApiCall ( `/v1/devices:requestSync?key=${ encodeURIComponent ( this . key ) }` , { agent_user_id : agentUserId , } ) ; } throw new Error ( 'An API key was not specified. ' + 'Please visit https://console.cloud.google.com/apis/api/homegraph.googleapis.com/overview' ) ; } , async reportState ( this , reportedState ) { common . warn . log ( homegraphWrapperDeprecationNotice ( 'reportState' ) ) ; if ( ! this . jwt ) { throw new Error ( 'A JWT was not specified. ' + 'Please visit https://console.cloud.google.com/apis/credentials' ) ; } return await makeApiCall ( '/v1/devices:reportStateAndNotification' , reportedState , this . jwt ) ; } , key : options . key , jwt : options . jwt , async handler ( this , body , headers , metadata = { } ) { const { intent } = body . inputs [ 0 ] ; const handler = this . _intents [ intent ] ; return { status : 200 , headers : { } , body : await handler ( body , headers , metadata ) , } ; } , } , options ) ;