]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: Add decorator to skip parameter encoding 23419/head
authorTiago Melo <tspmelo@gmail.com>
Fri, 3 Aug 2018 16:58:21 +0000 (17:58 +0100)
committerTiago Melo <tspmelo@gmail.com>
Thu, 16 Aug 2018 14:29:11 +0000 (15:29 +0100)
By enconding all parameters of api services we were also encoding parameters
that were being sent in the body of the request.
Those parameters don't need to be enconded and the server never decodes them.

With this new decorator you can specify if you don't want a parameter to be
enconded.

This is a regression introduced in f21d0da5a3e19e3f9bbde084e71b4a09f8dcb0a1.

Fixes: http://tracker.ceph.com/issues/26856
Signed-off-by: Tiago Melo <tmelo@suse.com>
src/pybind/mgr/dashboard/frontend/src/app/shared/api/rbd.service.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/api/rbd.service.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/decorators/cd-encode.spec.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/decorators/cd-encode.ts

index 46127509eda9e39e4ef05a6c9df5ff1a17f3c0b5..dcf2787685f3386ead5170136aabb2390f71a9dd 100644 (file)
@@ -126,12 +126,4 @@ describe('RbdService', () => {
     const req = httpTesting.expectOne('api/block/image/poolName/rbdName/snap/snapshotName');
     expect(req.request.method).toBe('DELETE');
   });
-
-  describe('Encode decorator', () => {
-    it('should encode the imageName', () => {
-      service.get('poolName', 'rbd/name').subscribe();
-      const req = httpTesting.expectOne('api/block/image/poolName/rbd%2Fname');
-      expect(req.request.method).toBe('GET');
-    });
-  });
 });
index 9a466adf9161a2b2b56cf564335e6b40835fcb85..93decddec2a946c6eae3692db4439ba5de268bc2 100644 (file)
@@ -1,7 +1,7 @@
 import { HttpClient } from '@angular/common/http';
 import { Injectable } from '@angular/core';
 
-import { cdEncode } from '../decorators/cd-encode';
+import { cdEncode, cdEncodeNot } from '../decorators/cd-encode';
 import { ApiModule } from './api.module';
 
 @cdEncode
@@ -47,7 +47,7 @@ export class RbdService {
     return this.http.get('api/block/image/default_features');
   }
 
-  createSnapshot(poolName, rbdName, snapshotName) {
+  createSnapshot(poolName, rbdName, @cdEncodeNot snapshotName) {
     const request = {
       snapshot_name: snapshotName
     };
@@ -56,7 +56,7 @@ export class RbdService {
     });
   }
 
-  renameSnapshot(poolName, rbdName, snapshotName, newSnapshotName) {
+  renameSnapshot(poolName, rbdName, snapshotName, @cdEncodeNot newSnapshotName) {
     const request = {
       new_snap_name: newSnapshotName
     };
@@ -65,7 +65,7 @@ export class RbdService {
     });
   }
 
-  protectSnapshot(poolName, rbdName, snapshotName, isProtected) {
+  protectSnapshot(poolName, rbdName, snapshotName, @cdEncodeNot isProtected) {
     const request = {
       is_protected: isProtected
     };
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/decorators/cd-encode.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/decorators/cd-encode.spec.ts
new file mode 100644 (file)
index 0000000..49b504f
--- /dev/null
@@ -0,0 +1,41 @@
+import { cdEncode, cdEncodeNot } from './cd-encode';
+
+describe('cdEncode', () => {
+  @cdEncode
+  class ClassA {
+    x2: string;
+    y2: string;
+
+    methodA(x1: string, @cdEncodeNot y1: string) {
+      this.x2 = x1;
+      this.y2 = y1;
+    }
+  }
+
+  class ClassB {
+    x2: string;
+    y2: string;
+
+    @cdEncode
+    methodB(x1: string, @cdEncodeNot y1: string) {
+      this.x2 = x1;
+      this.y2 = y1;
+    }
+  }
+
+  const word = 'a+b/c-d';
+
+  it('should encode all params of ClassA, with exception of y1', () => {
+    const a = new ClassA();
+    a.methodA(word, word);
+    expect(a.x2).toBe('a%2Bb%2Fc-d');
+    expect(a.y2).toBe(word);
+  });
+
+  it('should encode all params of methodB, with exception of y1', () => {
+    const b = new ClassB();
+    b.methodB(word, word);
+    expect(b.x2).toBe('a%2Bb%2Fc-d');
+    expect(b.y2).toBe(word);
+  });
+});
index c2e4f9f1c286d96cc22d35fab05c7b8aa54446f4..c77be120f9505d95e48cdd8468683d3a12cb7f15 100644 (file)
@@ -9,58 +9,71 @@ import * as _ from 'lodash';
  * @param {Function} [target=null]
  * @returns {*}
  */
-export function cdEncode(target: Function = null): any {
-  if (target) {
-    encodeClass(target);
+export function cdEncode(...args: any[]): any {
+  switch (args.length) {
+    case 1:
+      return encodeClass.apply(this, args);
+    case 3:
+      return encodeMethod.apply(this, args);
+    default:
+      throw new Error();
+  }
+}
+
+/**
+ * This decorator can be used in parameters only.
+ * It will exclude the parameter from being encode.
+ * This should be used in parameters that are going
+ * to be sent in the request's body.
+ *
+ * @export
+ * @param {Object} target
+ * @param {string} propertyKey
+ * @param {number} index
+ */
+export function cdEncodeNot(target: Object, propertyKey: string, index: number) {
+  const metadataKey = `__ignore_${propertyKey}`;
+  if (Array.isArray(target[metadataKey])) {
+    target[metadataKey].push(index);
   } else {
-    return encodeMethod();
+    target[metadataKey] = [index];
   }
 }
 
 function encodeClass(target: Function) {
   for (const propertyName of Object.keys(target.prototype)) {
     const descriptor = Object.getOwnPropertyDescriptor(target.prototype, propertyName);
+
     const isMethod = descriptor.value instanceof Function;
     if (!isMethod) {
       continue;
     }
 
-    const originalMethod = descriptor.value;
-    descriptor.value = function(...args: any[]) {
-      args.forEach((arg, i, argsArray) => {
-        if (_.isString(arg)) {
-          argsArray[i] = encodeURIComponent(arg);
-        }
-      });
-
-      const result = originalMethod.apply(this, args);
-      return result;
-    };
-
+    encodeMethod(target.prototype, propertyName, descriptor);
     Object.defineProperty(target.prototype, propertyName, descriptor);
   }
 }
 
-function encodeMethod() {
-  return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
-    if (descriptor === undefined) {
-      descriptor = Object.getOwnPropertyDescriptor(target, propertyKey);
-    }
-    const originalMethod = descriptor.value;
+function encodeMethod(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
+  if (descriptor === undefined) {
+    descriptor = Object.getOwnPropertyDescriptor(target, propertyKey);
+  }
+  const originalMethod = descriptor.value;
 
-    descriptor.value = function() {
-      const args = [];
+  descriptor.value = function() {
+    const metadataKey = `__ignore_${propertyKey}`;
+    const indices: number[] = target[metadataKey] || [];
+    const args = [];
 
-      for (let i = 0; i < arguments.length; i++) {
-        if (_.isString(arguments[i])) {
-          args[i] = encodeURIComponent(arguments[i]);
-        } else {
-          args[i] = arguments[i];
-        }
+    for (let i = 0; i < arguments.length; i++) {
+      if (_.isString(arguments[i]) && indices.indexOf(i) === -1) {
+        args[i] = encodeURIComponent(arguments[i]);
+      } else {
+        args[i] = arguments[i];
       }
+    }
 
-      const result = originalMethod.apply(this, args);
-      return result;
-    };
+    const result = originalMethod.apply(this, args);
+    return result;
   };
 }