Create or update a file.

@param {string} path - File path
@param body - File content
@param {string} contentType - File content-type
@param {Object} options
@param {string} options.ifNoneMatch - Only create of update the file if the
current ETag doesn't match this string
@returns {Promise} Resolves with an object containing the status code,
content-type and revision

@protected
function (path, body, contentType, options) {
    const fullPath = googleDrivePath(path);

    function putDone(response) {
      if (response.status >= 200 && response.status < 300) {
        const meta = JSON.parse(response.responseText);
        const etagWithoutQuotes = removeQuotes(meta.etag);
        return Promise.resolve({statusCode: 200, contentType: meta.mimeType, revision: etagWithoutQuotes});
      } else if (response.status === 412) {
        return Promise.resolve({statusCode: 412, revision: 'conflict'});
      } else {
        return Promise.reject("PUT failed with status " + response.status + " (" + response.responseText + ")");
      }
    }

    return this._getFileId(fullPath).then((id) => {
      if (id) {
        if (options && (options.ifNoneMatch === '*')) {
          return putDone({ status: 412 });
        }
        return this._updateFile(id, fullPath, body, contentType, options).then(putDone);
      } else {
        return this._createFile(fullPath, body, contentType, options).then(putDone);
      }
    });
  }