/**
 * Copyright (c) 2020, 2021 Oracle and/or its affiliates.  All rights reserved.
 * This software is dual-licensed to you under the Universal Permissive License (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or Apache License 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose either license.
 */ import { statSync , ReadStream } from "fs" ; import { OciError } from "./error" ; import { Range } from "./range" ; import { Readable , PassThrough } from "stream" ; import { addOpcRequestId , addUserAgent } from "./headers" ; import { isEmpty } from "./utils" ; import { RequestParams } from "./request-generator" ; import { RawData , BinaryBody } from "./types" ; import getChunk from "./chunker" ; interface ReqBodyAndContentLength { body : any ; contentLength : number ; } export function mapContainer ( obj , getJsonObj ) { const constructedObj = { } ; for ( let key in obj ) { constructedObj [ key ] = getJsonObj ( obj [ key ] ) ; } return constructedObj ; } export function handleErrorResponse ( response , body ) { const statusCode = response . status || - 1 ; const requestId = response . headers . get ( "opc-request-id" ) ; if ( body && body . code && body . message ) { return new OciError ( statusCode , body . code , body . message , requestId ) ; } else if ( typeof body == "string" && body . length > 0 ) { return new OciError ( statusCode , "None" , body , requestId ) ; } else if ( response . statusText && response . statusText . length > 0 ) { return new OciError ( statusCode , "None" , response . statusText , requestId ) ; } else { return new OciError ( statusCode , "None" , "unknown reason." , requestId ) ; } } export async function handleErrorBody ( response ) { let data = await response . text ( ) ; try { data = JSON . parse ( data ) ; } catch ( err ) { return data ; } return data ; } export function convertStringToType ( str , expectedType ) { if ( str == null ) return str ; expectedType = expectedType . toLowerCase ( ) ; switch ( expectedType ) { case "string" : return str ; case "number" : return Number ( str ) ; case "date" : return formatDateToRFC3339 ( new Date ( str ) ) ; case "common.Range" : return Range . parse ( str ) ; } } export async function getStringFromResponseBody ( body ) { if ( typeof body === "string" ) { return body as string ; } if ( body instanceof Readable ) { return readStringFromReadable ( body ) ; } else { throw new Error ( "Unable to read body content to sign the request" ) ; } } export async function readStringFromReadable ( readable ) { let contentBuffer = [ ] ; let size = 0 ; const MEMIBYTES = 1024 * 1024 ; const sizeLimit = 2000 * MEMIBYTES ; readable . setEncoding ( "utf8" ) ; return new Promise < string > ( ( resolve , reject ) => { readable . on ( "end" , ( ) => { resolve ( contentBuffer . join ( "" ) ) ; } ) ; readable . on ( "data" , chunk => { if ( size > sizeLimit ) { throw Error ( "Tried to read stream but content length is greater than 2GB." ) ; } contentBuffer . push ( chunk ) ; size += chunk . length ; } ) ; readable . on ( "error" , err => { reject ( err ) ; } ) ; } ) ; } export function getSignerAndReqBody ( body ? , forceExcludeBody ? ) { const singerAndReqBody = { signerBody : undefined , requestBody : undefined } ; if ( ! body || isEmpty ( body ) ) { return singerAndReqBody ; } if ( forceExcludeBody ) { singerAndReqBody . requestBody = body ; return singerAndReqBody ; } if ( typeof body === "string" ) { return { signerBody : body , requestBody : body } ; } else if ( body instanceof Readable ) { const signerbody = body . pipe ( new PassThrough ( ) ) ; const reqBody = body . pipe ( new PassThrough ( ) ) ; return { signerBody : signerbody , requestBody : reqBody } ; } else throw new Error ( "Unable to read body content" ) ; } export function addAdditionalHeaders ( headers , params ) { addOpcRequestId ( headers ) ; addUserAgent ( headers ) ; } export async function autoDetectContentLengthAndReadBody ( headers , params ) { const reqHeaders = params . headerParams ; if ( reqHeaders ) { const shouldReadBodyAndCalculateContentLength = ( "content-length" in reqHeaders && reqHeaders [ "content-length" ] === undefined ) || ( "Content-Length" in reqHeaders && reqHeaders [ "Content-Length" ] === undefined ) || params . backupBinaryBody ; if ( shouldReadBodyAndCalculateContentLength ) { const { body , contentLength } = await calculateContentLengthAndBodyContent ( params . bodyContent ! ) ; headers . append ( "content-length" , String ( contentLength ) ) ; return body ; } } } async function calculateContentLengthAndBodyContent ( body ) { try { const dataFeeder = getChunk ( body , Number . MAX_SAFE_INTEGER ) ; const dataPart = ( await dataFeeder . next ( ) ) . value as RawData ; const content = dataPart . data ; const contentLength = dataPart . size ; return { body : content , contentLength } ; } catch ( e ) { throw Error ( "SDK could not calculate contentLength from the request stream, please add contentLength and try again." ) ; } } export function formatDateToRFC3339 ( date ) { return ( date . getFullYear ( ) + "-" + ( "0" + ( date . getMonth ( ) + 1 ) ) . slice ( - 2 ) + "-" + ( "0" + date . getDate ( ) ) . slice ( - 2 ) + "T" + date . getHours ( ) + ":" + ( "0" + date . getMinutes ( ) ) . slice ( - 2 ) + ":" + ( "0" + date . getSeconds ( ) ) . slice ( - 2 ) + "Z" ) ; } export async function getStringFromRequestBody ( body ) { if ( typeof body === "string" ) { return body as string ; } if ( body instanceof Readable ) { return readStringFromReadable ( body ) ; } else { throw new Error ( "Unable to read body content to sign the request" ) ; } } export function isReadableStream ( body ) { if ( body . cancel && body . getReader && body . pipeThrough && body . pipeTo && body . tee ) { return true ; } return false ; } export function byteLength ( input ) { if ( input === null || input === undefined ) return 0 ; if ( typeof input === "string" ) input = Buffer . from ( input ) ; if ( typeof input . byteLength === "number" ) { return input . byteLength ; } else if ( typeof input . length === "number" ) { return input . length ; } else if ( typeof input . size === "number" ) { return input . size ; } return undefined ; }