Browse Source

feat: allows the schema factory return a schema name

e22m4u 2 days ago
parent
commit
d3f8a6c825
6 changed files with 90 additions and 55 deletions
  1. 2 2
      README.md
  2. 16 14
      dist/cjs/index.cjs
  3. 6 2
      src/project-data.d.ts
  4. 29 22
      src/project-data.js
  5. 25 5
      src/project-data.spec.js
  6. 12 10
      src/projection-schema.d.ts

+ 2 - 2
README.md

@@ -39,13 +39,13 @@ npm install @e22m4u/js-data-projection
 
 Сигнатура:
 
-- `projectData(schemaOrSource, data, [options])`: возвращает проекцию;
+- `projectData(schemaOrFactory, data, [options])`: возвращает проекцию;
   - `schemaOrFactory: object | Function | string`: схема, фабрика или имя схемы;
   - `data: object | object[]`: проектируемые данные;
   - `options?: object`: объект настроек;
     - `strict?: boolean`: строгий режим;
     - `scope?: string`: область проекции;
-    - `resolver?: (name: string) => object`: фабрика именных схем;
+    - `resolver?: Function`: функция разрешения схемы;
 
 #### Создание проекции
 

+ 16 - 14
dist/cjs/index.cjs

@@ -112,11 +112,11 @@ function validateProjectionSchema(schema, shallowMode = false) {
 __name(validateProjectionSchema, "validateProjectionSchema");
 
 // src/project-data.js
-function projectData(schemaOrSource, data, options = void 0) {
-  if (!schemaOrSource || typeof schemaOrSource !== "object" && typeof schemaOrSource !== "function" && typeof schemaOrSource !== "string" || Array.isArray(schemaOrSource)) {
+function projectData(schemaOrFactory, data, options = void 0) {
+  if (!schemaOrFactory || typeof schemaOrFactory !== "object" && typeof schemaOrFactory !== "function" && typeof schemaOrFactory !== "string" || Array.isArray(schemaOrFactory)) {
     throw new import_js_format2.InvalidArgumentError(
       "Projection schema must be an Object, a Function or a non-empty String, but %v was given.",
-      schemaOrSource
+      schemaOrFactory
     );
   }
   if (options !== void 0) {
@@ -147,26 +147,28 @@ function projectData(schemaOrSource, data, options = void 0) {
   }
   const strict = Boolean(options && options.strict);
   const scope = options && options.scope || void 0;
-  let schema = schemaOrSource;
-  if (typeof schemaOrSource === "function") {
-    schema = schemaOrSource();
-    if (!schema || typeof schema !== "object" || Array.isArray(schema)) {
+  let schemaOrName = schemaOrFactory;
+  if (typeof schemaOrFactory === "function") {
+    schemaOrName = schemaOrFactory();
+    if (!schemaOrName || typeof schemaOrName !== "object" && typeof schemaOrName !== "string" || Array.isArray(schemaOrName)) {
       throw new import_js_format2.InvalidArgumentError(
-        "Schema factory must return an Object, but %v was given.",
-        schema
+        "Projection schema factory must return an Object or a non-empty String, but %v was given.",
+        schemaOrName
       );
     }
-  } else if (typeof schemaOrSource === "string") {
+  }
+  let schema = schemaOrName;
+  if (schemaOrName && typeof schemaOrName === "string") {
     if (!options || !options.resolver) {
       throw new import_js_format2.InvalidArgumentError(
-        "Unable to resolve the schema %v without a specified resolver.",
-        schemaOrSource
+        "Unable to resolve the projection schema %v without a provided resolver.",
+        schemaOrName
       );
     }
-    schema = options.resolver(schemaOrSource);
+    schema = options.resolver(schemaOrName);
     if (!schema || typeof schema !== "object" || Array.isArray(schema)) {
       throw new import_js_format2.InvalidArgumentError(
-        "Schema resolver must return an Object, but %v was given.",
+        "Projection schema resolver must return an Object, but %v was given.",
         schema
       );
     }

+ 6 - 2
src/project-data.d.ts

@@ -1,6 +1,7 @@
 import {
   ProjectionSchema,
-  ProjectionSchemaSource,
+  ProjectionSchemaName,
+  ProjectionSchemaFactory,
 } from './projection-schema.js';
 
 /**
@@ -25,7 +26,10 @@ export type ProjectDataOptions = {
  * @param options
  */
 export declare function projectData<T>(
-  schemaOrSource: ProjectionSchema | ProjectionSchemaSource,
+  schemaOrFactory:
+    | ProjectionSchema
+    | ProjectionSchemaFactory
+    | ProjectionSchemaName,
   data: T,
   options?: ProjectDataOptions,
 ): T;

+ 29 - 22
src/project-data.js

@@ -4,24 +4,24 @@ import {validateProjectionSchema} from './validate-projection-schema.js';
 /**
  * Project data.
  *
- * @param {object|Function|string} schemaOrSource
+ * @param {object|Function|string} schemaOrFactory
  * @param {object} data
  * @param {object|undefined} options
  * @returns {*}
  */
-export function projectData(schemaOrSource, data, options = undefined) {
-  // schemaOrSource
+export function projectData(schemaOrFactory, data, options = undefined) {
+  // schemaOrFactory
   if (
-    !schemaOrSource ||
-    (typeof schemaOrSource !== 'object' &&
-      typeof schemaOrSource !== 'function' &&
-      typeof schemaOrSource !== 'string') ||
-    Array.isArray(schemaOrSource)
+    !schemaOrFactory ||
+    (typeof schemaOrFactory !== 'object' &&
+      typeof schemaOrFactory !== 'function' &&
+      typeof schemaOrFactory !== 'string') ||
+    Array.isArray(schemaOrFactory)
   ) {
     throw new InvalidArgumentError(
       'Projection schema must be an Object, a Function ' +
         'or a non-empty String, but %v was given.',
-      schemaOrSource,
+      schemaOrFactory,
     );
   }
   // options
@@ -64,35 +64,42 @@ export function projectData(schemaOrSource, data, options = undefined) {
   const scope = (options && options.scope) || undefined;
   // если вместо схемы передана фабрика,
   // то извлекается фабричное значение
-  let schema = schemaOrSource;
-  if (typeof schemaOrSource === 'function') {
-    schema = schemaOrSource();
-    // если не удалось извлечь схему проекции,
-    // то выбрасывается ошибка
-    if (!schema || typeof schema !== 'object' || Array.isArray(schema)) {
+  let schemaOrName = schemaOrFactory;
+  if (typeof schemaOrFactory === 'function') {
+    schemaOrName = schemaOrFactory();
+    // если фабричное значение не является строкой
+    // или объектом, то выбрасывается ошибка
+    if (
+      !schemaOrName ||
+      (typeof schemaOrName !== 'object' && typeof schemaOrName !== 'string') ||
+      Array.isArray(schemaOrName)
+    ) {
       throw new InvalidArgumentError(
-        'Schema factory must return an Object, but %v was given.',
-        schema,
+        'Projection schema factory must return an Object ' +
+          'or a non-empty String, but %v was given.',
+        schemaOrName,
       );
     }
   }
   // если вместо схемы передана строка,
   // то строка используется как название схемы
-  else if (typeof schemaOrSource === 'string') {
+  let schema = schemaOrName;
+  if (schemaOrName && typeof schemaOrName === 'string') {
     // если функция разрешения схемы не определена,
     // то выбрасывается ошибка
     if (!options || !options.resolver) {
       throw new InvalidArgumentError(
-        'Unable to resolve the schema %v without a specified resolver.',
-        schemaOrSource,
+        'Unable to resolve the projection schema %v ' +
+          'without a provided resolver.',
+        schemaOrName,
       );
     }
-    schema = options.resolver(schemaOrSource);
+    schema = options.resolver(schemaOrName);
     // если не удалось извлечь схему проекции,
     // то выбрасывается ошибка
     if (!schema || typeof schema !== 'object' || Array.isArray(schema)) {
       throw new InvalidArgumentError(
-        'Schema resolver must return an Object, but %v was given.',
+        'Projection schema resolver must return an Object, but %v was given.',
         schema,
       );
     }

+ 25 - 5
src/project-data.spec.js

@@ -151,8 +151,11 @@ describe('projectData', function () {
     it('should throw an error if the schema factory returns an invalid value', function () {
       const throwable = v => () => projectData(() => v, {});
       const error = s =>
-        format('Schema factory must return an Object, but %s was given.', s);
-      expect(throwable('str')).to.throw(error('"str"'));
+        format(
+          'Projection schema factory must return an Object ' +
+            'or a non-empty String, but %s was given.',
+          s,
+        );
       expect(throwable('')).to.throw(error('""'));
       expect(throwable(10)).to.throw(error('10'));
       expect(throwable(0)).to.throw(error('0'));
@@ -161,7 +164,8 @@ describe('projectData', function () {
       expect(throwable([])).to.throw(error('Array'));
       expect(throwable(null)).to.throw(error('null'));
       expect(throwable(undefined)).to.throw(error('undefined'));
-      throwable({})();
+      projectData(() => ({}), {});
+      projectData(() => 'mySchema', {}, {resolver: () => ({})});
     });
 
     it('should resolve a schema object from the given factory', function () {
@@ -195,7 +199,10 @@ describe('projectData', function () {
       const throwable = v => () =>
         projectData('mySchema', {}, {resolver: () => v});
       const error = s =>
-        format('Schema resolver must return an Object, but %s was given.', s);
+        format(
+          'Projection schema resolver must return an Object, but %s was given.',
+          s,
+        );
       expect(throwable('str')).to.throw(error('"str"'));
       expect(throwable('')).to.throw(error('""'));
       expect(throwable(10)).to.throw(error('10'));
@@ -211,7 +218,8 @@ describe('projectData', function () {
     it('should throw an error if no schema resolver is provided when a schema name is given', function () {
       const throwable = () => projectData('mySchema', {});
       expect(throwable).to.throw(
-        'Unable to resolve the schema "mySchema" without a specified resolver.',
+        'Unable to resolve the projection schema "mySchema" ' +
+          'without a provided resolver.',
       );
     });
 
@@ -242,6 +250,18 @@ describe('projectData', function () {
       expect(res).to.be.eql({foo: 10, bar: {baz: 20}});
       expect(invoked).to.be.eq(1);
     });
+
+    it('should resolve the schema name from the schema factory', function () {
+      let invoked = 0;
+      const resolver = name => {
+        expect(name).to.be.eq('mySchema');
+        invoked++;
+        return {foo: true, bar: false};
+      };
+      const res = projectData(() => 'mySchema', {foo: 10, bar: 20}, {resolver});
+      expect(res).to.be.eql({foo: 10});
+      expect(invoked).to.be.eq(1);
+    });
   });
 
   describe('strict mode', function () {

+ 12 - 10
src/projection-schema.d.ts

@@ -3,17 +3,19 @@
  */
 export type ProjectionSchema = {
   [property: string]: boolean | ProjectionSchemaPropertyOptions | undefined;
-}
+};
 
-/** 
- * Projection schema factory.
+/**
+ * Projection schema name.
  */
-export type ProjectionSchemaFactory = () => ProjectionSchema;
+export type ProjectionSchemaName = string;
 
 /**
- * Projection schema source.
+ * Projection schema factory.
  */
-export type ProjectionSchemaSource = string | ProjectionSchemaFactory;
+export type ProjectionSchemaFactory = () =>
+  | ProjectionSchema
+  | ProjectionSchemaName;
 
 /**
  * Projection schema property options.
@@ -21,19 +23,19 @@ export type ProjectionSchemaSource = string | ProjectionSchemaFactory;
 export type ProjectionSchemaPropertyOptions = {
   select?: boolean;
   scopes?: ProjectionSchemaScopes;
-  schema?: ProjectionSchema | ProjectionSchemaSource;
-}
+  schema?: ProjectionSchema | ProjectionSchemaFactory | ProjectionSchemaName;
+};
 
 /**
  * Projection schema scopes.
  */
 export type ProjectionSchemaScopes = {
   [scope: string]: boolean | ProjectionSchemaScopeOptions | undefined;
-}
+};
 
 /**
  * Projection schema scope options.
  */
 export type ProjectionSchemaScopeOptions = {
   select?: boolean;
-}
+};