# ApiMethod

## Шаблон ApiMethod <a href="#template_api_method" id="template_api_method"></a>

```xml
<ApiMethod Name="" Route="" Method="" VersionCode="">
  <Enabled></Enabled>
  <Parameters>
    <Parameter Name="" Type="" Source="" Array="" Required="" />
  </Parameters>
  <Commands>
    <Command Name="" />
  </Commands>
  <Response>
    <Objects>
      <Object Name="">
        <Command Name="" />
      </Object>
      <Object Name="" Array="">
        <Command Name="" />
      </Object>
    </Objects>
    <Relations>
      <Relation ChildObject="" ChildField="" ParentObject="" ParentField="" />
    </Relations>
  </Response>
</ApiMethod>
```

## Описание ApiMethod <a href="#description_api_method" id="description_api_method"></a>

```xml
<ApiMethod Name="" Route="" Method="" VersionCode="">
  <!--Тэги, специфичные для ApiMethod-->
</ApiMethod>
```

### Атрибуты ApiMethod <a href="#attributes_api_method" id="attributes_api_method"></a>

#### Name <a href="#name" id="name"></a>

Имя кастомного API-метода.

Обязательный атрибут. Значение атрибута `Name` может быть любым, но уникальным среди всех описанных API-методов.

#### Route <a href="#route" id="route"></a>

Описывает конечную точку маршрута, по которому будет выполняться действие, описанное в ApiMethod.

Обязательный атрибут. Значение атрибута `Route`: ожидается любое значение, допустимое в URL.

Полный маршрут будет иметь вид http\://\<host>:\<port>/data\_api/{Route}, где \<host> и \<port> - IP-адрес и порт компьютера, на котором запущена серверная часть.\
Например, для Route="user\_list" URL будет иметь вид <http://localhost:5005/data\\_api/user\\_list>.

#### Method <a href="#method" id="method"></a>

Метод HTTP запроса.

Обязательный атрибут. Ожидается один из вариантов: Get, Post, Put или Delete.

Поле носит информативный характер.

#### VersionCode <a href="#version_code" id="version_code"></a>

Версия используемого кода.

Обязательный атрибут. Ожидается положительное число больше нуля.

По умолчанию равно текущей версии кода. Зарезервировано для разделения изменений кода и соблюдения обратной совместимости.

## Тэги, специфичные для ApiMethod <a href="#tags_api_method" id="tags_api_method"></a>

### Enabled <a href="#enabled" id="enabled"></a>

Признак активности api-метода (только v3).

Необязательный тэг. Ожидается логическое выражение, результат условия или константа.

Если тэг `<Enabled>` отсутствует, то используется значение True.

Если значение тэга `<Enabled>` равно False, то в ответ на запрос сервер вернет статус 404 Not Found.

```xml
<Enabled>True</Enabled>
```

### Parameters <a href="#parameters" id="parameters"></a>

Параметры, передаваемые в запрос.

Необязательный тэг. В качестве значения тэга ожидается список тэгов [`<Parameter>`](#parameters_parameter).

```xml
<Parameters>
  <Parameter Name="" Type="" Source="" Required="" />
</Parameters>
```

#### Тэг `<Parameter>` <a href="#parameters_parameter" id="parameters_parameter"></a>

Параметр, который будет передан в команду при выполнении.

Необязательный тэг.

#### Атрибуты тэга `<Parameter>` <a href="#attributes_tag_parameters_parameter" id="attributes_tag_parameters_parameter"></a>

<table data-header-hidden><thead><tr><th width="161.6296484971982" align="center"></th><th width="454.3333333333333"></th></tr></thead><tbody><tr><td align="center">Name</td><td><p>Название параметра, по которому будет доступен в команде. </p><p></p><p>Обязательный атрибут.</p></td></tr><tr><td align="center">Type</td><td><p>Тип значения передаваемого в параметре.<br><br>Обязательный атрибут. Ожидается одно из значений:</p><ul><li>Integer</li><li>Decimal</li><li>DateTime</li><li>String</li><li>File</li><li>Boolean </li><li>Json</li></ul></td></tr><tr><td align="center">Source</td><td><p>Источник в HTTP-запросе для получения значения параметра.<br><br>Обязательный атрибут. Ожидается одно из значений:</p><ul><li>FromQuery - возвращает значения из строки запроса</li><li>FormData - multipart/form-data</li><li>FromBody - возвращает значения из текста запроса</li><li>FromHeader - возвращает значения из заголовков HTTP</li></ul></td></tr><tr><td align="center">Required</td><td>Признак, обязательный параметр или нет.<br><br>Необязательный атрибут. Ожидается логическое значение.<br><br>По умолчанию атрибут имеет значение True.</td></tr></tbody></table>

{% hint style="warning" %}
1\. Для `FromBody` есть ограничение на обработку параметров относительно уровня вложенности объектов. В качестве параметров запроса можем указывать только поля основного объекта, которые принимают значение простых типов (строка, число, дата, логическое) и массив простых типов. Например, для объекта

```json
{
  "id" : 1,
  "title" : "qwerty",
  "object" : {
    "field1" : "value1",
    "field2" : "value2"
  } 
}
```

мы укажем параметры "id"(Integer), "title"(String), но не можем указать параметры для "field1" и "field2". В этом случае мы должны указать параметр "object" с типом String. И вручную разбирать строку как json-объкт. PostgreSQL это позволяет.

2\. Type="Array" будет возвращать массив строк.
{% endhint %}

### Commands <a href="#commands" id="commands"></a>

Команды, выполняемые последовательно.

Необязательный тэг.&#x20;

Полностью соответствует команде SequentialCommand на серверной части.

Во всех командах доступны все параметры описанные в тэге [`<Parameters>`](#parameters).

```xml
<Commands>
  <Command Name="" />
</Commands>
```

### Response <a href="#response" id="response"></a>

Описывает дополнительные поля в ответе на запрос.

Необязательный тэг. Значение тэга `<Response>`: список тэгов [`<Objects>`](#response_objects) и [`<Relations>`](#response_relations).

Если тэг `<Response>` не указан, то по умолчанию в json-объекте будут поля "result\_code" и "error", если при выполнении запроса возникли ошибки.

```xml
<Response>
  <Objects>
    <Object Name="">
      <Command Name="" />
    </Object>
    <Object Name="" Array="">
      <Command Name="" />
    </Object>
  </Objects>
  <Relations>
    <Relation ChildObject="" ChildField="" ParentObject="" ParentField="" />
  </Relations>
</Response>
```

#### Тэг `<Objects>` <a href="#response_objects" id="response_objects"></a>

Описывает дополнительные поля в ответе на запрос.

Необязательный тэг. Значение тэга `<Objects>`: список тэгов [`<Object>`](#response_objects_object).

#### Тэг `<Object>` <a href="#response_objects_object" id="response_objects_object"></a>

Дополнительное поле в ответе на запрос.

Необязательный тэг. В качестве значения тэга `<Object>` ожидается тэг `<Command>` - обращение к команде. При этом если команда не выполнялась ранее, то она будет выполнена, иначе будет получено значение предыдущего выполнения. Так же можно обратится к конкретному параметру в результате команды по его имени через атрибут `Parameter`.

#### Атрибуты тэга `<Object>` <a href="#attributes_tag_response_objects_object" id="attributes_tag_response_objects_object"></a>

<table data-header-hidden><thead><tr><th align="center"></th><th width="542.3333333333333"></th></tr></thead><tbody><tr><td align="center">Name</td><td><p>Название поля, которое будет указываться в json-объекте в ответе на запрос.</p><p></p><p>Обязательный атрибут.</p></td></tr><tr><td align="center">Array</td><td><p>Будет ли поле представлено как массив или скалярное значение.</p><p></p><p>Необязательный атрибут. Ожидается логическое значение.</p><p></p><p>По умолчанию используется значение False. Если указан тэг <a href="#response_relations_relation"><code>&#x3C;Relation></code></a> с именем этого объекта, то значение атрибута игнорируется и берется значение True.</p></td></tr></tbody></table>

#### Тэг `<Relations>` <a href="#response_relations" id="response_relations"></a>

Описывает отношение дополнительных полей между собой. Для указания вложенных объектов. Если не указан, то все таблицы - как самостоятельные поля json.

Необязательный тэг. Значение тэга `<Relations>`: список тэгов [`<Relation>`](#response_relations_relation).

{% hint style="warning" %}
Необходимо соблюдать порядок объявления тэгов `<Relation>` для объектов с большой вложенностью: описываем каждый уровень последовательно, начиная с верхнего уровня, и проходя дерево в глубину.
{% endhint %}

#### Тэг `<Relation>` <a href="#response_relations_relation" id="response_relations_relation"></a>

Внешний ключ, по которому будет строиться зависимость вложенных объектов.

Необязательный тэг. Значение тэга `<Relation>`: не ожидается.

#### Атрибуты тэга `<Relation>` <a href="#attributes_tag_response_relations_relation" id="attributes_tag_response_relations_relation"></a>

<table data-header-hidden><thead><tr><th width="150" align="center"></th><th width="454.3333333333333"></th></tr></thead><tbody><tr><td align="center">ChildObject</td><td><p>Подчинённый объект. </p><p></p><p>Обязательный атрибут. Ожидается имя одного из <a href="#response_objects_object">Object</a>.</p></td></tr><tr><td align="center">ChildField</td><td>Ссылающееся поле.<br><br>Обязательный атрибут. Ожидается название поля подчинённого объекта.</td></tr><tr><td align="center">ParentObject</td><td><p>Главный объект. </p><p></p><p>Обязательный атрибут. Ожидается имя одного из <a href="#response_objects_object">Object</a>.</p></td></tr><tr><td align="center">ParentField</td><td>Целевое поле.<br><br>Обязательный атрибут. Ожидается название поля главного объекта.</td></tr></tbody></table>

{% hint style="info" %}
Возможные ситуации с тэгом `<Relation>`:

1. Указан неверный `ChildField` или `ParentField` - при выполнении запроса вывалится ошибка KeyNotFoundException.
2. Указан неверный `ChildObject` или `ParentObject` - при выполнении запроса такой Relation будет игнорироваться.
3. В качестве `ChildObject` и `ParentObject` указан один и тот же объект - выкинет ошибку при старте сервера.
   {% endhint %}

## Примеры <a href="#examples" id="examples"></a>

### Простой пример на получение списка пользователей <a href="#example_1" id="example_1"></a>

URL запроса <http://localhost:5005/data\\_api/user\\_list>.

```xml
<ApiMethod Route="user_list" Method="Get">
  <Response>
    <Objects>
      <Object Name="users" Array="True">
        <Command Name="UserListSelectSqlQueryCommand" />
      </Object>
    </Objects>
  </Response>
</ApiMethod>
```

UserListSelectSqlQueryCommand - SqlQueryCommand с текстом select запроса на получения списка.

В качестве ответа будет получен json объект вида:

```json
{
  "result_code": 0,
  "users": [
    {
      "user_name": "user2",
      "user_title": "Администратор"
    },
    {
      "user_name": "user",
      "user_title": "Разработчики"
    }
  ]
}
```

### Пример обновления записи в базе <a href="#example_2" id="example_2"></a>

URL запроса <http://localhost:5005/data\\_api/shift\\_inspection>.

Тело запроса содержит объект:

```json
{
  "id":66,
  "date":"2019-12-08 07:00:00Z",
  "reason_id":1,
  "comment":"комментарий"
}
```

```xml
<ApiMethod Route="shift_inspection" Method="Put">
  <Parameters>
    <Parameter Name="id" Type="Integer" Source="FromBody" />
    <Parameter Name="date" Type="DateTime" Source="FromBody" />
    <Parameter Name="reason_id" Type="Integer" Source="FromBody" />
    <Parameter Name="comment" Type="String" Source="FromBody" />
  </Parameters>
  <Commands>
    <Command Name="ShiftInspectionUpdateSqlQueryCommand" />
  </Commands>
</ApiMethod>
```

ShiftInspectionUpdateSqlQueryCommand содержит запрос вида:

```sql
UPDATE carrent.inspection
SET
  date = {date}::timestamp,
  reason_shift_id = {reason_id},
  comment = {comment}
WHERE
  inspection_id = {id};
```

В качестве ответа вернется стандартный объект:

```json
{
  "result_code": 0
}
```

### Пример построения вложенных объектов <a href="#example_3" id="example_3"></a>

URL запроса <http://localhost:5005/data\\_api/inspections?user\\_id=41>.

```xml
<ApiMethod Route="inspections" Method="Get">
  <Parameters>
    <Parameter Name="id" Type="Integer" Source="FromQuery" />
  </Parameters>
  <Response>
    <Objects>
      <Object Name="inspections" Array="True">
        <Command Name="InspectionSelectSqlQueryCommand" />
      </Object>
      <Object Name="damages" Array="True">
        <Command Name="DamageListSelectSqlQueryCommand" />
      </Object>
    </Objects>
    <Relations>
      <Relation ChildObject="damages" ChildField="inspection_id" ParentObject="inspections" ParentField="inspection_id" />
    </Relations>
  </Response>
</ApiMethod>
```

InspectionSelectSqlQueryCommand и DamageListSelectSqlQueryCommand - select запросы на получение списка записей для user\_id = 41.

Вариант ответа:

```json
{
  "result_code": 0,
  "inspections": [
    {
      "inspection_id": 11,
      "type": "inspection_pickup",
      "damages": [
        {
          "damage_id": 13,
          "description": "царапина"
        },
        {
          "damage_id": 14,
          "description": "вмятина"
        }
      ]
    },
    {
      "inspection_id": 12,
      "type": "inspection_pickup",
      "damages": [
        {
          "damage_id": 23,
          "description": "царапина"
        },
        {
          "damage_id": 41,
          "description": "вмятина"
        }
      ]
    }
  ]
}
```

Если бы в примере не был указан тэг `<Relation>`, то ответ имел бы вид:

```json
{
  "result_code": 0,
  "inspection": [
    {
      "inspection_id": 11,
      "type": "inspection_pickup"
    },
    {
      "inspection_id": 12,
      "type": "inspection_pickup"
    }
  ],
  "damages": [
    {
      "damage_id": 13,
      "description": "царапина"
    },
    {
      "damage_id": 14,
      "description": "вмятина"
    },
    {
      "damage_id": 23,
      "description": "царапина"
    },
    {
      "damage_id": 41,
      "description": "вмятина"
    }
  ]
}
```

### Пример передачи массива объектов в качестве параметра в теле запроса <a href="#example_4" id="example_4"></a>

URL запроса <http://localhost:5005/data\\_api/get\\_fines>.

Тело запроса:

```json
{
  "cars": [
    {
      "title":"car1",
      "number":"num_1"
    },
    {
      "title":"car2",
      "number":"num_2"
    },
    {
      "title":"car3",
      "number":"num_3"
    },
    {
      "title":"car4",
      "number":"num_4"
    }
  ]
}
```

```xml
<ApiMethod Route="get_fines" Method="Post">
  <Parameters>
    <Parameter Name="cars" Type="Array" Source="FromBody" />
  </Parameters>
  <Commands>
    <Command Name="TestSqlQueryCommand" />
  </Commands>
</ApiMethod>
```

Команда TestSqlQueryCommand содержит примерный код запроса:

```sql
SELECT
  cars ->> 'title' AS title,
  cars ->> 'number' AS number
FROM(
  SELECT
    unnest({cars}::json[]) AS cars
) T 
```

После замены параметра будет иметь вид:

```sql
SELECT
  cars ->> 'title' AS title,
  cars ->> 'number' AS number
FROM(
  SELECT
    unnest(ARRAY[
      '{"title": "car1","number": "num_1"}',
      '{"title": "car2","number": "num_2"}',
      '{"title": "car3","number": "num_3"}',
      '{"title": "car4","number": "num_4"}'
    ]::json[]) AS cars
) T
```

### Примеры обработки результатов кастомных команд <a href="#example_5" id="example_5"></a>

#### DataTable <a href="#example_5_1" id="example_5_1"></a>

1. ```xml
   <Object Name="inspections">
     <Command Name="MyCustomCommand"/>
   </Object>
   ```

   Вернёт первый элемент таблицы:

   ```json
   {
     "result_code": 0,
     "inspections": {
       "inspection_id": 56,
       "state": "new",
       "client": "Алёшин Пётр Алексеевич"
     }
   }
   ```
2. ```xml
   <Object Name="inspections">
     <Command Name="MyCustomCommand" Parameter="client"/>
   </Object>
   ```

   Вернёт значение поля "client" для первого элемента таблицы:

   ```json
   {
     "result_code": 0,
     "inspections": "Алёшин Пётр Алексеевич"
   }
   ```
3. ```xml
   <Object Name="inspections" Array="True">
     <Command Name="MyCustomCommand"/>
   </Object>
   ```

   Вернёт массив всех строк таблицы:

   ```json
   {
     "result_code": 0,
     "inspections": [
       {
         "inspection_id": 56,
         "state": "new",
         "client": "Алёшин Пётр Алексеевич"
       },
       {
         "inspection_id": 53,
         "state": "new",
         "client": "Иванов Иван Иванович"
       },
       {
         "inspection_id": 51,
         "state": "new",
         "client": "Сидоров Олег Петрович"
       }
     ]
   }
   ```
4. ```xml
   <Object Name="inspections" Array="True">
     <Command Name="MyCustomCommand" Parameter="client"/>
   </Object>
   ```

   Вернёт массив значений поля "client" для всех строк таблицы:

   ```json
   {
     "result_code": 0,
     "inspections": [
       "Алёшин Пётр Алексеевич",
       "Крутиков Сергей Владимирович",
       "Сидоров Петр Викторович"
     ]
   }
   ```

#### List\<object> OR object\[] <a href="#example_5_2" id="example_5_2"></a>

1. ```xml
   <Object Name="inspections">
     <Command Name="MyCustomCommand"/>
   </Object>
   ```

   Вернёт первый элемент списка/массива:

   ```json
   {
     "result_code": 0,
     "inspections": "Алёшин Пётр Алексеевич"
   }
   ```
2. ```xml
   <Object Name="inspections" Array="True">
     <Command Name="MyCustomCommand"/>
   </Object>
   ```

   Вернёт массив всех значений списка/массива:

   ```json
   {
     "result_code": 0,
     "inspections": [
       "Алёшин Пётр Алексеевич",
       "Крутиков Сергей Владимирович",
       "Сидоров Петр Викторович"
     ]
   }
   ```

#### Dictionary\<string, object> <a href="#example_5_3" id="example_5_3"></a>

1. ```xml
   <Object Name="inspections">
     <Command Name="MyCustomCommand"/>
   </Object>
   ```

   Вернёт первый элемент словаря:

   ```json
   {
     "result_code": 0,
     "inspection": {
       "inspection_id": 56,
       "state": "new"
     }
   }
   ```
2. ```xml
   <Object Name="inspections" Array="True">
     <Command Name="MyCustomCommand"/>
   </Object>
   ```

   Вернёт массив всех значений словаря:

   ```json
   {
     "result_code": 0,
     "inspections": [
       {
         "inspection_id": 56,
         "state": "new"
       }
     ]
   }
   ```
3. ```xml
   <Object Name="inspections">
     <Command Name="MyCustomCommand" Parameter="client"/>
   </Object>
   ```

   Вернёт:

   ```json
   {
     "result_code": 0,
     "inspections": "Алёшин Пётр Алексеевич"
   }
   ```
4. ```xml
   <Object Name="inspections" Array="True">
     <Command Name="MyCustomCommand" Parameter="client"/>
   </Object>
   ```

   Вернёт:

   ```json
   {
     "result_code": 0,
     "inspections": [
       "Алёшин Пётр Алексеевич"
     ]
   }
   ```

#### List\<Dictionary\<string, object>> <a href="#example_5_4" id="example_5_4"></a>

1. ```xml
   <Object Name="inspections">
     <Command Name="MyCustomCommand"/>
   </Object>
   ```

   Вернёт:

   ```json
   {
     "result_code": 0,
     "inspections": {
       "inspection_id": 56,
       "state": "new"
     }
   }
   ```
2. ```xml
   <Object Name="inspections" Array="True">
     <Command Name="MyCustomCommand"/>
   </Object>
   ```

   Вернёт:

   ```json
   {
     "result_code": 0,
     "inspections": [
       {
         "inspection_id": 56,
         "client": "Алёшин Пётр Алексеевич",
         "state": "new"
       },
       {
         "inspection_id": 53,
         "client": "Крутиков Сергей Владимирович",
         "state": "new"
       },
       {
         "inspection_id": 51,
         "client": "Сидоров Петр Викторович",
         "state": "new"
       }
     ]
   }
   ```
3. ```xml
   <Object Name="inspections">
     <Command Name="MyCustomCommand" Parameter="client"/>
   </Object>
   ```

   Вернёт:

   ```json
   {
     "result_code": 0,
     "inspections": "Алёшин Пётр Алексеевич"
   }
   ```
4. ```xml
   <Object Name="inspections" Array="True">
     <Command Name="MyCustomCommand" Parameter="client"/>
   </Object>
   ```

   Вернёт:

   ```json
   {
     "result_code": 0,
     "inspections": [
       "Алёшин Пётр Алексеевич",
       "Крутиков Сергей Владимирович",
       "Сидоров Петр Викторович"
     ]
   }
   ```

#### Dictionary\<string, List\<Dictionary\<string, object>>> <a href="#example_5_5" id="example_5_5"></a>

В процессе доработки!

```xml
<Object Name="data">
  <Command Name="MyCustomCommand" />
</Object>
<Object Name="inspections">
  <Command Name="MyCustomCommand"  Parameter="inspections"/>
</Object>
<Object Name="damages" Array="True">
  <Command Name="MyCustomCommand"  Parameter="damages"/>
</Object>
```

добавить поддержку Array

```json
{
  "result_code": 0,
  "inspections": [
    {
      "inspection_id": 56,
      "state": "new"
    },
    {
      "inspection_id": 53,
      "state": "new"
    },
    {
      "inspection_id": 51,
      "state": "new"
    }
  ],
  "damages": [
    {
      "damage_id": 17,
      "inspection_id": 56,
      "open_description": "test 1"
    },
    {
      "damage_id": 18,
      "inspection_id": 51,
      "open_description": "test 2"
    }
  ]
}
```
