# 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"
    }
  ]
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://wfsys.gitbook.io/workflow-engine-syntax/workflow_engine/api_method.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
