{"id":13651240,"url":"https://github.com/Tinkoff/gatling-picatinny","last_synced_at":"2025-04-22T22:30:47.347Z","repository":{"id":40206713,"uuid":"264132203","full_name":"Tinkoff/gatling-picatinny","owner":"Tinkoff","description":"Library with a bunch of usefull functions that extend Gatling DSL and make your performance better","archived":true,"fork":false,"pushed_at":"2023-08-07T22:01:59.000Z","size":333,"stargazers_count":35,"open_issues_count":12,"forks_count":21,"subscribers_count":5,"default_branch":"master","last_synced_at":"2024-05-30T13:41:33.095Z","etag":null,"topics":["gatling","performance"],"latest_commit_sha":null,"homepage":null,"language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Tinkoff.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2020-05-15T07:54:56.000Z","updated_at":"2023-11-22T09:55:16.000Z","dependencies_parsed_at":"2024-01-03T05:41:13.861Z","dependency_job_id":"7652d5ab-c31d-4153-aa6c-d83481e1c1cd","html_url":"https://github.com/Tinkoff/gatling-picatinny","commit_stats":null,"previous_names":["tinkoffcreditsystems/gatling-picatinny"],"tags_count":15,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Tinkoff%2Fgatling-picatinny","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Tinkoff%2Fgatling-picatinny/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Tinkoff%2Fgatling-picatinny/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Tinkoff%2Fgatling-picatinny/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Tinkoff","download_url":"https://codeload.github.com/Tinkoff/gatling-picatinny/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250333878,"owners_count":21413472,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["gatling","performance"],"created_at":"2024-08-02T02:00:46.952Z","updated_at":"2025-04-22T22:30:47.005Z","avatar_url":"https://github.com/Tinkoff.png","language":"Scala","funding_links":[],"categories":["Tools"],"sub_categories":["Plugins"],"readme":"# Gatling Picatinny\n\n![Build](https://github.com/TinkoffCreditSystems/gatling-picatinny/workflows/Build/badge.svg) [![Maven Central](https://img.shields.io/maven-central/v/ru.tinkoff/gatling-picatinny_2.13.svg?color=success)](https://search.maven.org/search?q=ru.tinkoff.gatling-picatinny) [![Scala Steward badge](https://img.shields.io/badge/Scala_Steward-helping-blue.svg?style=flat\u0026logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAQCAMAAAARSr4IAAAAVFBMVEUAAACHjojlOy5NWlrKzcYRKjGFjIbp293YycuLa3pYY2LSqql4f3pCUFTgSjNodYRmcXUsPD/NTTbjRS+2jomhgnzNc223cGvZS0HaSD0XLjbaSjElhIr+AAAAAXRSTlMAQObYZgAAAHlJREFUCNdNyosOwyAIhWHAQS1Vt7a77/3fcxxdmv0xwmckutAR1nkm4ggbyEcg/wWmlGLDAA3oL50xi6fk5ffZ3E2E3QfZDCcCN2YtbEWZt+Drc6u6rlqv7Uk0LdKqqr5rk2UCRXOk0vmQKGfc94nOJyQjouF9H/wCc9gECEYfONoAAAAASUVORK5CYII=)](https://scala-steward.org)\n## Table of contents\n\n* [General info](#general-info)\n* [Installation](#installation)\n* [Usage](#usage)\n    * [config](#config)\n    * [feeders](#feeders)\n        * [HC Vault feeder](#hc-vault-feeder)\n        * [SeparatedValuesFeeder](#separatedvaluesfeeder)\n        * [Phone Feeders](#phone-feeders)\n    * [influxdb](#influxdb)\n    * [profile](#profile)\n    * [redis](#redis)\n    * [templates](#templates)\n    * [utils](#utils)\n    * [assertion](#assertion)\n    * [transactions](#transactions)\n    * [example](#example)\n* [Built with](#built-with)\n* [Help](#help)\n* [License](#license)\n* [Authors](#authors)\n* [Acknowledgments](#acknowledgments)\n\n## General info\n\nLibrary with a bunch of useful functions that extend Gatling DSL and make your performance better.\n\n## Installation\n\n### Using Gatling Template Project\n\nIf you are using TinkoffCreditSystems/gatling-template.g8, you already have all dependencies in\nit. [Gatling Template Project](https://github.com/TinkoffCreditSystems/gatling-template.g8.git)\n\n### Install manually\n\nAdd dependency with version that you need\n\n```scala\nlibraryDependencies += \"ru.tinkoff\" %% \"gatling-picatinny\" % \"\u003cversion\u003e\"\n```\n\n## Usage\n\n### config\n\nThe only class that you need from this module is `SimulationConfig`. It could be used to attach some default variables\nsuch as `intensity`, `baseUrl`, `baseAuthUrl` and some others to your scripts. Also, it provides functions to get custom\nvariables fom config.\n\nImport:\n\nScala example:\n\n```scala\nimport ru.tinkoff.gatling.config.SimulationConfig._\n```\n\nJava example:\n\n```java\nimport static ru.tinkoff.gatling.javaapi.SimulationConfig.*;\n```\n\nKotlin example:\n\n```kotlin\nimport ru.tinkoff.gatling.javaapi.SimulationConfig.*\n```\n\nUsing default variables:\n\nScala example:\n\n```scala\nimport ru.tinkoff.gatling.config.SimulationConfig._\n\nval testPlan: Seq[OpenInjectionStep] = List(\n  rampUsersPerSec(0).to(intensity).during(rampDuration),\n  constantUsersPerSec(intensity).during(stageDuration)\n)\n```\n\nJava example:\n\n```java\nimport static ru.tinkoff.gatling.javaapi.SimulationConfig.*;\n\nSomeScenario.scn.injectOpen(\n        incrementUsersPerSec(intensity() / stagesNumber())\n        .times(stagesNumber())\n        .eachLevelLasting(stageDuration())\n        .separatedByRampsLasting(rampDuration())\n        .startingFrom(0)\n        )\n```\n\nKotlin example:\n\n```kotlin\nimport ru.tinkoff.gatling.javaapi.SimulationConfig.*\n\nSomeScenario.scn.injectOpen(\n  incrementUsersPerSec(intensity() / stagesNumber())\n    .times(stagesNumber())\n    .eachLevelLasting(stageDuration())\n    .separatedByRampsLasting(rampDuration())\n    .startingFrom(0.0),\n)\n```\n\nUsing functions to get custom variable:\n\n*simulation.conf*\n\n```\nstringVariable: \"FOO\",\nintVariable: 1,\ndoubleVariable: 3.1415,\nduration: {\n    durationVariable: 3600s\n}\nbooleanVariable: true\n```\n\nScala example:\n\n```scala\nimport ru.tinkoff.gatling.config.SimulationConfig._\n\nval stringVariable = getStringParam(\"stringVariable\")\nval intVariable = getIntParam(\"intVariable\")\nval doubleVariable = getDoubleParam(\"doubleVariable\")\nval durationVariable = getDurationParam(\"duration.durationVariable\")\nval booleanVariable = getBooleanParam(\"booleanVariable\")\n```\n\nJava example:\n\n```java\nimport static ru.tinkoff.gatling.javaapi.SimulationConfig.*;\n\nString stringVariable = getStringParam(\"stringVariable\");\nint intVariable = getIntParam(\"intVariable\");\ndouble doubleVariable = getDoubleParam(\"doubleVariable\");\nDuration durationVariable = getDurationParam(\"duration.durationVariable\");\nboolean booleanVariable = getBooleanParam(\"booleanVariable\");\n```\n\nkotlin example:\n\n```kotlin\nimport ru.tinkoff.gatling.javaapi.SimulationConfig.*\n\nval stringVariable = getStringParam(\"stringVariable\")\nval intVariable = getIntParam(\"intVariable\")\nval doubleVariable = getDoubleParam(\"doubleVariable\")\nval durationVariable = getDurationParam(\"duration.durationVariable\")\nval booleanVariable = getBooleanParam(\"booleanVariable\")\n```\n\n### feeders\n\nThis module contains vast number of random feeders. They could be used as regular feeders and realize common needs, i.e.\nrandom phone number or random digit. Now it supports feeders for dates, numbers and digits, strings, uuids, phones.\nBasic examples will be provided below. Other feeders can be used in a similar way.\n\nScala example:\n\n```scala\nimport ru.tinkoff.gatling.feeders._\n\n//creates feeder with name 'randomString' that gets random string of length 10\nval stringFeeder = RandomStringFeeder(\"randomString\", 10)\n\n//creates feeder with name 'digit' that gets random Int digit\nval digitFeeder = RandomDigitFeeder(\"digit\")\n\n//creates feeder with name 'uuid' that gets random uuid\nval uuidFeeder = RandomUUIDFeeder(\"uuid\")\n```\n\nJava example:\n\n```java\nimport static ru.tinkoff.gatling.javaapi.Feeders.*;\n\n//creates feeder with name 'randomString' that gets random string of length 10\nIterator\u003cMap\u003cString, Object\u003e\u003e stringFeeder = RandomStringFeeder(\"randomString\", 10);\n\n//creates feeder with name 'digit' that gets random Int digit\nIterator\u003cMap\u003cString, Object\u003e\u003e digitFeeder = RandomDigitFeeder(\"digit\");\n\n//creates feeder with name 'uuid' that gets random uuid\nIterator\u003cMap\u003cString, Object\u003e\u003e uuidFeeder = RandomUUIDFeeder(\"uuid\");\n\n```\n\nKotlin example:\n\n```kotlin\nimport ru.tinkoff.gatling.javaapi.Feeders.*\n\n//creates feeder with name 'randomString' that gets random string of length 10\nval stringFeeder = RandomStringFeeder(\"string\", 10)\n\n//creates feeder with name 'digit' that gets random Int digit\nval digitFeeder = RandomDigitFeeder(\"digit\")\n\n//creates feeder with name 'uuid' that gets random uuid\nval uuidFeeder = RandomUUIDFeeder(\"uuid\")\n```\n\n#### HC Vault feeder\n\nCreates feeder capable of retrieving secret data from HC Vault\n\n- authorises via approle;\n- uses v1 API;\n- works with kv Secret Engine;\n- does not iterate over keys, returns full map with keys it found on each call;\n- params:\n    - vaultUrl - vault URL *e.g. \"https://vault.ru\"*\n    - secretPath - path to secret data within your vault *e.g. \"testing/data\"*\n    - roleId - approle login\n    - secretId - approle password\n    - keys - list of keys you are willing to retrieve from vault\n\nScala example:\n```scala\n  val vaultFeeder = VaultFeeder(vaultUrl, secretPath, roleId, secretId, keys)\n```\n\nJava example:\n```Java\n  Iterator\u003cMap\u003cString, Object\u003e\u003e vaultFeeder = VaultFeeder(vaultUrl, secretPath, roleId, secretId, keys);\n```\n\nKotlin example:\n```Kotlin\n  val vaultFeeder = VaultFeeder(vaultUrl, secretPath, roleId, secretId, keys)\n```\n\n#### SeparatedValuesFeeder\n\nCreates a feeder with separated values from a source String, Seq[String] or Seq[Map[String, Any]].\n\n- params:\n    - paramName - feeder name\n    - source - data source\n    - separator - \",\", \";\", \"\\t\" or other delimiter which separates values. You can also use following methods for the\n      most common separators: .csv(...), .ssv(...), .tsv(...)\n\nGet separated values from a source: String\n\nScala example:\n```scala\nval sourceString = \"v21;v22;v23\"\nval separatedValuesFeeder: FeederBuilderBase[String] =\n  SeparatedValuesFeeder(\"someValues\", sourceString, ';') // Vector(Map(someValues -\u003e v21), Map(someValues -\u003e v22), Map(someValues -\u003e v23))\n```\n\nJava example:\n```Java\nString sourceString = \"v21;v22;v23\";\nIterator\u003cMap\u003cString, Object\u003e\u003e separatedValuesFeeder = SeparatedValuesFeeder.apply(\"someValues\", sourceString, ';');\n```\n\nKotlin example:\n```Kotlin\nval sourceString = \"v21;v22;v23\"\nval separatedValuesFeeder = SeparatedValuesFeeder.apply(\"someValues\", sourceString, ';')\n```\n\nGet separated values from a source: Seq[String]\n\nScala example:\n```scala\nval sourceSeq = Seq(\"1,two\", \"3,4\")\nval separatedValuesFeeder: FeederBuilderBase[String] =\n  SeparatedValuesFeeder.csv(\"someValues\", sourceSeq) // Vector(Map(someValues -\u003e 1), Map(someValues -\u003e two), Map(someValues -\u003e 3), Map(someValues -\u003e 4))\n```\n\nJava example:\n```Java\nList\u003cMap\u003cString, Object\u003e\u003e sourceList = Arrays.asList(\"1,two\", \"3,4\");\nIterator\u003cMap\u003cString, Object\u003e\u003e separatedValuesFeeder = SeparatedValuesFeeder.csv(\"someValues\", sourceList);\n```\n\nKotlin example:\n```Kotlin\nvar sourceList = listOf(\"1,two\", \"3,4\")\nvar separatedValuesFeeder1 = SeparatedValuesFeeder.csv(\"someValues\", sourceList)\n```\n\nGet separated values from a source: Seq[Map[String, Any]]\n\nScala example:\n```scala\nval vaultFeeder: FeederBuilderBase[String] = Vector(\n  Map(\n    \"HOSTS\" -\u003e \"host11,host12\",\n    \"USERS\" -\u003e \"user11\",\n  ),\n  Map(\n    \"HOSTS\" -\u003e \"host21,host22\",\n    \"USERS\" -\u003e \"user21,user22,user23\",\n  ),\n)\nval separatedValuesFeeder: FeederBuilderBase[String] =\n  SeparatedValuesFeeder(None, vaultFeeder.readRecords, ',') // Vector(Map(HOSTS -\u003e host11), Map(HOSTS -\u003e host12), Map(USERS -\u003e user11), Map(HOSTS -\u003e host21), Map(HOSTS -\u003e host22), Map(USERS -\u003e user21), Map(USERS -\u003e user22), Map(USERS -\u003e user23))\n```\n\nJava example:\n```Java\nList\u003cMap\u003cString, Object\u003e\u003e vaultData = Arrays.asList(Map.of(\"HOSTS\",\"host11,host12\"), Map.of(\"USERS\", \"user21,user22,user23\"));\nIterator\u003cMap\u003cString, Object\u003e\u003e separatedValuesFeeder = SeparatedValuesFeeder.apply(Optional.empty(), vaultData, ',');\n```\n\nKotlin example:\n```Kotlin\nvar sourceList = listOf(Map.of(\"HOSTS\", \"host11,host12\"), Map.of(\"USERS\", \"user21,user22,user23\"))\nvar separatedValuesFeeder1 = SeparatedValuesFeeder.csv(null, sourceList)\n```\n\n#### Phone Feeders\n\nCreates a feeder with phone numbers with formats from json file or `case class PhoneFormat`\n\nSimple phone feeder\n\nScala example:\n```scala\nval simplePhoneNumber: Feeder[String] = RandomPhoneFeeder(\"simplePhoneFeeder\")\n```\n\nJava example:\n```Java\nIterator\u003cMap\u003cString, Object\u003e\u003e simplePhoneNumber = RandomPhoneFeeder(\"simplePhoneFeeder\");\n```\n\nKotlin example:\n```Kotlin\nval simplePhoneNumber = RandomPhoneFeeder(\"simplePhoneNumber\")\n```\n\nPhone feeder with custom formats\n\nScala example:\n```scala\n val ruMobileFormat: PhoneFormat = PhoneFormat(\n  countryCode = \"+7\",\n  length = 10,\n  areaCodes = Seq(\"903\", \"906\", \"908\"),\n  prefixes = Seq(\"55\", \"81\", \"111\"),\n  format = \"+X XXX XXX-XX-XX\")\n\n  val randomPhoneNumber: Feeder[String]                 =\n  RandomPhoneFeeder(\"randomPhoneNumber\", ruMobileFormat)\n```\n\nJava example:\n```Java\nPhoneFormat ruMobileFormat = PhoneFormatBuilder.apply(\"+7\", 10, Arrays.asList(\"945\", \"946\"), \"+X XXX XXX-XX-XX\", Arrays.asList(\"55\", \"81\", \"111\"));\nIterator\u003cMap\u003cString, Object\u003e\u003e randomPhoneNumber = RandomPhoneFeeder(\"randomPhoneNumber\", ruMobileFormat);\n```\n\nKotlin example:\n```Kotlin\nval ruMobileFormat = PhoneFormatBuilder.apply(\n        \"+7\",\n        10,\n        listOf(\"945\", \"946\"),\n        \"+X XXX XXX-XX-XX\",\n        listOf(\"55\", \"81\", \"111\")\n    )\nval randomPhoneNumber = RandomPhoneFeeder(\"randomPhoneNumber\", ruMobileFormat)\n```\n\nPhone feeder with custom formats with file\n\nCreates file with formats, for example RESOURCES/phoneTemplates/ru.json\n\n```json\n{\n  \"formats\": [\n    {\n      \"countryCode\": \"+7\",\n      \"length\": 10,\n      \"areaCodes\": [\"903\", \"906\"],\n      \"prefixes\": [\"123\", \"321\", \"132\", \"231\"],\n      \"format\": \"+X(XXX)XXXXXXX\"\n    },\n    {\n      \"countryCode\": \"8\",\n      \"length\": 10,\n      \"areaCodes\": [\"495\", \"499\"],\n      \"prefixes\": [\"81\", \"82\", \"83\"],\n      \"format\": \"X(XXX)XXX-XX-XX\"\n    }\n  ]\n}\n```\n\nScala example:\n```scala\nval phoneFormatsFromFile: String   = \"phoneTemplates/ru.json\"\nval randomE164PhoneNumberFromJson: Feeder[String]     =\n    RandomPhoneFeeder(\"randomE164PhoneNumberFile\", phoneFormatsFromFile, TypePhone.E164PhoneNumber)\n```\n\nJava example:\n```Java\nString phoneFormatsFromFile = \"phoneTemplates/ru.json\";\nIterator\u003cMap\u003cString, Object\u003e\u003e randomE164PhoneNumberFromJson = RandomPhoneFeeder(\"randomE164PhoneNumberFile\", phoneFormatsFromFile, TypePhone.E164PhoneNumber());\n```\n\nKotlin example:\n```Kotlin\nval phoneFormatsFromFile = \"phoneTemplates/ru.json\"\nval randomE164PhoneNumberFromJson = RandomPhoneFeeder(\"randomE164PhoneNumberFile\", phoneFormatsFromFile, TypePhone.E164PhoneNumber())\n```\n\n### influxdb\n\nThis module allows you to write custom points to InfluxDB.\n\n#### Two kinds of usage\n\n* Write Start/Stop annotations before/after simulation run\n* Write custom points to InfluxDB\n\n##### First type denotes start and end of simulation and could be shown in Grafana for example.\n\nScala example:\n\nImport:\n\n```scala\nimport ru.tinkoff.gatling.influxdb.Annotations\n```\n\nFor using you should simply add `with Annotations` for your simulation class:\n\n```scala\nclass LoadTest extends Simulation with Annotations {\n  //some code\n}\n```\n\nJava example:\n\nImport:\n\n```java\nimport ru.tinkoff.gatling.javaapi.influxdb.SimulationWithAnnotations;\n```\n\nFor using you should extend your simulation class from `SimulationWithAnnotations`:\n\n```java\nclass LoadTest extends SimulationWithAnnotations {\n  //some code\n}\n```\n\nKotlin example:\n\nImport:\n\n```kotlin\nimport ru.tinkoff.gatling.javaapi.influxdb.SimulationWithAnnotations\n```\n\nFor using you should extend your simulation class from `SimulationWithAnnotations`:\n\n```kotlin\nclass LoadTest : SimulationWithAnnotations(){\n  //some code\n}\n```\n\nTo see annotations in Grafana you need this two queries, where `Perfix` is from `gatling.data.graphite.rootPathPrefix`\nin `gatling.conf`:\n\n```sql\nSELECT \"annotation_value\"  FROM \"${Prefix}\" where \"annotation\" = 'Start'\nSELECT \"annotation_value\"  FROM \"${Prefix}\" where \"annotation\" = 'Stop'\n```\n\n##### Second type allows you to write various data points from your scenario or test plan\n\n**!DANGER!** Read before use:\n\n* Not intended for load testing of InfluxDB.\n* You can easily waste InfluxDB with junk data. Don't use frequently changing keys.\n* When recording points in the setUp simulation, a separate script will be created, which will be displayed in the test\n  status in the console and in the final Gatling data.\n* Depending on your settings, Gatling will write simulation data to InfluxDB in batches every n seconds. In this case,\n  the timestamp of the custom point will be taken during its recording, which may cause inaccuracies when displaying\n  data.\n\nScala example:\n\nImport:\n\n```scala\nimport ru.tinkoff.gatling.influxdb.Annotations._\n```\n\nUsing:\n\n```scala\n//if default prepared Point doesn't suit you\nPoint(configuration.data.graphite.rootPathPrefix, System.currentTimeMillis() * 1000000)\n  .addTag(tagName, tagValue)\n  .addField(fieldName, fieldValue)\n\n//prepare custom Point*\n\nimport io.razem.influxdbclient.Point\n\ndef customPoint(tag: String, value: String) = Point(configuration.data.graphite.rootPathPrefix, System.currentTimeMillis() * 1000000)\n  .addTag(\"myTag\", tag)\n  .addField(\"myField\", value) //value: Boolean | String | BigDecimal | Long | Double\n```\n\n_*_[_Custom Point\nreference_](https://www.javadoc.io/doc/io.razem/scala-influxdb-client_2.13/0.6.2/io/razem/influxdbclient/Point.html)\n\n```scala\n//write custom prepared Point from scenario\n.exec(\n...)\n.userDataPoint(customPoint(\"custom_tag\", \"inside_scenario\"))\n  .exec(\n...)\n\n//write default prepared Point from scenario\n.exec(\n...)\n.userDataPoint(\"myTag\", \"tagValue\", \"myField\", \"fieldValue\")\n  //also you can use gatling Expression language for values (could waste DB):\n  .userDataPoint(\"myTag\", \"${variableFromGatlingSession}\", \"myField\", \"${anotherVariableFromSession}\")\n  .exec(\n...)\n\n//write Point from Simulation setUp\nsetUp(\n  firstScenario.inject(atOnceUsers(1))\n    //write point after firstScenario execution\n    //\"write_first_point\" the name of the scenario, will be displayed in the simulation stats\n    .userDataPoint(\"write_first_point\", customPoint(\"custom_tag\", \"between_scenarios\"))\n    .andThen(\n      secondScenario.inject(atOnceUsers(1))\n        //similar to simple .userDataPoint, write point after secondScenario execution\n        .andThen(\n          userDataPoint(\"write_second_point\", \"custom_tag\", \"after_second\", \"custom_field\", \"end\")\n        )\n    )\n)\n```\n\nJava example:\n\nImport:\n\n```java\nimport static ru.tinkoff.gatling.javaapi.influxdb.Annotations.*;\n```\n\nUsing:\n\n```java\n//if default prepared Point doesn't suit you\nPoint(configuration.data.graphite.rootPathPrefix, System.currentTimeMillis() * 1000000)\n  .addTag(tagName, tagValue)\n  .addField(fieldName, fieldValue)\n\n//prepare custom Point*\nimport io.razem.influxdbclient.Point;        \n\nstatic Point customPoint(tag: String, value: String){ return Point(configuration.data.graphite.rootPathPrefix,System.currentTimeMillis()*1000000)\n            .addTag(\"myTag\",tag)\n            .addField(\"myField\",value) //value: Boolean | String | BigDecimal | Long | Double\n        }\n```\n\n_*_[_Custom Point\nreference_](https://www.javadoc.io/doc/io.razem/scala-influxdb-client_2.13/0.6.2/io/razem/influxdbclient/Point.html)\n\n```java\n//write custom prepared Point from scenario\n.exec(\n...)\n.exec(userDataPoint(customPoint(\"custom_tag\", \"inside_scenario\")))\n.exec(\n...)\n\n//write default prepared Point from scenario\n.exec(\n...)\n.exec(userDataPoint(\"myTag\", \"tagValue\", \"myField\", \"fieldValue\"))\n  //also you can use gatling Expression language for values (could waste DB):\n.exec(userDataPoint(\"myTag\", \"${variableFromGatlingSession}\", \"myField\", \"${anotherVariableFromSession}\"))\n.exec(\n...)\n\n//write Point from Simulation setUp\nsetUp(\n  firstScenario.inject(atOnceUsers(1))\n    //write point after firstScenario execution\n    //\"write_first_point\" the name of the scenario, will be displayed in the simulation stats\n    .andThen(userDataPoint(\"write_first_point\", customPoint(\"custom_tag\", \"between_scenarios\")))\n    .andThen(\n      secondScenario.inject(atOnceUsers(1))\n        //similar to simple .userDataPoint, write point after secondScenario execution\n        .andThen(\n          userDataPoint(\"write_second_point\", \"custom_tag\", \"after_second\", \"custom_field\", \"end\")\n        )\n    )\n)\n```\n\nKotlin example:\n\nImport:\n\n```kotlin\nimport ru.tinkoff.gatling.javaapi.influxdb.Annotations.*\n```\n\nUsing:\n\n```kotlin\n//if default prepared Point doesn't suit you\nPoint(configuration.data.graphite.rootPathPrefix, System.currentTimeMillis() * 1000000)\n  .addTag(tagName, tagValue)\n  .addField(fieldName, fieldValue)\n\n//prepare custom Point*\nimport io.razem.influxdbclient.Point       \n\nfun customPoint(tag: String, value: String){ Point(configuration.data.graphite.rootPathPrefix,System.currentTimeMillis()*1000000)\n            .addTag(\"myTag\",tag)\n            .addField(\"myField\",value) //value: Boolean | String | BigDecimal | Long | Double\n        }\n```\n\n_*_[_Custom Point\nreference_](https://www.javadoc.io/doc/io.razem/scala-influxdb-client_2.13/0.6.2/io/razem/influxdbclient/Point.html)\n\n```scala\n//write custom prepared Point from scenario\n.exec(\n...)\n.exec(userDataPoint(customPoint(\"custom_tag\", \"inside_scenario\")))\n.exec(\n...)\n\n//write default prepared Point from scenario\n.exec(\n...)\n.exec(userDataPoint(\"myTag\", \"tagValue\", \"myField\", \"fieldValue\"))\n  //also you can use gatling Expression language for values (could waste DB):\n.exec(userDataPoint(\"myTag\", \"${variableFromGatlingSession}\", \"myField\", \"${anotherVariableFromSession}\"))\n.exec(\n...)\n\n//write Point from Simulation setUp\nsetUp(\n  firstScenario.inject(atOnceUsers(1))\n    //write point after firstScenario execution\n    //\"write_first_point\" the name of the scenario, will be displayed in the simulation stats\n    .andThen(userDataPoint(\"write_first_point\", customPoint(\"custom_tag\", \"between_scenarios\")))\n    .andThen(\n      secondScenario.inject(atOnceUsers(1))\n        //similar to simple .userDataPoint, write point after secondScenario execution\n        .andThen(\n          userDataPoint(\"write_second_point\", \"custom_tag\", \"after_second\", \"custom_field\", \"end\")\n        )\n    )\n)\n```\n\n### profile\n\n#### Features:\n\n* Load profile configs from HOCON or YAML files\n* Common traits to create profiles for any protocol\n* HTTP profile as an example\n\n#### Import:\n\n```scala\nimport ru.tinkoff.gatling.profile._\nimport pureconfig.generic.auto._\n```\n\n#### Using:\n\nHOCON configuration example:\n\n```hocon\n{\n  name: test-profile\n  profile: [\n    {\n      name: request-a\n      url: \"http://test.url\"\n      probability: 50.0\n      method: get\n    },\n    {\n      name: request-b\n      url: \"http://test.url\"\n      probability: 50.0\n      method: post\n      body: \"{\\\"a\\\": \\\"1\\\"}\"\n    }\n  ]\n}\n```\n\nYAML configuration example:\n\n```yaml\nname: test-profile\nprofile:\n  - name: request-a\n    url: \"http://test.url\"\n    probability: 50\n    method: get\n  - name: request-b\n    url: \"http://test.url\"\n    probability: 50\n    method: post\n    body: \"{\\\"a\\\": \\\"1\\\"}\"\n```\n\n*Simulation setUp*\n\n```scala\n  class test extends Simulation {\n  val profileConfigName = \"profile.conf\"\n  val someTestPlan = constantUsersPerSec(intensity) during stageDuration\n  val httpProtocol = http.baseUrl(baseUrl)\n  val config: HttpProfileConfig = new ProfileBuilder[HttpProfileConfig].buildFromYaml(profileConfigName)\n  val scn: ScenarioBuilder = config.toRandomScenario\n\n  setUp(\n    scn.inject(\n      atOnceUsers(10)\n    ).protocols(httpProtocol)\n  ).maxDuration(10)\n}\n```\n\n#### New style profile:\n\nNew profile YAML configuration example:\n\n```yaml\napiVersion: link.ru/v1alpha1\nkind: PerformanceTestProfiles\nmetadata:\n  name: performance-test-profile\n  description: performance test profile\nspec:\n  profiles:\n    - name: maxPerf\n      period: 10.05.2022 - 20.05.2022\n      protocol: http\n      profile:\n        - request: request-1\n          intensity: 100 rph\n          groups: [\"Group1\"]\n          params:\n            method: POST\n            path: /test/a\n            headers:\n              - 'Content-Type: application/json'\n              - 'Connection: keep-alive'\n            body: '{\"a\": \"b\"}'\n        - request: request-2\n          intensity: 200 rph\n          groups: [\"Group1\", \"Group2\"]\n          params:\n            method: GET\n            path: /test/b\n            body: '{\"c\": \"d\"}'\n        - request: request-3\n          intensity: 200 rph\n          groups: [ \"Group1\", \"Group2\" ]\n          params:\n            method: GET\n            path: /test/c\n            body: '{\"e\": \"f\"}'\n```\n\nOptional fields: groups, headers, body.\n\nIf there are no required fields, an exception will be thrown for the missing field.\n\n*Simulation setUp*\n\nScala example:\n```scala\nclass Debug extends Simulation {\n  val pathToProfile = \"path/to/profile.yml\"\n  val scn = ProfileBuilderNew.buildFromYaml(pathToProfile).selectProfile(\"maxPerf\").toRandomScenario\n\n  setUp(\n    scn.inject(\n      atOnceUsers(10)\n    ).protocols(httpProtocol)\n  )\n          .maxDuration(10)\n}\n```\n\nJava example:\n\n```java\nimport ru.tinkoff.gatling.javaapi.profile.ProfileBuilderNew;\n\npublic class Debug extends Simulation {\n\n  public static ScenarioBuilder scn = ProfileBuilderNew\n          .buildFromYaml(\"path/to/profile.yml\")\n          .selectProfile(\"maxPerf\")\n          .toRandomScenario();\n\n  {\n    setUp(\n            scn.injectOpen(atOnceUsers(1))\n    ).protocols(httpProtocol);\n  }\n}\n```\n\nKotlin example:\n```kotlin\nimport ru.tinkoff.gatling.javaapi.profile.ProfileBuilderNew\n\nclass Debug : Simulation() {\n  val scn: ScenarioBuilder = ProfileBuilderNew\n    .buildFromYaml(\"path/to/profile.yml\")\n    .selectProfile(\"maxPerf\")\n    .toRandomScenario()\n\n  init {\n    setUp(\n      scn.injectOpen(atOnceUsers(1)),\n    ).protocols(httpProtocol)\n  }\n}\n```\n\n### redis\n\nThis module allows you to use Redis commands.\n\n#### Features:\n\n- Support Redis commands: SADD, DEL, SREM\n- Support Gatling EL\n\n#### Read before use:\n\n- Мethods are not taken into account in statistics Gatling.\n- Not intended for load testing of Redis.\n\n#### Import:\n\nScala example:\n\n```scala\nimport com.redis.RedisClientPool\nimport ru.tinkoff.gatling.redis.RedisActionBuilder._\n```\n\nJava example:\n\n```java\nimport io.gatling.javaapi.redis.RedisClientPool;\nimport ru.tinkoff.gatling.javaapi.redis.RedisClientPoolJava;\n```\n\nKotlin  example:\n\n```kotlin\nimport io.gatling.javaapi.redis.RedisClientPool\nimport ru.tinkoff.gatling.javaapi.redis.RedisClientPoolJava\n```\n\n#### Using:\n\nFirst you need to prepare RedisClientPool:\n\nScala example:\n\n```scala\nval redisPool = new RedisClientPool(redisUrl, 6379)\n```\n\nJava example:\n\n```java\nstatic RedisClientPool redisClientPool = new RedisClientPool(\"localhost\", 6379);\nstatic RedisClientPoolJava redisClientPoolJava = new RedisClientPoolJava(redisClientPool);\n//or\nstatic RedisClientPoolJava redisClientPoolJava = new RedisClientPoolJava(\"localhost\", 6379);\n```\n\nKotlin  example:\n\n```kotlin\nval redisClientPool = RedisClientPool(\"localhost\", 6379)\nval redisClientPoolJava = RedisClientPoolJava(redisClientPool)\n//or\nval redisClientPoolJava = RedisClientPoolJava(\"localhost\", 6379)\n```\n\nAdd the Redis commands to your scenario chain:\n\nScala example:\n\n```scala\n.exec(redisPool.SADD(\"key\", \"values\", \"values\")) //add the specified members to the set stored at key\n  .exec(redisPool.DEL(\"key\", \"keys\")) //removes the specified keys\n  .exec(redisPool.SREM(\"key\", \"values\", \"values\")) //remove the specified members from the set stored at key\n```\n\nJava example:\n\n```java\n.exec(redisClientPoolJava.SADD(\"key\", \"values\", \"values\")) //add the specified members to the set stored at key\n  .exec(redisClientPoolJava.DEL(\"key\", \"keys\")) //removes the specified keys\n  .exec(redisClientPoolJava.SREM(\"key\", \"values\", \"values\")) //remove the specified members from the set stored at key\n```\n\nKotlin  example:\n\n```kotlin\n.exec(redisClientPoolJava.SADD(\"key\", \"values\", \"values\")) //add the specified members to the set stored at key\n  .exec(redisClientPoolJava.DEL(\"key\", \"keys\")) //removes the specified keys\n  .exec(redisClientPoolJava.SREM(\"key\", \"values\", \"values\")) //remove the specified members from the set stored at key\n```\n\n### templates\n\nThis module contains some syntax extensions for http requests with json body. It allows embed json-body in request\nwith `jsonBody` method for `HttpRequestBuilder`. And this module is provided ability to send request body templates from\nfiles in resource subfolder `resources/templates` by filename. Sending of templates may be done with\nmethod `postTemplate` from trait `Templates`\n\n#### jsonBody\n\nThis part contains http request Json body DSL.\n\nFor use this you need import this:\n\n```scala\nimport ru.tinkoff.gatling.templates.HttpBodyExt._\nimport ru.tinkoff.gatling.templates.Syntax._\n```\n\nThen use described later constructions for embed jsonBody in http requests. For example, you write something like this:\n\n```scala\nclass SampleScenario {\n  val sendJson: ScenarioBuilder =\n    scenario(\"Post some\")\n      .exec(\n        http(\"PostData\")\n          .post(url)\n          .jsonBody(\n            \"id\" - 23, // in json - \"id\" : 23 \n            \"name\", // in json it interpreted as - \"name\" : get value from session variable ${name}\n            \"project\" - ( // in json - \"project\" : { ... }\n              \"id\" ~ \"projectId\", // in json - \"id\" : value from session var ${projectId}\n              \"name\" - \"Super Project\", // in json - \"name\": \"Super Project\"\n              \"sub\" \u003e (1, 2, 3, 4, 5, 6) // in json - \"sub\" : [ 1,2,3,4,5,6 ]\n            )\n          )\n      )\n}\n```\n\nAs result this scenario sent POST request with body:\n\n```json\n{\n  \"id\": 23,\n  \"name\": \"Test\",\n  \"project\": {\n    \"id\": 23421,\n    \"name\": \"Super Project\",\n    \"sub\": [\n      1,\n      2,\n      3,\n      4,\n      5,\n      6\n    ]\n  }\n}\n```\n\nAs you can see in the example:\n\n- construction `\"some_name\" - \u003cval\u003e` map to `\"some_name\": \u003cval\u003e` in json;\n- construction `\"varName\"` map to `\"varName\" : \u003cget value from session variable ${varName}\u003e` in json;\n- construction `\"some_name\" ~ \"sesVar\"` map to `\"some_name\" : \u003cvalue from session var ${sesVar}\u003e` in json;\n- `\"some_name\" \u003e (\u003c...items\u003e)` map to array field `\"some_name\": [ ...items ]` in json;\n- `\"some_name\" - (\u003c...fields\u003e)` map to object field `\"some_name\": { ...fields }` in json;\n\n#### postTemplate\n\nSuppose in folder resources/templates contains this:\n\n```shell\n$ tree resources/\n.\n├── gatling.conf\n├── logback.xml\n├── pools\n│   └── example_pool.csv\n├── simulation.conf\n└── templates\n    └── example_template1.json\n    └── example_template2.json\n```\n\nFor use templates in `resources/templates` you need import trait `Templates`.\n\n```scala\nimport ru.tinkoff.gatling.templates.Templates._\n```\n\nThen add this trait to your Scenario and use `postTemplate` method like show later:\n\n```scala\nclass SampleScenario extends Templates {\n  val sendTemplates: ScenarioBuilder =\n    scenario(\"Templates scenario\")\n      .exec(postTemplate(\"example_template1\", \"/post_route\"))\n      .exec(postTemplate(\"example_template2\", \"/post_route\"))\n}\n```\n\nThis Scenario will send 2 post requests one with body from `example_template1.json`, second with body\nfrom `example_template2.json` to route `$baseUrl/post_route`. In template files you may use\n[gatling expression syntax](https://gatling.io/docs/gatling/reference/current/session/expression_el/).\n\n### utils\n\n#### jwt\n\n#### Features:\n\n* Generates a JWT token using a json template and stores it in a Gatling session, then you can use it to sign requests.\n\n#### Import:\n\nScala:\n```scala\nimport ru.tinkoff.gatling.utils.jwt._\n```\n\nJava:\n```java\nimport ru.tinkoff.gatling.utils.jwt.*;\nimport static ru.tinkoff.gatling.javaapi.utils.Jwt.*;\n```\n\nKotlin\n```kotlin\nimport ru.tinkoff.gatling.javaapi.utils.Jwt.*\n```\n\n#### Using:\n\nFirst you need to prepare jwt generator. For example\n\nScala\n```scala\nval jwtGenerator = jwt(\"HS256\", jwtSecretToken)\n  .defaultHeader\n  .payloadFromResource(\"jwtTemplates/payload.json\")\n```\n\nJava:\n```java\nstatic JwtGeneratorBuilder jwtGenerator = jwt(\"HS256\", \"jwtSecretToken\")  \n        .defaultHeader\n        .payloadFromResource(\"jwtTemplates/payload.json\");\n```\n\nKotlin\n```kotlin\nval jwtGenerator = jwt(\"HS256\", jwtSecretToken)\n  .defaultHeader\n  .payloadFromResource(\"jwtTemplates/payload.json\")\n```\n\nThis will generate tokens with headers:\n\n```json\n{\n  \"alg\": \"HS256\",\n  \"typ\": \"JWT\"\n}\n```\n\nPayload will be generated from json template, templating is done\nusing [Gatling EL](https://gatling.io/docs/current/session/expression_el/)\n\n```json\n{\n  \"userName\": \"${randomString}\",\n  \"date\": \"${simpleDate}\",\n  \"phone\": \"${randomPhone}\"\n}\n```\n\nAlso, the JWT generator has a DSL allowing you to (for java and kotlin similarly):\n\n```scala\njwt(\"HS256\", secret)\n  .header(\"\"\"{\"alg\": \"HS256\",\"typ\": \"JWT\", \"customField\": \"customData\"}\"\"\") //use custom headers from string, it must be valid json\n  .headerFromResource(\"jwtTemplates/header.json\") //use src/test/resources/jwtTemplates/header.json as header template\n  .defaultHeader //use default jwt header, algorithm from jwt(\"HS256\", secret), template: {\"alg\": \"$algorithm\",\"typ\": \"JWT\"}\n  .payload(\"\"\"{\"userName\": \"${randomString}\",\"date\": \"${simpleDate}\",\"phone\": \"${randomPhone}\"}\"\"\") //use custom payload from string, it must be valid json\n  .payloadFromResource(\"jwtTemplates/payload.json\") //use src/test/resources/jwtTemplates/payload.json as payload template\n```\n\nFor sign requests add this to your scenario chain:\n\n```scala\n    .exec(_.setJwt(jwtGenerator, \"jwtToken\")) //generates token and save it to gatling session as \"jwtToken\"\n  .exec(addCookie(Cookie(\"JWT_TOKEN\", \"${jwtToken}\").withDomain(jwtCookieDomain).withPath(\"/\"))) //set JWT_TOKEN cookie for subsequent requests\n```\n\nJava:\n```java\n.exec(setJwt(jwtGenerator, \"jwtToken\"))  //generates token and save it to gatling session as \"jwtToken\"\n    .exec(addCookie(Cookie(\"JWT_TOKEN\", \"#{jwtToken}\").withDomain(\"jwtCookieDomain\").withPath(\"/\"))) //set JWT_TOKEN cookie for subsequent requests\n```\n\nKotlin\n```kotlin\n.exec(setJwt(jwtGenerator, \"jwtToken\"))  //generates token and save it to gatling session as \"jwtToken\"\n  .exec(addCookie(Cookie(\"JWT_TOKEN\", \"#{jwtToken}\").withDomain(\"jwtCookieDomain\").withPath(\"/\"))) //set JWT_TOKEN cookie for subsequent requests\n```\n\n### assertion\n\nModule helps to load assertion configs from YAML files\n\n#### Import:\n\n```scala\nimport ru.tinkoff.gatling.assertions.AssertionsBuilder.assertionFromYaml\n```\n\n#### Using:\n\nFile nfr contains non-functional requirements.\n\nRequirements supported by Picatinny:\n\n|  requirement|  key |\n|---|---|\n|  99th percentile of the responseTime | 99 перцентиль времени выполнения  |\n|  95th percentile of the responseTime | 95 перцентиль времени выполнения  |\n|  75th percentile of the responseTime |  75 перцентиль времени выполнения |\n|  50th percentile of the responseTime |  50 перцентиль времени выполнения |\n|  percent of the failedRequests |  Процент ошибок |\n|  maximum of the responseTime |  Максимальное время выполнения |\n\nYAML configuration example:\n\n```yaml\nnfr:\n  - key: '99 перцентиль времени выполнения'\n    value:\n      GET /: '500'\n      MyGroup / MyRequest: '900'\n      request_1: '700'\n      all: '1000'\n  - key: 'Процент ошибок'\n    value:\n      all: '5'\n  - key: 'Максимальное время выполнения'\n    value:\n      GET /: '1000'\n      all: '2000'\n```\n\n*Scala example*\n\n```scala\n  class test extends Simulation {\n\n  setUp(\n    scn.inject(\n      atOnceUsers(10)\n    ).protocols(httpProtocol)\n  ).maxDuration(10)\n    .assertions(assertionFromYaml(\"src/test/resources/nfr.yml\"))\n}\n```\n\n*Java example*\n\n```java\n  import static ru.tinkoff.gatling.javaapi.Assertions.assertionFromYaml;\n\n  class test extends Simulation {\n\n  setUp(\n    scn.inject(\n      atOnceUsers(10)\n    ).protocols(httpProtocol)\n  ).maxDuration(10)\n    .assertions(assertionFromYaml(\"src/test/resources/nfr.yml\"))\n```\n\n*Kotlin example*\n\n```kotlin\n  import ru.tinkoff.gatling.javaapi.Assertions.assertionFromYaml;\n\n  class test extends Simulation {\n\n  setUp(\n    scn.inject(\n      atOnceUsers(10)\n    ).protocols(httpProtocol)\n  ).maxDuration(10)\n    .assertions(assertionFromYaml(\"src/test/resources/nfr.yml\"))\n```\n\n### transactions\n\nThis extension introduce new syntax (`startTransaction`/`endTransaction`) for gatling scenario. Transaction is union of\nactions, that able to measure summary response time of actions with pauses. It is same as groups, but response time\nmeasuring include pauses, and you may pass endTime manually. That make possible write something like:\n\n```scala\nexec(Actions.createEntity())\n  .startTransaction(\"transaction1\")\n  .doWhile(_ (\"i\").as[Int] \u003c 10)(\n    feed(feeder)\n      .exec(Actions.insertTest())\n      .pause(2)\n      .exec(Actions.selectTest)\n  )\n  .endTransaction(\"transaction1\")\n  .exec(Actions.batchTest)\n  .exec(Actions.selectAfterBatch)\n\n```\n\nJava example:\n\n```java\nexec(Actions.createEntity())\n  .exec(startTransaction(\"transaction1\"))\n  .exec(Actions.insertTest())\n  .pause(2)\n  .exec(Actions.selectTest)\n  .exec(endTransaction(\"transaction1\"))\n  .exec(Actions.batchTest)\n  .exec(Actions.selectAfterBatch)\n```\n\nKotlin example:\n\n```kotlin\nexec(Actions.createEntity())\n  .exec(startTransaction(\"transaction1\"))\n  .exec(Actions.insertTest())\n  .pause(2)\n  .exec(Actions.selectTest)\n  .exec(endTransaction(\"transaction1\"))\n  .exec(Actions.batchTest)\n  .exec(Actions.selectAfterBatch)\n```\n\n#### Usage:\n\nFor use this you need gatling with version greater or equal than **3.6.1** and import this in Scenario and Simulations:\n\n```scala\nimport ru.tinkoff.gatling.transactions.Predef._\n```\n\n**Attention!**\n*Your simulation should inherit the class `SimulationWithTransactions` instead of `Simulation`, then the transaction\nmechanism will work correctly.*\n\n#### Example Simulation:\n\n```scala\nimport io.gatling.core.Predef._\nimport io.gatling.core.structure.ScenarioBuilder\n..\n\n.\nimport ru.tinkoff.gatling.transactions.Predef._\n\nobject DebugScenario {\n  val scn: ScenarioBuilder = scenario(\"Debug\")\n    .exec(Actions.createEntity())\n    .startTransaction(\"transaction1\")\n    .doWhile(_ (\"i\").as[Int] \u003c 10)(\n      feed(feeder)\n        .exec(Actions.insertTest())\n        .pause(2)\n        .exec(Actions.selectTest)\n    )\n    .endTransaction(\"transaction1\")\n    .exec(Actions.batchTest)\n    .exec(Actions.selectAfterBatch)\n}\n\nclass DebugTest extends SimulationWithTransactions {\n\n  setUp(\n    DebugScenario.scn.inject(atOnceUsers(1))\n  ).protocols(dataBase)\n\n}\n```\n\n### example\n\nSee the examples in the examples/ directory.\n\nYou can run these from the sbt console with the commands ```project example```\nand then ```gatling:testOnly ru.tinkoff.load.example.SampleSimulation```.\n\nEnsure that the correct InfluxDB parameters are specified in gatling.conf and influx.conf.\n\n## Testing\n\nTo test your changes use `sbt test`.\n\n## Built with\n\n* Scala version: 2.13.8\n* SBT version: 1.6.1\n* Gatling version: 3.7.4\n* SBT Gatling plugin version: 4.1.2\n* SBT CI release plugin version: 1.5.10\n* json4s version: 4.0.2\n* pureconfig version: 0.17.1\n* scalatest version: 3.2.10\n* scalacheck version: 1.15.4\n* scalamock version: 5.2.0\n* generex version: 1.0.2\n* jwt-core version: 5.0.0\n* scala influxdb client 0.6.3\n\n## Help\n\ntelegram: @qa_load\n\ngatling docs: https://gatling.io/docs/current/general\n\n## Versioning\n\nWe use [SemVer](http://semver.org/) for versioning. For the versions available, see\nthe [tags on this repository](https://github.com/TinkoffCreditSystems/gatling-picatinny/tags).\n\n## Authors\n\n* **Maksim Sitnikov** - *profile module* - [m.sitnikov@tinkoff.ru](m.sitnikov@tinkoff.ru)\n\n* **Chepkasov Sergey** - *influxdb, feeders, config, utils modules* - [s.chepkasov@tinkoff.ru]([s.chepkasov@tinkoff.ru)\n\n* **Kalyokin Vyacheslav** - *templates module* - [v.kalyokin@tinkoff.ru](v.kalyokin@tinkoff.ru)\n\n* **Akhaltsev Ioann** - *founder and spiritual guidance* - [i.akhaltsev@tinkoff.ru](i.akhaltsev@tinkoff.ru)\n\nSee also the list of [contributors](https://github.com/TinkoffCreditSystems/gatling-picatinny/graphs/contributors) who\nparticipated in this project.\n\n## License\n\nThis project is licensed under the Apache 2.0 License - see the [LICENSE](LICENSE) file for details\n\n## Acknowledgments\n\nTBD\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FTinkoff%2Fgatling-picatinny","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FTinkoff%2Fgatling-picatinny","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FTinkoff%2Fgatling-picatinny/lists"}