Русский
Русский
English
Статистика
Реклама

Скрипт

Аналог R.string в android приложении

20.06.2021 12:09:43 | Автор: admin

Всем привет! Меня зовут Владимир, я Android-разработчик в компании Альфа-Капитал. Наверняка любое мобильное приложение в процессе развития нуждается в гибкой настройке текстовой информации за счет серверной части. В этой статье я поделюсь мыслями и решениями нашей команды. Также я покажу пример генерации кода с помощью gradle скрипта, сильно упростивший жизнь android команде.

С чего всё начиналось

В нашем приложении довольно много текстовой информации для пользователей, начиная от подсказок и описаний и заканчивая дисклеймерами и большими информационными экранами. Мы работаем в сфере финансовых технологий, и порой чтобы соответствовать требованиям Центробанка необходимо быстрое обновление информации. Это иногда нужно делать и по запросам наших юристов. Кроме того, порой требуется экран, где на первой неделе будет один текст, а на второй неделе другой текст.

Сразу оговорюсь: в текущем виде (и в ближайшем будущем) бизнес ориентирован на Россию, поэтому в проекте нет необходимости поддерживать несколько языков в приложении.

Столкнувшись с проблемой обновления информации, мы стали задумываться о хранении текстов (которые в перспективе придется корректировать) не в приложении, а на сервере. Часть (например, большой список дисклеймеров для юристов) удалось вытащить. Но оставалось еще множество строк, которые сложно как-то объединить и для которых не хотелось каждый раз делать сетевой запрос и экран загрузки.

Сначала мы попробовали держать тексты на Firebase. По функциональности такое решение вполне подходило, к тому же оно добавляло версионирование и возможность создания a/b тестов. Вскоре стало ясно, что это все-таки не то, что нам нужно. Тогда мы сформулировали свои требования:

  1. Удобный и единый источник текстов для всех мобильных платформ (android/ios);

  2. Обновление текстов в рантайме при старте приложения (для обновления важных мест без выпуска фиксов/релизов);

  3. В приложении мы не должны страдать от необходимости выполнения сетевого запроса или показа лоадинга ради загрузки лексем;

  4. Обновление текстов должно быть доступно без вмешательства разработчиков (т.е. чтобы условный аналитик / тестировщик смог спокойно обновить тексты при необходимости);

  5. Максимально простое создание дефолтных значений, которые будут лежать в приложении.

Firebase Remote Config не подошел слишком хороший функционал для простых текстов. У нас быстро получился большой список необходимых лексем, а их добавление / редактирование становилось слишком сложным. Нелегкой задачей была и установка дефолтных значений в приложении. Нам хотелось чего-то попроще.

Мы решили, что самым оптимальным будет объединение необходимых текстов в JSON файл. Почему именно JSON, а не XML, который кажется более нативным для Android? Так показалось удобней для обеих команд (Android и iOS). JSON понятный формат данных, его легко разберет любая платформа. Этот файл можно легко скачать, положить в проект и получить дефолтные данные.Схема работает и в обратную сторону. Пришла задача с новым текстом? Нужно добавить новые строки в проект, закинуть этот же JSON c ключами на сервер.

Пример json файла:

{ "screen1_text1": "Text 1", "screen1_text2": "Text 2 \nnext line", "screen1_text3": "Text 3", "screen1_text4": "Text 4"}

Первая реализация

В итоге мы получили JSON файл с текстами на сервере, этот же файл храним в проекте в папке assets. Сначала мы создали объект Lexemator, у которого можно по ключу запросить какой-то текст. При старте приложение подкачивает тексты с сервера в Lexemator, а если что-то пошло не так, берет дефолтные текста из папки assets.

object Lexemator {fun getString(key: String): String}

Использование в коде выглядит следующим образом:

class MainActivity : Activity() {   override fun onCreate(savedInstanceState: Bundle?) { ...       val textView = findViewById<TextView>(R.id.text1)       textView.text = Lexemator.getString("screen1_text1")   }}

По сравнению с Firebase стало лучше, но был один существенный недостаток: достаточно одной ошибки в ключе, и мы получаем не тот текст. Нам хотелось получить статическую поддержку, похожую на R.string, где Android Studio подсказывала бы константы и проект не компилировался бы при ошибке.

Это была предыстория, теперь переходим к коду.

Gradle - наше всё

Раз в проекте имеется JSON файл с текстами, значит, уже на этапе сборки мы понимаем, какие есть ключи для разных текстов. Если каких-то ключей нет, то они либо не нужны, либо их всё равно надо добавить для дефолтных значений. Выходит, на этапе сборки можно сгенерировать код, который будет содержать ключи для текстов. Мы решили сделать это с помощью gradle task.

Ниже представлен получившийся скрипт

import groovy.json.JsonSlurper/*** Таска ищет файл с текстами с названием strings.json и создает объект LL.* Для каждого текста из strings.json создает переменную LL.key внутри объекта** Если файла strings.json не существует - скрипт кинет Exception.** Чтобы сгенерить текста заново, достаточно перебилдить проект, или изменить файл strings.json*/def classFileName = "LL"def stringsFileName = "strings.json"def filePath = project.rootProject.getProjectDir().path + "/app/src/main/assets/json"def outputPath = project.rootProject.getProjectDir().path + "/app/build/generated/strings"def inputFile = new File(filePath + "/${stringsFileName}")def outputFile = new File(outputPath + "/${classFileName}.kt")task createStrings {   /**    * Если что-то изменится в inputFile, то при следующей сборке будет заново сгенерирован    * outputFile.    * Если ничего не изменилось, и outputFile уже есть, таска будет помечена "UP-TO-DATE" и    * не будет выполняться лишний раз.    */   inputs.file(inputFile)   outputs.file(outputFile)   doLast {       if (!inputFile.exists()) {           throw RuntimeException("файл ${inputFile} не найден")       }       println("Начало создания файла ${outputFile.path}")       outputFile.delete()       outputFile.createNewFile()       /**        * Тройные кавычки нужны для того, чтобы перевод строки (\n) в strings.json        * не ломал строки в созданном LL.kt файле.        */       def s1 = """package com.obolonnyy.lexemator//<!--Этот файл создан автоматически gradle скриптом из create_strings.gradle -->object ${classFileName} {"""       def s2 =               """      fun addLexems(map: Map<String, String>) {       map.forEach { k, v -> addLexem(k, v) }   }   fun addLexem(key: String, value: String) {       when(key) {"""       def json = new JsonSlurper().parse(inputFile)       assert json instanceof Map       json.each { entry ->           s1 += "    var ${entry.key} = \"\"\"${entry.value}\"\"\"\n        private set\n"           s2 += "            \"${entry.key}\" -> ${entry.key} = value\n"       }       def result = s1 + "\n\n" + s2 + """        }   }}"""       outputFile.write(result)       println("файл ${outputFile.path} успешно создан.")   }}/*** Показываем, что созданный файл теперь тоже является частью проекта.* Без этого мы не сможем использовать созданный LL.kt класс в своих классах.*/android {   sourceSets {       main {           java {               srcDirs += outputPath           }       }   }}

Скрипт создает object LL, у которого есть список ключей (поля типа String, с приватным сеттером) с дефолтными значениями, и две функции для обновления значения ключей. При старте приложения мы запрашиваем с сервера текста и обновляем значения через функцию addLexems().

Комментарий про название объекта LL: сначала мы думали назвать L (от слова Lexemator), чтобы было привычно как с R, но мешала константа android.icu.lang.UCharacter.GraphemeClusterBreak.L.Поэтому, мы не придумали ничего лучше, чем назвать класс LL.

Сгенерированный объект LL выглядит следующим образом:

//<!--Этот файл создан автоматически gradle скриптом из create_strings.gradle -->object LL {   var screen1_text1 = """Text 1"""       private set   var screen1_text2 = """Text 2next line"""       private set   var screen1_text3 = """Text 3"""       private set   var screen1_text4 = """Text 4"""       private set     fun addLexems(map: Map<String, String>) {       map.forEach { k, v -> addLexem(k, v) }   }   fun addLexem(key: String, value: String) {       when(key) {           "screen1_text1" -> screen1_text1 = value           "screen1_text2" -> screen1_text2 = value           "screen1_text3" -> screen1_text3 = value           "screen1_text4" -> screen1_text4 = value       }   }}

Пример использования объекта LL в коде выглядит следующим образом:

class MainActivity : Activity() {   override fun onCreate(savedInstanceState: Bundle?) {...       val textView = findViewById<TextView>(R.id.text1)       textView.text = LL.screen1_text1   }}

Получилось довольно просто и привычно.

Итоги

Мы сделали механизм управления текстами в приложении без необходимости перевыпуска релиза. Тексты хранятся на сервере, обновляются через git репозиторий. Для бизнеса планируется создать админку для управления текстами. Для Android команды мы сделали удобный механизм работы с этими текстами и статическую поддержку текстов в коде. Сейчас наш JSON файл насчитывает 180 различных строк, и найденное решение всех устраивает.

Рабочий пример можно найти по ссылке.

Подробнее..

Интерпретатор скрипта на С

24.12.2020 20:04:36 | Автор: admin
Всем привет.

Написал простой интерпретатор, конечно не конкурент lua, но тоже может пригодиться.
Кому интересно прошу.

Сразу пример, что получилось:

stringstream ss;ss << "$a = 5;"      "$b = 2;"      "while($a > 1){"      "  $a -= 1;"      "  $b = summ($b, $a);"      "  if($a < 4){"      "    break;"      "  }"      "}"      "$b";string res = ir.cmd(ss.str()); // 9

Что хотелось


Идея была в том, чтобы полностью отделить функционал (пользовательские операторы и функции) от самого языка скрипта, ограничиться минимумом ключевых слов (всем известных), ну и сделать интерпретатор и язык компактным и удобным для использования.

Что получилось


Скриптовый язык вышел простой и ограниченный конечно.

Состоит из трех компонентов переменные, выражения и функции, и нескольких базовых ключевых слов. Тип значений для всех компонентов строка.

Нет жестко забитых функций и операторов, все добавляет программист перед запуском скрипта.

Как это работает


Первый этап парсинг скрипта. На этом этапе идет синтаксический разбор текста скрипта: выделяются ключевые слова, операторы, функции.

Явно АСД не создается, но косвенно появляется (можно сказать в плоскости массива) в виде очереди операций, которые должны быть выполнены последовательно, то есть другими словами, все встречающиеся сущности попадают в массив операций сразу в нужном порядке выполнения.

Все ошибки написания скрипта находятся на этом этапе.

Второй этап выполнение скрипта. Здесь идет проход по массиву операций, с последовательным выполнением каждой.

Внутри все построено на рекурсивном вызове функций и проверках условий вызова.

Основные компоненты скрипта:

  • Переменная. Любая последовательность символов в коде скрипта начинающаяся с '$', считается переменной.

    Переменные используются для хранения промежуточных результатов вычислений, и для передачи параметров в функции. Имеют глобальную область видимости.

    Объявляются и используются только в коде скрипта, сразу использовать без объявления можно (значение по умолчанию пустая строка):

    $c = 5 + 6;summ($c, 6);
    

    Ко всем переменным в скрипте можно обращаться (и изменять их при необходимости) из основного кода, например, в функции:

    Intrerpreter ir;ir.addFunction("summScriptVars", [&ir](const vector<string>& args) ->string {    int res = 0;    for (auto& v : ir.allVariables()) {      if (isNumber(v.second)) res += stoi(v.second);    }    return to_string(res);  });
    
  • Выражение. Состоит из переменных, операторов и вызовов функций.

    Обязательно должно заканчиваться символом ';'.

    Может быть параметром функции, в этом случае его не нужно закрывать символом ';'.

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

    $b = 4; $c = 5 + $b + 3 - 7; $a = $b * (3 + $c) + summ($a, $b, $c + 1);
    
  • Функция. Любые функции создаются на уровне основного кода, в скрипте только используются. Функция принимает массив параметров, возвращает строку как результат работы.
    Сначала функцию нужно определить и добавить в основном коде:

    Interpreter ir;ir.addFunction("summ", [](const vector<string>& args) ->string {    int res = 0;    for (auto& v : args) {      if (isNumber(v)) res += stoi(v);    }    return to_string(res);  });
    

    В скрипте функция вызывается по имени, параметры передаются в скобочках, как обычно:

    $b = summ($b, $a);
    

    Функция может принимать другие функции и выражения:

    $b = 1;$c = summ($b, summ($b + 5, $b + $b - 1), 4);$a = $c - summ($b, 3);
    
  • Оператор. Любая последовательность символов в коде скрипта, заранее определенная в основном коде, считается оператором.

    Сначала оператор нужно определить и добавить в основном коде:

     Interpreter ir; ir.addOperator("+", [](string& leftOpd, string& rightOpd) ->string {    if (isNumber(leftOpd) && isNumber(rightOpd))      return to_string(stoi(leftOpd) + stoi(rightOpd));    else      return leftOpd + rightOpd;  }, 1);   ir.addOperator("==", [](string& leftOpd, string& rightOpd) ->string {    return leftOpd == rightOpd? "1" : "0";  }, 2);  ir.addOperator("=", [](string& leftOpd, string& rightOpd) ->string {    leftOpd = rightOpd;    return leftOpd;  }, 17);
    

    При создании оператора помимо определения нужно задать приоритет.

    Приоритет работает так же как в С++: нулевой наивысший, далее чем больше значение приоритета, тем позже будет выполнен оператор. Порядок выполнения операторов с одинаковым приоритетом слева направо.

    Операторы используются в выражениях.

    $c = 5 + 6;$b = 2;$a = $c + 5; $c = summ($a + 5 / $b);
    


Теперь опишу остальные ключевые слова языка скрипта, в основном это управляющие конструкции.

  • while(condition){body}. Выполняет циклически последовательность выражений (далее, тело цикла) в зависимости от результата выполнения условия.

    Условие заключается в скобочки '()' и, как и в любом языке, рассчитывается на каждой итерации цикла.

    Условие считается выполненным, если результат расчета условия для численного значения не равен 0, для строкового значения не пустая строка (численное значение значит, что строка может быть преобразована в целое число).

    Тело цикла заключается в фигурные скобки '{}', и состоит из неограниченной последовательности выражений и управляющих конструкций (то есть в теле цикла могут быть другие циклы).

    $c = 1;$b = 4; while($b > 0){  $c *= $b;   $b -= 1;}
    

  • if(condition){body}. Выполняет однократно последовательность выражений в зависимости от результата выполнения условия.

    $c = 1;$b = 4; if(($b - 4) == 0){  $c = $b;}
    

  • else{body}. Выполняет однократно последовательность выражений, если не было выполнено предыдущее условие.

    $c = 1;$b = 4; if(($b - 3) == 0){  $c = $b;}else{  $b = $c;}
    

  • elseif(condition){body}. Выполняет однократно последовательность выражений, если не было выполнено предыдущее условие и выполняется текущее условие.

    $c = 1;$b = 4; if(($b = $b - 3) == 0){  $c = $b;}elseif($c == summ($b)){  $b = $c;}
    

  • break;. Выполняет прерывание текущего цикла.
    continue;. Начинает заново текущий цикл.

    $b = 4; while($b > 0){  $b = rand(10);  if ($b == 3){    continue;  }  if ($b == 2){    break;   }}
    

  • #macro name{body}. Объявление макроса.

    #name;. Вставка тела макроса далее в коде.

    Под макросом здесь имеется в виду код, который повторяется много раз в скрипте, и можно его заменить именем.

    #macro myMc{ $c = 1; $b = 4; };$d = 5;#myMc;
    

  • goto l_name;. Перемещение на метку вверх или вниз по скрипту. Должен быть единственным оператором в выражении.

    l_name:. Метка, на которую можно переместиться.

    Метка обязательно должна начинаться с 'l_' (элл и нижнее подчеркивание) и заканчиваться ':'.

    $a = 5; while($a > 0){  $a -= 1;  if ($a == 2){    goto l_myLabel;  }  }l_myLabel: $a;
    

    На метку можно перемещаться из основного кода, например, в функции скрипта вызвать специальную функцию 'gotoOnLabel' (это конечно грязный хак, специально для месье, которые знают..):

    Interpreter ir;ir.addFunction("myJump", [&ir](const vector<string>& args) ->string {    if (!args.empty())      ir.gotoOnLabel(args[0]);    }    return "";  });
    


Как использовать и где может быть полезен


Предлагается использовать как код, то есть добавлять в свой проект файл исходного кода интерпретатора, он всего один. Заголовочный файл тоже единственный.

Может использоваться в простых случаях, когда не хочется подключать что-то внешнее, но нужно дать пользователю возможность интерактивно влиять на ход выполнения ПО.

Либо в случаях, когда не хочется давать пользователю в руки весь арсенал скриптовых языков, а ограничиться простым набором команд.

Еще можно попробовать построить RPC на его основе.

Что дальше, что планируется нового


Если коротко, то ничего.

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

Только поддержка, правка багов, возможно, стоит добавить несколько заранее определенных пользовательских функций, отдельно.

Распространяется свободно, лицензия MIT

Спасибо.

P.S.:

Я писал его ранее когда-то давно, там получилось не очень. Тут после одного письма пользователя, решил все это дело переписать по нормальному.

Вот думал, публиковать-нет статью на эту поделку, по сути тривиальный баян в принципе, и есть уже мастодонты всякие в этой нише.

Нажал все-таки кнопочку, может еще кому-то пригодится когда.
Подробнее..

Создание документов на диске Google на базе событий в CRM-системах аmoCRM и Битрикс24

18.02.2021 12:05:10 | Автор: admin

Практический кейс о том, как разработать свой сценарий интеграции.

Появившиеся в 2006 году сервисы Google по работе с текстовыми документами (Google Docs) и таблицами (Google Sheets), дополненные 6 лет спустя возможностями работы с виртуальным диском (Google Drive), завоевали широкую любовь пользователей, лишив компанию Microsoft сложившейся десятилетиями монополии на работу с офисным программным обеспечением.

Вспоминается, как один консультант, занимавшийся внедрением дорогостоящих систем бюджетирования, искренне радовался появлению бесплатной электронной Google-таблицы, в которую можно поместить требуемую бюджетную модель и дать к ней доступ всем, кому нужно. Совместная работа над текстом в Интернете тоже стала повсеместной практикой с появлением Google-сервисов.

В связи с популярностью последних часто возникает задача сделать новый или обновить существующий Google-файл в привязке к событиям в CRM-системе. Например, при переходе сделки на этап Составление коммерческого предложения необходимо, взяв из CRM-системы наименование клиента и сумму сделки, создать на Google Drive предварительный текст коммерческого предложения, дать к нему доступ юристу, отправив ссылку на редактирование контракта. А когда сделка будет успешно закрыта, может появиться другая задача внести данные по закрытой сделке в Google-таблицу сбора статистики продаж.

В базовом функционале CRM-систем интеграция с сервисами Google представлена лишь в Битрикс24 и предполагает создание пустого документа на базе диска с переходом в режим редактирования при последующем клике на него.

Конечно, в маркетплейс Битрикс24 или дополнениях amoCRM можно найти определенный набор решений для закрытия тех или иных более глубоких сценариев интеграции. Но как быть, если необходимо разработать свой сценарий?

Для того, чтобы выполнить какое-то действие на Google диске своего аккаунта есть два подхода: вызвать соответствующий REST API метод или запустить написанный Google Script. Как показал наш опыт, REST API библиотека Google пока имеет гораздо более бедный набор возможностей по отношению к скриптам, например, с помощью метода create можно создать исключительно пустой документ без наполнения. Поэтому, универсальным является сейчас комбинированный подход, заключающийся в написании исполняемого скрипта, который будет вызываться извне с помощью API с определенным набором параметров.

Разберемся, какие шаги необходимо выполнить, чтобы задействовать этот подход на практике. Нашей задачей будет создать из бизнес-процесса вот такой документ, в котором данные для текста, выделенного желтым, должны прийти из CRM-системы.

Шаг 1. Создание скрипта

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

Сам script добавляется на диске точно также как мы добавляем обычный файл на него.

Мы не будем подробно комментировать код скрипта, отметим лишь, что языком программирования выступает привычный Javascript, с описанием методов любому можно ознакомиться в документации Google.

Шаг 2. Привязка скрипта к проекту Google Cloud Platform

После того как скрипт написан рекомендуем прогнать его, нажав кнопку Выполнить в редакторе. Далее вам необходимо создать проект на Google Cloud Platform. Наиболее простым вариантом для этого является нажатие кнопки Enable Google Script API по ссылке:https://developers.google.com/apps-script/api/quickstart/php.

Пройдя все шаги мастера создания API, вы скачиваете файл credentials.json, который будет нужен для работы обработчика. После этого вы берете номер проекта и привязываете его к скрипту в настройках.

Шаг 3. Публикация скрипта

Чтобы скрипт был доступен извне, его надо развернуть через кнопку начать развертывание.

Полученный идентификатор необходимо скопировать, чтобы использовать в PHP-обработчике.

Шаг 4. Написание PHP-обработчика

Для работы с API Google существует готовая библиотека, которую вы подключаете к своему проекту командой: composer require google/apiclient:^2.0

В файле с подключенной библиотекой вызываете функцию для подключения токена авторизации:

function getClient(){    $client = new Google_Client();    $client->setApplicationName('Google Apps Script API PHP Quickstart');    // перечисляем области действия токена через пробел    $client->setScopes("https://www.googleapis.com/auth/documents https://www.googleapis.com/auth/drive");    // файл credential.json, полученный от Google$client->setAuthConfig('credentials.json');    $client->setAccessType('offline');    $client->setPrompt('select_account consent');    // Формируемый файл token.json    $tokenPath = 'token.json';    if (file_exists($tokenPath)) {        $accessToken = json_decode(file_get_contents($tokenPath), true);        $client->setAccessToken($accessToken);    }    // Действие если токен просрочен    if ($client->isAccessTokenExpired()) {        if ($client->getRefreshToken()) {            $client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());        } else {            $authUrl = $client->createAuthUrl();            printf("Open the following link in your browser:\n%s\n", $authUrl);            print 'Enter verification code: ';            $authCode = trim(fgets(STDIN));            $accessToken = $client->fetchAccessTokenWithAuthCode($authCode);            print_r($accessToken);            $client->setAccessToken($accessToken);                        if (array_key_exists('error', $accessToken)) {                throw new Exception(join(', ', $accessToken));            }        }        // Сохранение токена в файл        if (!file_exists(dirname($tokenPath))) {            mkdir(dirname($tokenPath), 0700, true);        }        file_put_contents($tokenPath, json_encode($client->getAccessToken()));    }    return $client;}

Файл с этой функцией должен быть изначально исполнен в командной строке ssh клиента php (имя файла).

При первом запуске на экране появится url, который вы открываете в своем браузере и дав необходимые разрешения копируете verification code из url ресурса приложения, который вы указали на шаге 1, вставляя его в командную строку. После этого система создает файл токена авторизации (token.json), сохраняя его в папке PHP-обработчика. Следует отметить,что данную операцию надо выполнить один раз.

С готовым файлом токена, исполняется вторая часть функции, предполагающая запуск самого скрипта.

// вызов ранее описанной функции для получения токена авторизации$client = getClient();$service = new Google_Service_Script($client);// код развернутого скрипта$scriptId = '***********************wKhTTdKL7ChremS5AkvzwlPJARnxqisW7TzDB ';$request = new Google_Service_Script_ExecutionRequest();// имя и параметры функции в рамках скрипта$request->setFunction('FillTemplate');$request->setParameters([$_REQUEST['customer'],$_REQUEST['link'],$_REQUEST['formsv'],$_REQUEST['socnet'],$_REQUEST['whatsi'],   $_REQUEST['ats'],$_REQUEST['pipeline'],$_REQUEST['outscope'],$_REQUEST['editor'],$_REQUEST['viewer']]);try {    // Make the API request.    $response = $service->scripts->run($scriptId, $request);    if ($response->getError()) {        // Обработчик ошибок        $error = $response->getError()['details'][0];        printf("Script error message: %s\n", $error['errorMessage']);        if (array_key_exists('scriptStackTraceElements', $error)) {            print "Script error stacktrace:\n";            foreach($error['scriptStackTraceElements'] as $trace) {                printf("\t%s: %d\n", $trace['function'], $trace['lineNumber']);            }        }    }     } catch (Exception $e) {    // Обработчик исключения    echo 'Caught exception: ', $e->getMessage(), "\n";}

Шаг 5. Вызов PHP-обработчика из CRM

Имея протестированный обработчик, можно запустить его в привязке к событиям CRM.

Базовым вариантом запуска является вызов исходящего вебхука в привязке к событию CRM и данному обработчику, см. cтатьи про настройки вебхуков в:

При этом, в Битрикс24 вебхук может быть встроен в бизнес-процесс, получая в качестве своих Get-параметров данные из сделки, переменных и констант бизнес-процесса.

Итогом работы данного вебхука стали добавляемые документы в соответствующую папку Google Drive:

Существенным ограничением запусков обработчиков через вебхуки является предельно допустимая длина URL, в которой будут передаваться Get-параметры. Напомним, что она составляет 4 кб (или 2048 символов).

Поэтому, если передаваемый в документ текст будет достаточно длинным, то пользователям Битрикс24 мы рекомендуем написать собственную активити бизнес-процесса. Информацию о подходах к ее разработке можно взять изданной статьи.

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru