Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix file upload - consider metadata options by creating form data #500

Merged
merged 30 commits into from
Mar 21, 2018

Conversation

KharitonOff
Copy link
Contributor

Summary

fix file upload issue, now you can upload buffer stream as expected:

let file = fs.readFileSync('../file.png')
slackClient.files.upload({ channels, file, filename: 'file.png' })

as well as proposed fix in #307:

let file = fs.readFileSync('../file.png')
slackClient.files.upload({ channels, file: {value: file, options:{filename: 'file.png'}}, filename: 'file.png' })

related to #307

Requirements (place an x in each [ ])

@CLAassistant
Copy link

CLAassistant commented Mar 14, 2018

CLA assistant check
All committers have signed the CLA.

@codecov
Copy link

codecov bot commented Mar 14, 2018

Codecov Report

Merging #500 into master will decrease coverage by 0.37%.
The diff coverage is 100%.

Impacted file tree graph

@@            Coverage Diff            @@
##           master    #500      +/-   ##
=========================================
- Coverage   80.08%   79.7%   -0.38%     
=========================================
  Files           6       6              
  Lines         241     271      +30     
  Branches       37      42       +5     
=========================================
+ Hits          193     216      +23     
- Misses         30      38       +8     
+ Partials       18      17       -1
Impacted Files Coverage Δ
src/WebClient.ts 81.88% <100%> (+3.25%) ⬆️
src/util.ts 67.56% <100%> (ø) ⬆️
src/IncomingWebhook.ts 60% <0%> (-13.34%) ⬇️
src/errors.ts 100% <0%> (ø) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update c49ba78...7c89cac. Read the comment docs.

@@ -390,7 +400,7 @@ describe('WebClient', function () {
// verify that any requests after maxRequestConcurrency were delayed by the responseDelay
const queuedResponses = responses.slice(3);
const minDiff = concurrentResponses[concurrentResponses.length - 1].diff + responseDelay;
queuedResponses.forEach(r => assert.isAbove(r.diff, minDiff));
queuedResponses.forEach(r => assert.isAtLeast(r.diff, minDiff));
Copy link
Contributor

Choose a reason for hiding this comment

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

was this actually causing a failure? i think your change is more technically correct, but i'm wondering if you actually got a test run where the timers were so perfect that r.diff and minDiff were actually equal.

Copy link
Contributor

Choose a reason for hiding this comment

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

I can confirm that I've run into the issue of getting a test run where the timers matched up and a false positive was thrown--just once, but it has happened.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes, I added this fix to the PR, because the build of my first commit broke accidentally

const minDiff = concurrentResponses[concurrentResponses.length - 1].diff + responseDelay;
queuedResponses.forEach(r => assert.isAbove(r.diff, minDiff));
queuedResponses.forEach(r => {
assert.isAtLeast(r.diff, minDiff)
Copy link
Contributor

Choose a reason for hiding this comment

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

i think this could fit in a single line rather than a block.

// intentially vague about the method name
return this.client.apiCall('upload', {
file: { value: imageBuffer, options: { filename: 'train.png' } },
filename: 'train.png',
Copy link
Contributor

Choose a reason for hiding this comment

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

i was trying to avoid this usage all together, because it seems redundant to have to specify the filename twice. can you see any advantage to doing it this way?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The main advantage I see is the current implementation state of all the folks who used the old style. I don't know whether there is a need for someone to provide further attributes in the options field (e.g. contentType or knownLength)

Copy link
Contributor

Choose a reason for hiding this comment

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

i also want to make sure the implementation accounts for those who used the old style, but i prefer that the path we take to get there is not to redo the same hack, but to smooth out the API so that you don't need to redundantly specify the filename.

i wouldn't call it a hack if there was legitimately a use case or need for using the { value, options } format for file. i don't see any benefit from exposing contentType and/or knownLength at this time, since that only has the potential to force to incorrect values and cause the upload to fail (but I'm happy to be corrected if there is a real use case).

maybe we can channel this effort towards investigating why the following code didn't behave as we want:

// options.file = {
// // @ts-ignore
// value: options.file,
// options: {
// // @ts-ignore
// filename: options.filename,
// },
// };

AFAICT it should result in the same thing...

also, have you tried this implementation to upload actual files? i'm not 100% confident that the nock expectations are correct.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry, but I don't really understand your comment... this code snippet, you mentioned above works now as well. Off course I have tried it before I've opened this PR. And the problem with it, why it didn't work before was in the implementation of

if (containsBinaryData) {
return flattened.reduce((form, [key, value]) => {
form.append(key, value);
return form;
}, new FormData());
}

I've added this part in order to provide the attribute options to the form builder.

if (key === 'file' && value.value) {
    form.append(key, value.value, value.options);
    return form;
}

https://github.com/KharitonOff/node-slack-sdk/blob/45c77022a81cd2631a5a2bd3ee0dd29e401f51d7/src/WebClient.ts#L554-L563

This is actually the main goal of this PR and all other things we talked here about, were done with intention not to break the current state. As I mentioned in the PR description, you can upload files now with no need of additional/redundant information

let file = fs.readFileSync('../file.png')
slackClient.files.upload({ channels, file, filename: 'file.png' })

Should i remove the 'backup' solution to restrict the file upload to only one way?

Copy link
Contributor

Choose a reason for hiding this comment

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

@KharitonOff thanks so much for your clarification. i missed that diff amidst the rest of the changes but i now see where the issue was before.

yes, i'd prefer removing the backup solution so that we don't unnecessarily expand the API contract. if you can do that, i'm happy to land this PR!

assert.lengthOf(parts.files, 1);
const file = parts.files[0];
// options were not provided to the form builder
assert.include(file, { fieldname: 'file' });
Copy link
Contributor

Choose a reason for hiding this comment

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

still not asserting that the filename field exists, and removed the comment that would remind us to improve the tests so that it is asserted. why?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

sorry, I've overseen this assertion statement and felt that the fix solved the problem pointed in the comment. I'll bring your TODO and assertion back.

// defined error would occur, or if Slack just doesn't need a filename for the part
// assert.include(file, { fieldname: 'file', filename: 'train.jpg' });
// NOTE: it seems the file and its filename are emitted as a field in addition to the token, not sure if
// this was happening in the old implementation.
assert.include(file, { fieldname: 'file' });
Copy link
Contributor

Choose a reason for hiding this comment

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

still not asserting that the filename field exists.

@aoberoi
Copy link
Contributor

aoberoi commented Mar 14, 2018

@KharitonOff thanks for your contribution! can you help clarify by answering my comments above?

@KharitonOff
Copy link
Contributor Author

I've changed your test again in order to fix the build. I hope it is ok for you.
The last travis build failed because of timeout. This seems to be an occasional fail:
build for node v9 succeeds:
screen shot 2018-03-15 at 16 10 39
build for node v8 fails:
screen shot 2018-03-15 at 16 11 08
https://travis-ci.org/slackapi/node-slack-sdk/builds/353822357

@@ -4,7 +4,7 @@ import { check, checkDirectory } from 'typings-tester';
describe('typescript typings tests', () => {
it('should allow WebClient to work without a token', () => {
check([`${__dirname}/sources/webclient-no-token.ts`], `${__dirname}/tsconfig-strict.json`);
});
Copy link
Contributor

Choose a reason for hiding this comment

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

maybe the best place to override this value is in test/mocha.opts, so that it doesn't just apply to this one case. i don't see anything unique to this case that should make it run longer than the other. its my guess that Travis generally has unpredictable speed and we should make the ceiling high enough to accomodate.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agree! I didn't find this setting before, that's why I added it only to this test.

@aoberoi
Copy link
Contributor

aoberoi commented Mar 20, 2018

@KharitonOff its a small world. I just noticed that you work on CLA Assistant (which we use) and you helped review an implementation for a feature i requested, way back when (cla-assistant/cla-assistant#246). 🙌

@KharitonOff
Copy link
Contributor Author

@aoberoi yes, indeed, glad to meet you again! It is nice to see that you are using CLA Assistant and it was very interesting to have a contributer role and sign your CLA 😄

// TODO: understand why this assertion is failing. already employed the buffer metadata workaround, should
// look into the details about whether that workaround is still required, or why else the `source.on` is not
// defined error would occur, or if Slack just doesn't need a filename for the part
// assert.include(file, { fieldname: 'file', filename: 'train.jpg' });
Copy link
Contributor

Choose a reason for hiding this comment

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

note to self: should the assertion with filename work now?

// look into the details about whether that workaround is still required, or why else the `source.on` is not
// defined error would occur, or if Slack just doesn't need a filename for the part
// assert.include(file, { fieldname: 'file', filename: 'train.jpg' });
assert.include(file, { fieldname: 'file', filename: 'train.jpg' });

// NOTE: it seems the file and its filename are emitted as a field in addition to the token, not sure if
Copy link
Contributor

Choose a reason for hiding this comment

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

note to self: the following assertion is probably redundant now

@aoberoi
Copy link
Contributor

aoberoi commented Mar 21, 2018

This is so great!! Extra thanks for cleaning up the style issues and some incorrect behavior in the existing tests.

I'm going to follow up with a PR that addresses the "note to self" comments above.

@aoberoi aoberoi merged commit 68c0874 into slackapi:master Mar 21, 2018
@aoberoi aoberoi mentioned this pull request Mar 21, 2018
2 tasks
aoberoi added a commit that referenced this pull request Mar 21, 2018
@KharitonOff
Copy link
Contributor Author

Cool! Thanks for merging it 👍
I hope you'll release it soon, our 🤖 is eagerly waiting for the fix 😃

@aoberoi aoberoi mentioned this pull request Mar 22, 2018
2 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants