From 5095203f0fcf60e0a294a9ea3f6ef13e55e319dc Mon Sep 17 00:00:00 2001 From: Patrick Leary Date: Fri, 22 Nov 2024 18:08:38 -0500 Subject: [PATCH] fix for some requests to endpoints implementing upload where field names contain brackets --- build/inaturalistjs.js | 18 ++++++++++++++---- lib/inaturalist_api.js | 18 ++++++++++++++---- package.json | 2 +- test/inaturalist_api.js | 8 ++++++++ 4 files changed, 37 insertions(+), 9 deletions(-) diff --git a/build/inaturalistjs.js b/build/inaturalistjs.js index 7cdd262..97c4da0 100644 --- a/build/inaturalistjs.js +++ b/build/inaturalistjs.js @@ -274,13 +274,23 @@ var iNaturalistAPI = /*#__PURE__*/function () { key: "multipartBodyForResuest", value: function multipartBodyForResuest(parameters) { var body = new LocalFormData(); - var bodyContainsObjects = false; + var bodyNeedsMultipart = false; // Before params get "flattened" extract the fields and encode them as a // single JSON string, which the server can handle var fields = parameters.fields; if (fields) { body.append("fields", _typeof(fields) === "object" ? JSON.stringify(fields) : fields); } + // if any of the incoming parameters have brackets, these are requests that + // are expecting an older behavior of inaturalistjs which is to use multipart/form-data + // for all requests that might contain a file. This behavior has since been changed to use + // application/json where possible. For now, have these requests use the previous behavior, + // thus they will need to use a multipart request + Object.keys(parameters).forEach(function (k) { + if (k.match(/\[/)) { + bodyNeedsMultipart = true; + } + }); // multipart requests reference all nested parameter names as strings // so flatten arrays into "arr[0]" and objects into "obj[prop]" var params = iNaturalistAPI.flattenMultipartParams(parameters); @@ -291,17 +301,17 @@ var iNaturalistAPI = /*#__PURE__*/function () { // FormData params can include options like file upload sizes if (params[k] && params[k].type === "custom" && params[k].value) { body.append(k, params[k].value, params[k].options); - bodyContainsObjects = true; + bodyNeedsMultipart = true; } else { if (params[k] !== null && _typeof(params[k]) === "object") { - bodyContainsObjects = true; + bodyNeedsMultipart = true; } body.append(k, typeof params[k] === "boolean" ? params[k].toString() : params[k]); } }); // there are no parameters with type object, so there are no files in this // request. Return null as this request does not need to be multipart - if (!bodyContainsObjects) { + if (!bodyNeedsMultipart) { return null; } return body; diff --git a/lib/inaturalist_api.js b/lib/inaturalist_api.js index 2026fc9..939dca5 100644 --- a/lib/inaturalist_api.js +++ b/lib/inaturalist_api.js @@ -193,13 +193,23 @@ const iNaturalistAPI = class iNaturalistAPI { static multipartBodyForResuest( parameters ) { const body = new LocalFormData( ); - let bodyContainsObjects = false; + let bodyNeedsMultipart = false; // Before params get "flattened" extract the fields and encode them as a // single JSON string, which the server can handle const { fields } = parameters; if ( fields ) { body.append( "fields", typeof ( fields ) === "object" ? JSON.stringify( fields ) : fields ); } + // if any of the incoming parameters have brackets, these are requests that + // are expecting an older behavior of inaturalistjs which is to use multipart/form-data + // for all requests that might contain a file. This behavior has since been changed to use + // application/json where possible. For now, have these requests use the previous behavior, + // thus they will need to use a multipart request + Object.keys( parameters ).forEach( k => { + if ( k.match( /\[/ ) ) { + bodyNeedsMultipart = true; + } + } ); // multipart requests reference all nested parameter names as strings // so flatten arrays into "arr[0]" and objects into "obj[prop]" const params = iNaturalistAPI.flattenMultipartParams( parameters ); @@ -210,17 +220,17 @@ const iNaturalistAPI = class iNaturalistAPI { // FormData params can include options like file upload sizes if ( params[k] && params[k].type === "custom" && params[k].value ) { body.append( k, params[k].value, params[k].options ); - bodyContainsObjects = true; + bodyNeedsMultipart = true; } else { if ( params[k] !== null && typeof ( params[k] ) === "object" ) { - bodyContainsObjects = true; + bodyNeedsMultipart = true; } body.append( k, ( typeof params[k] === "boolean" ) ? params[k].toString( ) : params[k] ); } } ); // there are no parameters with type object, so there are no files in this // request. Return null as this request does not need to be multipart - if ( !bodyContainsObjects ) { + if ( !bodyNeedsMultipart ) { return null; } return body; diff --git a/package.json b/package.json index 575c4b7..b15c0f2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "inaturalistjs", - "version": "2.14.0", + "version": "2.15.0", "description": "inaturalistjs", "author": "iNaturalist", "license": "MIT", diff --git a/test/inaturalist_api.js b/test/inaturalist_api.js index c5dbb63..1ae5a1f 100644 --- a/test/inaturalist_api.js +++ b/test/inaturalist_api.js @@ -109,6 +109,14 @@ describe( "iNaturalistAPI", ( ) => { expect( body.constructor.name ).to.eq( "FormData" ); } ); + it( "returns FormData if any field names contain brackets", ( ) => { + const requestParameters = { + "field_with[brackets]": true + }; + const body = iNaturalistAPI.multipartBodyForResuest( requestParameters ); + expect( body.constructor.name ).to.eq( "FormData" ); + } ); + it( "returns fields as a JSON string", ( ) => { const fields = { field1: true,