Skip to content
This repository has been archived by the owner on Jul 12, 2019. It is now read-only.

[PDE-683] (BREAKING) Resolve curlies to their original data type #139

Merged
merged 1 commit into from
Feb 25, 2019

Conversation

stevelikesmusic
Copy link
Contributor

Description

CLI integrations always use bundle values directly from the bundle we inject to their request functions:

const perform = (z, bundle) => {
  const options = {
    url: 'http://api.example.com/post', 
	method: 'POST',
    body: {
      // We directly access the variable -- names: ['Ron', 'Veronica', 'Brick']
      names: bundle.inputData.names,
    }
  }

  z.request(options);
}

But, if we are using curly tokens to be replaced with an actual value, core interpolates value in a string. One example is in a perform shorthand RequestSchema.

const perform = {
  url: 'http://api.example.com/post',
  method: 'POST',
  body: {
    // String.prototype.replace will coerce the data type to a string
    // Using the case above -- names: 'Ron,Veronica,Brick' 
    names: '{{bundle.inputData.names}}',
  }

This fix keeps the data types of values replaced via curlies. The one exception is when we have an interpolated value as part of a string (ex. 'Bearer {{bundle.authData.access_token}}'). In this case we:

  1. Add to the string as we did before
  2. Throw an error if the type is a plain object or array

The tests do a better job of illustrating what we're doing now.

Jira

https://zapierorg.atlassian.net/browse/PDE-683

const s = String(key).replace(/[-[\]/{}()\\*+?.^$|]/g, '\\$&');
const re = new RegExp(s, 'g');
out = out.replace(re, bank[key]);
const escapedKey = String(key).replace(/[-[\]/{}()\\*+?.^$|]/g, '\\$&');
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed the names here to be a bit more descriptive. I think we could drop the String constructor. Since key is derived from Object.keys, we should be able to safely assume it is always a string.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you're right. Let's drop it!

@@ -14,6 +14,18 @@ const isPlainObj = o => {

const comparison = (obj, needle) => obj === needle;

const getObjectType = obj => {
if (_.isPlainObject(obj)) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason we have our own isPlainObject? Is it preferred to lodash's?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤷‍♂️ but yeah, I think we should stick to lodash's.

@@ -130,8 +142,7 @@ const recurseExtract = (obj, matcher) => {
const _IGNORE = {};

// Flatten a nested object.
const flattenPaths = (data, sep) => {
sep = sep || '.';
const flattenPaths = (data, sep = '.') => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💅

Copy link
Member

@eliangcs eliangcs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested locally, I don't find any issues. Good work!

There's a failing test you need to fix in test/logger.js. Also got some minor suggestions but overall it's a 👍from me. Since it's a breaking change, maybe @bcooksey would like to take a quick look?

const s = String(key).replace(/[-[\]/{}()\\*+?.^$|]/g, '\\$&');
const re = new RegExp(s, 'g');
out = out.replace(re, bank[key]);
const escapedKey = String(key).replace(/[-[\]/{}()\\*+?.^$|]/g, '\\$&');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you're right. Let's drop it!

const isPartOfString = valueParts.length > 1;

if (isPartOfString) {
if (_.isArray(replacementValue) || _.isPlainObject(replacementValue)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think here we can use native Array.isArray instead of lodash's _.isArray.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Totally agree. Only used _.isArray since we were grabbing _.isPlainObject right next to it. No strong feeling about it though

}

out = isPartOfString
? valueParts.join('').replace(matchesKey, replacementValue)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like valueParts.join('') always equals to out here. So can we simplify this line to the following?

Suggested change
? valueParts.join('').replace(matchesKey, replacementValue)
? out.replace(matchesKey, replacementValue)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doh. Nice catch @eliangcs

return 'Object';
}

if (_.isArray(obj)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likewise, native Array.isArray is preferred.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, same reason as above. Also don't feel strong about it here either.

const flattenPaths = require('./data').flattenPaths;
const { isPlainObj, getObjectType } = require('./data');
const { recurseReplace } = require('./data');
const { flattenPaths } = require('./data');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice cleanup! Can we make it even better by importing them in one statement?

const { isPlainObj, getObjectType, recurseReplace, flattenPaths } = require('./data');

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haha yikes. I just now noticed that those were coming from the same file. Thanks for that!

@eliangcs eliangcs changed the title [PDE-683] Resolve curlies to their original data type [PDE-683] (BREAKING) Resolve curlies to their original data type Feb 25, 2019
Copy link
Contributor

@xavdid xavdid left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with CH's great review (as per usual). Added a minor gripe on the error message text, but otherwise 👍

'Cannot interpolate objects or arrays into a string.',
`You've sent an ${getObjectType(replacementValue)},`,
`which will get coerced to ${replacementValue}`
].join(' ')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a reason this is an array vs a string that prettier breaks up automatically?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I just found it easier to read this way than the way that prettier was formatting it. I don't feel strongly about, though. Can totally be just a string.

message: 'No arrays, thank you: {{bundle.inputData.badData}}'
}
}).should.be.rejectedWith(
"Cannot interpolate objects or arrays into a string. You've sent an Array, which will get coerced to 1,2,3"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

coerced to 1,2,3

I find this wording a little confusing, since it's not clear that 1,2,3 is a string. Maybe change the error to be

which will get coerced into the string "1,2,3"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also maybe change it to Cannot reliably interpolate... to make it clear that we can, but it might have weird behavior.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good points

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is which will get coerced into the string actually true? We are always going to throw this error, yea? Maybe just a stringified dump of the first 10-20 characters to help them know which array we are getting?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah it will always throw. The intent was to say, "Hey there, here's what would have gone through". But maybe that's unnecessary. We could just say, "Can't do it. Here's what you sent".

We are still sending them the data above. But I can see the value in keeping the output simpler.

message: 'No arrays, thank you: {{bundle.inputData.badData}}'
}
}).should.be.rejectedWith(
"Cannot interpolate objects or arrays into a string. You've sent an Array, which will get coerced to 1,2,3"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is which will get coerced into the string actually true? We are always going to throw this error, yea? Maybe just a stringified dump of the first 10-20 characters to help them know which array we are getting?

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants