npm package: @journeyapps-solutions/cc-util-data
Description: Simplified Journey JSON Data
Author: Anton Van Zyl
Latest version: 1.0.21

# cc-util-data

The purpose of this module is to enable cloud code to easily generate a json payload for use in PDF/Excel reports.

Normally you would manually get all the data before you add it to a handlebars template or a Excel report. There is a couple of issues to overcome doing it via the journey DB module. Such as the `object is not extensible` issue. Anyway, to overcome this, this module exists. Its sole purpose is to easily gather the data for your PDF or Excel report without any hassles.

As a added benefit, you can also upload your PDF to the backend for reference at a later stage. 

## Documentation
All code is clearly documented, please see code for more details.
View an example below.

## Installation

#### Per machine

```
# Once as setup
npm login
 > journeyapps-solutions-user
 > see password on 1Password
 > infrastructure+npmsolutions@journeyapps.com

yarn add @journeyapps-solutions/cc-util-data --save
```

#### Per CloudCode app

```
NPM_TOKEN=... # Token for journeyapps-solutions-user from 1Password
echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ./app/cloudcode/.npmrc
```

#### Per CloudCode task

```
yarn add @journeyapps-solutions/cc-util-data
```

## Deploying

```
yarn version
```

## Usage

This might not be apparent at the very start, but there is a specific way of using this to retrieve the correct data.

The main function `fetchData` has 3 parameters:

```js
/**
 * Retrieves the Data of the object with its children.
 * @param {string}   ObjectID       ID of the main object, the main object is the model
 * @param  {string}  model          The Entry model name. Found in <model name="<here>" label="label">
 * @param  {array}   relatedModels  Array of related models by name. Found in <has-many model="<here second>" name="<here first>" /> or <belongs-to model="<here second>" name="<here first>" />
 * @return {JSON}    JSON object representing the data
 */
async fetchData(objectID, model, relatedModels) { ... }

/**
 * Uploads a Attachment to the backend.
 * @param  {JourneyAttachment}  attachment      Journey Apps attachment object
 * @param  {Object}  object                     Destination Object for upload
 * @param  {String}  [field="generated_report"] Field to upload to, defaults to generated_report
 */
async uploadAttachment(attachment, object, field = "generated_report") {
```
It is extremely important to use the name of the relationships and only the model name as a fallback.
The related models do not include the main model as this is derived.

Example fetchData:
```js
let _ccUtilData = new ccUtilData(this.backend); //Pass in the backend object (The tokens live in here)
await _ccUtilData.fetchData('87cbe05a-904a-4255-b020-dd9d9ccedc3e', 'hotel',['rooms', 'rooms.bookings']);
await _ccUtilData.fetchData('87cbe05a-904a-4255-b020-dd9d9ccedc3e', 'booking',['room', 'room.hotel']);
```
Notice the difference between a hotel has_many rooms vs a booking belongs_to a room.

Example uploadAttachment:
```js
let _ccUtilData = new ccUtilData(this.backend); //Pass in the backend object (The tokens live in here)

let _hotel = DB.hotel.first();
let _attachmentContent = await _hotel.broshure.toBase64();

let _attachment = {
  content: _attachmentContent,
  filename: "test.pdf"
}

let _object = DB.hotel.first('87cbe05a-904a-4255-b020-dd9d9ccedc3e');

await _data.uploadAttachment(_attachment, _object, 'broshure');
```



### Cloud Code:

When accessing the Data Model of the current environment the Cloud Code task is running in, we only need to pass in the `this.backend` object, which contains all the config `cc-util-data` requires.

index.js:

```js
// make sure config.js has the relevant credentials
const config = require('./config');
const ccUtilData  = require('@journeyapps-solutions/cc-util-data');

export async function run() {

  const _options = {
    	validateMediaUploaded: true
  };

  let _ccUtilData = new ccUtilData(this.backend); //Pass in the backend object (The tokens live in here)

  var _data = await _ccUtilData.fetchData('87cbe05a-904a-4255-b020-dd9d9ccedc3e', 'hotel',['rooms', 'rooms.bookings']);

  //Do something with the data

}
```

### options
You have the ability to pass in some options, these define some behavior of the module. Below are the defaults.

```js
/**
 * Default options used by the util data
 * @type {Object}
 */
const DEFAULT_OPTIONS = {
		/**
	 * If true, signatures and photos will be validate for state=uploaded
	 * @type {Boolean}
	 */
	validateMediaUploaded: true,
	/**
	 * If true, the util wil auto download the images that it finds.
	 * @type {Boolean}
	 */
	preloadImages: false,
	/**
	 * Sets the default image to pre download
	 * @type {string}   original | fullscreen | thumbnail
	 */
	preloadImageSize: 'fullscreen',
	/**
	 * Set the format to download the pre image in
	 * @type {string}  buffer | base64
	 */
	preLoadImageFormat: 'buffer',
	/**
	 * Sets to false to hide all the logs that get printed
	 * @type {boolean}  
	 */
  printLogs: true,
  /**
	 * Enable sorting of json result for easier navigation
	 */
	sorting: true,
	/**
	 * Items to sort to the top of the list
	 */
	sortToTop: ['id', 'type', 'display']
};
```

### How To:

* ##### Signature/Photo:

  A special flag `ready` gets added on every (not null) photo or signature field that can be used in handlebars to check if the signature has been uploaded. A signature can be directly reference by using a img object and passing in the URL. The PDF document compiler takes care of the rest.

  ```html
  {{#if signature}}
    <div class="signature">
       {{#if signature.ready}}  <img class="img-responsive" src="{{signature.original}}"> {{/if}}
    </div>
  {{/if}}
  ```
* ##### Loading Images

  You are able to preload images by setting the option flat `preloadImages`. You can also retrieve the images afterwards the same way you can with JourneyApps DB.
  Note, `preliadImages` will fail if the image is not ready.

```js
let _d = await _data.fetchData('6e59d067-7d41-4b7b-8794-d9de46b3a2b4', 'room');

// Original
let _url = await _d.photo.url;
let _buffer = await _d.photo.toBuffer();
let _base64 = await _d.photo.toBase64();

// Fullscreen
let _url = await _d.photo.fullscreen.url;
let _buffer = await _d.photo.fullscreen.toBuffer();
let _base64 = await _d.photo.fullscreen.toBase64();

// Thumbnail
let _url = await _d.photo.thumbnail.url;
let _buffer = await _d.photo.thumbnail.toBuffer();
let _base64 = await _d.photo.thumbnail.toBase64();

```

* ##### Adding queries to a `has_many` relationship

  When dealing with a ``has_many`` relationship, you might only want to get the object with specific values.
  The queries are placed at the end of a related object between `{` and `}`. They only work for ``has_many`` relationships and can contain anything you can use with the Backend API. See https://docs.journeyapps.com/reference#api-querying-objects

```js
// make sure config.js has the relevant credentials
const config = require('./config');
const ccUtilData  = require('@journeyapps-solutions/cc-util-data');

export async function run() {

  let _ccUtilData = new ccUtilData(this.backend); //Pass in the backend object (The tokens live in here)


  //Get bookings where the price is greater than 300
  let _data = await _ccUtilData.fetchData('87cbe05a-904a-4255-b020-dd9d9ccedc3e', 'hotel',['rooms', 'rooms.bookings{q[price.gt]=300}']);

  //Get all the bookings for the family room
  _data = await _ccUtilData.fetchData('87cbe05a-904a-4255-b020-dd9d9ccedc3e', 'hotel',['rooms{q[name]=Family}', 'rooms.bookings']);


  //Do something with the data

}
```

* ##### Use a different environment

  We can specify a custom environment by explicitly adding the `url`, `username` and `password` to the configuration.  

  config.js:
  ```js
  // KitchenSink Credentials used
  let config = {
    url: 'https://run-testing-us.journeyapps.com/api/v4/5989b1c2ecc8cf019e5d3db0',
    username: '5989b1c2ecc8cf019e5d3db0',
    password: 'KQy9ENvVkfKfDOXTzcO8'
  }

  ```
  index.js:
  ```js
  // make sure config.js has the relevant credentials
  const config = require('./config');
  const ccUtilData  = require('@journeyapps-solutions/cc-util-data');

  export async function run() {

    let _ccUtilData = new ccUtilData(config); //Pass in the custom config containing the credentials

    var _data = await _ccUtilData.fetchData('87cbe05a-904a-4255-b020-dd9d9ccedc3e', 'hotel',['rooms', 'rooms.bookings']);

    //Do something with the data

  }
  ```

## Testing

```js
yarn test
```