Source: utils/JSONBuilder.js

'use strict';

import Utils from '../utils/Utils.js';

export default class JSONBuilder {
  /**
   * This class helps to create JSON representation from a model.
   * Usage:
   *
   *       return new JSONBuilder()
   *         .appendIfDefined('inReplyToMessageId', this.inReplyToMessageId)
   *         .appendIfDefined('isUnread', this.isUnread)
   *         .appendIfDefined('isFlagged', this.isFlagged)
   *         .build();
   *
   * @constructor
   **/
  constructor() {
    this.result = {};
  }

  /**
   * Will append the _value_ to the building object, using the _name_ as key.
   *
   * @param name {String} The name to use as key.
   * @param value {*} The value to append.
   *
   * @return this builder
   **/
  append(name, value) {
    Utils.assertRequiredParameterIsPresent(name, 'name');
    Utils.assertRequiredParameterIsPresent(value, 'value');

    this.result[name] = value;

    return this;
  }

  /**
   * If defined, it will append the _value_ to the building object, using the _name_ as key.
   *
   * @param name {String} The name to use as key.
   * @param [value] {*} The value to append.
   *
   * @return this builder
   **/
  appendIfDefined(name, value) {
    if (Utils.isDefined(value)) {
      this.append(name, value);
    }

    return this;
  }

  /**
   * If defined, it will append _date_ to the building object as an ISO Date String, using _name_ as the key.
   *
   * @param name {String} The name to use as key.
   * @param [date] {Date} The `Date` to append.
   *
   * @return This {@link JSONBuilder} instance
   *
   * @see http://jmap.io/spec.html#the-date-datatypes
   **/
  appendDateIfDefined(name, date) {
    if (Utils.isDefined(date)) {
      Utils.assertRequiredParameterHasType(date, 'date', Date);

      this.append(name, date.toISOString().replace(/.\d+Z/, 'Z')); // Milliseconds should not be there, as per https://tools.ietf.org/html/rfc3339
    }

    return this;
  }

  /**
   * If defined and not empty, it will append the _value_ array to the building object, using the _name_ as key.
   *
   * @param name {String} The name to use as key.
   * @param [value] {Array} The value to append.
   *
   * @return this builder
   **/
  appendIfNotEmpty(name, value) {
    if (value) {
      Utils.assertRequiredParameterIsArrayWithMinimumLength(value, name);
      if (value.length > 0) {
        this.append(name, value.map(item => item.toJSONObject ? item.toJSONObject() : item));
      }
    }

    return this;
  }

  /**
   * Will append the _value_ object to the building object by recursively calling toJSONObject()
   * on each child if an object.
   *
   * @param name {String} The name to use as key.
   * @param value {Array} The `Object` to append
   */
  appendObject(name, value) {
    let key, obj = {};

    Utils.assertRequiredParameterIsObject(value, 'value');

    for (key in value) {
      if (value.hasOwnProperty(key)) {
        if (typeof value[key] === 'object' && value[key].toJSONObject) {
          obj[key] = value[key].toJSONObject();
        } else {
          obj[key] = value[key];
        }
      }
    }

    this.append(name, obj);

    return this;
  }

  /**
   * @return an object with all appended values
   **/
  build() {
    return this.result;
  }

}