Source: Adaptor.js

"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.relationship = relationship;
exports.execute = execute;
exports.steps = steps;
Object.defineProperty(exports, "alterState", {
  enumerable: true,
  get: function () {
    return _languageCommon.alterState;
  }
});
Object.defineProperty(exports, "arrayToString", {
  enumerable: true,
  get: function () {
    return _languageCommon.arrayToString;
  }
});
Object.defineProperty(exports, "beta", {
  enumerable: true,
  get: function () {
    return _languageCommon.beta;
  }
});
Object.defineProperty(exports, "combine", {
  enumerable: true,
  get: function () {
    return _languageCommon.combine;
  }
});
Object.defineProperty(exports, "dataPath", {
  enumerable: true,
  get: function () {
    return _languageCommon.dataPath;
  }
});
Object.defineProperty(exports, "dataValue", {
  enumerable: true,
  get: function () {
    return _languageCommon.dataValue;
  }
});
Object.defineProperty(exports, "dateFns", {
  enumerable: true,
  get: function () {
    return _languageCommon.dateFns;
  }
});
Object.defineProperty(exports, "each", {
  enumerable: true,
  get: function () {
    return _languageCommon.each;
  }
});
Object.defineProperty(exports, "field", {
  enumerable: true,
  get: function () {
    return _languageCommon.field;
  }
});
Object.defineProperty(exports, "fields", {
  enumerable: true,
  get: function () {
    return _languageCommon.fields;
  }
});
Object.defineProperty(exports, "fn", {
  enumerable: true,
  get: function () {
    return _languageCommon.fn;
  }
});
Object.defineProperty(exports, "http", {
  enumerable: true,
  get: function () {
    return _languageCommon.http;
  }
});
Object.defineProperty(exports, "humanProper", {
  enumerable: true,
  get: function () {
    return _languageCommon.humanProper;
  }
});
Object.defineProperty(exports, "index", {
  enumerable: true,
  get: function () {
    return _languageCommon.index;
  }
});
Object.defineProperty(exports, "join", {
  enumerable: true,
  get: function () {
    return _languageCommon.join;
  }
});
Object.defineProperty(exports, "lastReferenceValue", {
  enumerable: true,
  get: function () {
    return _languageCommon.lastReferenceValue;
  }
});
Object.defineProperty(exports, "map", {
  enumerable: true,
  get: function () {
    return _languageCommon.map;
  }
});
Object.defineProperty(exports, "merge", {
  enumerable: true,
  get: function () {
    return _languageCommon.merge;
  }
});
Object.defineProperty(exports, "referencePath", {
  enumerable: true,
  get: function () {
    return _languageCommon.referencePath;
  }
});
Object.defineProperty(exports, "scrubEmojis", {
  enumerable: true,
  get: function () {
    return _languageCommon.scrubEmojis;
  }
});
Object.defineProperty(exports, "source", {
  enumerable: true,
  get: function () {
    return _languageCommon.source;
  }
});
Object.defineProperty(exports, "sourceValue", {
  enumerable: true,
  get: function () {
    return _languageCommon.sourceValue;
  }
});
Object.defineProperty(exports, "toArray", {
  enumerable: true,
  get: function () {
    return _languageCommon.toArray;
  }
});
exports.reference = exports.update = exports.upsertIf = exports.upsert = exports.createIf = exports.create = exports.destroy = exports.bulk = exports.query = exports.retrieve = exports.describe = void 0;

var _languageCommon = require("@openfn/language-common");

var _jsforce = _interopRequireDefault(require("jsforce"));

var _lodashFp = require("lodash-fp");

var _axios = _interopRequireDefault(require("axios"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/** @module Adaptor */

/**
 * @typedef {Object} State
 * @property {object} data JSON Data.
 * @property {Array<Reference>} references History of all previous operations.
 */

/**
 * @typedef {Function} Operation
 * @param {State} state
 */

/**
 * Adds a lookup relation or 'dome insert' to a record.
 * @public
 * @example
 * Data Sourced Value:
 *  relationship("relationship_name__r", "externalID on related object", dataSource("path"))
 * Fixed Value:
 *  relationship("relationship_name__r", "externalID on related object", "hello world")
 * @constructor
 * @param {string} relationshipName - `__r` relationship field on the record.
 * @param {string} externalId - Salesforce ExternalID field.
 * @param {string} dataSource - resolvable source.
 * @returns {object}
 */
function relationship(relationshipName, externalId, dataSource) {
  return (0, _languageCommon.field)(relationshipName, state => {
    if (typeof dataSource == 'function') {
      return {
        [externalId]: dataSource(state)
      };
    }

    return {
      [externalId]: dataSource
    };
  });
}
/**
 * Outputs basic information about an sObject to `STDOUT`.
 * @public
 * @example
 * describe('obj_name')
 * @constructor
 * @param {String} sObject - API name of the sObject.
 * @param {State} state - Runtime state.
 * @returns {State}
 */


const describe = (0, _lodashFp.curry)(function (sObject, state) {
  let {
    connection
  } = state;
  return connection.sobject(sObject).describe().then(result => {
    console.log('Label : ' + result.label);
    console.log('Num of Fields : ' + result.fields.length);
    return { ...state,
      references: [result, ...state.references]
    };
  });
});
/**
 * Retrieves a Salesforce sObject(s).
 * @public
 * @example
 * retrieve('ContentVersion', '0684K0000020Au7QAE/VersionData');
 * @constructor
 * @param {String} sObject - The sObject to retrieve
 * @param {String} id - The id of the record
 * @param {Function} callback - A callback to execute once the record is retrieved
 * @param {State} state - Runtime state
 * @returns {State}
 */

exports.describe = describe;
const retrieve = (0, _lodashFp.curry)(function (sObject, id, callback, state) {
  let {
    connection
  } = state;
  const finalId = (0, _languageCommon.expandReferences)(id)(state);
  return connection.sobject(sObject).retrieve(finalId).then(result => {
    return { ...state,
      references: [result, ...state.references]
    };
  }).then(state => {
    if (callback) {
      return callback(state);
    }

    return state;
  });
});
/**
 * Execute an SOQL query.
 * Note that in an event of a query error,
 * error logs will be printed but the operation will not throw the error.
 * @public
 * @example
 * query(`SELECT Id FROM Patient__c WHERE Health_ID__c = '${state.data.field1}'`);
 * @constructor
 * @param {String} qs - A query string.
 * @param {State} state - Runtime state.
 * @returns {Operation}
 */

exports.retrieve = retrieve;
const query = (0, _lodashFp.curry)(function (qs, state) {
  let {
    connection
  } = state;
  qs = (0, _languageCommon.expandReferences)(qs)(state);
  console.log(`Executing query: ${qs}`);
  return connection.query(qs, function (err, result) {
    if (err) {
      return console.error(err);
    }

    console.log(result);
    return { ...state,
      references: [result, ...state.references]
    };
  });
});
/**
 * Create and execute a bulk job.
 * @public
 * @example
 * bulk('Patient__c', 'insert', { failOnError: true, pollInterval: 3000, pollTimeout: 240000 }, state => {
 *   return state.data.someArray.map(x => {
 *     return { 'Age__c': x.age, 'Name': x.name }
 *   })
 * });
 * @constructor
 * @param {String} sObject - API name of the sObject.
 * @param {String} operation - The bulk operation to be performed
 * @param {Object} options - Options passed to the bulk api.
 * @param {Function} fun - A function which takes state and returns an array.
 * @param {State} state - Runtime state.
 * @returns {Operation}
 */

exports.query = query;
const bulk = (0, _lodashFp.curry)(function (sObject, operation, options, fun, state) {
  const {
    connection
  } = state;
  const {
    failOnError,
    allowNoOp,
    pollTimeout,
    pollInterval
  } = options;
  const finalAttrs = fun(state);
  return new Promise((resolve, reject) => {
    if (allowNoOp && finalAttrs.length === 0) {
      console.info(`No items in ${sObject} array. Skipping bulk ${operation} operation.`);
      resolve(state);
      return state;
    }

    const timeout = pollTimeout || 240000;
    const interval = pollInterval || 6000;
    console.info(`Creating bulk ${operation} job for ${sObject}`, finalAttrs);
    const job = connection.bulk.createJob(sObject, operation, options);
    job.on('error', err => reject(err));
    console.info('Creating batch for job.');
    var batch = job.createBatch();
    console.info('Executing batch.');
    batch.execute(finalAttrs);
    batch.on('error', function (err) {
      job.close();
      console.error('Request error:');
      reject(err);
    });
    return batch.on('queue', function (batchInfo) {
      console.info(batchInfo);
      const batchId = batchInfo.id;
      var batch = job.batch(batchId);
      batch.poll(interval, timeout);
    }).then(res => {
      job.close();
      const errors = res.map((r, i) => ({ ...r,
        position: i + 1
      })).filter(item => {
        return !item.success;
      });
      errors.forEach(err => {
        err[`${options.extIdField}`] = finalAttrs[err.position - 1][options.extIdField];
      });

      if (failOnError && errors.length > 0) {
        console.error('Errors detected:');
        reject(JSON.stringify(errors, null, 2));
      } else {
        console.log('Result : ' + JSON.stringify(res, null, 2));
        resolve({ ...state,
          references: [res, ...state.references]
        });
        return { ...state,
          references: [res, ...state.references]
        };
      }
    });
  });
});
/**
 * Delete records of an object.
 * @public
 * @example
 * destroy('obj_name', [
 *  '0060n00000JQWHYAA5',
 *  '0090n00000JQEWHYAA5
 * ], { failOnError: true })
 * @constructor
 * @param {String} sObject - API name of the sObject.
 * @param {Object} attrs - Array of IDs of records to delete.
 * @param {Object} options - Options for the destroy delete operation.
 * @param {State} state - Runtime state.
 * @returns {Operation}
 */

exports.bulk = bulk;
const destroy = (0, _lodashFp.curry)(function (sObject, attrs, options, state) {
  let {
    connection
  } = state;
  const finalAttrs = (0, _languageCommon.expandReferences)(attrs)(state);
  const {
    failOnError
  } = options;
  console.info(`Deleting ${sObject} records`);
  return connection.sobject(sObject).del(finalAttrs).then(function (result) {
    const successes = result.filter(r => r.success);
    console.log('Sucessfully deleted: ', JSON.stringify(successes, null, 2));
    const failures = result.filter(r => !r.success);
    console.log('Failed to delete: ', JSON.stringify(failures, null, 2));
    if (failOnError && result.some(r => !r.success)) throw 'Some deletes failed; exiting with failure code.';
    return { ...state,
      references: [result, ...state.references]
    };
  });
});
/**
 * Create a new object.
 * @public
 * @example
 * create('obj_name', {
 *   attr1: "foo",
 *   attr2: "bar"
 * })
 * @constructor
 * @param {String} sObject - API name of the sObject.
 * @param {Object} attrs - Field attributes for the new object.
 * @param {State} state - Runtime state.
 * @returns {Operation}
 */

exports.destroy = destroy;
const create = (0, _lodashFp.curry)(function (sObject, attrs, state) {
  let {
    connection
  } = state;
  const finalAttrs = (0, _languageCommon.expandReferences)(attrs)(state);
  console.info(`Creating ${sObject}`, finalAttrs);
  return connection.create(sObject, finalAttrs).then(function (recordResult) {
    console.log('Result : ' + JSON.stringify(recordResult));
    return { ...state,
      references: [recordResult, ...state.references]
    };
  });
});
/**
 * Create a new object if conditions are met.
 * @public
 * @example
 * createIf(true, 'obj_name', {
 *   attr1: "foo",
 *   attr2: "bar"
 * })
 * @constructor
 * @param {boolean} logical - a logical statement that will be evaluated.
 * @param {String} sObject - API name of the sObject.
 * @param {Object} attrs - Field attributes for the new object.
 * @param {State} state - Runtime state.
 * @returns {Operation}
 */

exports.create = create;
const createIf = (0, _lodashFp.curry)(function (logical, sObject, attrs, state) {
  let {
    connection
  } = state;
  logical = (0, _languageCommon.expandReferences)(logical)(state);

  if (logical) {
    const finalAttrs = (0, _languageCommon.expandReferences)(attrs)(state);
    console.info(`Creating ${sObject}`, finalAttrs);
    return connection.create(sObject, finalAttrs).then(function (recordResult) {
      console.log('Result : ' + JSON.stringify(recordResult));
      return { ...state,
        references: [recordResult, ...state.references]
      };
    });
  } else {
    console.info(`Not creating ${sObject} because logical is false.`);
    return { ...state
    };
  }
});
/**
 * Upsert an object.
 * @public
 * @example
 * upsert('obj_name', 'ext_id', {
 *   attr1: "foo",
 *   attr2: "bar"
 * })
 * @constructor
 * @param {String} sObject - API name of the sObject.
 * @param {String} externalId - ID.
 * @param {Object} attrs - Field attributes for the new object.
 * @param {State} state - Runtime state.
 * @returns {Operation}
 */

exports.createIf = createIf;
const upsert = (0, _lodashFp.curry)(function (sObject, externalId, attrs, state) {
  let {
    connection
  } = state;
  const finalAttrs = (0, _languageCommon.expandReferences)(attrs)(state);
  console.info(`Upserting ${sObject} with externalId`, externalId, ':', finalAttrs);
  return connection.upsert(sObject, finalAttrs, externalId).then(function (recordResult) {
    console.log('Result : ' + JSON.stringify(recordResult));
    return { ...state,
      references: [recordResult, ...state.references]
    };
  });
});
/**
 * Upsert if conditions are met.
 * @public
 * @example
 * upsertIf(true, 'obj_name', 'ext_id', {
 *   attr1: "foo",
 *   attr2: "bar"
 * })
 * @constructor
 * @param {boolean} logical - a logical statement that will be evaluated.
 * @param {String} sObject - API name of the sObject.
 * @param {String} externalId - ID.
 * @param {Object} attrs - Field attributes for the new object.
 * @param {State} state - Runtime state.
 * @returns {Operation}
 */

exports.upsert = upsert;
const upsertIf = (0, _lodashFp.curry)(function (logical, sObject, externalId, attrs, state) {
  let {
    connection
  } = state;
  logical = (0, _languageCommon.expandReferences)(logical)(state);

  if (logical) {
    const finalAttrs = (0, _languageCommon.expandReferences)(attrs)(state);
    console.info(`Upserting ${sObject} with externalId`, externalId, ':', finalAttrs);
    return connection.upsert(sObject, finalAttrs, externalId).then(function (recordResult) {
      console.log('Result : ' + JSON.stringify(recordResult));
      return { ...state,
        references: [recordResult, ...state.references]
      };
    });
  } else {
    console.info(`Not upserting ${sObject} because logical is false.`);
    return { ...state
    };
  }
});
/**
 * Update an object.
 * @public
 * @example
 * update('obj_name', {
 *   attr1: "foo",
 *   attr2: "bar"
 * })
 * @constructor
 * @param {String} sObject - API name of the sObject.
 * @param {Object} attrs - Field attributes for the new object.
 * @param {State} state - Runtime state.
 * @returns {Operation}
 */

exports.upsertIf = upsertIf;
const update = (0, _lodashFp.curry)(function (sObject, attrs, state) {
  let {
    connection
  } = state;
  const finalAttrs = (0, _languageCommon.expandReferences)(attrs)(state);
  console.info(`Updating ${sObject}`, finalAttrs);
  return connection.update(sObject, finalAttrs).then(function (recordResult) {
    console.log('Result : ' + JSON.stringify(recordResult));
    return { ...state,
      references: [recordResult, ...state.references]
    };
  });
});
/**
 * Get a reference ID by an index.
 * @public
 * @example
 * reference(0)
 * @constructor
 * @param {number} position - Position for references array.
 * @param {State} state - Array of references.
 * @returns {State}
 */

exports.update = update;
const reference = (0, _lodashFp.curry)(function (position, state) {
  const {
    references
  } = state;
  return references[position].id;
});
/**
 * Creates a connection.
 * @example
 * createConnection(state)
 * @function
 * @param {State} state - Runtime state.
 * @returns {State}
 */

exports.reference = reference;

function createConnection(state) {
  const {
    loginUrl
  } = state.configuration;

  if (!loginUrl) {
    throw new Error('loginUrl missing from configuration.');
  }

  return { ...state,
    connection: new _jsforce.default.Connection({
      loginUrl
    })
  };
}
/**
 * Performs a login.
 * @example
 * login(state)
 * @function
 * @param {State} state - Runtime state.
 * @returns {State}
 */


function login(state) {
  const {
    username,
    password,
    securityToken
  } = state.configuration;
  let {
    connection
  } = state;
  console.info(`Logging in as ${username}.`);
  return connection.login(username, password + securityToken) // NOTE: Uncomment this to debug connection issues.
  // .then(response => {
  //   console.log(connection);
  //   console.log(response);
  //   return state;
  // })
  .then(() => state);
}
/**
 * Executes an operation.
 * @function
 * @param {Operation} operations - Operations
 * @returns {State}
 */


function execute(...operations) {
  const initialState = {
    logger: {
      info: console.info.bind(console),
      debug: console.log.bind(console)
    },
    references: [],
    data: null,
    configuration: {}
  };
  return state => {
    // Note: we no longer need `steps` anymore since `commonExecute`
    // takes each operation as an argument.
    return (0, _languageCommon.execute)(createConnection, login, ...(0, _lodashFp.flatten)(operations), cleanupState)({ ...initialState,
      ...state
    });
  };
}
/**
 * Removes unserializable keys from the state.
 * @example
 * cleanupState(state)
 * @function
 * @param {State} state
 * @returns {State}
 */


function cleanupState(state) {
  delete state.connection;
  return state;
}
/**
 * Flattens an array of operations.
 * @example
 * steps(
 *   createIf(params),
 *   update(params)
 * )
 * @function
 * @returns {Array}
 */


function steps(...operations) {
  return (0, _lodashFp.flatten)(operations);
} // Note that we expose the entire axios package to the user here.


exports.axios = _axios.default;