1.1.0 • Published 1 year ago

@dlabs71/d-dto v1.1.0

Weekly downloads
-
License
MIT
Repository
github
Last release
1 year ago

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"
undefinedundefined
nullnull
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
123123
"123"123
true1
false0
undefinedundefined
nullnull
NaNNaN
{ a: "qwe", b: "qwe", data: 123 }NaN
123, 234, 345NaN

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

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

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

class Dto {
    @JsonField("value") @TypeBool value;
}
Значение JSONЗначение DTO
"value"false
123false
truetrue
falsefalse
undefinedundefined
nullnull
NaNfalse
{ a: "qwe", b: "qwe", data: 123 }false
123, 234, 345false

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
123false
truefalse
falsefalse
undefinedundefined
nullnull
NaNfalse
{ a: "qwe", b: "qwe", data: 123 }false
123, 234, 345false

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
123null
truenull
falsenull
undefinedundefined
nullnull
NaNnull
{ a: "qwe", b: "qwe", data: 123 }null
123, 234, 345null
"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
123null
truenull
falsenull
undefinedundefined
nullnull
NaNnull
{ a: "qwe", b: "qwe", data: 123 }null
123, 234, 345null
"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
123null
truenull
falsenull
undefinedundefined
nullnull
NaNnull
{ 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, 345123, 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" }
undefinedundefined
nullnull
NaNnull

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
undefinedundefined
nullnull
NaNnull

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
}
 */