Browse Source

refactor: renames DataProjector to DataProjectionService

e22m4u 1 month ago
parent
commit
c4b5e7b991
6 changed files with 290 additions and 139 deletions
  1. 54 13
      README.md
  2. 33 11
      dist/cjs/index.cjs
  3. 30 6
      src/data-projection-service.js
  4. 172 0
      src/data-projection-service.spec.js
  5. 0 108
      src/data-projector.spec.js
  6. 1 1
      src/index.js

+ 54 - 13
README.md

@@ -231,6 +231,47 @@ console.log(outputData);
 // }
 ```
 
+Использование сокращенных методов для работы с областями.
+
+```js
+import {DataProjectionService} from '@e22m4u/js-data-projection';
+
+const dps = new DataProjectionService();
+
+dps.defineProjection({
+  name: 'user',
+  schema: {
+    username: true,
+    password: {
+      scopes: {
+        input: true,   // поле password доступно для "input" области
+        output: false, // поле password скрыто для "output" области
+      },
+    },
+  },
+});
+
+const data = {
+  username: 'john_doe',
+  password: 'secret123',
+}
+
+// аналог dps.projectData('user', data, {scope: 'input'})
+const input = dps.projectInput('user', data);
+console.log(input);
+// {
+//   username: 'john_doe',
+//   password: 'secret123'
+// }
+
+// аналог dps.projectData('user', data, {scope: 'output'})
+const output = dps.projectOutput('user', data);
+console.log(output);
+// {
+//   username: 'john_doe'
+// }
+```
+
 ### Фабричные функции
 
 Использование фабрики вместо объекта схемы.
@@ -330,11 +371,11 @@ console.log(result);
 
 ```js
 import {ServiceContainer} from '@e22m4u/js-service';
-import {DataProjector} from '@e22m4u/js-data-projection';
+import {DataProjectionService} from '@e22m4u/js-data-projection';
 
 // сервис-контейнер доступен только
-// при использовании DataProjector
-const projector = new DataProjector();
+// при использовании DataProjectionService
+const dps = new DataProjectionService();
 
 // по умолчанию сервис-контейнер передается
 // первым аргументом фабрики
@@ -348,7 +389,7 @@ const data = {
   secret: 'john123',
 };
 
-const result = projector.project(getSchema, data);
+const result = dps.projectData(getSchema, data);
 console.log(result);
 // {
 //   name: 'John',
@@ -360,12 +401,12 @@ console.log(result);
 Регистрация и применение именованной схемы.
 
 ```js
-import {DataProjector} from '@e22m4u/js-data-projection';
+import {DataProjectionService} from '@e22m4u/js-data-projection';
 
-const projector = new DataProjector();
+const dps = new DataProjectionService();
 
 // регистрация схемы
-projector.defineSchema({
+dps.defineProjection({
   name: 'user', // <= имя схемы
   schema: {
     id: true,
@@ -381,7 +422,7 @@ const data = {
 };
 
 // проекция данных по зарегистрированному имени
-const result = projector.project('user', data);
+const result = dps.projectData('user', data);
 console.log(result);
 // {
 //   id: 10,
@@ -392,12 +433,12 @@ console.log(result);
 Использование вложенных именованных схем.
 
 ```js
-import {DataProjector} from '@e22m4u/js-data-projection';
+import {DataProjectionService} from '@e22m4u/js-data-projection';
 
-const projector = new DataProjector();
+const dps = new DataProjectionService();
 
 // регистрация схемы "address"
-projector.defineSchema({
+dps.defineProjection({
   name: 'address',
   schema: {
     city: true,
@@ -406,7 +447,7 @@ projector.defineSchema({
 });
 
 // регистрация схемы "user"
-projector.defineSchema({
+dps.defineProjection({
   name: 'user',
   schema: {
     name: true,
@@ -425,7 +466,7 @@ const data = {
   },
 };
 
-const result = projector.project('user', data);
+const result = dps.projectData('user', data);
 console.log(result);
 // {
 //   name: 'Fedor',

+ 33 - 11
dist/cjs/index.cjs

@@ -20,7 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
 // src/index.js
 var index_exports = {};
 __export(index_exports, {
-  DataProjector: () => DataProjector,
+  DataProjectionService: () => DataProjectionService,
   ProjectionSchemaRegistry: () => ProjectionSchemaRegistry,
   projectData: () => projectData,
   validateProjectionSchema: () => validateProjectionSchema,
@@ -230,7 +230,7 @@ function _shouldSelect(propOptions, strict, scope) {
 }
 __name(_shouldSelect, "_shouldSelect");
 
-// src/data-projector.js
+// src/data-projection-service.js
 var import_js_service2 = require("@e22m4u/js-service");
 
 // src/projection-schema-registry.js
@@ -330,39 +330,61 @@ var _ProjectionSchemaRegistry = class _ProjectionSchemaRegistry extends import_j
 __name(_ProjectionSchemaRegistry, "ProjectionSchemaRegistry");
 var ProjectionSchemaRegistry = _ProjectionSchemaRegistry;
 
-// src/data-projector.js
-var _DataProjector = class _DataProjector extends import_js_service2.Service {
+// src/data-projection-service.js
+var _DataProjectionService = class _DataProjectionService extends import_js_service2.Service {
   /**
-   * Define schema.
+   * Define projection.
    *
    * @param {object} schemaDef
    * @returns {this}
    */
-  defineSchema(schemaDef) {
+  defineProjection(schemaDef) {
     this.getService(ProjectionSchemaRegistry).defineSchema(schemaDef);
     return this;
   }
   /**
-   * Project.
+   * Project data.
    *
    * @param {object|Function|string} schema
    * @param {object|object[]|*} data
    * @param {object} [options]
    * @returns {*}
    */
-  project(schema, data, options) {
+  projectData(schema, data, options) {
     const registry = this.getService(ProjectionSchemaRegistry);
     const defaultNameResolver = /* @__PURE__ */ __name((name) => registry.getSchema(name), "defaultNameResolver");
     const nameResolver = options && options.nameResolver || defaultNameResolver;
     const factoryArgs = options && options.factoryArgs || [this.container];
     return projectData(schema, data, { ...options, nameResolver, factoryArgs });
   }
+  /**
+   * Project input.
+   *
+   * @param {object|Function|string} schema
+   * @param {object|object[]|*} data
+   * @param {object} [options]
+   * @returns {*}
+   */
+  projectInput(schema, data, options) {
+    return this.projectData(schema, data, { ...options, scope: "input" });
+  }
+  /**
+   * Project output.
+   *
+   * @param {object|Function|string} schema
+   * @param {object|object[]|*} data
+   * @param {object} [options]
+   * @returns {*}
+   */
+  projectOutput(schema, data, options) {
+    return this.projectData(schema, data, { ...options, scope: "output" });
+  }
 };
-__name(_DataProjector, "DataProjector");
-var DataProjector = _DataProjector;
+__name(_DataProjectionService, "DataProjectionService");
+var DataProjectionService = _DataProjectionService;
 // Annotate the CommonJS export names for ESM import in node:
 0 && (module.exports = {
-  DataProjector,
+  DataProjectionService,
   ProjectionSchemaRegistry,
   projectData,
   validateProjectionSchema,

+ 30 - 6
src/data-projector.js → src/data-projection-service.js

@@ -3,29 +3,29 @@ import {projectData} from './project-data.js';
 import {ProjectionSchemaRegistry} from './projection-schema-registry.js';
 
 /**
- * Data projector.
+ * Data projection service.
  */
-export class DataProjector extends Service {
+export class DataProjectionService extends Service {
   /**
-   * Define schema.
+   * Define projection.
    *
    * @param {object} schemaDef
    * @returns {this}
    */
-  defineSchema(schemaDef) {
+  defineProjection(schemaDef) {
     this.getService(ProjectionSchemaRegistry).defineSchema(schemaDef);
     return this;
   }
 
   /**
-   * Project.
+   * Project data.
    *
    * @param {object|Function|string} schema
    * @param {object|object[]|*} data
    * @param {object} [options]
    * @returns {*}
    */
-  project(schema, data, options) {
+  projectData(schema, data, options) {
     const registry = this.getService(ProjectionSchemaRegistry);
     const defaultNameResolver = name => registry.getSchema(name);
     const nameResolver =
@@ -33,4 +33,28 @@ export class DataProjector extends Service {
     const factoryArgs = (options && options.factoryArgs) || [this.container];
     return projectData(schema, data, {...options, nameResolver, factoryArgs});
   }
+
+  /**
+   * Project input.
+   *
+   * @param {object|Function|string} schema
+   * @param {object|object[]|*} data
+   * @param {object} [options]
+   * @returns {*}
+   */
+  projectInput(schema, data, options) {
+    return this.projectData(schema, data, {...options, scope: 'input'});
+  }
+
+  /**
+   * Project output.
+   *
+   * @param {object|Function|string} schema
+   * @param {object|object[]|*} data
+   * @param {object} [options]
+   * @returns {*}
+   */
+  projectOutput(schema, data, options) {
+    return this.projectData(schema, data, {...options, scope: 'output'});
+  }
 }

+ 172 - 0
src/data-projection-service.spec.js

@@ -0,0 +1,172 @@
+import {expect} from 'chai';
+import {DataProjectionService} from './data-projection-service.js';
+import {ProjectionSchemaRegistry} from './projection-schema-registry.js';
+
+describe('DataProjectionService', function () {
+  describe('defineProjection', function () {
+    it('should validate the given definition', function () {
+      const dps = new DataProjectionService();
+      const throwable = () => dps.defineProjection({});
+      expect(throwable).to.throw(
+        'Definition option "name" must be a non-empty String, ' +
+          'but undefined was given.',
+      );
+    });
+
+    it('should register the given definition', function () {
+      const def = {name: 'mySchema', schema: {}};
+      const dps = new DataProjectionService();
+      dps.defineProjection(def);
+      const registry = dps.getService(ProjectionSchemaRegistry);
+      const res = registry.getDefinition(def.name);
+      expect(res).to.be.eql(def);
+    });
+
+    it('should throw an error if the schema name is already registered', function () {
+      const dps = new DataProjectionService();
+      const def = {name: 'mySchema', schema: {}};
+      dps.defineProjection(def);
+      const throwable = () => dps.defineProjection(def);
+      expect(throwable).to.throw(
+        'Projection schema "mySchema" is already registered.',
+      );
+    });
+
+    it('should return the current instance', function () {
+      const dps = new DataProjectionService();
+      const res = dps.defineProjection({name: 'mySchema', schema: {}});
+      expect(res).to.be.eq(dps);
+    });
+  });
+
+  describe('projectData', function () {
+    it('should project the data object by the given schema', function () {
+      const dps = new DataProjectionService();
+      const res = dps.projectData({foo: true, bar: false}, {foo: 10, bar: 20});
+      expect(res).to.be.eql({foo: 10});
+    });
+
+    it('should project the data object by the schema name', function () {
+      const dps = new DataProjectionService();
+      dps.defineProjection({name: 'mySchema', schema: {foo: true, bar: false}});
+      const res = dps.projectData('mySchema', {foo: 10, bar: 20, baz: 30});
+      expect(res).to.be.eql({foo: 10, baz: 30});
+    });
+
+    it('should exclude properties without rules in the strict mode', function () {
+      const dps = new DataProjectionService();
+      dps.defineProjection({name: 'mySchema', schema: {foo: true, bar: false}});
+      const res = dps.projectData(
+        'mySchema',
+        {foo: 10, bar: 20, baz: 30},
+        {strict: true},
+      );
+      expect(res).to.be.eql({foo: 10});
+    });
+
+    it('should allow override the "nameResolver" option', function () {
+      const dps = new DataProjectionService();
+      const schemaName = 'mySchema';
+      let invoked = 0;
+      const nameResolver = name => {
+        invoked++;
+        expect(name).to.be.eq(schemaName);
+        return {foo: true, bar: false};
+      };
+      const res = dps.projectData(
+        'mySchema',
+        {foo: 10, bar: 20},
+        {nameResolver},
+      );
+      expect(res).to.be.eql({foo: 10});
+      expect(invoked).to.be.eq(1);
+    });
+
+    it('should pass the service container to the schema factory', function () {
+      const dps = new DataProjectionService();
+      let invoked = 0;
+      const factory = container => {
+        invoked++;
+        expect(container).to.be.eq(dps.container);
+        return {foo: true, bar: false};
+      };
+      const res = dps.projectData(factory, {foo: 10, bar: 20});
+      expect(res).to.be.eql({foo: 10});
+      expect(invoked).to.be.eq(1);
+    });
+
+    it('should allow override the "factoryArgs" option', function () {
+      const dps = new DataProjectionService();
+      let invoked = 0;
+      const factoryArgs = [1, 2, 3];
+      const factory = (...args) => {
+        invoked++;
+        expect(args).to.be.eql(factoryArgs);
+        return {foo: true, bar: false};
+      };
+      const res = dps.projectData(factory, {foo: 10, bar: 20}, {factoryArgs});
+      expect(res).to.be.eql({foo: 10});
+      expect(invoked).to.be.eq(1);
+    });
+  });
+
+  describe('projectInput', function () {
+    it('should invoke the "projectData" method with the "input" scope and return its result', function () {
+      const dps = new DataProjectionService();
+      const schema = {foo: true, bar: false};
+      const data = {foo: 10, bar: 20};
+      const options = {extra: true};
+      const result = {foo: 10};
+      let invoked = 0;
+      dps.projectData = (...args) => {
+        expect(args).to.be.eql([schema, data, {extra: true, scope: 'input'}]);
+        invoked++;
+        return result;
+      };
+      const res = dps.projectInput(schema, data, options);
+      expect(res).to.be.eq(result);
+      expect(invoked).to.be.eq(1);
+    });
+
+    it('should project the given object with the "input" scope', function () {
+      const dps = new DataProjectionService();
+      const schema = {
+        foo: {select: false, scopes: {input: true}},
+        bar: {select: true, scopes: {input: false}},
+      };
+      const data = {foo: 10, bar: 20};
+      const res = dps.projectInput(schema, data);
+      expect(res).to.be.eql({foo: 10});
+    });
+  });
+
+  describe('projectOutput', function () {
+    it('should invoke the "projectData" method with the "output" scope and return its result', function () {
+      const dps = new DataProjectionService();
+      const schema = {foo: true, bar: false};
+      const data = {foo: 10, bar: 20};
+      const options = {extra: true};
+      const result = {foo: 10};
+      let invoked = 0;
+      dps.projectData = (...args) => {
+        expect(args).to.be.eql([schema, data, {extra: true, scope: 'output'}]);
+        invoked++;
+        return result;
+      };
+      const res = dps.projectOutput(schema, data, options);
+      expect(res).to.be.eq(result);
+      expect(invoked).to.be.eq(1);
+    });
+
+    it('should project the given object with the "output" scope', function () {
+      const dps = new DataProjectionService();
+      const schema = {
+        foo: {select: false, scopes: {output: true}},
+        bar: {select: true, scopes: {output: false}},
+      };
+      const data = {foo: 10, bar: 20};
+      const res = dps.projectOutput(schema, data);
+      expect(res).to.be.eql({foo: 10});
+    });
+  });
+});

+ 0 - 108
src/data-projector.spec.js

@@ -1,108 +0,0 @@
-import {expect} from 'chai';
-import {DataProjector} from './data-projector.js';
-import {ProjectionSchemaRegistry} from './projection-schema-registry.js';
-
-describe('DataProjector', function () {
-  describe('defineSchema', function () {
-    it('should validate the given definition', function () {
-      const S = new DataProjector();
-      const throwable = () => S.defineSchema({});
-      expect(throwable).to.throw(
-        'Definition option "name" must be a non-empty String, ' +
-          'but undefined was given.',
-      );
-    });
-
-    it('should register the given definition', function () {
-      const def = {name: 'mySchema', schema: {}};
-      const S = new DataProjector();
-      S.defineSchema(def);
-      const registry = S.getService(ProjectionSchemaRegistry);
-      const res = registry.getDefinition(def.name);
-      expect(res).to.be.eql(def);
-    });
-
-    it('should throw an error if the schema name is already registered', function () {
-      const S = new DataProjector();
-      const def = {name: 'mySchema', schema: {}};
-      S.defineSchema(def);
-      const throwable = () => S.defineSchema(def);
-      expect(throwable).to.throw(
-        'Projection schema "mySchema" is already registered.',
-      );
-    });
-
-    it('should return the current instance', function () {
-      const S = new DataProjector();
-      const res = S.defineSchema({name: 'mySchema', schema: {}});
-      expect(res).to.be.eq(S);
-    });
-  });
-
-  describe('project', function () {
-    it('should project the data object by the given schema', function () {
-      const S = new DataProjector();
-      const res = S.project({foo: true, bar: false}, {foo: 10, bar: 20});
-      expect(res).to.be.eql({foo: 10});
-    });
-
-    it('should project the data object by the schema name', function () {
-      const S = new DataProjector();
-      S.defineSchema({name: 'mySchema', schema: {foo: true, bar: false}});
-      const res = S.project('mySchema', {foo: 10, bar: 20, baz: 30});
-      expect(res).to.be.eql({foo: 10, baz: 30});
-    });
-
-    it('should exclude properties without rules in the strict mode', function () {
-      const S = new DataProjector();
-      S.defineSchema({name: 'mySchema', schema: {foo: true, bar: false}});
-      const res = S.project(
-        'mySchema',
-        {foo: 10, bar: 20, baz: 30},
-        {strict: true},
-      );
-      expect(res).to.be.eql({foo: 10});
-    });
-
-    it('should allow override the "nameResolver" option', function () {
-      const S = new DataProjector();
-      const schemaName = 'mySchema';
-      let invoked = 0;
-      const nameResolver = name => {
-        invoked++;
-        expect(name).to.be.eq(schemaName);
-        return {foo: true, bar: false};
-      };
-      const res = S.project('mySchema', {foo: 10, bar: 20}, {nameResolver});
-      expect(res).to.be.eql({foo: 10});
-      expect(invoked).to.be.eq(1);
-    });
-
-    it('should pass the service container to the schema factory', function () {
-      const S = new DataProjector();
-      let invoked = 0;
-      const factory = container => {
-        invoked++;
-        expect(container).to.be.eq(S.container);
-        return {foo: true, bar: false};
-      };
-      const res = S.project(factory, {foo: 10, bar: 20});
-      expect(res).to.be.eql({foo: 10});
-      expect(invoked).to.be.eq(1);
-    });
-
-    it('should allow override the "factoryArgs" option', function () {
-      const S = new DataProjector();
-      let invoked = 0;
-      const factoryArgs = [1, 2, 3];
-      const factory = (...args) => {
-        invoked++;
-        expect(args).to.be.eql(factoryArgs);
-        return {foo: true, bar: false};
-      };
-      const res = S.project(factory, {foo: 10, bar: 20}, {factoryArgs});
-      expect(res).to.be.eql({foo: 10});
-      expect(invoked).to.be.eq(1);
-    });
-  });
-});

+ 1 - 1
src/index.js

@@ -1,5 +1,5 @@
 export * from './project-data.js';
-export * from './data-projector.js';
+export * from './data-projection-service.js';
 export * from './projection-schema-registry.js';
 export * from './validate-projection-schema.js';
 export * from './validate-projection-schema-definition.js';