...

Реализация своих методов API

Тема в разделе "Интеграции", создана пользователем kamyshev, 11 фев 2022.

  1. kamyshev

    kamyshev Активный участник

    Часто на проектах возникают ситуации когда необходимо реализовать свои собственные методы API. Один из ярких примеров с одного из моих проектов: возникла необходимость интегрировать сторонний frontend-интерфейс с ELMA365. В данном случае платформа ELMA365 выступает как backend-сервер. И для интеграции понадобилось создания точек интеграции между двумя системами.

    Для общения систем спроектируем свой API. При проектировании своих методов необходимо придерживаться архитектурного стиля REST. Подробнее с этим стилем и теорией по протоколу http предлагаю ознакомиться здесь и на Wikipedia (EN).

    Платформа ELMA365 предоставляет удобные инструменты для создания своих методов API. Они реализуются в рамках модулей и ознакомиться с функционалом можно в статье справки. Ниже приведу пример работы с этим функционалом.

    Рассмотрим работу с методом GET. Согласно спецификации клиент может передавать параметры выполнения запроса в URI целевого ресурса после символа '?'. Нам необходимо будет разобрать эту строку:

    Код:
    
    async function getAppForGuardianship(req: FetchRequest): Promise<HttpResponse | void> {
        
    // Создаем новый экземпляр объекта для ответа клиенту
        
    const response = new HttpResponse()
        const 
    phones: string[] = []
        
    // Здесь как раз получаем параметр id из строки запроса
        
    const id = req.query?.['id'] as string

        
    const result = await Global.ns._clients.app._leads.search().size(10000).where(f => f.__id.eq(id)).first()

        if (
    result) {
            for (
    let phone of result.data.contact_phone!) {
                
    phones.push(phone.tel)
            }

            
    //  "Собираем" наш ответ клиенту
            
    response
                
    .status(200) // Обязательно указываем код статуса! 200 - успех, подробнее по ссылкам в конце статьи
                // Собираем тело ответа в виде JSON объекта, stringify применять уже не надо
                
    .json({
                    
    msg: 'success',
                    
    name: result.data.__name ? result.data.__name : 'name is empty',
                    
    parent_fullname: result.data.parent_fullname,
                    
    child_fullname: result.data.child_fullname,
                    
    child_birthdate: result.data.birthdate!.format('DD.MM.YYYY'),
                    
    city: result.data.city,
                    
    child_citizenship: result.data.citizenship,
                    
    email: result.data.email ? result.data.email.email : 'e-mail не указан',
                    
    contact_phone: phones,
                    
    pa_diagnosis: result.data.pa_diagnosis
                
    })
                .
    set('Content-Type', 'application/json') // Явно указываем тип содержимого ответа
        
    }
        else {
            
    // При разработке REST API важно обрабатывать все ошибки и отдавать клиенту вразумительный ответ, чтобы можно было обработать его допустим на фронте
            
    response
                
    .status(404)
                .
    json({
                    
    msg: 'error: App for Guardianship not found'
                
    })
                .
    set('Content-Type', 'application/json')
        }

        return 
    response
    }
    Вызов этого метода на клиенте:
    Код:
    
    async function getUserDataFromElma() {
        const 
    elmaUserId = $('#elma-id').html();
        const 
    apiUrl = `https://aeql4vg6lxcee.elma365.ru/api/extensions/3d15932c-766e-4e91-b8ff-fed442649de2/script/get/appgs?id=${elmaUserId}`;
        return 
    await fetch(apiUrl, {
            
    method: 'GET',
            
    headers: {
                
    'Content-Type': 'application/json',
                
    'Authorization': 'Bearer 8657d620-f5eb-4552-997d-d3ec41688c39',
            }
        })
    }
    Теперь рассмотрим работу с методом POST. Согласно спецификации содержимое запроса передается в его теле, т.е. нам необходимо будет разобрать содержимое тела запроса:
    Код:
    
    async function createAppForGuardianship(req: FetchRequest): Promise<HttpResponse | void> {
        
    // Создаем новый экземпляр объекта для ответа клиенту
        
    const response = new HttpResponse()
        
    // Клиент передал нам содержимое в виде JSON-пакета, поэтому разберем его в массив
        
    const request = JSON.parse(req.body! as string)

        try {
            const 
    result = Global.ns._clients.app._leads.create()
            
    result.data.contact_phone = []
            
    result.data.parent_fullname = request['form_submit_data']['parent-name']
            
    result.data.child_fullname = request['form_submit_data']['child-name']
            
    result.data.birthdate = new Datetime(request['form_submit_data']['date-birth'], "d.m.Y").getDate()
            
    result.data.city = request['form_submit_data']['city']
            
    result.data.email = result.fields.email.create(request['form_submit_data']['your-email'])
            
    result.data.contact_phone.push(result.fields.contact_phone.create(request['form_submit_data']['your-number'], PhoneType.Main))
            
    result.data.pa_diagnosis = request['form_submit_data']['diagnosis']
            
    result.data.description = request['form_submit_data']['your-condition']

            
    await result.save()
            
    await result.setStatus(result.fields.__status.groups.wards.variants.new)

            
    // Формируем ответ клиенту
            
    response
                
    .status(200)
                .
    json({
                    
    msg: 'success',
                    
    id: result.data.__id
                
    })
                .
    set('Content-Type', 'application/json')
        }
        catch (
    e) {
            
    // Отдаем ошибку клиенту, если что-то пошло не так
            
    response
                
    .status(500)
                .
    json({
                    
    msg: 'error: new app do not create',
                })
                .
    set('Content-Type', 'application/json')
        }

        return 
    response
    }
    Пример вызова метода на стороне клиента:
    Код:
    
    async function updateUserDataFromElma() {
        const 
    urlParam = formId ? 'medical' : 'main';
        const 
    apiUrl = `https://aeql4vg6lxcee.elma365.ru/api/extensions/3d15932c-766e-4e91-b8ff-fed442649de2/script/ward/${urlParam}/update`;
        
    requestBody = JSON.stringify({
                        
    id: userId,
                        
    form_id: formId,
                        
    context: contextObject.context
                    
    })
        return 
    await fetch(apiUrl, {
            
    method: 'POST',
            
    headers: {
                
    'Content-Type': 'application/json',
                
    'Authorization': 'Bearer 8657d620-f5eb-4552-997d-d3ec41688c39',
            },
            
    body: requestBody
        
    })
    }
    Подведём итог. Для работы со строкой запроса метода GET используем поле FetchRequest.query, а для работы с телом метода POST парсим поле FetchRequest.body как JSON объект. Ниже приведу полное описание полей и методов интерфейсов FetchRequest и HttpResponse.
    Код:
    
    req: FetchRequest

    req
    .body - тело запроса, тип string или FormData
    req
    .headers - заголовки запроса, можно разобрать методами get() для типа Record
    req
    .method - тип метода запроса
    req
    .query - строка из самого запроса

    const response = new HttpResponse()
    response
      
    .content('') - отдать содержимое в виде строки, можно формировать html страницы
      
    .cookie('','') - установить куку, формат (параметр,значение)
      .
    redirect('') - выполнить редирект (важно отдать код из группы 300), можно таким образом поддерживать устаревшие методы
      
    .setFile(file,'') - отдать в теле ответа файл
      
    .status(200) - указать код статуса ответа
      
    .json({}) - сформировать тело ответа в виде JSON объекта
      
    .set('Content-Type', 'application/json')
    Особое внимание необходимо уделить адекватным кодам статусов ответов. Ознакомиться с ними можно здесь.
    Последнее редактирование: 11 фев 2022
  2. a.sysoev

    a.sysoev Новичок

    Вот тут точно должно быть так:
    const elmaUserId = $('#elma-id').html();

    Т.е. мы в elmaUserId передаём HTML-код?

    Если #elma-id -- это у нас input, то логичнее было бы:
    const elmaUserId = $('#elma-id').val();
  3. kamyshev

    kamyshev Активный участник

    На стороне фронтенда для теста был просто div с данным id, роли в данной статье абсолютно не играет
  4. ioyamaleev

    ioyamaleev Новичок

    Добрый день. Подскажите, а примера как получить данные если body не строка а FormData имеются? Задача стоит получить файлы и данные из другой системы в формате FormData и сохранить их в ELMA.
  5. grebina

    grebina Техническая поддержка

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