/**
 * Forge SDK
 * The Forge Platform contains an expanding collection of web service components that can be used with Autodesk cloud-based products or your own technologies. Take advantage of Autodesk’s expertise in design and engineering.
 *
 * Contact: forge.help@autodesk.com
 *
 * NOTE: This class is auto generated by the swagger code generator program.
 * https://github.com/swagger-api/swagger-codegen.git
 * Do not edit the class manually.
 *
 * 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.
 */
/*jshint esversion: 9 */

module.exports = (function () {
	'use strict';

	const ApiClient = require('../ApiClient');
	const BucketObjects = require('../model/BucketObjects');
	const ObjectDetails = require('../model/ObjectDetails');
	const ObjectFullDetails = require('../model/ObjectFullDetails');
	const ObjectS3Download = require('../model/ObjectS3Download');
	const ObjectS3Upload = require('../model/ObjectS3Upload');
	const ObjectCompleteS3Upload = require('../model/ObjectCompleteS3Upload');
	const PostBucketsSigned = require('../model/PostBucketsSigned');
	const PostObjectSigned = require('../model/PostObjectSigned');
	const Reason = require('../model/Reason');
	const Result = require('../model/Result');
	const crypto = require('crypto');
	const _path = require('path');
	const __fs = require('fs');
	const _fs = require('fs/promises');
	const axios = require('axios');
	//const rax = require('retry-axios');

	/**
	 * Objects service.
	 * @module api/ObjectsApi
	 */

	/**
	 * Constructs a new ObjectsApi.
	 * @alias module:api/ObjectsApi
	 * @class
	 * @param {module:ApiClient} apiClient Optional API client implementation to use,
	 * default to {@link module:ApiClient#instance} if unspecified.
	 */
	const exports = function (apiClient) {
		this.apiClient = apiClient || ApiClient.instance;

		/**
		 * Upload an object. If the specified object name already exists in the bucket, the uploaded content will overwrite the existing content for the bucket name/object name combination.
		 * @param {String} bucketKey bucket key (will be URL-encoded automatically)
		 * @param {String} objectKey object key (will be URL-encoded automatically)
		 * @param {Integer} contentLength Indicates the size of the request body.
		 * @param {File} body
		 * @param {Object=} opts Optional parameters
		 * @param {Integer=} opts.contentType Optional array of possible Content-Type header
		 * @param {String=} opts.contentDisposition The suggested default filename when downloading this object to a file after it has been uploaded.
		 * @param {String=} opts.ifMatch If-Match header containing a SHA-1 hash of the bytes in the request body can be sent by the calling service or client application with the request. If present, OSS will use the value of If-Match header to verify that a SHA-1 calculated for the uploaded bytes server side matches what was sent in the header. If not, the request is failed with a status 412 Precondition Failed and the data is not written.
		 * @param {String=} opts.xAdsContentSha1 A SHA-1 checksum of the object represented as a hexadecimal string. If the SHA-1 hash in the header does not match the SHA-1 hash computed by the server for the uploaded object, the request fails (status code 400).
		 * data is of type: {module:model/ObjectDetails}
		 * @param {Object} oauth2client oauth2client for the call
		 * @param {Object} credentials credentials for the call
		 * @remark https://forge.autodesk.com/en/docs/data/v2/reference/http/buckets-:bucketKey-objects-:objectKey-PUT/
		 * @async
		 * @returns {Promise<ObjectDetails>}
		 *
		 * @deprecated
		 */
		this.uploadObject = function (bucketKey, objectKey, contentLength, body, opts, oauth2client, credentials) {
			opts = opts || {};
			const postBody = body;

			// verify the required parameter 'bucketKey' is set
			if (!bucketKey)
				return (Promise.reject("Missing the required parameter 'bucketKey' when calling uploadObject"));
			// verify the required parameter 'objectKey' is set
			if (!objectKey)
				return (Promise.reject("Missing the required parameter 'objectKey' when calling uploadObject"));
			// verify the required parameter 'contentLength' is set
			if (!contentLength)
				return (Promise.reject("Missing the required parameter 'contentLength' when calling uploadObject"));
			// verify the required parameter 'body' is set
			if (!body)
				return (Promise.reject("Missing the required parameter 'body' when calling uploadObject"));

			const pathParams = {
				bucketKey,
				objectKey,
			};
			const queryParams = {};
			const headerParams = {
				'Content-Length': contentLength,
				'Content-Disposition': opts.contentDisposition || opts['Content-Disposition'],
				'If-Match': opts.ifMatch || opts['If-Match'],
				'x-ads-content-sha1': opts.xAdsContentSha1 || opts['x-ads-content-sha1'],
			};
			const formParams = {};

			const contentTypes = opts.contentType || ['application/octet-stream'];
			const accepts = ['application/vnd.api+json', 'application/json'];
			const returnType = ObjectDetails;

			return (this.apiClient.callApi(
				'/oss/v2/buckets/{bucketKey}/objects/{objectKey}', 'PUT',
				pathParams, queryParams, headerParams, formParams, postBody,
				contentTypes, accepts, returnType, oauth2client, credentials
			));
		};

		/**
		 * This endpoint allows resumable uploads for large files in chunks.
		 * @param {String} bucketKey bucket key (will be URL-encoded automatically)
		 * @param {String} objectKey object key (will be URL-encoded automatically)
		 * @param {Integer} contentLength Indicates the size of the request body.
		 * @param {String} contentRange Byte range of a segment being uploaded
		 * @param {String} sessionId Unique identifier of a session of a file being uploaded
		 * @param {File} body
		 * @param {Object=} opts Optional parameters
		 * @param {String=} opts.contentType Optional array of possible Content-Type header
		 * @param {String=} opts.contentDisposition The suggested default filename when downloading this object to a file after it has been uploaded.
		 * @param {String=} opts.ifMatch If-Match header containing a SHA-1 hash of the bytes in the request body can be sent by the calling service or client application with the request. If present, OSS will use the value of If-Match header to verify that a SHA-1 calculated for the uploaded bytes server side matches what was sent in the header. If not, the request is failed with a status 412 Precondition Failed and the data is not written.
		 * @param {String=} opts.xAdsChunkSha1 A SHA-1 checksum of the chunk represented as a hexadecimal string. If the SHA-1 hash in the header does not match the SHA-1 hash computed by the server for the uploaded chunk, the request fails (status code 400). You can try to upload it again.
		 * data is of type: {module:model/ObjectDetails}
		 * @param {Object} oauth2client oauth2client for the call
		 * @param {Object} credentials credentials for the call
		 * @remark https://forge.autodesk.com/en/docs/data/v2/reference/http/buckets-:bucketKey-objects-:objectKey-resumable-PUT/
		 * @async
		 * @returns {Promise<ObjectDetails>}
		 *
		 * @deprecated
		 */
		this.uploadChunk = function (bucketKey, objectKey, contentLength, contentRange, sessionId, body, opts, oauth2client, credentials) {
			opts = opts || {};
			const postBody = body;

			// verify the required parameter 'bucketKey' is set
			if (!bucketKey)
				return (Promise.reject("Missing the required parameter 'bucketKey' when calling uploadChunk"));
			// verify the required parameter 'objectKey' is set
			if (!objectKey)
				return (Promise.reject("Missing the required parameter 'objectKey' when calling uploadChunk"));
			// verify the required parameter 'contentLength' is set
			if (!contentLength)
				return (Promise.reject("Missing the required parameter 'contentLength' when calling uploadChunk"));
			// verify the required parameter 'contentRange' is set
			if (!contentRange)
				return (Promise.reject("Missing the required parameter 'contentRange' when calling uploadChunk"));
			// verify the required parameter 'sessionId' is set
			if (!sessionId)
				return (Promise.reject("Missing the required parameter 'sessionId' when calling uploadChunk"));
			// verify the required parameter 'body' is set
			if (!body)
				return (Promise.reject("Missing the required parameter 'body' when calling uploadChunk"));

			const pathParams = {
				bucketKey,
				objectKey
			};
			const queryParams = {};
			const headerParams = {
				'Content-Length': contentLength,
				'Content-Range': contentRange,
				'Content-Disposition': opts.contentDisposition || opts['Content-Disposition'],
				'x-ads-chunk-sha1': opts.xAdsChunkSha1 || opts['x-ads-chunk-sha1'],
				'Session-Id': sessionId,
			};
			const formParams = {};

			const contentTypes = opts.contentType || opts['Content-Type'] || ['application/octet-stream'];
			const accepts = ['application/vnd.api+json', 'application/json'];
			const returnType = ObjectDetails;

			return (this.apiClient.callApi(
				'/oss/v2/buckets/{bucketKey}/objects/{objectKey}/resumable', 'PUT',
				pathParams, queryParams, headerParams, formParams, postBody,
				contentTypes, accepts, returnType, oauth2client, credentials
			));
		};

		/**
		 * This endpoint returns status information about a resumable upload.
		 * @param {String} bucketKey bucket key (will be URL-encoded automatically)
		 * @param {String} objectKey object key (will be URL-encoded automatically)
		 * @param {String} sessionId Unique identifier of a session of a file being uploaded
		 * @param {Object} oauth2client oauth2client for the call
		 * @param {Object} credentials credentials for the call
		 * @remark https://forge.autodesk.com/en/docs/data/v2/reference/http/buckets-:bucketKey-objects-:objectKey-status-:sessionId-GET/
		 * @async
		 * @retuns {Promise<void>}
		 */
		this.getStatusBySessionId = function (bucketKey, objectKey, sessionId, oauth2client, credentials) {
			const postBody = null;

			// verify the required parameter 'bucketKey' is set
			if (!bucketKey)
				return (Promise.reject("Missing the required parameter 'bucketKey' when calling getStatusBySessionId"));
			// verify the required parameter 'objectKey' is set
			if (!objectKey)
				return (Promise.reject("Missing the required parameter 'objectKey' when calling getStatusBySessionId"));
			// verify the required parameter 'sessionId' is set
			if (!sessionId)
				return (Promise.reject("Missing the required parameter 'sessionId' when calling getStatusBySessionId"));

			const pathParams = {
				bucketKey,
				objectKey,
				sessionId,
			};
			const queryParams = {};
			const headerParams = {};
			const formParams = {};

			const contentTypes = ['application/json'];
			const accepts = ['application/vnd.api+json', 'application/json'];
			const returnType = null;

			return (this.apiClient.callApi(
				'/oss/v2/buckets/{bucketKey}/objects/{objectKey}/status/{sessionId}', 'GET',
				pathParams, queryParams, headerParams, formParams, postBody,
				contentTypes, accepts, returnType, oauth2client, credentials
			));
		};

		/**
		 * List objects in a bucket. It is only available to the bucket creator.
		 * @param {String} bucketKey bucket key (will be URL-encoded automatically)
		 * @param {Object=} opts Optional parameters
		 * @param {Integer=} [opts.limit=10] Limit to the response size, Acceptable values: 1-100 Default = 10  (default to 10)
		 * @param {String=} opts.beginsWith Provides a way to filter the based on object key name
		 * @param {String=} opts.startAt Key to use as an offset to continue pagination This is typically the last bucket key found in a preceding GET buckets response
		 * data is of type: {module:model/BucketObjects}
		 * @param {Object} oauth2client oauth2client for the call
		 * @param {Object} credentials credentials for the call
		 * @remark https://forge.autodesk.com/en/docs/data/v2/reference/http/buckets-:bucketKey-objects-GET/
		 * @async
		 * @returns {Promise<BucketObjects>}
		 */
		this.getObjects = function (bucketKey, opts, oauth2client, credentials) {
			opts = opts || {};
			const postBody = null;

			// verify the required parameter 'bucketKey' is set
			if (!bucketKey)
				return (Promise.reject("Missing the required parameter 'bucketKey' when calling getObjects"));

			const pathParams = {
				bucketKey,
			};
			const queryParams = {
				limit: opts.limit,
				beginsWith: opts.beginsWith,
				startAt: opts.startAt,
			};
			const headerParams = {};
			const formParams = {};

			const contentTypes = ['application/json'];
			const accepts = ['application/vnd.api+json', 'application/json'];
			const returnType = BucketObjects;

			return (this.apiClient.callApi(
				'/oss/v2/buckets/{bucketKey}/objects', 'GET',
				pathParams, queryParams, headerParams, formParams, postBody,
				contentTypes, accepts, returnType, oauth2client, credentials
			));
		};

		/**
		 * Returns object details in JSON format.
		 * @param {String} bucketKey bucket key (will be URL-encoded automatically)
		 * @param {String} objectKey object key (will be URL-encoded automatically)
		 * @param {Object=} opts Optional parameters
		 * @param {Date=} opts.ifModifiedSince If the requested object has not been modified since the time specified in this field, an entity will not be returned from the server; instead, a 304 (not modified) response will be returned without any message body.
		 * @param {(String|String[])=} opts.with Extra information in details; multiple uses are supported Acceptable values: `createdDate`, `lastAccessedDate`, `lastModifiedDate`
		 * data is of type: {module:model/ObjectFullDetails}
		 * @param {Object} oauth2client oauth2client for the call
		 * @param {Object} credentials credentials for the call
		 * @remark https://forge.autodesk.com/en/docs/data/v2/reference/http/buckets-:bucketKey-objects-:objectKey-details-GET/
		 * @async
		 * @returns {Promise<ObjectFullDetails>}
		 */
		this.getObjectDetails = function (bucketKey, objectKey, opts, oauth2client, credentials) {
			opts = opts || {};
			const postBody = null;

			// verify the required parameter 'bucketKey' is set
			if (!bucketKey)
				return (Promise.reject("Missing the required parameter 'bucketKey' when calling getObjectDetails"));
			// verify the required parameter 'objectKey' is set
			if (!objectKey)
				return (Promise.reject("Missing the required parameter 'objectKey' when calling getObjectDetails"));

			const pathParams = {
				bucketKey,
				objectKey
			};
			const queryParams = {
				with: opts._with || opts.with,
			};
			const headerParams = {
				'If-Modified-Since': opts.ifModifiedSince || opts['If-Modified-Since'],
			};
			const formParams = {};

			const contentTypes = ['application/json'];
			const accepts = ['application/vnd.api+json', 'application/json'];
			const returnType = ObjectFullDetails;

			return (this.apiClient.callApi(
				'/oss/v2/buckets/{bucketKey}/objects/{objectKey}/details', 'GET',
				pathParams, queryParams, headerParams, formParams, postBody,
				contentTypes, accepts, returnType, oauth2client, credentials
			));
		};

		/**
		 * Download an object.
		 * @param {String} bucketKey bucket key (will be URL-encoded automatically)
		 * @param {String} objectKey object key (will be URL-encoded automatically)
		 * @param {Object=} opts Optional parameters
		 * @param {String=} opts.range A range of bytes to download from the specified object.
		 * @param {String=} opts.ifNoneMatch The value of this header is compared to the ETAG of the object. If they match, the body will not be included in the response. Only the object information will be included.
		 * @param {Date=} opts.ifModifiedSince If the requested object has not been modified since the time specified in this field, an entity will not be returned from the server; instead, a 304 (not modified) response will be returned without any message body.
		 * @param {String=} opts.acceptEncoding When gzip is specified, a gzip compressed stream of the object’s bytes will be returned in the response. Cannot use “Accept-Encoding:gzip” with Range header containing an end byte range. End byte range will not be honored if “Accept-Encoding: gzip” header is used.
		 * @param {String=} opts.accepts Optional array of possible Accepts header
		 * data is of type: {Object}
		 * @param {Object} oauth2client oauth2client for the call
		 * @param {Object} credentials credentials for the call
		 * @remark https://forge.autodesk.com/en/docs/data/v2/reference/http/buckets-:bucketKey-objects-:objectKey-GET/
		 * @async
		 * @returns {Promise<any>}
		 *
		 * @deprecated
		 */
		this.getObject = function (bucketKey, objectKey, opts, oauth2client, credentials, responseType) {
			opts = opts || {};
			const postBody = null;
			responseType = responseType || 'arraybuffer'

			// verify the required parameter 'bucketKey' is set
			if (!bucketKey)
				return (Promise.reject("Missing the required parameter 'bucketKey' when calling getObject"));
			// verify the required parameter 'objectKey' is set
			if (!objectKey)
				return (Promise.reject("Missing the required parameter 'objectKey' when calling getObject"));

			const pathParams = {
				bucketKey,
				objectKey,
			};
			const queryParams = {};
			const headerParams = {
				Range: opts.range || opts.Range,
				'If-None-Match': opts.ifNoneMatch || opts['If-None-Match'],
				'If-Modified-Since': opts.ifModifiedSince || opts['If-Modified-Since'],
				'Accept-Encoding': opts.acceptEncoding || opts['Accept-Encoding'],
			};
			const formParams = {};

			const contentTypes = ['application/json'];
			const accepts = opts.accepts || ['application/octet-stream'];
			const returnType = Object;
			
			return (this.apiClient.callApi(
				'/oss/v2/buckets/{bucketKey}/objects/{objectKey}', 'GET',
				pathParams, queryParams, headerParams, formParams, postBody,
				contentTypes, accepts, returnType, oauth2client, credentials,
				responseType
			));
		};

		/**
		 * This endpoint creates a signed URL that can be used to download an object within the specified expiration time. Be aware that if the object the signed URL points to is deleted or expires before the signed URL expires, then the signed URL will no longer be valid. A successful call to this endpoint requires bucket owner access.
		 * @param {String} bucketKey bucket key (will be URL-encoded automatically)
		 * @param {String} objectKey object key (will be URL-encoded automatically)
		 * @param {module:model/PostBucketsSigned} postBucketsSigned Body Structure
		 * @param {Object=} opts Optional parameters
		 * @param {String} [opts.access=read] Access for signed resource Acceptable values: `read`, `write`, `readwrite`. Default value: `read`  (default to read)
		 * @param {Boolean=} [opts.useCdn=true] If true, this will generate a CloudFront URL for the S3 object
		 * data is of type: {module:model/PostObjectSigned}
		 * @param {Object} oauth2client oauth2client for the call
		 * @param {Object} credentials credentials for the call
		 * @remark https://forge.autodesk.com/en/docs/data/v2/reference/http/buckets-:bucketKey-objects-:objectKey-signed-POST/
		 * @async
		 * @returns {Promise<PostObjectSigned>}
		 */
		this.createSignedResource = function (bucketKey, objectKey, postBucketsSigned, opts, oauth2client, credentials) {
			opts = opts || {};
			const postBody = postBucketsSigned;

			// verify the required parameter 'bucketKey' is set
			if (!bucketKey)
				return (Promise.reject("Missing the required parameter 'bucketKey' when calling createSignedResource"));
			// verify the required parameter 'objectKey' is set
			if (!objectKey)
				return (Promise.reject("Missing the required parameter 'objectKey' when calling createSignedResource"));
			// verify the required parameter 'postBucketsSigned' is set
			if (!postBucketsSigned)
				return (Promise.reject("Missing the required parameter 'postBucketsSigned' when calling createSignedResource"));

			const pathParams = {
				bucketKey,
				objectKey,
			};
			const queryParams = {
				access: opts.access || 'read',
				useCdn: opts.useCdn === false ? opts.useCdn : true,
			};
			const headerParams = {};
			const formParams = {};

			const contentTypes = ['application/json'];
			const accepts = ['application/vnd.api+json', 'application/json'];
			const returnType = PostObjectSigned;

			return (this.apiClient.callApi(
				'/oss/v2/buckets/{bucketKey}/objects/{objectKey}/signed', 'POST',
				pathParams, queryParams, headerParams, formParams, postBody,
				contentTypes, accepts, returnType, oauth2client, credentials
			));
		};

		/**
		 * Overwrite a existing object using a signed URL.  Conditions to call this operation:  Object is available Expiration period is valid Signed URL should be created with write or readwrite
		 * @param {String} id Id of signed resource
		 * @param {Integer} contentLength Indicates the size of the request body.
		 * @param {File} body
		 * @param {Object=} opts Optional parameters
		 * @param {String=} opt.contentType Optional array of possible Content-Type header
		 * @param {String=} opts.contentDisposition The suggested default filename when downloading this object to a file after it has been uploaded.
		 * @param {String} [opts.xAdsRegion=US] The region where the bucket resides Acceptable values: `US`, `EMEA` Default is `US`  (default to US)
		 * @param {String} opts.ifMatch If-Match header containing a SHA-1 hash of the bytes in the request body can be sent by the calling service or client application with the request. If present, OSS will use the value of If-Match header to verify that a SHA-1 calculated for the uploaded bytes server side matches what was sent in the header. If not, the request is failed with a status 412 Precondition Failed and the data is not written.
		 * data is of type: {module:model/ObjectDetails}
		 * @param {Object} oauth2client oauth2client for the call
		 * @param {Object} credentials credentials for the call
		 * @remark https://forge.autodesk.com/en/docs/data/v2/reference/http/signedresources-:id-PUT/
		 * @async
		 * @returns {Promise<ObjectDetails>}
		 *
		 * @deprecated
		 */
		this.uploadSignedResource = function (id, contentLength, body, opts, oauth2client, credentials) {
			opts = opts || {};
			const postBody = body;

			// verify the required parameter 'id' is set
			if (!id)
				return (Promise.reject("Missing the required parameter 'id' when calling uploadSignedResource"));
			// verify the required parameter 'contentLength' is set
			if (!contentLength)
				return (Promise.reject("Missing the required parameter 'contentLength' when calling uploadSignedResource"));
			// verify the required parameter 'body' is set
			if (!body)
				return (Promise.reject("Missing the required parameter 'body' when calling uploadSignedResource"));

			const pathParams = {
				id,
			};
			const queryParams = {};
			const headerParams = {
				'Content-Length': contentLength || opts['Content-Length'],
				'Content-Disposition': opts.contentDisposition || opts['Content-Disposition'],
				'x-ads-region': opts.xAdsRegion || opts['x-ads-region'] || 'US',
				'If-Match': opts.ifMatch || opts['If-Match'],
				'x-ads-content-sha1': opts.xAdsContentSha1 || opts['x-ads-content-sha1'],
			};
			const formParams = {};

			const contentTypes = opts.contentType || ['application/octet-stream'];
			const accepts = ['application/vnd.api+json', 'application/json'];
			const returnType = ObjectDetails;

			return (this.apiClient.callApi(
				'/oss/v2/signedresources/{id}', 'PUT',
				pathParams, queryParams, headerParams, formParams, postBody,
				contentTypes, accepts, returnType, oauth2client, credentials
			));
		};

		/**
		 * Resumable upload for signed URLs.
		 * @param {String} id Id of signed resource
		 * @param {String} contentRange Byte range of a segment being uploaded
		 * @param {String} sessionId Unique identifier of a session of a file being uploaded
		 * @param {File} body
		 * @param {Object=} opts Optional parameters
		 * @param {String=} opts.contentType Optional array of possible Content-Type header
		 * @param {String=} opts.contentDisposition The suggested default filename when downloading this object to a file after it has been uploaded.
		 * @param {String} [opts.xAdsRegion=US] The region where the bucket resides Acceptable values: `US`, `EMEA` Default is `US`  (default to US)
		 * data is of type: {module:model/ObjectDetails}
		 * @param {Object} oauth2client oauth2client for the call
		 * @param {Object} credentials credentials for the call
		 * @remark https://forge.autodesk.com/en/docs/data/v2/reference/http/signedresources-:id-resumable-PUT/
		 * @async
		 * @returns {Promise<ObjectDetails>}
		 *
		 * @deprecated
		 */
		this.uploadSignedResourcesChunk = function (id, contentRange, sessionId, body, opts, oauth2client, credentials) {
			opts = opts || {};
			const postBody = body;

			// verify the required parameter 'id' is set
			if (!id)
				return (Promise.reject("Missing the required parameter 'id' when calling uploadSignedResourcesChunk"));
			// verify the required parameter 'contentRange' is set
			if (!contentRange)
				return (Promise.reject("Missing the required parameter 'contentRange' when calling uploadSignedResourcesChunk"));
			// verify the required parameter 'sessionId' is set
			if (!sessionId)
				return (Promise.reject("Missing the required parameter 'sessionId' when calling uploadSignedResourcesChunk"));
			if (!body)
				// verify the required parameter 'body' is set
				return (Promise.reject("Missing the required parameter 'body' when calling uploadSignedResourcesChunk"));

			const pathParams = {
				id,
			};
			const queryParams = {};
			const headerParams = {
				'Content-Range': contentRange || opts['Content-Range'],
				'Content-Disposition': opts.contentDisposition || opts['Content-Disposition'],
				'x-ads-region': opts.xAdsRegion || opts['x-ads-region'] || 'US',
				'x-ads-chunk-sha1': opts.xAdsChunkSha1 || opts['x-ads-chunk-sha1'],
				'Session-Id': sessionId,
			};
			const formParams = {};

			const contentTypes = opts.contentType || ['application/octet-stream'];
			const accepts = ['application/vnd.api+json', 'application/json'];
			const returnType = ObjectDetails;

			return (this.apiClient.callApi(
				'/oss/v2/signedresources/{id}/resumable', 'PUT',
				pathParams, queryParams, headerParams, formParams, postBody,
				contentTypes, accepts, returnType, oauth2client, credentials
			));
		};

		/**
		 * Download an object using a signed URL.
		 * @param {String} id Id of signed resource
		 * @param {Object=} opts Optional parameters
		 * @param {String=} opts.range A range of bytes to download from the specified object.
		 * @param {String=} opts.ifNoneMatch The value of this header is compared to the ETAG of the object. If they match, the body will not be included in the response. Only the object information will be included.
		 * @param {Date=} opts.ifModifiedSince If the requested object has not been modified since the time specified in this field, an entity will not be returned from the server; instead, a 304 (not modified) response will be returned without any message body.
		 * @param {String=} opts.acceptEncoding When gzip is specified, a gzip compressed stream of the object’s bytes will be returned in the response. Cannot use “Accept-Encoding:gzip” with Range header containing an end byte range. End byte range will not be honored if “Accept-Encoding: gzip” header is used.
		 * @param {String} [opts.region=US] The region where the bucket resides Acceptable values: `US`, `EMEA` Default is `US`  (default to US)
		 * @param {String=} opts.accepts Optional array of possible Accepts header
		 * data is of type: {Object}
		 * @param {Object} oauth2client oauth2client for the call
		 * @param {Object} credentials credentials for the call
		 * @remark https://forge.autodesk.com/en/docs/data/v2/reference/http/signedresources-:id-GET/
		 * @async
		 * @returns {Promise<any>}
		 *
		 * @deprecated
		 */
		this.getSignedResource = function (id, opts, oauth2client, credentials) {
			opts = opts || {};
			const postBody = null;

			// verify the required parameter 'id' is set
			if (!id)
				return (Promise.reject("Missing the required parameter 'id' when calling getSignedResource"));

			const pathParams = {
				id,
			};
			const queryParams = {
				region: opts.region || 'US'
			};
			const headerParams = {
				Range: opts.range || opts.Range,
				'If-None-Match': opts.ifNoneMatch || opts['If-None-Match'],
				'If-Modified-Since': opts.ifModifiedSince || opts['If-Modified-Since'],
				'Accept-Encoding': opts.acceptEncoding || opts['Accept-Encoding'],
			};
			const formParams = {};

			const contentTypes = ['application/json'];
			const accepts = opts.accepts || ['application/octet-stream'];
			const returnType = Object;

			return (this.apiClient.callApi(
				'/oss/v2/signedresources/{id}', 'GET',
				pathParams, queryParams, headerParams, formParams, postBody,
				contentTypes, accepts, returnType, oauth2client, credentials
			));
		};

		/**
		 * Deletes an object from the bucket.
		 * @param {String} bucketKey bucket key (will be URL-encoded automatically)
		 * @param {String} objectKey object key (will be URL-encoded automatically)
		 * @param {Object} oauth2client oauth2client for the call
		 * @param {Object} credentials credentials for the call
		 * @remark https://forge.autodesk.com/en/docs/data/v2/reference/http/signedresources-:id-DELETE/
		 * @async
		 * @returns {Promise<void>}
		 */
		this.deleteObject = function (bucketKey, objectKey, oauth2client, credentials) {
			const postBody = null;

			// verify the required parameter 'bucketKey' is set
			if (!bucketKey)
				return (Promise.reject("Missing the required parameter 'bucketKey' when calling deleteObject"));
			// verify the required parameter 'objectKey' is set
			if (!objectKey)
				return (Promise.reject("Missing the required parameter 'objectKey' when calling deleteObject"));

			const pathParams = {
				bucketKey,
				objectKey,
			};
			const queryParams = {};
			const headerParams = {};
			const formParams = {};

			const contentTypes = ['application/json'];
			const accepts = [];
			const returnType = null;

			return (this.apiClient.callApi(
				'/oss/v2/buckets/{bucketKey}/objects/{objectKey}', 'DELETE',
				pathParams, queryParams, headerParams, formParams, postBody,
				contentTypes, accepts, returnType, oauth2client, credentials
			));
		};

		/**
		 * Copies an object to another object name in the same bucket.
		 * @param {String} bucketKey bucket key (will be URL-encoded automatically)
		 * @param {String} objectKey object key (will be URL-encoded automatically)
		 * @param {String} newObjectKey bject key to use as the destination (will be URL-encoded automatically)
		 * data is of type: {module:model/ObjectDetails}
		 * @param {Object} oauth2client oauth2client for the call
		 * @param {Object} credentials credentials for the call
		 * @remark https://forge.autodesk.com/en/docs/data/v2/reference/http/buckets-:bucketKey-objects-:objectKey-copyto-:newObjectKey-PUT/
		 * @async
		 * @returns {Promise<ObjectDetails>}
		 */
		this.copyTo = function (bucketKey, objectKey, newObjectKey, oauth2client, credentials) {
			const postBody = null;

			// verify the required parameter 'bucketKey' is set
			if (!bucketKey)
				return (Promise.reject("Missing the required parameter 'bucketKey' when calling copyTo"));
			// verify the required parameter 'objectKey' is set
			if (!objectKey)
				return (Promise.reject("Missing the required parameter 'objectKey' when calling copyTo"));
			// verify the required parameter 'newObjName' is set
			if (!newObjectKey)
				return (Promise.reject("Missing the required parameter 'newObjectKey' when calling copyTo"));

			const pathParams = {
				bucketKey,
				objectKey,
				newObjectKey,
			};
			const queryParams = {};
			const headerParams = {};
			const formParams = {};

			const contentTypes = ['application/json'];
			const accepts = ['application/vnd.api+json', 'application/json'];
			const returnType = ObjectDetails;

			return (this.apiClient.callApi(
				'/oss/v2/buckets/{bucketKey}/objects/{objectKey}/copyto/{newObjectKey}', 'PUT',
				pathParams, queryParams, headerParams, formParams, postBody,
				contentTypes, accepts, returnType, oauth2client, credentials
			));
		};

		/**
		 * Delete a signed URL. A successful call to this endpoint requires bucket owner access.
		 * @param {String} id Id of signed resource
		 * @param {Object=} opts Optional parameters
		 * @param {String=} [opts.region=US] The region where the bucket resides Acceptable values: `US`, `EMEA` Default is `US`  (default to US)
		 * @param {Object} oauth2client oauth2client for the call
		 * @param {Object} credentials credentials for the call
		 * @remark https://forge.autodesk.com/en/docs/data/v2/reference/http/buckets-:bucketKey-objects-:objectKey-DELETE/
		 * @async
		 * @returns {Promise<void>}
		 */
		this.deleteSignedResource = function (id, opts, oauth2client, credentials) {
			opts = opts || {};
			const postBody = null;

			// verify the required parameter 'id' is set
			if (!id)
				return (Promise.reject("Missing the required parameter 'id' when calling deleteSignedResource"));

			const pathParams = {
				id
			};
			const queryParams = {
				region: opts.region || 'US'
			};
			const headerParams = {};
			const formParams = {};

			const contentTypes = ['application/json'];
			const accepts = ['text/plain'];
			const returnType = null;

			return (this.apiClient.callApi(
				'/oss/v2/signedresources/{id}', 'DELETE',
				pathParams, queryParams, headerParams, formParams, postBody,
				contentTypes, accepts, returnType, oauth2client, credentials
			));
		};

		// S3 direct access

		/**
		 * Returns a signed S3 URL.
		 * @param {String} bucketKey bucket key (will be URL-encoded automatically)
		 * @param {String} objectKey bucobjectket key (will be URL-encoded automatically)
		 * @param {Object=} opts Optional parameters
		 * @param {String=} opts.ifNoneMatch If the value of this header matches the ETag of the object, an entity will not be returned from the server;
		 * instead a 304 (not modified) response will be returned without any message-body.
		 * @param {Date=} opts.ifModifiedSince If the requested object has not been modified since the time specified in this field,
		 * an entity will not be returned from the server; instead, a 304 (not modified) response will be returned without any message-body.
		 * @param {String=} opts.responseContentType Value of the Content-Type header that the client expects to receive.
		 * If this attribute is not provided, it defaults to the value corresponding to the object.
		 * @param {String=} opts.responseContentDisposition Value of the Content Disposition header the client expects to receive.
		 * If this attribute is not provided, it defaults to the value corresponding to the object.
		 * @param {String=} opts.responseCacheControl Value of the Cache-Control header that the client expects to receive.
		 * If this attribute is not provided, it defaults to the value corresponding to the object.
		 * @param {Boolean=} [opts.publicResourceFallback=false] Allows fallback to OSS signed URLs in case of unmerged resumable uploads.
		 * @param {Boolean=} [opts.useCdn=true] Will generate a CloudFront URL for the S3 object.
		 * @param {Integer=} [opts.minutesExpiration=2] The custom expiration time within the 1 to 60 minutes range, if not specified, default is 2 minutes.
		 * data is of type: {module:model/ObjectS3Download}
		 * @param {Object} oauth2client oauth2client for the call
		 * @param {Object} credentials credentials for the call
		 * @remark https://forge.autodesk.com/en/docs/data/v2/reference/http/buckets-:bucketKey-objects-:objectKey-signeds3download-GET/
		 * @async
		 * @returns {Promise<ObjectS3Download>}
		 */
		this.getS3DownloadURL = function (bucketKey, objectKey, opts, oauth2client, credentials) {
			opts = opts || {};
			const postBody = null;

			// verify the required parameter 'bucketKey' is set
			if (!bucketKey)
				return (Promise.reject("Missing the required parameter 'bucketKey' when calling getS3DownloadURL"));
			// verify the required parameter 'objectKey' is set
			if (!objectKey)
				return (Promise.reject("Missing the required parameter 'objectKey' when calling getS3DownloadURL"));

			const pathParams = {
				bucketKey,
				objectKey,
			};
			const queryParams = {
				'response-content-type': opts.responseContentType || opts['response-content-type'],
				'response-content-disposition': opts.responseContentDisposition || opts['response-content-disposition'],
				'response-cache-control': opts.responseCacheControl || opts['response-cache-control'],
				'public-resource-fallback': opts.publicResourceFallback || opts['public-resource-fallback'] || false,
				useCdn: opts.useCdn === false ? opts.useCdn : true,
				minutesExpiration: opts.minutesExpiration || 2,
			};
			const headerParams = {
				'If-None-Match': opts.ifNoneMatch || opts['If-None-Match'],
				'If-Modified-Since': opts.ifModifiedSince || opts['If-Modified-Since'],
			};
			const formParams = {};

			const contentTypes = ['application/json'];
			const accepts = ['application/vnd.api+json', 'application/json'];
			const returnType = ObjectS3Download;

			return (this.apiClient.callApi(
				'/oss/v2/buckets/{bucketKey}/objects/{objectKey}/signeds3download', 'GET',
				pathParams, queryParams, headerParams, formParams, postBody,
				contentTypes, accepts, returnType, oauth2client, credentials
			));
		};

		/**
		 * Gets one or more signed URLs to objects. The signed URLs can be used to download the objects directly from S3, skipping OSS servers.
		 * Be aware that expiration time for the signed URL(s) is just 60 seconds. So, a request to the URL(s) must begin within 60 seconds; the transfer
		 * of the data can exceed 60 seconds.
		 * A successful call to this endpoint requires bucket owner access.
		 * Note that resumable uploads store each chunk individually. After upload completes, an async process merges all the chunks and creates the
		 * definitive OSS file. This async process can take time. If you request an S3 download URL before the async process completes, the response returns
		 * a map of S3 URLs, one per chunk where the key is the corresponding range bytes. In case you don’t want multiple URLs in the response, you can use
		 * OSS signed URL functionality, with the public-resource-fallback query parameter set to true.
		 * Note: While this endpoint does not support range headers, the returned URL(s) can be used for ranged downloads. This way, downloads can be
		 * parallelized using multiple ranges for maximum speed.
		 * @param {String} bucketKey bucket key (will be URL-encoded automatically)
		 * @param {Object} body body parameter
		 * @param {Object[]} body.requests An array of objects representing each request to get an S3 URL to download from.
		 * @param {String} body.requests[].objectKey Object name to create a download S3 signed URL for
		 * @param {String=} body.requests[].response-content-type Value of the Content-Type header that the client expects to receive.
		 * If this attribute is not provided, it defaults to the value corresponding to the object.
		 * @param {String=} body.requests[].response-content-disposition Value of the Content Disposition header the client expects to receive.
		 * If this attribute is not provided, it defaults to the value corresponding to the object.
		 * @param {String=} body.requests[].response-cache-control Value of the Cache-Control header that the client expects to receive.
		 * If this attribute is not provided, it defaults to the value corresponding to the object.
		 * @param {String=} body.requests[].If-None-Match The value of this attribute is compared to the ETAG of the object.
		 * If they match, the response body will show the status of this item as “skipped” with the reason as “Not modified”.
		 * @param {Date=} body.requests[].If-Modified-Since If the requested object has not been modified since the time specified in this attribute,
		 * the response body will show the status of this item as “skipped” with the reason as “Not modified”.
		 * @param {Object=} opts Optional parameters
		 * @param {Boolean=} [opts.publicResourceFallback=false] (public-resource-fallback) Allows fallback to OSS signed URLs in case of unmerged resumable uploads.
		 * @param {Boolean=} [opts.useCdn=true] Will generate a CloudFront URL for the S3 object.
		 * @param {Integer=} [opts.minutesExpiration=2] The custom expiration time within the 1 to 60 minutes range, if not specified, default is 2 minutes.
		 * data is of type: {Object.<module:model/ObjectS3Download>}
		 * @param {Object} oauth2client oauth2client for the call
		 * @param {Object} credentials credentials for the call
		 * @remark https://forge.autodesk.com/en/docs/data/v2/reference/http/buckets-:bucketKey-objects-batchsigneds3download-POST/
		 * @async
		 * @returns {Promise<any>}
		 */
		this.getS3DownloadURLs = function (bucketKey, body, opts, oauth2client, credentials) {
			opts = opts || {};
			const postBody = body;

			// verify the required parameter 'bucketKey' is set
			if (!bucketKey)
				return (Promise.reject("Missing the required parameter 'bucketKey' when calling getS3DownloadURLs"));
			// verify the required parameter 'body' is set
			if (!body)
				return (Promise.reject("Missing the required parameter 'body' when calling getS3DownloadURLs"));
			if (!body.requests || !Array.isArray(body.requests) || body.requests.length === 0)
				return (Promise.reject("Missing the required parameter 'body.requests' when calling getS3DownloadURLs"));
			for (let i = 0; i < body.requests.length; i++) {
				if (!body.requests[i].objectKey)
					return (Promise.reject("Missing the required parameter 'body.requests[].objectKey' when calling getS3DownloadURLs"));
			}

			const pathParams = {
				bucketKey,
			};
			const queryParams = {
				'public-resource-fallback': opts.publicResourceFallback || opts['public-resource-fallback'] || false,
				useCdn: opts.useCdn === false ? opts.useCdn : true,
				minutesExpiration: opts.minutesExpiration || 2,
			};
			const headerParams = {};
			const formParams = {};

			const contentTypes = ['application/json'];
			const accepts = ['application/vnd.api+json', 'application/json'];
			const returnType = Object;

			return (this.apiClient.callApi(
				'/oss/v2/buckets/{bucketKey}/objects/batchsigneds3download', 'POST',
				pathParams, queryParams, headerParams, formParams, postBody,
				contentTypes, accepts, returnType, oauth2client, credentials
			));
		};

		/**
		 * Requests an S3 signed URL with which to upload an object, or an array of signed URLs with which to upload an object in multiple parts.
		 * @param {String} bucketKey bucket key (will be URL-encoded automatically)
		 * @param {String} objectKey object key (will be URL-encoded automatically)
		 * @param {Object=} opts Optional parameters
		 * @param {String=} opts.uploadKey Get a new set of signed urls if the ones that were generated before have already expired and the user
		 * still needs to upload some of them.
		 * @param {Integer=} [opts.firstPart=1] For a multipart upload, is the starting index when getting upload part URL.
		 * If this parameter is not specified the default value is firstPart = 1.
		 * Example: To retrieve the parts from 10 to 15 you should pass firstPart = 10 and parts = 6, this will retrieve the parts 10, 11, 12, 13, 14 and 15.
		 * @param {Integer=} [opts.parts=1] For a multipart upload, is the starting index when getting upload part URL.
		 * If this parameter is not specified the default value is firstPart = 1.
		 * Example: To retrieve the parts from 10 to 15 you should pass firstPart = 10 and parts = 6, this will retrieve the parts 10, 11, 12, 13, 14 and 15.
		 * @param {Boolean=true} opts.useAcceleration Whether or not to generate an accelerated signed URL (ie: URLs of
		 * the form …s3-accelerate.amazonaws.com… vs …s3.amazonaws.com…).
		 * When not specified, defaults to true. Providing non-boolean values will result in a 400 error.
		 * @param {Integer=2} opts.minutesExpiration The custom expiration time within the 1 to 60 minutes range, if not specified, default is 2 minutes.
		 * @param {Object} oauth2client oauth2client for the call
		 * @param {Object} credentials credentials for the call
		 * @remark https://forge.autodesk.com/en/docs/data/v2/reference/http/buckets-:bucketKey-objects-:objectKey-signeds3upload-GET/
		 * @async
		 * @returns {Promise<any>}
		 */
		this.getS3UploadURL = function (bucketKey, objectKey, opts, oauth2client, credentials) {
			opts = opts || {};
			const postBody = null;

			// verify the required parameter 'bucketKey' is set
			if (!bucketKey)
				return (Promise.reject("Missing the required parameter 'bucketKey' when calling getS3UploadURL"));
			// verify the required parameter 'objectKey' is set
			if (!objectKey)
				return (Promise.reject("Missing the required parameter 'objectKey' when calling getS3UploadURL"));

			const pathParams = {
				bucketKey,
				objectKey,
			};
			const queryParams = {
				uploadKey: opts.uploadKey,
				firstPart: opts.firstPart || 1,
				parts: opts.parts || 1,
				useAcceleration: opts.useAcceleration,
				minutesExpiration: opts.minutesExpiration || 2,
			};
			const headerParams = {};
			const formParams = {};

			const contentTypes = ['application/json'];
			const accepts = ['application/vnd.api+json', 'application/json'];
			const returnType = Object;

			return (this.apiClient.callApi(
				'/oss/v2/buckets/{bucketKey}/objects/{objectKey}/signeds3upload', 'GET',
				pathParams, queryParams, headerParams, formParams, postBody,
				contentTypes, accepts, returnType, oauth2client, credentials
			));
		};

		/**
		 * Instructs OSS to complete the object creation process after the bytes have been uploaded directly to S3.
		 * @param {String} bucketKey bucket key (will be URL-encoded automatically)
		 * @param {String} objectKey object key (will be URL-encoded automatically)
		 * @param {Object} body
		 * @param {String} body.uploadKey The identifier of the upload session, which was provided by OSS in the response to the Get Upload URL/s request.
		 * @param {Integer=} body.size The expected size of the uploaded object. If provided, OSS will check this against the blob in S3 and return
		 * an error if the size does not match.
		 * @param {String[]=} body.eTags An array of eTags. S3 returns an eTag to each upload request, be it for a chunk or an entire file.
		 * For a single-part upload, this array contains the expected eTag of the entire object. For a multipart upload, this array contains the expected
		 * eTag of each part of the upload; the index of an eTag in the array corresponds to its part number in the upload. If provided, OSS will validate
		 * these eTags against the content in S3, and return an error if the eTags do not match (indicating some form of data corruption).
		 * @param {Object=} opts Optional parameters
		 * @param {String=} opts.xAdsMetaContentType (x-ads-meta-Content-Type) The Content-Type value that OSS will store in the record for the uploaded object.
		 * @param {String=} opts.xAdsMetaContentDisposition (x-ads-meta-Content-Disposition) The Content-Disposition value that OSS will store in the record for the uploaded object.
		 * @param {String=} opts.xAdsMetaContentEncoding (x-ads-meta-Content-Encoding) The Content-Encoding value that OSS will store in the record for the uploaded object.
		 * @param {String=} opts.xAdsMetaCacheControl (x-ads-meta-Cache-Control) The Cache-Control value that OSS will store in the record for the uploaded object.
		 * @param {Object} oauth2client oauth2client for the call
		 * @param {Object} credentials credentials for the call
		 * @remark https://forge.autodesk.com/en/docs/data/v2/reference/http/buckets-:bucketKey-objects-:objectKey-signeds3upload-POST/
		 * @async
		 * @returns {Promise<any>}
		 */
		this.completeS3Upload = function (bucketKey, objectKey, body, opts, oauth2client, credentials) {
			opts = opts || {};
			const postBody = body;

			// verify the required parameter 'bucketKey' is set
			if (!bucketKey)
				return (Promise.reject("Missing the required parameter 'bucketKey' when calling completeS3Upload"));
			// verify the required parameter 'objectKey' is set
			if (!objectKey)
				return (Promise.reject("Missing the required parameter 'objectKey' when calling completeS3Upload"));

			// verify the required parameter 'body' is set
			if (!body)
				return (Promise.reject("Missing the required parameter 'body' when calling completeS3Upload"));
			// if (!body.eTags)
			// 	return (Promise.reject("Missing the required parameter 'eTags' when calling completeS3Upload"));
			if (body.eTags && (!Array.isArray(body.eTags) || body.eTags.length === 0))
				return (Promise.reject("Invalid 'body.eTags' parameter when calling completeS3Upload"));
			if (!body.uploadKey)
				return (Promise.reject("Missing the required parameter 'body.uploadKey' when calling completeS3Upload"));

			const pathParams = {
				bucketKey,
				objectKey,
			};
			const queryParams = {
				useCdn: opts.useCdn,
			};
			const headerParams = {
				'x-ads-meta-Content-Type': opts.xAdsMetaContentType || opts['x-ads-meta-Content-Type'],
				'x-ads-meta-Content-Disposition': opts.xAdsMetaContentDisposition || opts['x-ads-meta-Content-Disposition'],
				'x-ads-meta-Content-Encoding': opts.xAdsMetaContentEncoding || opts['x-ads-meta-Content-Encoding'],
				'x-ads-meta-Cache-Control': opts.xAdsMetaCacheControl || opts['x-ads-meta-Cache-Control'],
			};
			const formParams = {};

			const contentTypes = ['application/json'];
			const accepts = ['application/vnd.api+json', 'application/json'];
			const returnType = ObjectCompleteS3Upload;

			return (this.apiClient.callApi(
				'/oss/v2/buckets/{bucketKey}/objects/{objectKey}/signeds3upload', 'POST',
				pathParams, queryParams, headerParams, formParams, postBody,
				contentTypes, accepts, returnType, oauth2client, credentials
			));
		};

		/**
		 * Instructs OSS to complete the object creation process for numerous objects after their bytes have been uploaded directly to S3.
		 * @param {String} bucketKey bucket key (will be URL-encoded automatically)
		 * @param {Object} body body parameter
		 * @param {Object[]} body.requests An array of objects representing each request to get an S3 URL to download from.
		 * @param {String} body.requests[].objectKey The key/name of the object for which to complete an upload.
		 * @param {String} body.requests[].uploadKey The identifier of the upload session, which was provided by OSS in the response to the Get Upload URL/s request.
		 * @param {Integer=} body.requests[].size The expected size of the uploaded object. If provided, OSS will check this against the blob in S3 and return
		 * an error if the size does not match.
		 * @param {String[]=} body.requests[].eTags An array of eTags. S3 returns an eTag to each upload request, be it for a chunk or an entire file.
		 * For a single-part upload, this array contains the expected eTag of the entire object. For a multipart upload, this array contains the expected
		 * eTag of each part of the upload; the index of an eTag in the array corresponds to its part number in the upload. If provided, OSS will validate
		 * these eTags against the content in S3, and return an error if the eTags do not match (indicating some form of data corruption).
		 * @param {String=} body.requests[].xAdsMetaContentType (x-ads-meta-Content-Type) The Content-Type value that OSS will store in the record for the uploaded object.
		 * @param {String=} body.requests[].xAdsMetaContentDisposition (x-ads-meta-Content-Disposition) The Content-Disposition value that OSS will store in the record for the uploaded object.
		 * @param {String=} body.requests[].xAdsMetaContentEncoding (x-ads-meta-Content-Encoding) The Content-Encoding value that OSS will store in the record for the uploaded object.
		 * @param {String=} body.requests[].xAdsMetaCacheControl (x-ads-meta-Cache-Control) The Cache-Control value that OSS will store in the record for the uploaded object.
		 * @param {Object=} opts Optional parameters
		 * @param {Object} oauth2client oauth2client for the call
		 * @param {Object} credentials credentials for the call
		 * @remark https://forge.autodesk.com/en/docs/data/v2/reference/http/buckets-:bucketKey-objects-:objectKey-batchcompleteupload-POST/
		 * @async
		 * @returns {Promise<any>}
		 */
		this.completeS3Uploads = function (bucketKey, body, opts, oauth2client, credentials) {
			opts = opts || {};
			const postBody = body;

			// verify the required parameter 'bucketKey' is set
			if (!bucketKey)
				return (Promise.reject("Missing the required parameter 'bucketKey' when calling completeS3Upload"));
			// verify the required parameter 'body' is set
			if (!body)
				return (Promise.reject("Missing the required parameter 'body' when calling completeS3Upload"));
			if (!body.requests)
				return (Promise.reject("Missing the required parameter 'body.requests' when calling completeS3Uploads"));
			if (body.requests && (!Array.isArray(body.requests) || body.requests.length === 0))
				return (Promise.reject("Invalid 'body.requests' parameter when calling completeS3Uploads"));
			for (let i = 0; i < body.requests.length; i++) {
				if (!body.requests[i].objectKey)
					return (Promise.reject("Missing the required parameter 'body.requests[].objectKey' when calling completeS3Uploads"));
				if (!body.requests[i].uploadKey)
					return (Promise.reject("Missing the required parameter 'body.requests[].uploadKey' when calling completeS3Uploads"));
				if (body.requests[i].eTags && (!Array.isArray(body.requests[i].eTags) || body.requests[i].eTags.length === 0))
					return (Promise.reject("Invalid 'body.requests[i].eTags' parameter when calling completeS3Upload"));
				body.requests[i]['x-ads-meta-Content-Type'] = body.requests[i]['x-ads-meta-Content-Type'] || body.requests[i].xAdsMetaContentType;
				delete body.requests[i].xAdsMetaContentType;
				body.requests[i]['x-ads-meta-Content-Disposition'] = body.requests[i]['x-ads-meta-Content-Disposition'] || body.requests[i].xAdsMetaContentDisposition;
				delete body.requests[i].xAdsMetaContentDisposition;
				body.requests[i]['x-ads-meta-Content-Encoding'] = body.requests[i]['x-ads-meta-Content-Encoding'] || body.requests[i].xAdsMetaContentEncoding;
				delete body.requests[i].xAdsMetaContentEncoding;
				body.requests[i]['x-ads-meta-Cache-Control'] = body.requests[i]['x-ads-meta-Cache-Control'] || body.requests[i].xAdsMetaCacheControl;
				delete body.requests[i].xAdsMetaCacheControl;
			}

			const pathParams = {
				bucketKey,
			};
			const queryParams = {};
			const headerParams = {};
			const formParams = {};

			const contentTypes = ['application/json'];
			const accepts = ['application/vnd.api+json', 'application/json'];
			const returnType = Object;

			return (this.apiClient.callApi(
				'/oss/v2/buckets/{bucketKey}/objects/batchcompleteupload', 'POST',
				pathParams, queryParams, headerParams, formParams, postBody,
				contentTypes, accepts, returnType, oauth2client, credentials
			));
		};

		/**
		 * Requests a batch of S3 signed URL with which to upload multiple objects or chunks of multiple objects.
		 * @param {String} bucketKey bucket key (will be URL-encoded automatically)
		 * @param {Object} body body parameter
		 * @param {Object[]} body.requests An array of objects representing each request to get an S3 URL to download from.
		 * @param {String} body.requests[].objectKey The key/name of the object for which to create an S3 upload URL. If neither the “part” nor “parts”
		 * attribute is provided, OSS will return a single upload URL with which to upload the entire object.
		 * @param {String=} body.requests[].uploadKey Get a new set of signed urls if the ones that were generated before have already expired and the user
		 * still needs to upload some of them.
		 * @param {Integer=} [body.requests[].firstPart=1] For a multipart upload, is the starting index when getting upload part URL.
		 * If this parameter is not specified the default value is firstPart = 1.
		 * Example: To retrieve the parts from 10 to 15 you should pass firstPart = 10 and parts = 6, this will retrieve the parts 10, 11, 12, 13, 14 and 15.
		 * @param {Integer=} [body.requests[].parts=1] For a multipart upload, is the starting index when getting upload part URL.
		 * If this parameter is not specified the default value is firstPart = 1.
		 * Example: To retrieve the parts from 10 to 15 you should pass firstPart = 10 and parts = 6, this will retrieve the parts 10, 11, 12, 13, 14 and 15.
		 * @param {Object=} opts Optional parameters
		 * @param {Boolean=} [opts.useAcceleration=true] Whether or not to generate an accelerated signed URL (ie: URLs of
		 * the form …s3-accelerate.amazonaws.com… vs …s3.amazonaws.com…).
		 * When not specified, defaults to true. Providing non-boolean values will result in a 400 error.
		 * @param {Integer=} [opts.minutesExpiration=2] The custom expiration time within the 1 to 60 minutes range, if not specified, default is 2 minutes.
		 * @param {Object} oauth2client oauth2client for the call
		 * @param {Object} credentials credentials for the call
		 * @remark https://forge.autodesk.com/en/docs/data/v2/reference/http/buckets-:bucketKey-objects-:objectKey-batchsigneds3upload-POST/
		 * @async
		 * @returns {Promise<any>}
		 */
		this.getS3UploadURLs = function (bucketKey, body, opts, oauth2client, credentials) {
			opts = opts || {};
			const postBody = body;

			// verify the required parameter 'bucketKey' is set
			if (!bucketKey)
				return (Promise.reject("Missing the required parameter 'bucketKey' when calling getS3UploadURLs"));
			// verify the required parameter 'body' is set
			if (!body)
				return (Promise.reject("Missing the required parameter 'body' when calling getS3UploadURLs"));
			if (!body.requests || !Array.isArray(body.requests) || body.requests.length === 0)
				return (Promise.reject("Missing the required parameter 'body.requests' when calling getS3UploadURLs"));
			for (let i = 0; i < body.requests.length; i++) {
				if (!body.requests[i].objectKey)
					return (Promise.reject("Missing the required parameter 'body.requests[].objectKey' when calling getS3UploadURLs"));
			}

			const pathParams = {
				bucketKey,
			};
			const queryParams = {
				useAcceleration: opts.useAcceleration === false ? opts.useAcceleration : true,
				minutesExpiration: opts.minutesExpiration || 2,
			};
			const headerParams = {};
			const formParams = {};

			const contentTypes = ['application/json'];
			const accepts = ['application/vnd.api+json', 'application/json'];
			const returnType = Object;

			return (this.apiClient.callApi(
				'/oss/v2/buckets/{bucketKey}/objects/batchsigneds3upload', 'POST',
				pathParams, queryParams, headerParams, formParams, postBody,
				contentTypes, accepts, returnType, oauth2client, credentials
			));
		};

		// Workflow implementations

		/**
		 * Download a resource.
		 * @param {String} bucketKey bucket key (will be URL-encoded automatically)
		 * @param {Object|Object[]} objects Object or Object array of resource to uplaod with their parameters
		 * @param {String} object[].objectKey object key
		 * @param {String} object[].responseType Resource to upload
		 * If String, it is the expected response type (defaults to json) ['arraybuffer', 'document', 'json', 'text', 'stream']
		 * If you 'stream', you need to provide a writable stream ('data'), the method will pipe content into it.
		 * @param {Object=} opts Optional parameters
		 * @param {Boolean=} [opts.publicResourceFallback=false] Allows fallback to OSS signed URLs in case of unmerged resumable uploads.
		 * @param {Boolean=} [opts.useCdn=true] Will generate a CloudFront URL for the S3 object.
		 * @param {Integer=} [opts.minutesExpiration=2] The custom expiration time within the 1 to 60 minutes range, if not specified, default is 2 minutes.
		 * @param {Integer=0} opts.chunkSize Chunk size in Mb. Should not be below 5Mb. Default is 0, download file in one piece.
		 * @param {Function=} opts.onDownloadProgress (progressEvent) => {}
		 * @param {Function=} opts.onRefreshToken () => {}
		 * @param {Object} oauth2client oauth2client for the call
		 * @param {Object} credentials credentials for the call
		 * @async
		 * @returns {Promise<any[]>}
		 */
		this.downloadResources = async function (bucketKey, objects, opts, auth2client, credentials) {
			const _this = this;
			opts = opts || {};
			if (auth2client)
				auth2client.setCredentials(credentials);

			const isWritableStream = ApiClient.isWritableStream;

			if (!bucketKey)
				return (Promise.reject("Missing the required parameter 'bucketKey' when calling downloadResources"));
			if (!objects)
				return (Promise.reject("Missing the required parameter 'objects' when calling downloadResources"));
			if (!Array.isArray(objects))
				objects = [objects];
			for (let i = 0; i < objects.length; i++) {
				if (!objects[i].objectKey)
					return (Promise.reject("Missing the required parameter 'objects[].objectKey' when calling downloadResources"));
				if (!objects[i].responseType)
					return (Promise.reject("Missing the required parameter 'objects[].responseType' when calling downloadResources"));
				if (objects[i].responseType === 'stream' && (!objects[i].data || !isWritableStream(objects[i].data)))
					return (Promise.reject("Missing the required parameter 'objects[].data' when calling downloadResources with responseType === 'stream'"));
			}

			const requestSize = async (bucketKey, objects) => {
				try {
					if (opts.onRefreshToken
						&& ((auth2client && auth2client.isAboutToExpire())
							|| (credentials.expires_at && credentials.expires_at - 300000 < Date.now())
						)
					) {
						let temp = await opts.onRefreshToken();
						if (temp)
							credentials = temp;
						if (auth2client)
							auth2client.setCredentials(credentials);
					}
					const requests = objects.map((entry) => ({ objectKey: entry.objectKey, }));
					const downloadParams = await _this.getS3DownloadURLs(
						bucketKey,
						{ requests },
						opts,
						auth2client, credentials
					); // Automatically retries 429 and 500-599 responses

					const size = Object.keys(downloadParams.body.results).map((objectKey) => {
						return (downloadParams.body.results[objectKey].status === 'complete' ?
							downloadParams.body.results[objectKey].size
							: 0
						);
					}).reduce((total, len) => total + len, 0);
					return (size);
				} catch (ex) { }
				return (0);
			};

			const requestURL = async (bucketKey, record) => {
				try {
					if (opts.onRefreshToken
						&& ((auth2client && auth2client.isAboutToExpire())
							|| (credentials.expires_at && credentials.expires_at - 300000 < Date.now())
						)
					) {
						let temp = await opts.onRefreshToken();
						if (temp)
							credentials = temp;
						if (auth2client)
							auth2client.setCredentials(credentials);
					}
					record.downloadParams = await _this.getS3DownloadURLs(
						bucketKey,
						{ requests: [{ objectKey: record.objectKey, }], },
						opts,
						auth2client, credentials
					); // Automatically retries 429 and 500-599 responses
					if (record.downloadParams.body.results[record.objectKey].status === 'complete')
						record.downloadUrl = record.downloadParams.body.results[record.objectKey].url;
					else
						record.error = true;
				} catch (ex) {
					record.error = true;
					record.download = ex;
				}
				return (record);
			};

			const pipeItAll = async (data, to) => {
				return (new Promise((resolve, reject) => {
					data.pipe(to);
					let error = null;
					to.on('error', (err) => {
						error = err;
						to.close();
						reject(err);
					});
					to.on('close', () => {
						if (!error)
							resolve(true);
						// no need to call the reject here, as it will have been called in the 'error' stream;
					});
				}));
			};

			const downloadData = async (record) => {
				try {
					if (record.error || !record.downloadUrl)
						return;
					const isStreamEntry = isWritableStream(record.data);

					// `responseType` indicates the type of data that the server will respond with
					// options are: 'arraybuffer', 'json', 'text', 'stream' (default json)
					// 'stream' with response.data.pipe(__fs.createWriteStream('...'))

					let responseType = 'json'; // jshint ignore:line
					if (isStreamEntry)
						responseType = 'stream';
					else if (typeof record.responseType === 'string')
						responseType = record.responseType;

					const headers = {}
					// Object.keys(record).map((key) => {
					// 	if (key.startsWith('response-'))
					// 		headers[key.substring(9)] = record[key];
					// });

					record.download = await axios({
						method: 'GET',
						url: record.downloadUrl,
						maxContentLength: Infinity,
						maxBodyLength: Infinity,
						headers,
						responseType,

						// onDownloadProgress: (progressEvent) => {
						// 	let percentComplete = progressEvent.loaded / progressEvent.total
						// 	percentComplete = parseInt(percentComplete * 100);
						// 	console.log(percentComplete);
						// },

					});
					if (isStreamEntry)
						await pipeItAll(record.download.data, record.data);
					else
						record.data = record.download.data;
					return (record.download);
				} catch (err) {
					record.error = true;
					record.download = err;
				}
			};

			const startTS = Date.now();
			opts.onDownloadProgress && opts.onDownloadProgress({ progress: 0, elapsed: 0, objects, }); // jshint ignore:line
			const totalSize = await requestSize(bucketKey, objects);
			let dataRead = 0;
			for (let entry = 0; entry < objects.length; entry++) {
				const record = objects[entry];
				const isStreamTarget = isWritableStream(record.data);

				await requestURL(bucketKey, record);
				await downloadData(record);

				dataRead += record.downloadParams.body.results[record.objectKey].size || 0;
				record.progress = 1;
				opts.onDownloadProgress && opts.onDownloadProgress({
					progress: dataRead / totalSize,
					elapsed: Date.now() - startTS,
					objects,
				}); // jshint ignore:line
			}
			opts.onDownloadProgress && opts.onDownloadProgress({
				progress: 1,
				elapsed: Date.now() - startTS,
				//objects,
			}); // jshint ignore:line
			return (objects);
		};

		/**
		 * Upload a resource. If the specified object name already exists in the bucket, the uploaded content will overwrite the existing content for the bucket name/object name combination.
		 * @param {String} bucketKey bucket key (will be URL-encoded automatically)
		 * @param {Object|Object[]} objects Object or Object array of resource to uplaod with their parameters
		 * @param {String} object[].objectKey object key
		 * @param {String|Buffer|Stream} object[].data Resource to upload (String| Buffer | Stream)
		 * @param {String[]=} object[].eTags An array of eTags. S3 returns an eTag to each upload request, be it for a chunk or an entire file.
		 * For a single-part upload, this array contains the expected eTag of the entire object. For a multipart upload, this array contains the expected
		 * eTag of each part of the upload; the index of an eTag in the array corresponds to its part number in the upload. If provided, OSS will validate
		 * these eTags against the content in S3, and return an error if the eTags do not match (indicating some form of data corruption).
		 * @param {String=} object[].xAdsMetaContentType (x-ads-meta-Content-Type) The Content-Type value that OSS will store in the record for the uploaded object.
		 * @param {String=} object[].xAdsMetaContentDisposition (x-ads-meta-Content-Disposition) The Content-Disposition value that OSS will store in the record for the uploaded object.
		 * @param {String=} object[].xAdsMetaContentEncoding (x-ads-meta-Content-Encoding) The Content-Encoding value that OSS will store in the record for the uploaded object.
		 * @param {String=} object[].xAdsMetaCacheControl (x-ads-meta-Cache-Control) The Cache-Control value that OSS will store in the record for the uploaded object.
		 * @param {Object=} opts Optional parameters
		 * @param {Integer=5} chunkSize Chunk size in Mb. Should not be below 5Mb.
		 * @param {Integer=25} maxBatches Maximum batch to produces. Should not be above 25 or below 1.
		 * @param {Boolean=true} opts.useAcceleration Whether or not to generate an accelerated signed URL (ie: URLs of
		 * the form …s3-accelerate.amazonaws.com… vs …s3.amazonaws.com…).
		 * When not specified, defaults to true. Providing non-boolean values will result in a 400 error.
		 * @param {Integer=2} opts.minutesExpiration The custom expiration time within the 1 to 60 minutes range, if not specified, default is 2 minutes.
		 * @param {Function=} opts.onUploadProgress (progressEvent) => {}
		 * @param {Function=} opts.onRefreshToken () => {}
		 * @param {Object} oauth2client oauth2client for the call
		 * @param {Object} credentials credentials for the call
		 * @async
		 * @returns {Promise<any[]>}
		 */
		this.uploadResources = async function (bucketKey, objects, opts, auth2client, credentials) {
			const _this = this;
			opts = opts || {};
			if (auth2client)
				auth2client.setCredentials(credentials);

			const isReadableStream = ApiClient.isReadableStream;

			if (!bucketKey)
				return (Promise.reject("Missing the required parameter 'bucketKey' when calling uploadResources"));
			if (!objects)
				return (Promise.reject("Missing the required parameter 'objects' when calling uploadResources"));
			if (!Array.isArray(objects))
				objects = [objects];
			for (let i = 0; i < objects.length; i++) {
				if (!objects[i].objectKey)
					return (Promise.reject("Missing the required parameter 'objects[].objectKey' when calling uploadResources"));
				if (!objects[i].data)
					return (Promise.reject("Missing the required parameter 'objects[].data' when calling uploadResources"));
				if (typeof objects[i].data !== 'string' && !Buffer.isBuffer(objects[i].data) && !isReadableStream(objects[i].data))
					return (Promise.reject("Required parameter 'objects[].data' is neither a string, Buffer, or Stream when calling uploadResources"));
			}
			if (opts && opts.chunkSize && opts.chunkSize < 5)
				return (Promise.reject("Required parameter 'opts.chunkSize' should be >= 5 when calling uploadResources"));
			if (opts && opts.maxBatches && (opts.maxBatches < 1 || opts.maxBatches > 25))
				return (Promise.reject("Required parameter 'opts.maxBatches' should be >= 1 and <= 25 when calling uploadResources"));

			const requestURLs = async (bucketKey, record, firstPart, parts) => {
				try {
					if (opts.onRefreshToken
						&& ((auth2client && auth2client.isAboutToExpire())
							|| (credentials.expires_at && credentials.expires_at - 300000 < Date.now())
						)
					) {
						let temp = await opts.onRefreshToken();
						if (temp)
							credentials = temp;
						if (auth2client)
							auth2client.setCredentials(credentials);
					}
					const uploadParams = await _this.getS3UploadURLs(
						bucketKey,
						{ requests: [{ objectKey: record.objectKey, uploadKey: record.uploadKey, firstPart, parts, }], },
						opts,
						auth2client, credentials
					); // Automatically retries 429 and 500-599 responses
					record.uploadUrls = uploadParams.body.results[record.objectKey].urls.slice();
					if (record.uploadKey === undefined)
						record.uploadKey = uploadParams.body.results[record.objectKey].uploadKey;
				} catch (ex) {
					record.error = true;
					record.uploads = ex;
				}
				return (record);
			};

			const completeObjects = async (bucketKey, record) => {
				try {
					if (opts.onRefreshToken
						&& ((auth2client && auth2client.isAboutToExpire())
							|| (credentials.expires_at && credentials.expires_at - 300000 < Date.now())
						)
					) {
						let temp = await opts.onRefreshToken();
						if (temp)
							credentials = temp;
						if (auth2client)
							auth2client.setCredentials(credentials);
					}
					record.completedResponse = await _this.completeS3Uploads(
						bucketKey,
						{
							requests: [
								{
									objectKey: record.objectKey,
									uploadKey: record.uploadKey,
									'Content-Type': (record.contentType || record['Content-Type']),
									eTags: record.eTags,
									'x-ads-meta-Content-Type': record.xAdsMetaContentType || record['x-ads-meta-Content-Type'],
									'x-ads-meta-Content-Disposition': record.xAdsMetaContentDisposition || record['x-ads-meta-Content-Disposition'],
									'x-ads-meta-Content-Encoding': record.xAdsMetaContentEncoding || record['x-ads-meta-Content-Encoding'],
									'x-ads-meta-Cache-Control': record.xAdsMetaCacheControl || record['x-ads-meta-Cache-Control'],
								},
							],
						},
						opts,
						auth2client, credentials
					);
					//console.log(record.completed);
					delete record.uploadUrls;
					record.completed = record.completedResponse.body.results[record.objectKey];
					if (record.completed.status === 'error')
						record.error = true;
				} catch (ex) {
					record.error = true;
					record.completed = ex;
				}
				return (record);
			};

			const uploadData = async (url, record) => {
				try {
					// `responseType` indicates the type of data that the server will respond with
					// options are: 'arraybuffer', 'document', 'json', 'text', 'stream' (default json)

					const res = await axios({
						method: 'PUT',
						url,
						maxContentLength: Infinity,
						maxBodyLength: Infinity,
						data: record.chunk,
						// onUploadProgress: (progressEvent) => {
						// 	let percentComplete = progressEvent.loaded / progressEvent.total;
						// 	percentComplete = parseInt(percentComplete * 100);
						// 	console.log(percentComplete);
						// },
						// transformRequest: [(data, headers) => { return (data); }],
					});
					record.eTags = record.eTags || [];
					record.eTags.push(res.headers.etag.replace(/"/g, ''));
					return (res);
				} catch (err) {
					const status = err.response && err.response.status;
					if (status === 403 /* FORBIDDEN */) {
						_this.apiClient.debug('Got 403, refreshing upload URLs');
						record.uploadUrls = []; // Couldn't this cause an infinite loop? (i.e., could the server keep responding with 403 - FORBIDDEN indefinitely?)
					} else {
						throw err;
					}
				}
				return (false);
			};

			async function* bufferChunks (input, minChunkSize) { // jshint ignore:line
				const buffer = Buffer.alloc(2 * minChunkSize);
				let bytesRead = 0;
				for await (const chunk of input) {
					chunk.copy(buffer, bytesRead);
					bytesRead += chunk.byteLength;
					if (bytesRead >= minChunkSize) {
						yield buffer.slice(0, bytesRead);
						bytesRead = 0;
					}
				}
				if (bytesRead > 0)
					yield buffer.slice(0, bytesRead);
			}

			const processChunk = async (bucketKey, record, partsUploaded, totalParts, MaxBatches) => {
				while (true) {
					_this.apiClient.debug('Uploading part', bucketKey, record.objectKey, partsUploaded + 1, '/', totalParts);
					if (!record.uploadUrls || record.uploadUrls.length === 0)
						await requestURLs(bucketKey, record, partsUploaded + 1, Math.min(totalParts - partsUploaded, MaxBatches));

					const url = record.uploadUrls.shift();
					const res = await uploadData(url, record);
					if (res)
						break;
				}
			};

			const startTS = Date.now();
			const totalSize = objects
				.map((record) => record.length || record.data.length || record.data.byteLength)
				.reduce((total, len) => total + len, 0);
			let dataSent = 0;
			const ChunkSize = opts.chunkSize ? opts.chunkSize << 20 : 5 << 20;
			const MaxBatches = opts.maxBatches || 25;
			opts.onUploadProgress && opts.onUploadProgress({ progress: 0, elapsed: 0, objects, }); // jshint ignore:line
			for (let entry = 0; entry < objects.length; entry++) {
				const record = objects[entry];
				const isStreamEntry = isReadableStream(record.data);
				if (isStreamEntry && !record.length)
					throw new Error('Missing parameter length for a stream object');
				const totalParts = Math.ceil((record.length || record.data.length || record.data.byteLength) / ChunkSize);
				let partsUploaded = 0;
				if (isStreamEntry) {
					for await (const chunk of bufferChunks(record.data, ChunkSize)) {
						record.chunk = chunk;
						await processChunk(bucketKey, record, partsUploaded, totalParts, MaxBatches);
						dataSent += record.chunk.length;
						delete record.chunk;
						_this.apiClient.debug('Part successfully uploaded', partsUploaded + 1);
						partsUploaded++;
						record.progress = partsUploaded / totalParts;
						opts.onUploadProgress && opts.onUploadProgress({
							progress: dataSent / totalSize,
							elapsed: Date.now() - startTS,
							objects,
						}); // jshint ignore:line
					}
				} else {
					while (partsUploaded < totalParts) {
						//record.chunk = record.data;
						record.chunk = record.data.slice(
							partsUploaded * ChunkSize,
							Math.min((partsUploaded + 1) * ChunkSize, record.data.byteLength || record.data.length)
						);
						await processChunk(bucketKey, record, partsUploaded, totalParts, MaxBatches);
						dataSent += (record.chunk.length || record.chunk.byteLength);
						delete record.chunk;
						_this.apiClient.debug('Part successfully uploaded', partsUploaded + 1);
						partsUploaded++;
						record.progress = partsUploaded / totalParts;
						opts.onUploadProgress && opts.onUploadProgress({
							progress: dataSent / totalSize,
							elapsed: Date.now() - startTS,
							objects,
						}); // jshint ignore:line
					}
				}
				_this.apiClient.debug('Completing parts upload');
				await completeObjects(bucketKey, record);
			}
			opts.onUploadProgress && opts.onUploadProgress({
				progress: 1,
				elapsed: Date.now() - startTS,
				//objects,
			}); // jshint ignore:line
			return (objects);
		};

		/**
		 * Calc SHA1
		 * @param {String|Buffer|Stream} strorbuffer Content to calc SHA1 for
		 * @async
		 * @returns {Promise<String>}
		 */
		this.calculateSHA1 = function (strorbufferorstream) {
			return (new Promise((fulfill, reject) => {
				const shasum = crypto.createHash('sha1');
				if (ApiClient.isReadableStream(strorbufferorstream) || ApiClient.isWritableStream(strorbufferorstream)) { // this is a stream
					const tmp = __fs.createReadStream(strorbufferorstream.path);
					tmp.on('end', () => {
						//shasum.end(); .read();
						fulfill(shasum.digest('hex'));
					});
					tmp.pipe(shasum);
					return;
				}
				shasum.update(strorbufferorstream);
				fulfill(shasum.digest('hex'));
			}));
		};

	};

	return exports;
}());
