npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@dlabs71/d-dto

v1.1.0

Published

Library for creating API DTO models

Downloads

14

Readme

D-dto

Библиотека для создания классов DTO, предоставляющая поддержку конвертации из json в dto класс и обратно

NPM Version License

Установка NPM

npm i @dlabs71/d-dto

Использование

Данная библиотека может быть использована в любом js приложении, вне зависимости от фреймворка. Она предназначена для определения классов DTO описывающих инфо модели API-интерфейса. Также библиотека предоставляет декораторы функций реализующих REST запросы, они конвертируют JSON результат в экземпляры указанных классов DTO.

Для того чтобы создать dto класс, достаточно определить класс с полями над которыми будут вспомогательные декораторы. Список всех декораторов смотрите в документации

import {JsonField, TypeString, TypeNumber} from '@dlabs71/d-dto';

class ArticleDto {
    @JsonField("article_id") @TypeNumber id;
    @JsonField("article_name") @TypeString name;
    @JsonField("article_desc") @TypeString description;
}

Далее данный dto класс можно использовать в декораторах функций API.

import axios from 'axios';
import {GetMapper} from '@dlabs71/d-dto';

export default {

    @GetMapper(ArticleDto)
    getArticleById(articleId) {
        return axios.get(`/article/${articleId}`);
    },

    @GetMapper(ArticleDto)
    getAllArticles() {
        return axios.get(`/article/all`);
    }
}

В результате выполнения функции будет следующий ответ

/*
    JSON response API: {
        config: ....,
        status: 200,
        data: {
            article_id: 123,
            article_name: "Name",
            article_desc: "Description"
        }
    }
 */


getArticleById(123).then(dto => {
    /*  
        dto = ArticleDto {
            id = 123;
            name = "Name";
            description = "Description";
        }
     */
});

getAllArticles().then(result => {
    /*  
        result = [
            ArticleDto {
                id = 123;
                name = "Name";
                description = "Description";
            },
            ArticleDto {
                id = 124;
                name = "Name 2";
                description = "Description 2";
            },
            ArticleDto {
                id = 125;
                name = "Name 3";
                description = "Description 3";
            }
        ]
     */
});

Документация

Оглавление

1. Создание DTO-моделей

DTO-модель - это класс(-ы) описывающий REST API представление, которым клиент и сервер обмениваются между друг другом. Простыми словами, DTO-модель - это класс описывающий JSON, который передаётся в теле запроса или ответа. Для создания DTO-модели применяются специальные JS декораторы, предоставляемые библиотекой.

1.1. Декоратор @JsonField

Данный декоратор предназначен для определения наименования поля в JSON объекте. При конвертации из JSON в DTO-модель значение в поле класса будет записано из указанного наименования поля JSON в данном декораторе. При создании JSON объекта из DTO-модели наименование поля будет создано из наименования указанного в декораторе.

import {JsonField} from '@dlabs71/d-dto';

class ArticleDto {
    @JsonField("article_id") id;
    @JsonField("article_name") name;
    @JsonField("article_desc") description;
}

/*
Соответствующий JSON для данной модели будет следующим:
{
    article_id: 123,
    article_name: "Name",
    article_desc: "Description"  
}
 */

Параметр данного декоратора также может быть и массивом. При этом при конвертации из JSON объекта в DTO-модель значение для записи в поле класса будет выбрано из поля в JSON, которое первое найдется из списка. При конвертации в JSON для наименования поля будет использовано первое значение из списка в параметре декоратора.

import {JsonField} from '@dlabs71/d-dto';

class LookupDto {
    @JsonField(["value", "id"]) value;
    @JsonField(["title", "text"]) title;
}

/*
Соответствующие JSON-ы для данной модели будет следующим:
{
    value: 123,
    title: "Name"
},
{
    id: 123,
    title: "Name"
},
{
    id: 123,
    text: "Name"
}

При преобразовании из DTO-модели в JSON:
{
    value: 123,
    title: "Name"
}
 */

1.2. Декораторы типов

Декораторы типов предназначены для указания какого типа должно быть значение поля. Для каждого типа создан специальны декоратор.

1.2.1. Декоратор @TypeString

@TypeString - предназначен для преобразования в string. Если в json поле содержится значение другого типа, то оно будет конвертировано в string. Если это объект или массив, то к нему будет применена функция JSON.stringify.

import {JsonField, TypeString} from '@dlabs71/d-dto';

class Dto {
    @JsonField("value") @TypeString value;
}

| Значение JSON | Значение DTO | | :---------------------------------- | :------------------------------------ | | "value" | "value" | | 123 | "123" | | true | "true" | | undefined | undefined | | null | null | | NaN | "NaN" | | { a: "qwe", b: "qwe", data: 123 } | "{ a: "qwe", b: "qwe", data: 123 }" | | [123, 234, 345] | "[123, 234, 345]" |

1.2.2. Декоратор @TypeNumber

@TypeNumber - предназначен для преобразования в number.

import {JsonField, TypeNumber} from '@dlabs71/d-dto';

class Dto {
    @JsonField("value") @TypeNumber value;
}

| Значение JSON | Значение DTO | | :---------------------------------- | :------------------------- | | "value" | NaN | | 123 | 123 | | "123" | 123 | | true | 1 | | false | 0 | | undefined | undefined | | null | null | | NaN | NaN | | { a: "qwe", b: "qwe", data: 123 } | NaN | | [123, 234, 345] | NaN |

1.2.3. Декоратор @TypeBool

@TypeBool - предназначен для преобразования в boolean.

import {JsonField, TypeBool} from '@dlabs71/d-dto';

class Dto {
    @JsonField("value") @TypeBool value;
}

| Значение JSON | Значение DTO | | :---------------------------------- | :------------------------- | | "value" | false | | 123 | false | | true | true | | false | false | | undefined | undefined | | null | null | | NaN | false | | { a: "qwe", b: "qwe", data: 123 } | false | | [123, 234, 345] | false |

1.2.4. Декоратор @TypeYesNo

@TypeYesNo - предназначен для преобразования из "Y"/"N" в boolean.

import {JsonField, TypeYesNo} from '@dlabs71/d-dto';

class Dto {
    @JsonField("value") @TypeYesNo value;
}

| Значение JSON | Значение DTO | | :---------------------------------- | :------------------------- | | "value" | false | | "Y" | true | | 123 | false | | true | false | | false | false | | undefined | undefined | | null | null | | NaN | false | | { a: "qwe", b: "qwe", data: 123 } | false | | [123, 234, 345] | false |

1.2.5. Декоратор @TypeDate()

@TypeDate() - предназначен для преобразования из строки в дату (экземпляр класса moment.Moment). Смотрите документацию по moment.

Данный декоратор имеет два параметра:

  • format - формат даты.
  • l10n - локализация даты.

Форматы смотрите в документации к библиотеки moment Варианты локализации также смотрите в документации к библиотеки moment

import {JsonField, TypeDate} from '@dlabs71/d-dto';

class Dto {
    @JsonField("value") @TypeDate() value;
    @JsonField("value1") @TypeDate("YYYY-MM-DD") value1;

    // сможет распознать следующий вид даты: 1 января 2022
    @JsonField("value2") @TypeDate("DD MMMM YYYY", "ru") value2;
}

| Значение JSON | Значение DTO | | :---------------------------------| :----------------------------------| | "value" | null | | 123 | null | | true | null | | false | null | | undefined | undefined | | null | null | | NaN | null | | { a: "qwe", b: "qwe", data: 123 } | null | | [123, 234, 345] | null | | "20.01.2022" | moment("2022-01-20", "YYYY-MM-DD") |

Форматы, которые могут быть распознаны по умолчанию указаны в списке ниже:

  • DD.MM.YYYY
  • DD-MM-YYYY
  • DD/MM/YYYY
  • YYYY-MM-DD

1.2.6. Декоратор @TypeDateTime()

@TypeDateTime() - предназначен для преобразования из строки в дату со временем (экземпляр класса moment.Moment). Смотрите документацию по moment.

Данный декоратор имеет два параметра:

  • format - формат даты.
  • l10n - локализация даты.

Форматы смотрите в документации к библиотеки moment Варианты локализации также смотрите в документации к библиотеки moment

import {JsonField, TypeDateTime} from '@dlabs71/d-dto';

class Dto {
    @JsonField("value") @TypeDateTime() value;
    @JsonField("value1") @TypeDateTime("YYYY-MM-DDTHH:mm:ss") value1;

    // сможет распознать следующий вид даты: 1 января 2022 20:01
    @JsonField("value2") @TypeDate("DD MMMM YYYY HH:mm", "ru") value2;
}

| Значение JSON | Значение DTO | | :---------------------------------- | :----------------------------------------------------- | | "value" | null | | 123 | null | | true | null | | false | null | | undefined | undefined | | null | null | | NaN | null | | { a: "qwe", b: "qwe", data: 123 } | null | | [123, 234, 345] | null | | "2022-01-01T20:01:22" | moment("2022-01-01T20:01:22", "YYYY-MM-DDTHH:mm:ss") |

Форматы, которые могут быть распознаны по умолчанию указаны в списке ниже:

Форматы обычных дат. При этом время будет выставлено в 00:00:00.

  • DD.MM.YYYY
  • DD-MM-YYYY
  • DD/MM/YYYY
  • YYYY-MM-DD

Форматы дат со временем:

  • DD.MM.YYYY HH:mm:ss
  • DD-MM-YYYY HH:mm:ss
  • DD/MM/YYYY HH:mm:ss
  • YYYY-MM-DD HH:mm:ss
  • DD.MM.YYYY HH:mm:ssZ
  • DD-MM-YYYY HH:mm:ssZ
  • DD/MM/YYYY HH:mm:ssZ
  • YYYY-MM-DD HH:mm:ssZ
  • DD.MM.YYYYTHH:mm:ss
  • DD-MM-YYYYTHH:mm:ss
  • DD/MM/YYYYTHH:mm:ss
  • YYYY-MM-DDTHH:mm:ss
  • DD.MM.YYYYTHH:mm:ssZ
  • DD-MM-YYYYTHH:mm:ssZ
  • DD/MM/YYYYTHH:mm:ssZ
  • YYYY-MM-DDTHH:mm:ssZ

1.2.7. Декоратор @TypeJsonObj

@TypeJsonObj - предназначен для преобразования из простого JS объекта или массива.

import {JsonField, TypeJsonObj} from '@dlabs71/d-dto';

class Dto {
    @JsonField("value") @TypeJsonObj value;
}

| Значение JSON | Значение DTO | | :------------------------------------ | :---------------------------------- | | "value" | null | | 123 | null | | true | null | | false | null | | undefined | undefined | | null | null | | NaN | null | | { a: "qwe", b: "qwe", data: 123 } | { a: "qwe", b: "qwe", data: 123 } | | "{ a: "qwe", b: "qwe", data: 123 }" | { a: "qwe", b: "qwe", data: 123 } | | [123, 234, 345] | [123, 234, 345] | | "[123, 234, 345]" | [123, 234, 345] |

1.2.8. Декоратор @TypeCustom()

@TypeCustom() - предназначен для преобразования из простого JS объекта в DTO-модель.

import {JsonField, TypeCustom, TypeString} from '@dlabs71/d-dto';

class SubDto {
    @JsonField("value") @TypeString value;
}

class Dto {
    @JsonField("value") @TypeCustom(SubDto) value;
}

| Значение JSON | Значение DTO | | :---------------------------------- | :------------------------- | | { a: "qwe", b: "qwe", data: 123 } | SubDto { value: null } | | { value: "123" } | SubDto { value: "123" } | | undefined | undefined | | null | null | | NaN | null |

1.2.9. Декоратор @TypeArr()

@TypeArr() - предназначен для преобразования из массива. У данного декоратора есть три параметра:

  • type - тип значения элемента массива. Используйте константу-перечисление DATA_TYPE для указания данного типа.
  • customClass - класс DTO-модели. Используется если указан тип DATA_TYPE.CUSTOM
  • format - строковый формат даты/даты со временем. Используется если указан тип DATA_TYPE.DATE или DATA_TYPE.DATE_TIME Смотрите документацию по декораторам @TypeDate и @TypeDateTime
import {JsonField, TypeString, TypeArr, DATA_TYPE} from '@dlabs71/d-dto';

class SubDto {
    @JsonField("value") @TypeString value;
}

class Dto {
    @JsonField("arr1") @TypeArr(DATA_TYPE.CUSTOM, SubDto) arr1;
    @JsonField("arr2") @TypeArr(DATA_TYPE.STRING) arr1;
    @JsonField("arr3") @TypeArr(DATA_TYPE.DATE, null, "YYYY-MM-DD") arr3;
}

/*
{
    arr1: [{"value": "value1"}, {"value": "value2"}],
    arr2: ["value1", "value2"],
    arr3: ["2022-02-02", "2022-01-01"]
}
 */

| Значение JSON | Значение DTO | | :-------------------------- | :------------------------- | | undefined | undefined | | null | null | | NaN | null |

1.3. Хуки конвертаций DTO-моделей

Для выполнения дополнительных действий при конвертации из JSON в DTO-модель и наоборот существует 4 функции:

| Функция | Описание | | :----------------------------------- | :------------------------------------------------------------------------- | | beforeJ2cMapping(jsonObj, dto) | Функция будет выполнена до процесса конвертации из JSON в DTO-модель | | afterJ2cMapping(jsonObj, dto) | Функция будет выполнена после процесса конвертации из JSON в DTO-модель | | beforeC2jMapping(dto, resultJsonObj) | Функция будет выполнена до процесса конвертации из DTO-модели в JSON | | afterC2jMapping(dto, resultJsonObj) | Функция будет выполнена после процесса конвертации из DTO-модели в JSON |

Пример использования хуков конвертации из DTO-модели в JSON:

class Model {
    @JsonField("first_name") @TypeString firstName;
    @JsonField("second_name") @TypeString secondName;
    @JsonField("age") @TypeNumber age;

    beforeC2jMapping(dtoModel, resultJsonObj) {
        resultJsonObj["beforeC2j"] = "beforeC2j";
    }

    afterC2jMapping(dtoModel, resultJsonObj) {
        resultJsonObj["afterC2j"] = "afterC2j";
    }
}

let dto = new Model();
dto.firstName = "Danila";
dto.secondName = "Ivanov";
dto.age = 24;

let json = c2jMapperWrapper(dto);

/*
    json = {
        first_name: "Danila",
        second_name: "Ivanov",
        age: 24,
        beforeC2j: "beforeC2j",
        afterC2j: "afterC2j"
    }
 */

Пример использования хуков конвертации из JSON в DTO-модель:

let sourceJson = {
    first_name: "Danila",
    second_name: "Ivanov",
    age: 24
};

class Model {
    @JsonField("first_name") @TypeString firstName;
    @JsonField("second_name") @TypeString secondName;
    @JsonField("age") @TypeNumber age;

    beforeJ2cMapping(jsonObj, dtoModel) {
        this.beforeJ2c = jsonObj["first_name"];
    }

    afterJ2cMapping(jsonObj, dtoModel) {
        this.afterJ2c = jsonObj["second_name"];
    }
}

let dto = j2cMapperWrapper(sourceJson, Model);

/*
    dto = Model {
        firstName = "Danila";
        secondName = "Ivanov";
        age = 24;
        beforeJ2c = "Danila";
        afterJ2c = "Ivanov";
    }
 */

2. Создание API сервисов

Под созданием API сервисов подразумевается создание функций реализующих REST API запросы. Для автоматической конвертации ответов данных сервисов библиотека предоставляет декораторы.

2.1. Декораторы @GetMapper и @DeleteMapper

@GetMapper и @DeleteMapper - декораторы предназначенные для конвертации ответов GET и DELETE запросов. Они имеют несколько параметров:

  • modelResponse - класс DTO-модели описывающий тело ответа
  • pathToData - путь до поля с данными ответа в JSON-е. По умолчанию = data.
  • strict - если true то включается строгий режим конвертации (по умолчанию false). Т.е. все поля класса должны быть помечены декоратором @JsonField

Если функция возвращает Promise, то при применении декоратора он конвертирует ответ, который будет в .then() и передаст его дальше по цепочке.

Если функция возвращает просто объект, то декоратор конвертирует этот объект и вернет экземпляр класса DTO-модели указанной в его параметрах.

Если функция возвращает массив (вне зависимости в Promise или нет), то декоратор считает, что функция возвращает массив объектов, каждый из которых может быть конвертируем в DTO-модель указанную в параметрах декоратора. Соответственно, функция вернет массив экземпляров классов DTO-моделей.

import axios from 'axios';
import {JsonField, TypeString, TypeNumber, GetMapper, DeleteMapper} from '@dlabs71/d-dto';

class ArticleDto {
    @JsonField("article_id") @TypeNumber id;
    @JsonField("article_name") @TypeString name;
    @JsonField("content") @TypeString content;
}

export default {

    /*
        пример получения одного объекта
        запрос вернет следующий JSON: {
            status: 200,
            statusText: 'OK',
            headers: {},
            config: {},
            request: {},
            data: {
                article_id: 1,
                article_name: "Name 1",
                content: "Content"
            }
        }
     */
    @GetMapper(ArticleDto)
    getArticleById(articleId) {
        return axios.get(`/article/${articleId}`);
    },

    /*
        пример получения массива объектов
        запрос вернет следующий JSON: {
            status: 200,
            statusText: 'OK',
            headers: {},
            config: {},
            request: {},
            data: [
                {
                    article_id: 1,
                    article_name: "Name 1",
                    content: "Content"
                },
                {
                    article_id: 2,
                    article_name: "Name 2",
                    content: "Content"
                },
                {
                    article_id: 3,
                    article_name: "Name 3",
                    content: "Content"
                }
            ]
        }
     */
    @GetMapper(ArticleDto)
    getAllArticles() {
        return axios.get(`/article/all`);
    },

    /*
        пример удаления
        запрос вернет следующий JSON: {
            status: 200,
            statusText: 'OK',
            headers: {},
            config: {},
            request: {},
            data: {
                article_id: 1,
                article_name: "Name 1",
                content: "Content"
            }
        }
     */
    @DeleteMapper(ArticleDto)
    deleteArticleById(articleId) {
        return axios.delete(`/article/${articleId}`);
    }
}


getArticleById(1).then(article => {
    /*
        article = ArticleDto {
            id = 1;
            name = "Name 1";
            content = "Content";
        }
     */
});

getAllArticles().then(articles => {
    /*
        articles = [
            ArticleDto {
                id = 1;
                name = "Name 1";
                content = "Content";
            },
            ArticleDto {
                id = 2;
                name = "Name 2";
                content = "Content";
            },
            ArticleDto {
                id = 3;
                name = "Name 3";
                content = "Content";
            },
        ]
     */
});

deleteArticleById(1).then(article => {
    /*
        article = ArticleDto {
            id = 1;
            name = "Name 1";
            content = "Content";
        }
     */
});

2.2. Декораторы @PostMapper и @PutMapper

@PostMapper и @PutMapper - декораторы предназначенные для конвертации ответов POST и PUT запросов. Они имеют несколько параметров:

  • modelRequest - класс DTO-модели описывающий тело запроса
  • modelResponse - класс DTO-модели описывающий тело ответа. По умолчанию он будет равен modelRequest
  • dtoArgNumber - индекс параметра функции в котором передается DTO для отправки в теле запроса. По умолчанию = 0.
  • pathToData - путь до поля с данными ответа в JSON-е. По умолчанию = data
  • strict - если true то включается строгий режим конвертации (по умолчанию false). Т.е. все поля класса должны быть помечены декоратором @JsonField

По обработке ответа данные декораторы работают аналогично @GetMapper и @DeleteMapper.

Данные декораторы также могут конвертировать из экземпляра класса DTO-модели в JSON объект перед выполнением функции. Для этого существуют параметры modelRequest и dtoArgNumber.

Если тело запроса и тело ответа одинаковое, то можно указать только параметр modelRequest.

import axios from 'axios';
import {JsonField, TypeString, TypeNumber, TypeCustom, PostMapper, PutMapper} from '@dlabs71/d-dto';

class ArticleDto {
    @JsonField("article_id") @TypeNumber id;
    @JsonField("article_name") @TypeString name;
    @JsonField("content") @TypeString content;
}

class SendResponseDto {
    @JsonField("request_id") @TypeNumber requestId;
    @JsonField("request_status") @TypeString requestStatus;
    @JsonField("article") @TypeCustom(ArticleDto) article;
}

export default {

    /*
        пример создания объекта.
        запрос вернет следующий JSON: {
            status: 200,
            statusText: 'OK',
            headers: {},
            config: {},
            request: {},
            data: {
                article_id: 1,
                article_name: "Name 1",
                content: "Content"
            }
        }
     */
    @PostMapper(ArticleDto)
    createArticle(articleDto) {
        /*
            Так как указан декоратор @PostMapper, то перед выполнением данной функции параметр articleDto
            будет конвертирован в JSON объект. 
            И здесь он уже будет равен:
            articleDto = {
                article_id: 1,
                article_name: "Name 1",
                content: "Content"
            }
         */
        return axios.post(`/article`, articleDto);
    },

    /*
        пример создания объекта.
        запрос вернет следующий JSON: {
            status: 200,
            statusText: 'OK',
            headers: {},
            config: {},
            request: {},
            data: {
                request_id: 1,
                request_status: "SUCCESS",
                article: {
                    article_id: 1,
                    article_name: "Name 1",
                    content: "Content"
                }
            }
        }
     */
    @PostMapper(ArticleDto, SendResponseDto, 2)
    sendArticle(userIdFrom, userIdTo, articleDto) {
        /*
            Так как указан декоратор @PostMapper, то перед выполнением данной функции параметр с 
            индексом = 2 т.е. articleDto будет конвертирован в JSON объект. 
            И здесь он уже будет равен:
            articleDto = {
                article_id: 1,
                article_name: "Name 1",
                content: "Content"
            }
         */
        return axios.post(`/article/send/${userIdFrom}/${userIdTo}`, articleDto);
    },

    /*
        пример обновления объекта.
        запрос вернет следующий JSON: {
            status: 200,
            statusText: 'OK',
            headers: {},
            config: {},
            request: {},
            data: {
                article_id: 1,
                article_name: "Article Name",
                content: "Content article"
            }
        }
     */
    @PutMapper(ArticleDto)
    updateArticle(articleDto) {
        /*
            Так как указан декоратор @PutMapper, то перед выполнением данной функции параметр articleDto
            будет конвертирован в JSON объект. 
            И здесь он уже будет равен:
            articleDto = {
                article_id: 1,
                article_name: "Article Name",
                content: "Content article"
            }
         */
        return axios.put(`/article`, articleDto);
    },
}

let articleDto = new ArticleDto();
articleDto.id = 1;
articleDto.name = "Name 1";
articleDto.content = "Content";

createArticle(articleDto).then(article => {
    /*
        article = ArticleDto {
            id = 1;
            name = "Name 1";
            content = "Content";
        }
     */
});

sendArticle(1, 2, articleDto).then(sendResponse => {
    /*
        sendResponse = SendResponseDto {
            requestId = 1;
            requestStatus = "SUCCESS";
            article = ArticleDto {
                id = 1;
                name = "Name 1";
                content = "Content";
            }
        }
     */
});

articleDto.name = "Article Name";
articleDto.content = "Content article";
updateArticle(articleDto).then(article => {
    /*
        article = ArticleDto {
            id = 1;
            name = "Article Name";
            content = "Content article";
        }
     */
});

2.3. Декораторы @StorableGetMapper и @VuexGetMapper

@StorableGetMapper и @VuexGetMapper - декораторы работающие аналогично @GetMapper и умеющие кэшировать результаты выполнения функции. И соответственно, при повторном вызове брать данные из кэша.

2.3.1. Декоратор @StorableGetMapper

@StorableGetMapper - общий кэширующий декоратор, требующий описания функций сохранения и получения из кэша данных. Он не только умеет работать с функциями возвращающими всегда одни и те же данные, но и сохранять данные в зависимости от параметров функции.

Параметры:

  • modelResponse - класс DTO-модели описывающий тело ответа
  • separateStorageConf - конфигурация раздельного хранения данных на основе параметров функции.
  separateStorageConf = {
     argIdx: 1 // индекс параметра функции на основе которого будет происходить раздельное сохранение результата
     conditions: { // условия для параметров функции, которые должны быть выполнены, чтобы произошло сохранение в кэш
         arg_0: value => !!value,
         arg_1: value => !!value,
         arg_2: value => !!value
     }
 }
  • saveToStoreFn - функция для сохранения данных в кэш. Принимает один аргумент — данные, которые нужно сохранить
  • getFromStoreFn - функция получения данных из кэша.
  • pathToData - путь до поля с данными ответа в JSON-е. По умолчанию = data
  • strict - если true то включается строгий режим конвертации (по умолчанию false). Т.е. все поля класса должны быть помечены декоратором @JsonField

cache.js

/**
 * Кэш для хранения справочников
 */
let CACHE = {};

/**
 * Функция сохранения данных в кэше
 * @param data - данные для сохранения
 * @param lookupName - имя справочника для его дальнейшей идентификации
 */
export function saveToCache(data, lookupName) {
    CACHE[lookupName] = data;
}

/**
 * Функция получения данных из кэша
 * @param lookupName - имя справочника (идентификатор)
 */
export function getFromCache(lookupName) {
    return CACHE[lookupName];
}

example.js

import axios from 'axios';
import {saveToCache, getFromCache} from 'cache.js';
import {JsonField, TypeString, TypeNumber, StorableGetMapper} from '@dlabs71/d-dto';

class LookupDto {
    @JsonField("value") @TypeNumber value;
    @JsonField("title") @TypeString title;
}

export default {

    /*
       запрос вернет следующий JSON: {
           status: 200,
           statusText: 'OK',
           headers: {},
           config: {},
           request: {},
           data: [
                {value: 1, title: "Type 1"},
                {value: 2, title: "Type 2"},
                {value: 3, title: "Type 3"},
           ]
       }
    */
    @StorableGetMapper(
            LookupDto,
            null,
            (data) => saveToCache(data, "articleTypes"),
            () => getFromCache("articleTypes")
    )
    getArticleTypes() {
        return axios.get(`/article/types`);
    },

    /*
       При articleTypeValue = 1 запрос вернет следующий JSON: 
       {
           status: 200,
           statusText: 'OK',
           headers: {},
           config: {},
           request: {},
           data: [
                {value: 1, title: "Kind 1 1"},
                {value: 2, title: "Kind 1 2"},
                {value: 3, title: "Kind 1 3"},
           ]
       },
     
       При articleTypeValue = 2 запрос вернет следующий JSON: 
       {
           status: 200,
           statusText: 'OK',
           headers: {},
           config: {},
           request: {},
           data: [
                {value: 4, title: "Kind 2 1"},
                {value: 5, title: "Kind 2 2"},
                {value: 6, title: "Kind 2 3"},
           ]
       },
     
       При articleTypeValue = 2 и userId = 1 запрос вернет следующий JSON: 
       {
           status: 200,
           statusText: 'OK',
           headers: {},
           config: {},
           request: {},
           data: [
                {value: 7, title: "Kind 2 1 1"},
                {value: 8, title: "Kind 2 1 2"},
                {value: 9, title: "Kind 2 1 3"},
           ]
       }
    */
    @StorableGetMapper(
            LookupDto,
            {
                argIdx: 0, // это означает что раздельное хранение необходимо организовывать на основе articleTypeValue
                conditions: {
                    arg_0: value => !!value, // это означает что articleTypeValue не должно быть пустым
                    arg_1: value => value === null // это означает что userId должен быть null
                }
            },
            (data) => saveToCache(data, "articleTypes"),
            () => getFromCache("articleTypes")
    )
    getArticleKinds(articleTypeValue, userId) {
        return axios.get(`/article/kinds/${articleTypeValue}/${userId}`);
    }
}

getArticleTypes().then(result => {
    /*
        result = [
            LookupDto {value: 1, title: "Type 1"},
            LookupDto {value: 2, title: "Type 2"},
            LookupDto {value: 3, title: "Type 3"}
        ]
     */

    /*
        При повторном вызове данной функции, оргинальная функция уже не будет вызвана, а результат выполнения будет
        взят из кэша.
     */
});

getArticleKinds(1, null).then(result => {
    /*
        result = [
            LookupDto {value: 1, title: "Kind 1 1"},
            LookupDto {value: 2, title: "Kind 1 2"},
            LookupDto {value: 3, title: "Kind 1 3"}
        ]
     */


    /*
        При повторном вызове данной фкнкции с аналогичными параметрами, результат будет взят из кэша.
     */
});

getArticleKinds(2, null).then(result => {
    /*
        result = [
            LookupDto {value: 4, title: "Kind 2 1"},
            LookupDto {value: 5, title: "Kind 2 2"},
            LookupDto {value: 6, title: "Kind 2 3"}
        ]
     */


    /*
        При повторном вызове данной фкнкции с аналогичными параметрами, результат будет взят из кэша.
     */
});

getArticleKinds(2, 1).then(result => {
    /*
        result = [
            LookupDto {value: 7, title: "Kind 2 1 1"},
            LookupDto {value: 8, title: "Kind 2 1 2"},
            LookupDto {value: 9, title: "Kind 2 1 3"}
        ]
     */


    /*
        Так как второе условие в условиях сохранения в кэш не выполняется (userId != null), то результат 
        выполнения не будет сохранен в кэш. А значит при повторном вызове функции данные также будут запрошенны
        с сервера.
     */
});

2.3.2. Декоратор @VuexGetMapper

@VuexGetMapper - частный случай декоратора @StorableGetMapper, предназначенный для сохранения кэша во VUEX хранилище. Обладает следующими параметрами:

  • store - экземпляр Vuex хранилища.
  • modelResponse - класс DTO-модели описывающий тело ответа
  • lookupName - идентификатор данных в кэше
  • separateStorageConf - конфигурация раздельного хранения данных на основе параметров функции. Пример смотрите в пункте про @StorableGetMapper
  • pathToData - путь до поля с данными ответа в JSON-е. По умолчанию = data
  • strict - если true то включается строгий режим конвертации (по умолчанию false). Т.е. все поля класса должны быть помечены декоратором @JsonField

Также библиотека предоставляет готовый модуль Vuex хранилища для хранения данных. store.js

import Vue from "vue";
import Vuex from "vuex";
import {serviceCacheModule} from '@dlabs71/d-dto';

const state = {};

const getters = {};

const mutation = {};

const actions = {};

export default new Vuex.Store({
    plugins: [],
    modules: {
        "serviceCache": serviceCacheModule
    },
    state,
    getters,
    mutation,
    actions,
    strict: true,
});

example.js

import axios from 'axios';
import store from 'store.js';
import {JsonField, TypeString, TypeNumber, VuexGetMapper} from '@dlabs71/d-dto';

class LookupDto {
    @JsonField("value") @TypeNumber value;
    @JsonField("title") @TypeString title;
}

export default {

    /*
       запрос вернет следующий JSON: {
           status: 200,
           statusText: 'OK',
           headers: {},
           config: {},
           request: {},
           data: [
                {value: 1, title: "Type 1"},
                {value: 2, title: "Type 2"},
                {value: 3, title: "Type 3"},
           ]
       }
    */
    @VuexGetMapper(store, LookupDto)
    getArticleTypes() {
        return axios.get(`/article/types`);
    },

    /*
       При articleTypeValue = 1 запрос вернет следующий JSON: 
       {
           status: 200,
           statusText: 'OK',
           headers: {},
           config: {},
           request: {},
           data: [
                {value: 1, title: "Kind 1 1"},
                {value: 2, title: "Kind 1 2"},
                {value: 3, title: "Kind 1 3"},
           ]
       },
     
       При articleTypeValue = 2 запрос вернет следующий JSON: 
       {
           status: 200,
           statusText: 'OK',
           headers: {},
           config: {},
           request: {},
           data: [
                {value: 4, title: "Kind 2 1"},
                {value: 5, title: "Kind 2 2"},
                {value: 6, title: "Kind 2 3"},
           ]
       },
     
       При articleTypeValue = 2 и userId = 1 запрос вернет следующий JSON: 
       {
           status: 200,
           statusText: 'OK',
           headers: {},
           config: {},
           request: {},
           data: [
                {value: 7, title: "Kind 2 1 1"},
                {value: 8, title: "Kind 2 1 2"},
                {value: 9, title: "Kind 2 1 3"},
           ]
       }
    */
    @VuexGetMapper(
            store,
            LookupDto,
            {
                argIdx: 0, // это означает что раздельное хранение необходимо организовывать на основе articleTypeValue
                conditions: {
                    arg_0: value => !!value, // это означает что articleTypeValue не должно быть пустым
                    arg_1: value => value === null // это означает что userId должен быть null
                }
            }
    )
    getArticleKinds(articleTypeValue, userId) {
        return axios.get(`/article/kinds/${articleTypeValue}/${userId}`);
    }
}

getArticleTypes().then(result => {
    /*
        result = [
            LookupDto {value: 1, title: "Type 1"},
            LookupDto {value: 2, title: "Type 2"},
            LookupDto {value: 3, title: "Type 3"}
        ]
     */

    /*
        При повторном вызове данной функции, оргинальная функция уже не будет вызвана, а результат выполнения будет
        взят из кэша.
     */
});

getArticleKinds(1, null).then(result => {
    /*
        result = [
            LookupDto {value: 1, title: "Kind 1 1"},
            LookupDto {value: 2, title: "Kind 1 2"},
            LookupDto {value: 3, title: "Kind 1 3"}
        ]
     */


    /*
        При повторном вызове данной фкнкции с аналогичными параметрами, результат будет взят из кэша.
     */
});

getArticleKinds(2, null).then(result => {
    /*
        result = [
            LookupDto {value: 4, title: "Kind 2 1"},
            LookupDto {value: 5, title: "Kind 2 2"},
            LookupDto {value: 6, title: "Kind 2 3"}
        ]
     */


    /*
        При повторном вызове данной фкнкции с аналогичными параметрами, результат будет взят из кэша.
     */
});

getArticleKinds(2, 1).then(result => {
    /*
        result = [
            LookupDto {value: 7, title: "Kind 2 1 1"},
            LookupDto {value: 8, title: "Kind 2 1 2"},
            LookupDto {value: 9, title: "Kind 2 1 3"}
        ]
     */


    /*
        Так как второе условие в условиях сохранения в кэш не выполняется (userId != null), то результат 
        выполнения не будет сохранен в кэш. А значит при повторном вызове функции данные также будут запрошенны
        с сервера.
     */
});

3. Использование функций мапперов

Библиотека предоставляет готовые функции-мапперы для конвертации из DTO-модели в JSON и обратно.

Функция c2jMapperWrapper - предназначена для конвертации из DTO-модели в JSON. Параметры данной функции:

  • dtoModel - экземпляр класса DTO-модели
  • skipIfNotDefine - пропускать атрибуты класса не помеченные декоратором @JsonField. Если false - будет исключение.

Функция j2cMapperWrapper - предназначена для конвертации из JSON в DTO-модель. Параметры данной функции:

  • jsonObj - исходный JSON объект
  • DtoModel - класс DTO-модели
  • skipIfNotDefine - пропускать атрибуты класса не помеченные декоратором @JsonField. Если false - будет исключение.
class PersonDto {
    @JsonField("first_name") @TypeString firstName;
    @JsonField("second_name") @TypeString secondName;
    @JsonField("age") @TypeNumber age;
}

let dto = new PersonDto();
dto.firstName = "Danila";
dto.secondName = "Ivanov";
dto.age = 24;

let json = c2jMapperWrapper(dto);
/*
json = {
    first_name: "Danila",
    second_name: "Ivanov",
    age: 24
}
 */
class PersonDto {
    @JsonField("first_name") @TypeString firstName;
    @JsonField("second_name") @TypeString secondName;
    @JsonField("age") @TypeNumber age;
}

let sourceJson = {
    first_name: "Danila",
    second_name: "Ivanov",
    age: 24
};

let dto = j2cMapperWrapper(sourceJson, PersonDto);

/*
dto = PersonDto {
    firstName = "Danila"
    secondName = "Ivanov"
    age = 24
}
 */