Repository: googlemaps/google-maps-services-js (ts)

Issue:
Places placesNearby method (and maybe others) tainting input params object
#### Environment details

OS type and version: Docker FROM node:12.18.2-stretch
Package.json dependency line: "@googlemaps/google-maps-services-js": "3.1.5"

#### Steps to reproduce


1. Create this test.js file:
```
 const GoogleMapsServicesClient = require( '@googlemaps/google-maps-services-js' ).Client;

( async function () {

  const client = new GoogleMapsServicesClient( {} );

  const key = '<your key here>';

  const params = {
    key: key,
    location: { lat: 32.814, lng: -96.868 },
    keyword: 'find something',
    type: 'restaurant',
    rankby: 'distance'
  };

  console.log( 'google param before', params );
  await client.placesNearby( { params: params } );
  console.log( 'google param after', params );

} )();
```

2. Run these bash commands:

```
npm install @googlemaps/google-maps-services-js@3.1.5
node test.js
```

#### Actual Results
```
google param before {
  key: '<your key>',
  location: { lat: 32.814, lng: -96.868 },
  keyword: 'find something',
  type: 'restaurant',
  rankby: 'distance'
}
google param after {
  key: '<your key>',
  location: '32.814,-96.868',  // location field was modified on the input object
  keyword: 'find something',
  type: 'restaurant',
  rankby: 'distance'
}
```

#### Expected Results

```
google param before {
  key: '<your key>',
  location: { lat: 32.814, lng: -96.868 },
  keyword: 'find something',
  type: 'restaurant',
  rankby: 'distance'
}
google param after {
  key: '<your key>',
  location: { lat: 32.814, lng: -96.868 },  // location field is NOT modified on the input object
  keyword: 'find something',
  type: 'restaurant',
  rankby: 'distance'
}
```

Pull request:
fix: do not mutate params in serializer
closes #431 

Patch:
diff --git a/src/serialize.ts b/src/serialize.ts
index b0a9a1381..2336f2178 100644
--- a/src/serialize.ts
+++ b/src/serialize.ts
@@ -105,12 +105,16 @@ export function serializer(
   }
 ) {
   return (params: { [key: string]: any }) => {
+    // avoid mutating params
+    const serializedParams = { ...params };
+
     Object.keys(format).forEach((key: string) => {
-      if (key in params) {
-        params[key] = format[key](params[key]);
+      if (key in serializedParams) {
+        serializedParams[key] = format[key](serializedParams[key]);
       }
     });
-    return qs(params, queryStringOptions);
+
+    return qs(serializedParams, queryStringOptions);
   };
 }
 


Test patch:
diff --git a/src/serialize.test.ts b/src/serialize.test.ts
index 5832f47fa..8b2bba933 100644
--- a/src/serialize.test.ts
+++ b/src/serialize.test.ts
@@ -60,6 +60,16 @@ test("serializer", () => {
   ).toBe("foo=0%2C1|2%2C3");
 });
 
+test("serializer should not mutate params", () => {
+  const location = { lat: 0, lng: 1 };
+  const params = {
+    location,
+  };
+
+  serializer({ location: latLngToString })(params);
+  expect(params.location).toBe(location);
+});
+
 test("serializer should return pipe joined arrays by default", () => {
   expect(serializer({})({ foo: ["b", "a", "r"] })).toBe("foo=b|a|r");
 });