CRUD REST для настраиваемых полей
Удаление настраиваемых полей
Удаление настраиваемых полей работает не совсем так, как ожидается при «наивной» реализации.
Во-первых, удаление логическое, а не физическое — запись помечается как удаленная, но сохраняется в базе. Здесь мы намеренно идём на конфликт с MapAdmin, т.к. он удаляет записи физически. Получается, что если Cerebellum удалит настраиваемое поле, то MapAdmin по-прежнему будет его видеть.
Второй момент, который облегчает понимание, для чего нужен первый: настраиваемое поле, помеченное как удаленное, не отображается в списке доступных для задачи настраиваемых полей, но при этом обрабатывается при сохранении задачи в базе. Например, пусть есть поле myCustomField и есть задача, в которой его значение равно «test». Пользователь помечает myCustomField как удалённое, а затем меняет заголовок задачи и сохраняет его. Так как myCustomField удалено, то пользователь не увидит его значение в задаче. Но так как поле обрабатывается при сохранении, то его значение, равное «test» фактически не теряется и по-прежнему будет сохранено в базе (более того, поле когда-нибудь может быть восстановлено вместе со своим значением).
Получается, если мы удалим какое-то поле, то оно более не будет видно пользователю в задачах, но сохранение всех использующих его задач не сломается. Этот плюс компенсирует то неудобство, что MapAdmin будет отображать как удалённые, так и активные настраиваемые поля. Собственно, с его точки зрения удалённые поля таковыми и не являются, т.к. по прежнему сохраняются в базе.
С точки зрения реализации это будет выглядеть следующим образом. В классе CustomField
есть четыре метода для доступа к настраиваемым полям:
List<CustomField> all();
CustomField byId(Long id);
List<CustomField> required(Long issueTypeId);
List<CustomField> defaults(Long issueTypeId);
Первый из них используется при отдаче пользователю списка доступных для редактирования настраиваемых полей. Этот метод не должен возвращать логически удалённые поля. Остальные методы используются при обработке значений настраиваемых полей во время сохранения. Соответственно, они должны работать с логически удалёнными полями как с обычными.
Еще более правильным решением будет создать private-версии методов byId(Long id)
, required(Long issueTypeId)
, defaults(Long issueTypeId)
и пользоваться ими только при сохранении настраиваемых полей.
[AI] GET /custom_field/:id
Получение информации по настраиваемому полю.
Если поле не существует или логически удалено, клиенту возвращается статус 404.
// OUT <---
{
"res": 1,
"resText": "",
"id": "123", // id нового поля
"name": "Адрес филиала организации", // название поля
"translit": "string",// транслитерация названия поля
"format": "text", // формат данных поля, возможные значения: "int", "float", "bool", "geometry", "string", "text", "list", "date"
"group_name": "Информация о филиалах организации", // название группы полей
"possible_values": ["первое значение", "второе значение", "третье значение"], // список возможных значений, доступно только для поля формата "list"
"regexp": "[0-9]{1,3}", // регулярное выражение, доступно только для полей форматов "string", "text", "int", "float"
"min_length": "5", // минимальная длина, доступно только для полей форматов "string", "text"
"max_length": "50", // максимальная длина, доступно только для полей форматов "string", "text"
"is_required": "f", // обязательное ли поле: "t", "f"
"default_value": "РТ, г.Казань", // значение по умолчанию, доступно для всех форматов полей, за исключением формата "geometry",
// значение задается с соответствии с форматом поля (текст, число, дата, булево значение)
"visible": "f", // видимость данного поля при отображении полной информации по задаче: "t", "f"
"is_for_all": "f", // доступно ли данное поле для всех типов новостей (видов работ): "t", "f
"news_type_ids": "["1", "2"]", //массив id типов новостей, для которых поле доступно, если оно не доступно всем типам новостей
"order": "10" // номер данного поля для отображения в списке настраиваемых полей
}
[AI] POST /custom_field
Создание нового настраиваемого (кастомного) поля.
// IN --->
{
"name": "Фактический адрес организации", // название нового поля, обязательный параметр
"format": "text", // формат данных поля, возможные значения: "int", "float", "bool", "geometry", "string", "text", "list", "date"; обязательный параметр
"group_name": "Информация об организации", // название группы полей, при указании несуществующей группы полей в таблицу будет добавлена запись.
"possible_values": ["первое значение", "второе значение"], // список возможных значений, доступно только для поля формата "list", и является обязательным параметром для поля формата "list"
"regexp": "[0-9]{1,3}", // регулярное выражение, доступно только для полей форматов "string", "text", "int", "float"
"min_length": "2", // минимальная длина, доступно только для полей форматов "string", "text"
"max_length": "25", // максимальная длина, доступно только для полей форматов "string", "text"
"is_required": "t", // обязательное ли поле: "t", "f", по умолчанию "f"
"default_value": "г.Москва", // значение по умолчанию, доступно для всех форматов полей, за исключением формата "geometry",
// значение задается с соответствии с форматом поля (текст, число, дата, булево значение)
"visible": "t" // видимость данного поля при отображении полной информации по задаче: "t", "f", по умолчанию "t"
}
При возникновении одной из следующих ситуаций клиенту возвращается статус 400:
- Передано поле possible_values, но формат настраиваемого поля не «list»;
- передано поле regexp, но формат не «string», «text», «int» или «float»;
- переданы поля min_length или max_length, но формат не «string» или «text»;
- передано поле default_value, но формат «geometry»;
- значение default_value (переданное или текущее)
- не соответствует формату поля (для форматов «bool», «int», «float», «date»);
- и/или не проходит валидацию по regexp/min_length/max_length;
- и/или не является одним из possible_values (для поля формата «list»).
// OUT <---
{
"res": 1,
"resText": "",
"id": "123", // id нового поля
"name": "Фактический адрес организации", // название нового поля
"translit": "Fakticheskiy_adres_organizatsi",// транслитерация названия поля
"format": "text", // формат данных поля, возможные значения: "int", "float", "bool", "geometry", "string", "text", "list", "date"
"group_name": "Информация об организации", // название группы полей, при указании несуществующей группы полей в таблицу будет добавлена запись.
"possible_values": ["первое значение", "второе значение"], // список возможных значений, доступно только для поля формата "list"
"regexp": "[0-9]{1,3}", // регулярное выражение, доступно только для полей форматов "string", "text", "int", "float"
"min_length": "2", // минимальная длина, доступно только для полей форматов "string", "text"
"max_length": "25", // максимальная длина, доступно только для полей форматов "string", "text"
"is_required": "t", // обязательное ли поле: "t", "f"
"default_value": "РТ, г.Москва", // значение по умолчанию, доступно для всех форматов полей, за исключением формата "geometry",
// значение задается с соответствии с форматом поля (текст, число, дата, булево значение)
"visible": "t", // видимость данного поля при отображении полной информации по задаче: "t", "f"
"is_for_all": "f", // доступно ли данное поле для всех типов новостей (видов работ): "t", "f"
"news_type_ids": "["1", "2"]", //массив id типов новостей, для которых поле доступно, если оно не доступно всем типам новостей
"order": "10" // номер данного поля для отображения в списке настраиваемых полей
}
[AI] PUT /custom_field/:id
Редактирование настраиваемого поля.
Формат поля изменить нельзя, т.к. это может вызвать массовую некорректность данных настраиваемых полей в задачах.
// IN --->
{
"name": "Адрес филиала организации", // измененное название поля
"group_name": "Информация о филиалах организации", // измененное название группы полей
"possible_values": ["первое значение", "второе значение", "третье значение"], // список возможных значений, доступно только для поля формата "list"
"regexp": "[0-9]{1,3}", // регулярное выражение, доступно только для полей форматов "string", "text", "int", "float"
"min_length": "5", // минимальная длина, доступно только для полей форматов "string", "text"
"max_length": "50", // максимальная длина, доступно только для полей форматов "string", "text"
"is_required": "f", // обязательное ли поле: "t", "f"
"default_value": "РТ, г.Казань", // значение по умолчанию, доступно для всех форматов полей, за исключением формата "geometry",
// значение задается с соответствии с форматом поля (текст, число, дата, булево значение)
"visible": "f" // видимость данного поля при отображении полной информации по задаче: "t", "f"
}
Если поле не существует или логически удалено, клиенту возвращается статус 404.
При возникновении одной из следующих ситуаций клиенту возвращается статус 400:
- Передано поле possible_values, но формат настраиваемого поля не «list»;
- передано поле regexp, но формат не «string», «text», «int» или «float»;
- переданы поля min_length или max_length, но формат не «string» или «text»;
- передано поле default_value, но формат «geometry»;
- значение default_value (переданное или текущее)
- не соответствует формату поля (для форматов «bool», «int», «float», «date»);
- и/или не проходит валидацию по regexp/min_length/max_length;
- и/или не является одним из possible_values (для поля формата «list»).
Если массив «possible_values» не будет передан, то элементы списка не изменятся. При передаче массива «possible_values» все предыдущие значения списка будут удалены.
// OUT <---
{
"res": 1,
"resText": "",
"id": "123", // id нового поля
"name": "Адрес филиала организации", // название поля
"translit": "string",// транслитерация названия поля
"format": "text", // формат данных поля, возможные значения: "int", "float", "bool", "geometry", "string", "text", "list", "date"
"group_name": "Информация о филиалах организации", // название группы полей
"possible_values": ["первое значение", "второе значение", "третье значение"], // список возможных значений, доступно только для поля формата "list"
"regexp": "[0-9]{1,3}", // регулярное выражение, доступно только для полей форматов "string", "text", "int", "float"
"min_length": "5", // минимальная длина, доступно только для полей форматов "string", "text"
"max_length": "50", // максимальная длина, доступно только для полей форматов "string", "text"
"is_required": "f", // обязательное ли поле: "t", "f"
"default_value": "РТ, г.Казань", // значение по умолчанию, доступно для всех форматов полей, за исключением формата "geometry",
// значение задается с соответствии с форматом поля (текст, число, дата, булево значение)
"visible": "f", // видимость данного поля при отображении полной информации по задаче: "t", "f"
"is_for_all": "f", // доступно ли данное поле для всех типов новостей (видов работ): "t", "f"
"news_type_ids": "["1", "2"]", //массив id типов новостей, для которых поле доступно, если оно не доступно всем типам новостей
"order": "10" // номер данного поля для отображения в списке настраиваемых полей
}
[AI] DELETE /custom_field/:id
Логическое удаление настраиваемого поля.
Если поле не существует или логически удалено, клиенту возвращается статус 404.
// OUT <---
{
"res": 1,
"resText": ""
}
[AI] POST /custom_field/sort
Ручная сортировка настраиваемых полей.
Сортировке подлежат только неудаленные настраиваемые поля.
Отсортированный список будет сохраняться в БД и отдаваться клиентам при последующих запросах списка (в ответе к данному запросу, а также в ответе на GET-запрос /news/custom_fields).
1. Сортировка ведется по полю order типа integer.
2. Для ручной сортировки будет добавлен POST-запрос /custom_field/sort, который можно будет выполнить неоднократно.
3. В теле POST-запроса /custom_field/sort клиент должен будет передать id-шники настраиваемых полей в отсортированном им порядке:
// IN --->
{ "ids": [4, 1, 3, 6, 2, 5]
}
Если переданный список id-шников будет некорректным, в ответе будет выдан BadRequest.
Корректность списка id-шников проверяется по следующим пунктам:
- неверный формат переданных данных,
- передача несуществующих id-шников,
- передача дублирующихся id-шников,
- передача id-шников удаленных настраиваемых полей,
- передача неверного количества id-шников (должны быть переданы все существующие в БД id-шники).
4. В БД будет произведен UPDATE: порядок полученных id-шников будет занесен в таблицу issues.custom_fields в столбец order.
5. Пользователю в ответ на POST-запрос /custom_field/sort, а также в ответ на GET-запрос /news/custom_fields будет выдаваться список настраиваемых полей, отсортированный в указанном порядке.
6. Для новых настраиваемых полей order будет назначаться равным 0. В БД также имеются настраиваемые поля с order = null (созданные до ввода дефолтного значения order = 0).
Настраиваемые поля с order , равным 0 или null, будут располагаться в списке после отсортированных по order полей, и они будут отсортированы по полю id.
// OUT <---
{
"res": 1,
"resText": "",
"custom_fields": [
{
"id": "19",
"name": "Знак зодиака (string, len<=8)",
"translit": "Znak_zodiaka__string__len<=8_",
"format": "string",
"group_name": "",
"possible_values": null,
"regexp": "",
"min_length": "0",
"max_length": "8",
"is_required": "f",
"default_value": "",
"visible": "t",
"is_for_all": "t",
"order": "1",
"news_type_ids": [
"14"
]
},
{
"id": "9",
"name": "Доп.поле для тестов (5-10)",
"translit": "Dop_pole_dlya_testov",
"format": "text",
"group_name": "",
"possible_values": null,
"regexp": "",
"min_length": "5",
"max_length": "10",
"is_required": "f",
"default_value": "12345",
"visible": "t",
"is_for_all": "t",
"order": "2",
"news_type_ids": []
},
...
]
}