diff --git a/.ai-memory-bank/.gitkeep b/.ai-memory-bank/.gitkeep
new file mode 100644
index 000000000000..689752efa566
--- /dev/null
+++ b/.ai-memory-bank/.gitkeep
@@ -0,0 +1,2 @@
+# This file is used to keep the memory-bank directory in the repository.
+# It can be safely deleted if the directory contains other files.
\ No newline at end of file
diff --git a/.ai-memory-bank/comprehensive-file-index.md b/.ai-memory-bank/comprehensive-file-index.md
new file mode 100644
index 000000000000..c5009320bf70
--- /dev/null
+++ b/.ai-memory-bank/comprehensive-file-index.md
@@ -0,0 +1,350 @@
+# Comprehensive File Index
+
+## Root Directory
+- .ai_system_prompt - AI system prompt configuration file
+- .clang-format - Code formatting configuration for C++ code
+- .clang-tidy - Static analysis configuration for C++ code
+- .cmake-format.py - Configuration for formatting CMake files
+- .editorconfig - Editor configuration for consistent coding styles across editors
+- .gitattributes - Git attributes configuration for file handling
+- .gitignore - Git ignore patterns for excluding files from version control
+- .mapping.json - JSON mapping configuration file
+- .piglet-meta.json - Metadata configuration for Piglet tooling
+- 🛡️ .roorules - Project-specific rules and guidelines for AI agents
+- AUTHORS - List of contributors and authors of the project
+- CMakeLists.txt - Root CMake build configuration file
+- CODE_OF_CONDUCT.md - Guidelines for community conduct and interactions
+- CODEOWNERS - File defining code ownership for different parts of the project
+- conanfile.py - Conan package manager configuration file
+- CONTRIBUTING.md - Guidelines for contributing to the project
+- LICENSE - License information for the project
+- Makefile - Make build system configuration
+- pyproject.toml - Python project configuration file
+- pytest.ini - Configuration file for pytest testing framework
+- README.md - Main project documentation and introduction
+- SECURITY.md - Security policies and reporting procedures
+- THIRD_PARTY.md - Information about third-party dependencies and licenses
+
+### GitHub Configuration (.github/)
+- dependabot.yml - Configuration for automated dependency updates
+- docker-compose.yml - Docker Compose configuration for development environment
+- pull_request_template.md - Template for pull request descriptions
+- ISSUE_TEMPLATE/ - Templates for different types of issues
+- workflows/ - GitHub Actions workflow definitions
+
+## Core Framework (core/)
+The core framework provides the foundation components for building high-performance C++ services with asynchronous I/O and coroutine support.
+
+### Core Components
+- benchmarks/ - Performance benchmarking tools and tests
+- build_config.hpp.in - Build configuration template file
+- CMakeLists.txt - CMake build configuration for core framework
+- dynamic_configs/ - Dynamic configuration management system
+- functional_tests/ - Functional tests for core framework components
+- include/ - Public header files for core framework components
+- internal/ - Internal implementation details not exposed in public API
+- library.yaml - Library configuration file
+- libc_include_fixes/ - Fixes for standard library includes
+- README.md - Core framework documentation
+- src/ - Source code implementation of core framework components
+- static_configs/ - Static configuration files
+- sys_coro/ - System coroutine implementation
+- uboost_coro/ - Boost coroutine integration
+- utest/ - Unit testing framework and utilities
+
+### Core Subdirectories
+#### alerts/
+Framework for handling alert notifications and monitoring
+- source.hpp - Alert source interface
+
+#### baggage/
+Distributed tracing baggage propagation implementation
+- baggage_manager.hpp - Baggage manager implementation
+- baggage.hpp - Baggage data structure
+- fwd.hpp - Forward declarations for baggage components
+
+#### cache/
+Caching framework for in-memory and external cache solutions
+- cache_config.hpp - Cache configuration structures
+- cache_statistics.hpp - Cache statistics tracking
+- caching_component_base.hpp - Base class for caching components
+- expirable_lru_cache.hpp - LRU cache with expiration support
+- lru_cache_component_base.hpp - Base class for LRU cache components
+- lru_cache_config.hpp - LRU cache configuration
+- lru_cache_statistics.hpp - LRU cache statistics
+- nway_lru_cache.hpp - N-way LRU cache implementation
+- update_type.hpp - Cache update type definitions
+
+#### clients/
+HTTP and other client implementations for external service communication
+
+##### clients/dns/
+DNS client implementation
+- component.hpp - DNS client component
+- resolver.hpp - DNS resolver implementation
+
+##### clients/http/
+HTTP client implementation
+- client.hpp - HTTP client interface
+- component.hpp - HTTP client component
+- request.hpp - HTTP request representation
+- response.hpp - HTTP response representation
+- plugins/ - HTTP client plugins for additional functionality
+
+#### components/
+Component system for modular service architecture with lifecycle management
+- component_base.hpp - Base class for all components
+- component_list.hpp - Component list management
+- run.hpp - Service startup and shutdown functions
+
+#### concurrent/
+Concurrency primitives and utilities for thread-safe operations
+
+#### congestion_control/
+Network congestion control mechanisms
+- component.hpp - Congestion control component
+- controller.hpp - Congestion controller interface
+- limiter.hpp - Request rate limiter
+- sensor.hpp - Congestion monitoring sensor
+
+#### dist_lock/
+Distributed locking implementation for coordination between services
+
+#### dump/
+Data dump and restore functionality for caching components
+- config.hpp - Dump configuration structures
+- dumper.hpp - Data dumper interface
+- operations.hpp - Dump operations
+
+#### dynamic_config/
+Dynamic configuration system for runtime configuration changes
+- snapshot.hpp - Configuration snapshot
+- source.hpp - Configuration source
+- value.hpp - Typed configuration value wrapper
+
+#### engine/
+Coroutine-based engine for asynchronous I/O operations
+- async.hpp - Asynchronous task execution
+- task.hpp - Task management
+- mutex.hpp - Coroutine-aware mutex
+- condition_variable.hpp - Coroutine-aware condition variable
+
+#### fs/
+File system operations and utilities
+
+#### logging/
+Structured logging framework with multiple output options
+
+#### middlewares/
+Middleware components for request/response processing
+- pipeline.hpp - Middleware pipeline implementation
+- runner.hpp - Middleware execution runner
+
+#### net/
+Networking utilities and implementations
+
+#### os_signals/
+Operating system signal handling
+- component.hpp - OS signals handling component
+- processor.hpp - Signal processor implementation
+
+#### rcu/
+Read-Copy-Update (RCU) synchronization mechanism
+- rcu.hpp - RCU implementation
+- rcu_map.hpp - RCU-protected map data structure
+
+#### server/
+HTTP server implementation and related components
+
+#### storages/
+Storage abstractions for various database systems
+
+#### tracing/
+Distributed tracing implementation
+
+#### utils/
+General utility functions and classes
+
+## Code Generation Components
+
+### Chaotic (chaotic/)
+Code generation framework for data structures and serialization
+
+#### Chaotic Directories
+- bin/ - Executable scripts for code generation
+- bin-dynamic-configs/ - Dynamic configuration generation scripts
+- chaotic/ - Core chaotic implementation
+- golden_tests/ - Reference tests for generated code
+- include/ - Public header files for chaotic components
+- integration_tests/ - Integration tests for chaotic components
+- mypy/ - Python type checking configuration
+- src/ - Source code implementation
+- tests/ - Unit tests for chaotic components
+
+### Chaotic OpenAPI (chaotic-openapi/)
+OpenAPI-based code generation for client and server implementations
+
+#### Chaotic OpenAPI Directories
+- bin/ - Executable scripts for OpenAPI code generation
+- chaotic_openapi/ - Core OpenAPI implementation
+- golden_tests/ - Reference tests for generated OpenAPI code
+- include/ - Public header files for OpenAPI components
+- integration_tests/ - Integration tests for OpenAPI components
+- mypy/ - Python type checking configuration
+- src/ - Source code implementation
+- tests/ - Unit tests for OpenAPI components
+
+## Database Components
+
+### ClickHouse (clickhouse/)
+ClickHouse database driver and components for analytics workloads
+
+### MongoDB (mongo/)
+MongoDB database driver and components for document-based storage
+
+### MySQL (mysql/)
+MySQL database driver and components for relational data storage
+
+### PostgreSQL (postgresql/)
+PostgreSQL database driver and components for relational data storage
+
+### Redis/Valkey (redis/)
+Redis/Valkey database driver and components for in-memory data storage
+
+### RocksDB (rocks/)
+RocksDB key-value storage driver and components
+- CMakeLists.txt - CMake build configuration for RocksDB component
+- library.yaml - Library configuration file
+- include/ - Public header files for RocksDB components
+- src/ - Source code implementation for RocksDB components
+
+### SQLite (sqlite/)
+SQLite database driver and components for embedded relational storage
+
+### YDB (ydb/)
+YDB (Yandex Database) driver and components for distributed storage
+
+## Communication Components
+
+### gRPC (grpc/)
+gRPC communication framework for high-performance RPC calls
+
+### Kafka (kafka/)
+Kafka messaging system integration for event streaming
+
+### RabbitMQ (rabbitmq/)
+RabbitMQ messaging system integration for message queuing
+
+## Messaging Theme Rules (ai-development-settings/extreme/.roo/rules/20-themes/messaging/)
+Comprehensive messaging patterns and best practices for building robust messaging systems
+
+### Messaging Theme Rule Files
+- async-messaging.md - Asynchronous messaging patterns and processing strategies
+- event-driven.md - Event-driven architecture and event sourcing patterns
+- kafka.md - Kafka integration patterns and implementation guidelines
+- message-patterns.md - General message queue patterns and best practices
+- rabbitmq.md - RabbitMQ patterns and message queue management
+
+## Other Components
+
+### ODBC (odbc/)
+ODBC database connectivity layer
+
+### OpenTelemetry (otlp/)
+OpenTelemetry protocol implementation for distributed tracing and metrics
+
+## Development Support
+
+### CMake (cmake/)
+CMake build system files and configurations for building userver applications
+
+### Libraries (libraries/)
+Additional libraries and dependencies
+
+### Scripts (scripts/)
+Development scripts and tools for various tasks
+
+### Testsuite (testsuite/)
+Testing framework and utilities for functional and integration testing
+
+### Third Party (third_party/)
+Third-party dependencies and libraries
+
+### Universal (universal/)
+Universal components that work across different systems
+
+## Sample Services (samples/)
+Example services demonstrating how to use various components of the userver framework
+
+### Sample Services List
+- chaotic_openapi_service/ - Service using OpenAPI code generation
+- chaotic_service/ - Service using chaotic code generation
+- clickhouse_service/ - Service using ClickHouse database
+- config_service/ - Service demonstrating configuration management
+- digest_auth_service/ - Service with digest authentication
+- dns-resolver/ - Service demonstrating DNS resolution
+- embedded_files/ - Service with embedded files
+- flatbuf_service/ - Service using FlatBuffers serialization
+- grpc_middleware_service/ - Service with gRPC middleware
+- grpc_service/ - Service using gRPC communication
+- grpc-generic-proxy/ - Generic gRPC proxy service
+- hello_service/ - Basic hello world service example
+- http_caching/ - Service demonstrating HTTP caching
+- http_middleware_service/ - Service with HTTP middleware
+- http-client-perf/ - HTTP client performance testing service
+- https_service/ - Service with HTTPS support
+- json2yaml/ - Service for converting JSON to YAML
+- kafka_service/ - Service using Kafka messaging
+- mongo_service/ - Service using MongoDB database
+- mongo-support/ - MongoDB support utilities
+- multipart_service/ - Service handling multipart requests
+- mysql_service/ - Service using MySQL database
+- netcat/ - Network utility service
+- otlp_service/ - Service using OpenTelemetry protocol
+- postgres_auth/ - Service with PostgreSQL authentication
+- postgres_cache_order_by/ - Service with PostgreSQL caching
+- postgres_service/ - Service using PostgreSQL database
+- postgres-support/ - PostgreSQL support utilities
+- production_service/ - Example production-ready service
+- rabbitmq_service/ - Service using RabbitMQ messaging
+- redis_service/ - Service using Redis database
+- s3api/ - Service with S3 API integration
+- static_service/ - Service serving static files
+- tcp_full_duplex_service/ - Full-duplex TCP service
+- tcp_service/ - TCP-based service
+- testsuite-support/ - Testsuite support utilities
+- websocket_service/ - Service with WebSocket support
+- ydb_service/ - Service using YDB database
+
+## Service Template (service_template/)
+Template for creating new services with standard structure and configuration
+
+### Service Template Components
+- .clang-format - Code formatting configuration
+- .gitignore - Git ignore patterns
+- CMakeLists.txt - CMake build configuration
+- CMakePresets.json - CMake presets configuration
+- Makefile - Make build system configuration
+- README.md - Service documentation
+- requirements.txt - Python requirements
+- run_as_user.sh - Script for running service as specific user
+- .devcontainer/ - Development container configuration
+- .github/ - GitHub workflow configurations
+- cmake/ - CMake modules and configurations
+- configs/ - Configuration files
+- postgresql/ - PostgreSQL schema files
+- proto/ - Protocol buffer definitions
+- src/ - Source code implementation
+- tests/ - Test files
+
+## Memory Bank (memory-bank/)
+Project-specific documentation and context files for AI agents
+
+### Memory Bank Files
+- projectbrief.md - Foundation document defining core requirements and goals
+- productContext.md - Product context including problems solved and user experience goals
+- systemPatterns.md - System architecture and technical decisions
+- techContext.md - Technology context including development setup and dependencies
+- activeContext.md - Current work focus, recent changes, and next steps
+- progress.md - Current status, completed work, and known issues
+- comprehensive-file-index.md - Detailed file index with descriptions (this document)
\ No newline at end of file
diff --git a/.ai_system_prompt b/.ai_system_prompt
new file mode 100644
index 000000000000..d6cdeebb25f2
--- /dev/null
+++ b/.ai_system_prompt
@@ -0,0 +1,84 @@
+# AI System Prompt for userver Service Development
+
+I am an expert C++ software engineer specializing in developing services using the userver framework. My purpose is to help create high-performance, scalable services that leverage the userver framework's component-based architecture and asynchronous capabilities.
+
+## Core Principles
+
+### Service Development Focus
+- I develop services that use the userver framework, not the framework itself
+- I focus on implementing business logic in handlers and components
+- I use existing framework components rather than creating new ones
+- I follow service development patterns and best practices
+
+### Component-Based Architecture
+- Services are composed of components registered in main.cpp
+- Components have well-defined lifecycles (Constructor → Start → Stop → Destructor)
+- Components are configured through static_config.yaml
+- Dependencies between components are declared explicitly
+
+### Asynchronous Programming Model
+- All I/O operations use userver's asynchronous drivers
+- Operations are non-blocking and coroutine-safe
+- Deadline propagation ensures responsive service behavior
+- Cancellation support handles long-running operations gracefully
+
+## Available Resources
+
+### Examples and Templates
+- `samples/` directory contains working examples of various userver features
+- `service_template/` provides a base template for creating new services
+- `.ai-memory-bank/comprehensive-file-index.md` contains project file overview
+
+### Key Directories
+- `samples/hello_service/` - Simple HTTP service example
+- `samples/postgres_service/` - Service with PostgreSQL integration
+- `samples/redis_service/` - Service with Redis integration
+- `samples/grpc_service/` - gRPC service example
+
+## Development Workflow
+
+### 1. Service Creation
+- Start with the service_template/ as a base
+- Copy template: `cp -r service_template/ my_new_service/`
+- Customize CMakeLists.txt for project-specific dependencies
+- Implement handlers in src/ directory following established patterns
+- Configure components in configs/static_config.yaml
+
+### 2. Building and Running
+- Create build directory: `mkdir build && cd build`
+- Configure: `cmake ..`
+- Build: `make -j$(nproc)`
+- Run: `./my_service --config ../configs/static_config.yaml`
+
+### 3. Testing
+- Unit tests: `make test`
+- Functional tests: `cd tests && python3 -m pytest`
+
+## Best Practices
+
+### Performance Optimization
+- Use asynchronous operations to minimize context switches
+- Implement connection pooling for database and HTTP clients
+- Apply deadline propagation to all downstream requests
+- Use caching for frequently accessed data
+
+### Development Practices
+- Follow component lifecycle guidelines for initialization and cleanup
+- Use framework-provided synchronization primitives
+- Implement structured logging with contextual information
+- Write tests at multiple levels (unit, functional, integration)
+
+## Quality Standards
+
+### Code Quality
+- Follow userver coding guidelines and patterns
+- Use RAII and exception-safe coding practices
+- Implement proper memory management with smart pointers
+- Maintain consistent code formatting
+
+### Testing Requirements
+- Unit tests for core business logic
+- Functional tests for service endpoints
+- Integration tests for database and external service interactions
+
+This system prompt provides the foundation for developing high-quality services on the userver framework while maintaining consistency with established patterns and best practices. For detailed implementation guidance, refer to the .roorules file and examples in the samples/ directory.
diff --git a/.clang-tidy b/.clang-tidy
index 2c46a3b74dd3..bd9896841315 100644
--- a/.clang-tidy
+++ b/.clang-tidy
@@ -112,3 +112,67 @@ CheckOptions:
value: '1'
- key: modernize-loop-convert.UseCxx20ReverseRanges
value: 'false'
+
+ - key: readability-identifier-naming.ClassCase
+ value: CamelCase
+ # Allow DISABLED_CamelCase to skip gtest tests.
+ - key: readability-identifier-naming.ClassIgnoredRegexp
+ value: '^DISABLED_([A-Z][a-z]+)+$'
+ - key: readability-identifier-naming.StructCase
+ value: CamelCase
+ - key: readability-identifier-naming.EnumCase
+ value: CamelCase
+ - key: readability-identifier-naming.UnionCase
+ value: CamelCase
+ - key: readability-identifier-naming.MemberCase
+ value: lower_case
+ - key: readability-identifier-naming.PrivateMemberSuffix
+ value: '_'
+ - key: readability-identifier-naming.ProtectedMemberSuffix
+ value: '_'
+ - key: readability-identifier-naming.MethodCase
+ value: CamelCase
+ - key: readability-identifier-naming.PublicMethodIgnoredRegexp
+ value: "(begin|end|empty|size|ysize|front|back|parse|format)"
+ - key: readability-identifier-naming.FunctionCase
+ value: CamelCase
+ - key: readability-identifier-naming.ParameterCase
+ value: lower_case
+ - key: readability-identifier-naming.ParameterPackCase
+ value: lower_case
+ # - key: readability-identifier-naming.VariableCase
+ # value: lower_case
+ # - key: readability-identifier-naming.ConstexprVariableCase
+ # value: CamelCase
+ # - key: readability-identifier-naming.ConstexprVariablePrefix
+ # value: k
+ # - key: readability-identifier-naming.GlobalVariableCase
+ # value: CamelCase
+ # - key: readability-identifier-naming.GlobalVariablePrefix
+ # value: k
+ # - key: readability-identifier-naming.StaticVariableCase
+ # value: CamelCase
+ # - key: readability-identifier-naming.StaticVariablePrefix
+ # value: k
+ - key: readability-identifier-naming.MacroDefinitionCase
+ value: UPPER_CASE
+ - key: readability-identifier-naming.NamespaceCase
+ value: lower_case
+ # Allow NCamelCase for NMonitoring.
+ - key: readability-identifier-naming.NamespaceIgnoredRegexp
+ value: ^N([A-Z][a-z]+)+$
+ # Allow kCamelCase.
+ - key: readability-identifier-naming.ValueTemplateParameterIgnoredRegexp
+ value: ^k([A-Z][a-z]+)+$
+ - key: readability-identifier-naming.ConceptCase
+ value: CamelCase
+ - key: readability-identifier-naming.TemplateParameterCase
+ value: CamelCase
+ - key: readability-identifier-naming.TemplateTemplateParameterCase
+ value: CamelCase
+ - key: readability-identifier-naming.TypedefCase
+ value: CamelCase
+ - key: readability-identifier-naming.TypeTemplateParameterCase
+ value: CamelCase
+ - key: readability-identifier-naming.ValueTemplateParameterCase
+ value: CamelCase
diff --git a/.github/workflows/alpine.yml b/.github/workflows/alpine.yml
index 1a349ef7037b..55cf16f71a8f 100644
--- a/.github/workflows/alpine.yml
+++ b/.github/workflows/alpine.yml
@@ -22,7 +22,7 @@ jobs:
strategy:
fail-fast: false
- name: ubuntu-24.04 (build-only)
+ name: alpine (build-only)
runs-on: ubuntu-24.04
env:
@@ -46,7 +46,7 @@ jobs:
-DUSERVER_FORCE_DOWNLOAD_GRPC=1
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
with:
fetch-depth: 0
diff --git a/.github/workflows/archlinux.yml b/.github/workflows/archlinux.yml
new file mode 100644
index 000000000000..542f88087a1d
--- /dev/null
+++ b/.github/workflows/archlinux.yml
@@ -0,0 +1,156 @@
+name: Arch
+
+'on':
+ pull_request:
+ push:
+ branches:
+ - master
+ - develop
+ - feature/**
+
+env:
+ UBSAN_OPTIONS: print_stacktrace=1
+ ASAN_OPTIONS: detect_odr_violation=2
+ CXX: clang++
+ CC: clang
+ CCACHE_DIR: /home/runner/.cache/ccache
+ CCACHE_NOHASHDIR: true
+ CPM_SOURCE_CACHE: /home/runner/.cache/CPM
+
+jobs:
+ archlinux:
+ strategy:
+ fail-fast: false
+
+ name: archlinux-latest
+ runs-on: ubuntu-latest
+ container:
+ image: archlinux:latest
+
+ env:
+ CMAKE_FLAGS: >-
+ -DCMAKE_BUILD_TYPE=Debug
+ -DCMAKE_CXX_STANDARD=17
+ -DUSERVER_USE_LD=lld
+ -DUSERVER_NO_WERROR=1
+ -DUSERVER_BUILD_ALL_COMPONENTS=1
+ -DUSERVER_BUILD_SAMPLES=1
+ -DUSERVER_BUILD_TESTS=1
+ -DUSERVER_FEATURE_GRPC=0
+ -DUSERVER_FEATURE_GRPC_REFLECTION=0
+ -DUSERVER_FEATURE_GRPC_PROTOVALIDATE=0
+ -DUSERVER_FEATURE_MONGODB=0
+ -DUSERVER_FEATURE_OTLP=0
+ -DUSERVER_FEATURE_ROCKS=0
+ -DUSERVER_USE_STATIC_LIBS=0
+ -DUSERVER_FEATURE_PATCH_LIBPQ=0
+
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Restore cached directories
+ id: restore-cache
+ uses: actions/cache/restore@v4
+ with:
+ path: |
+ ${{env.CCACHE_DIR}}
+ ${{env.CPM_SOURCE_CACHE}}
+ key: 'archlinux-cache-dir ${{github.ref}} run-${{github.run_number}}'
+ restore-keys: |
+ archlinux-cache-dir ${{github.ref}}
+ archlinux-cache-dir
+
+ - name: Setup host cache dirs
+ run: |
+ mkdir -p ${{env.CCACHE_DIR}}
+ mkdir -p ${{env.CPM_SOURCE_CACHE}}
+
+ - name: Update system and install base dependencies
+ run: |
+ pacman -Syu --noconfirm
+ pacman -S --needed --noconfirm \
+ base-devel \
+ git \
+ cmake \
+ make \
+ clang \
+ lld \
+ ccache \
+ python \
+ pkg-config
+
+ - name: Install development dependencies
+ run: |
+ useradd build
+
+ pacman -S --needed --noconfirm $(cat scripts/docs/en/deps/arch.md | grep -v -- 'makepkg|' )
+ set -x ; \
+ cat scripts/docs/en/deps/arch.md | grep -oP '^makepkg\|\K.*' | while read REPLY; \
+ do \
+ DIR=$(mktemp -d) ;\
+ git clone --branch $REPLY --single-branch https://github.com/archlinux/aur.git $DIR ;\
+ pushd $DIR ;\
+
+ chmod a+x .
+ chmod a+rw -R .
+
+ yes | su -s /usr/bin/makepkg - build ;\
+ pacman -U --noconfirm *.zst
+
+ popd ;\
+ rm -rf $DIR ;\
+ done
+
+ - name: Install test dependencies
+ if: ${{ false }} # Not working yet
+ run: |
+ pacman -S --needed --noconfirm \
+ postgresql \
+ redis \
+ clickhouse \
+ rabbitmq \
+ mongodb
+
+ - name: Setup caches
+ run: |
+ echo "Cached CPM packages:"
+ du -h -d 1 ${{env.CPM_SOURCE_CACHE}} || true
+ for f in $(find ${{env.CPM_SOURCE_CACHE}} -name "cmake.lock" 2>/dev/null || true);
+ do
+ repo=$(ls -d $(dirname $f)*/ 2>/dev/null || true);
+ echo "Repository: $repo";
+ git config --global --add safe.directory $repo 2>/dev/null || true;
+ done
+
+ ccache -M 2.0GB
+ ccache -s -v
+
+ - name: Run cmake
+ run: |
+ cmake -S . -B build_debug $CMAKE_FLAGS
+
+ - name: Compile
+ run: |
+ cmake --build build_debug -j$(nproc)
+
+ - name: Save cached directories
+ uses: actions/cache/save@v4
+ with:
+ path: |
+ ${{env.CCACHE_DIR}}
+ ${{env.CPM_SOURCE_CACHE}}
+ key: ${{ steps.restore-cache.outputs.cache-primary-key }}
+
+ - name: Show cache stats
+ run: |
+ du -h -d 1 ${{env.CCACHE_DIR}} || true
+ du -h -d 1 ${{env.CPM_SOURCE_CACHE}} || true
+ ccache -s -v
+
+ - name: Run tests
+ if: ${{ false }} # Not working yet
+ run: |
+ cd build_debug
+ ctest -V
diff --git a/.github/workflows/ci-conan.yml b/.github/workflows/ci-conan.yml
index 121cb20bc950..2da1bcf62a96 100644
--- a/.github/workflows/ci-conan.yml
+++ b/.github/workflows/ci-conan.yml
@@ -14,7 +14,8 @@ env:
jobs:
build:
runs-on: ${{ matrix.os }}
- name: ${{ matrix.os }}
+ container: ${{ matrix.container }}
+ name: "${{ matrix.os }} (container: ${{ matrix.container || 'GithubCI' }})"
strategy:
fail-fast: false
matrix:
@@ -22,53 +23,74 @@ jobs:
- os: ubuntu-22.04
conanflags: ''
tests-env: 'JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64'
- - os: macos-latest
+ - os: ubuntu-22.04
+ container: ubuntu:22.04
+ conanflags: ''
+ tests-env: 'JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64'
+ - os: macos-14
conanflags: '-o python_path=python3.11'
tests-env: ''
steps:
- - name: Checkout
- uses: actions/checkout@v4
+ - name: Install sudo
+ if: matrix.container == 'ubuntu:22.04'
+ run: |
+ DEBIAN_FRONTEND=noninteractive apt update
+ DEBIAN_FRONTEND=noninteractive apt install -y sudo
- name: Install Ubuntu packages
if: matrix.os == 'ubuntu-22.04'
run: |
- sudo apt update
- sudo apt install -y \
- gcc g++ cmake wget git clang-format \
+ sudo DEBIAN_FRONTEND=noninteractive apt update
+ sudo DEBIAN_FRONTEND=noninteractive apt install -y \
+ libpq-dev \
+ gcc g++ cmake git clang-format \
python3 python3-pip python3-venv
- - name: Install test dependencies
- if: matrix.os == 'ubuntu-22.04'
- run: |
- wget -qO- https://pgp.mongodb.com/server-7.0.asc | sudo gpg --dearmor | sudo tee /usr/share/keyrings/mongodb-server-7.0.gpg >/dev/null
- echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-7.0.gpg ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/7.0 multiverse" \
- | sudo tee -a /etc/apt/sources.list.d/mongodb-org-7.0.list
-
- sudo apt update
- sudo apt install -y postgresql redis mongodb-org mongodb-mongosh
- sudo ./scripts/kafka/ubuntu_install_kafka.sh
-
- ./scripts/rabbitmq/ubuntu_install_rabbitmq_server.sh
-
- name: Install MacOS packages
- if: matrix.os == 'macos-latest'
+ if: matrix.os == 'macos-14'
run: |
brew update
brew tap mongodb/brew
brew install clang-format postgresql redis kafka rabbitmq mongodb-community
brew install python@3.11
+ - name: Checkout
+ uses: actions/checkout@v5
+
+ - name: Change permissions
+ if: matrix.container == 'ubuntu:22.04'
+ run: |
+ useradd -m -G sudo -s /bin/bash test-user
+ chown -R test-user .
+
- name: Install common packages
run: |
pip install "conan==2.8.0"
pip install numpy
- conan profile detect
- conan profile show
+ ${{ matrix.container && 'sudo -u test-user' }} conan profile detect
+ ${{ matrix.container && 'sudo -u test-user' }} conan profile show
- name: Run conan
run: |
- conan create . --build=missing -s:a compiler.cppstd=17 -pr:b=default ${{matrix.conanflags}}
+ ${{ matrix.container && 'sudo -u test-user' }} git config --global --add safe.directory `pwd`
+ ${{ matrix.container && 'sudo -u test-user' }} conan create . --build=missing -s:a compiler.cppstd=17 -pr:b=default ${{matrix.conanflags}}
+
+ - name: Install test dependencies
+ if: matrix.os == 'ubuntu-22.04'
+ run: |
+ sudo DEBIAN_FRONTEND=noninteractive apt install -y curl wget lsb-release
+
+ wget -qO- https://pgp.mongodb.com/server-7.0.asc | sudo gpg --dearmor | sudo tee /usr/share/keyrings/mongodb-server-7.0.gpg >/dev/null
+ echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-7.0.gpg ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/7.0 multiverse" \
+ | sudo tee -a /etc/apt/sources.list.d/mongodb-org-7.0.list
+
+ sudo DEBIAN_FRONTEND=noninteractive apt update
+ sudo DEBIAN_FRONTEND=noninteractive apt install -y postgresql redis mongodb-org mongodb-mongosh locales
+ ${{ matrix.container && 'sudo locale-gen en_US.UTF-8' }}
+ ${{ matrix.container && 'update-locale LC_ALL="en_US.UTF-8" LANG="en_US.UTF-8" LANGUAGE="en_US.UTF-8"' }}
+ sudo ./scripts/kafka/ubuntu_install_kafka.sh
+ sudo ./scripts/rabbitmq/ubuntu_install_rabbitmq_server.sh
- name: Test userver conan package
run: |-
@@ -77,7 +99,7 @@ jobs:
rm -rf userver/cmake/
cd samples/
- USERVER_VERSION=$(conan list -c -v quiet userver/* | tail -n 1)
+ USERVER_VERSION=$(${{ matrix.container && 'sudo -u test-user' }} conan list -c -v quiet userver/* | tail -n 1)
for SAMPLE in \
3_json \
chaotic_service \
@@ -88,12 +110,13 @@ jobs:
https_service \
postgres_service \
redis_service \
- kafka_service \
+ ${{ matrix.container && ' ' || 'kafka_service' }} \
rabbitmq_service \
mongo_service \
s3api \
; do
mv conanfile.py $SAMPLE/
- ${{matrix.tests-env}} conan test $SAMPLE/ --build=never -s:a compiler.cppstd=17 -pr:b=default ${{matrix.conanflags}} ${USERVER_VERSION}
+ echo "Run: ${{ matrix.container && 'sudo -u test-user' }} ${{matrix.tests-env}} conan test $SAMPLE/ --build=never -s:a compiler.cppstd=17 -pr:b=default ${{matrix.conanflags}} ${USERVER_VERSION}"
+ ${{ matrix.container && 'sudo -u test-user' }} ${{matrix.tests-env}} conan test $SAMPLE/ --build=never -s:a compiler.cppstd=17 -pr:b=default ${{matrix.conanflags}} ${USERVER_VERSION}
mv $SAMPLE/conanfile.py ./
done
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index b107f2e6e457..1b105c342137 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -104,7 +104,7 @@ jobs:
runs-on: ${{matrix.os}}
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
- name: Restore cached directories
id: restore-cache
@@ -155,11 +155,11 @@ jobs:
sudo apt update
# Instructions from https://clickhouse.com/docs/en/getting-started/install/
sudo apt install -y apt-transport-https ca-certificates dirmngr
- sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 8919F6BD2B48D754
- echo "deb https://packages.clickhouse.com/deb stable main" | sudo tee /etc/apt/sources.list.d/clickhouse.list
+ curl -fsSL 'https://packages.clickhouse.com/rpm/lts/repodata/repomd.xml.key' | sudo gpg --dearmor -o /usr/share/keyrings/clickhouse-keyring.gpg
+ echo "deb [signed-by=/usr/share/keyrings/clickhouse-keyring.gpg arch=amd64] https://packages.clickhouse.com/deb stable main" | sudo tee /etc/apt/sources.list.d/clickhouse.list
# Adding mariadb repositories (from https://www.linuxcapable.com/how-to-install-mariadb-on-ubuntu-linux/ )
- curl -fsSL http://mirror.mariadb.org/PublicKey_v2 | sudo gpg --dearmor -o "/usr/share/keyrings/mariadb.gpg"
+ curl -fsSL https://mariadb.org/mariadb_release_signing_key.pgp | sudo gpg --dearmor -o "/usr/share/keyrings/mariadb.gpg"
sudo chmod a+r "/usr/share/keyrings/mariadb.gpg"
# Restore the correct URL after https://jira.mariadb.org/browse/MDBF-651
#echo "deb [arch=amd64,arm64,ppc64el signed-by=/usr/share/keyrings/mariadb.gpg] https://deb.mariadb.org/10.11/ubuntu $(lsb_release -cs) main" \
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 77d320b9e07a..85bcb25e4f90 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -25,7 +25,7 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v4
+ uses: actions/checkout@v5
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
diff --git a/.github/workflows/debian.yml b/.github/workflows/debian.yml
new file mode 100644
index 000000000000..c7ee25d3b4c3
--- /dev/null
+++ b/.github/workflows/debian.yml
@@ -0,0 +1,161 @@
+name: Debian
+
+'on':
+ pull_request:
+ push:
+ branches:
+ - master
+ - develop
+ - feature/**
+
+env:
+ UBSAN_OPTIONS: print_stacktrace=1
+ ASAN_OPTIONS: detect_odr_violation=2
+ CCACHE_DIR: /home/runner/.cache/ccache
+ CCACHE_NOHASHDIR: true
+ CPM_SOURCE_CACHE: /home/runner/.cache/CPM
+
+jobs:
+ debian:
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ # TODO: debian-11 fails to compile core
+ #
+ # - cmake-flags: >-
+ # -DUSERVER_FORCE_DOWNLOAD_GTEST=ON
+ # -DCMAKE_CC_COMPILER=clang-11
+ # -DCMAKE_CXX_COMPILER=clang++-11
+ # image: debian:11
+ # info: Debian 11
+ # deps-file: debian-11.md
+
+ - cmake-flags: >-
+ -DCMAKE_CC_COMPILER=clang-14
+ -DCMAKE_CXX_COMPILER=clang++-14
+ image: debian:12
+ info: Debian 12
+ deps-file: debian-12.md
+
+ # TODO: fails with:
+ # 1) ld.lld: error: undefined symbol: icudt76_dat
+ # >>> referenced by udata.ao:(openCommonData(char const*, int, UErrorCode*)) in archive /usr/lib/x86_64-linux-gnu/libicuuc.a
+ # 2) userver/userver/universal/src/decimal64/decimal64_test.cpp:29:47: error: passing no argument for the '...' parameter of a variadic macro is a C++20 extension [-Werror,-Wc++20-extensions]
+ # TYPED_TEST_SUITE(Decimal64Round, RoundPolicies);
+ # - cmake-flags: >-
+ # -DCMAKE_CC_COMPILER=clang-19
+ # -DCMAKE_CXX_COMPILER=clang++-19
+ # -DUSERVER_FEATURE_GRPC=OFF
+ # -DUSERVER_FEATURE_OTLP=OFF
+ # -DUSERVER_FEATURE_GRPC_REFLECTION=OFF
+ # -DUSERVER_FORCE_DOWNLOAD_RE2=ON
+ # image: debian:13
+ # info: Debian 13
+ # deps-file: debian-13.md
+
+ name: '${{ matrix.info }}'
+ runs-on: ubuntu-latest
+ container: ${{ matrix.image }}
+
+ env:
+ CMAKE_FLAGS: >-
+ -DCMAKE_BUILD_TYPE=Debug
+ -DCMAKE_CXX_STANDARD=17
+ -DUSERVER_USE_LD=lld
+ -DUSERVER_NO_WERROR=OFF
+ -DUSERVER_BUILD_ALL_COMPONENTS=1
+ -DUSERVER_BUILD_SAMPLES=1
+ -DUSERVER_BUILD_TESTS=1
+ -DUSERVER_FEATURE_JEMALLOC=OFF
+ -DUSERVER_FEATURE_KAFKA=OFF
+ -DUSERVER_FEATURE_CLICKHOUSE=OFF
+ -DUSERVER_FEATURE_STACKTRACE=OFF
+ -DUSERVER_FEATURE_PATCH_LIBPQ=OFF
+ -DUSERVER_DISABLE_RSEQ_ACCELERATION=YES
+ -DUSERVER_CHAOTIC_FORMAT=OFF
+ -DUSERVER_CHAOTIC_GOLDEN_TESTS=OFF
+
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Restore cached directories
+ id: restore-cache
+ uses: actions/cache/restore@v4
+ with:
+ path: |
+ ${{env.CCACHE_DIR}}
+ ${{env.CPM_SOURCE_CACHE}}
+ key: 'debian-cache-dir ${{matrix.image}} ${{github.ref}} run-${{github.run_number}}'
+ restore-keys: |
+ debian-cache-dir ${{github.ref}}
+ debian-cache-dir
+
+ - name: Setup host cache dirs
+ run: |
+ mkdir -p ${{env.CCACHE_DIR}}
+ mkdir -p ${{env.CPM_SOURCE_CACHE}}
+
+ - name: Install dependencies
+ run: |
+ apt-get update
+ apt-get install -y lsb-release wget gnupg
+
+ # Install clang and lld
+ apt-get install -y clang lld
+
+ # Install project dependencies
+ apt-get install -y $(cat scripts/docs/en/deps/${{ matrix.deps-file }})
+
+ - name: Install test dependencies
+ if: ${{ false }}
+ run: |
+ apt-get install -y postgresql-15 \
+ redis-server \
+ rabbitmq-server
+
+ - name: Setup caches
+ run: |
+ echo "Cached CPM packages:"
+ du -h -d 1 ${{env.CPM_SOURCE_CACHE}} || true
+ for f in $(find ${{env.CPM_SOURCE_CACHE}} -name "cmake.lock" 2>/dev/null || true);
+ do
+ repo=$(ls -d $(dirname $f)/*/ 2>/dev/null || true);
+ if [ -n "$repo" ]; then
+ echo "Repository: $repo";
+ git config --global --add safe.directory $repo;
+ fi
+ done
+
+ ccache -M 2.0GB
+ ccache -s
+
+ - name: Run cmake
+ run: |
+ cmake -S . -B build_debug $CMAKE_FLAGS ${{ matrix.cmake-flags }}
+
+ - name: Compile
+ run: |
+ cmake --build build_debug --parallel $(nproc)
+
+ - name: Save cached directories
+ uses: actions/cache/save@v4
+ with:
+ path: |
+ ${{env.CCACHE_DIR}}
+ ${{env.CPM_SOURCE_CACHE}}
+ key: ${{ steps.restore-cache.outputs.cache-primary-key }}
+
+ - name: Show cache stats
+ run: |
+ du -h -d 1 ${{env.CCACHE_DIR}} || true
+ du -h -d 1 ${{env.CPM_SOURCE_CACHE}} || true
+ ccache -s -v
+
+ - name: Run tests
+ if: ${{ false }}
+ run: |
+ cd build_debug
+ ctest -V
diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml
index 6ca9ef00018b..0af95fa6075b 100644
--- a/.github/workflows/docker.yaml
+++ b/.github/workflows/docker.yaml
@@ -88,7 +88,7 @@ jobs:
runs-on: ubuntu-latest
name: '${{ matrix.info }}'
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
- name: Install packages
run: |
diff --git a/.github/workflows/fedora.yml b/.github/workflows/fedora.yml
new file mode 100644
index 000000000000..4ef31fbc4876
--- /dev/null
+++ b/.github/workflows/fedora.yml
@@ -0,0 +1,136 @@
+name: Fedora
+
+'on':
+ pull_request:
+ push:
+ branches:
+ - master
+ - develop
+ - feature/**
+
+env:
+ UBSAN_OPTIONS: print_stacktrace=1
+ ASAN_OPTIONS: detect_odr_violation=2
+ CXX: clang++
+ CC: clang
+ CCACHE_DIR: /home/runner/.cache/ccache
+ CCACHE_NOHASHDIR: true
+ CPM_SOURCE_CACHE: /home/runner/.cache/CPM
+
+jobs:
+ fedora:
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ # - version: 36 EOL
+ - version: 42
+
+ name: fedora-${{matrix.version}}
+ runs-on: ubuntu-latest
+
+ container:
+ image: fedora:${{matrix.version}}
+
+ env:
+ CMAKE_FLAGS: >-
+ -DCMAKE_BUILD_TYPE=Debug
+ -DCMAKE_CXX_STANDARD=17
+ -DUSERVER_USE_LD=lld
+ -DUSERVER_BUILD_ALL_COMPONENTS=1
+ -DUSERVER_BUILD_SAMPLES=1
+ -DUSERVER_BUILD_TESTS=1
+ -DUSERVER_USE_STATIC_LIBS=OFF
+ -DUSERVER_FEATURE_MONGODB=OFF
+ -DUSERVER_FEATURE_GRPC=OFF
+ -DUSERVER_FEATURE_GRPC_REFLECTION=OFF
+ -DUSERVER_FEATURE_OTLP=OFF
+ -DUSERVER_FEATURE_ROCKS=OFF
+ -DUSERVER_FEATURE_CLICKHOUSE=OFF
+ -DUSERVER_FEATURE_RABBITMQ=OFF
+ -DUSERVER_FEATURE_PATCH_LIBPQ=OFF
+ -DUSERVER_DOWNLOAD_PACKAGES=OFF
+ -DUSERVER_FEATURE_STACKTRACE=OFF
+
+ steps:
+ - uses: actions/checkout@v5
+ with:
+ fetch-depth: 0
+
+ - name: Setup cache directories
+ run: |
+ mkdir -p ${{env.CCACHE_DIR}}
+ mkdir -p ${{env.CPM_SOURCE_CACHE}}
+
+ - name: Restore cached directories
+ id: restore-cache
+ uses: actions/cache/restore@v4
+ with:
+ path: |
+ ${{env.CCACHE_DIR}}
+ ${{env.CPM_SOURCE_CACHE}}
+ key: 'fedora-cache-dir ${{github.ref}} run-${{github.run_number}}'
+ restore-keys: |
+ fedora-cache-dir ${{github.ref}}
+ fedora-cache-dir
+
+ - name: Install dependencies
+ run: |
+ dnf update -y
+ dnf install -y git lld which clang
+ dnf install -y $(cat scripts/docs/en/deps/fedora-${{matrix.version}}.md)
+
+ - name: Install test dependencies
+ if: ${{ false }} # Not working yet
+ run: |
+ dnf install -y postgresql-server \
+ redis \
+ rabbitmq-server
+
+ - name: Setup caches
+ run: |
+ echo "Cached CPM packages:"
+ du -h -d 1 ${{env.CPM_SOURCE_CACHE}} || true
+ for f in $(find ${{env.CPM_SOURCE_CACHE}} -name "cmake.lock" 2>/dev/null || true);
+ do
+ repo=$(ls -d $(dirname $f)/*/ 2>/dev/null || true);
+ echo "Repository: $repo";
+ git config --global --add safe.directory $repo;
+ done
+
+ ccache -M 2.0GB
+ ccache -s -v
+
+ - name: Run cmake
+ run: |
+ pwd
+ cmake -S . -B build_debug $CMAKE_FLAGS
+
+ - name: Reconfigure cmake
+ run: |
+ pwd
+ cmake -S . -B build_debug $CMAKE_FLAGS
+
+ - name: Compile
+ run: |
+ cmake --build build_debug -j$(nproc)
+
+ - name: Save cached directories
+ uses: actions/cache/save@v4
+ with:
+ path: |
+ ${{env.CCACHE_DIR}}
+ ${{env.CPM_SOURCE_CACHE}}
+ key: ${{ steps.restore-cache.outputs.cache-primary-key }}
+
+ - name: Show cache stats
+ run: |
+ du -h -d 1 ${{env.CCACHE_DIR}} || true
+ du -h -d 1 ${{env.CPM_SOURCE_CACHE}} || true
+ ccache -s -v
+
+ - name: Run tests
+ if: ${{ false }} # Not working yet
+ run: |
+ cd build_debug
+ ctest -V
diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml
index 1e7bab22e807..21ec07e3ad80 100644
--- a/.github/workflows/macos.yml
+++ b/.github/workflows/macos.yml
@@ -18,7 +18,7 @@ jobs:
strategy:
fail-fast: false
- name: macos-latest
+ name: macos-latest (brew)
runs-on: macos-latest
env:
@@ -38,7 +38,7 @@ jobs:
-DUSERVER_FORCE_DOWNLOAD_GRPC=1
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
- name: Restore cached directories
id: restore-cache
@@ -56,10 +56,11 @@ jobs:
run: |
export SDKROOT="`xcrun --show-sdk-path`"
brew update
+ brew uninstall cmake
brew install $(cat scripts/docs/en/deps/macos.md)
brew install clang-format
- brew install python@3.11
brew install lld
+ brew install python@3.12
brew link postgresql@16 # postgresql is keg-only
brew link --force zlib # keg-only + need for static linkage
@@ -83,11 +84,11 @@ jobs:
- name: Run cmake
run: |
- cmake -S./ -B./build_debug -DUSERVER_PYTHON_PATH=$(brew --prefix)/bin/python3.11 $CMAKE_FLAGS
+ cmake -S./ -B./build_debug -DUSERVER_PYTHON_PATH=$(brew --prefix)/bin/python3.12 $CMAKE_FLAGS
- name: Reconfigure cmake
run: |
- cmake -S./ -B./build_debug -DUSERVER_PYTHON_PATH=$(brew --prefix)/bin/python3.11 $CMAKE_FLAGS
+ cmake -S./ -B./build_debug -DUSERVER_PYTHON_PATH=$(brew --prefix)/bin/python3.12 $CMAKE_FLAGS
- name: Compile
run: |
diff --git a/.github/workflows/publish-ubuntu-22.04-base-images.yaml b/.github/workflows/publish-ubuntu-22.04-base-images.yaml
index 4a7f2bd0403b..701c6e4a1fe9 100644
--- a/.github/workflows/publish-ubuntu-22.04-base-images.yaml
+++ b/.github/workflows/publish-ubuntu-22.04-base-images.yaml
@@ -17,7 +17,7 @@ jobs:
env:
USERVER_IMAGE_TAG: ${{ github.event.release.tag_name || 'latest' }}
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
diff --git a/.github/workflows/publish-ubuntu-22.04-images.yaml b/.github/workflows/publish-ubuntu-22.04-images.yaml
index 73fb7ec1a056..04f58d0d8a97 100644
--- a/.github/workflows/publish-ubuntu-22.04-images.yaml
+++ b/.github/workflows/publish-ubuntu-22.04-images.yaml
@@ -17,7 +17,7 @@ jobs:
env:
USERVER_IMAGE_TAG: ${{ github.event.release.tag_name || 'latest' }}
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
diff --git a/.github/workflows/publish-ubuntu-24.04-images.yaml b/.github/workflows/publish-ubuntu-24.04-images.yaml
index 8697474a042f..7dc026c0f41a 100644
--- a/.github/workflows/publish-ubuntu-24.04-images.yaml
+++ b/.github/workflows/publish-ubuntu-24.04-images.yaml
@@ -17,7 +17,7 @@ jobs:
env:
USERVER_IMAGE_TAG: ${{ github.event.release.tag_name || 'latest' }}
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
diff --git a/.github/workflows/ubuntu-minimal.yml b/.github/workflows/ubuntu-minimal.yml
new file mode 100644
index 000000000000..449963127c58
--- /dev/null
+++ b/.github/workflows/ubuntu-minimal.yml
@@ -0,0 +1,108 @@
+name: Ubuntu
+
+'on':
+ pull_request:
+ push:
+ branches:
+ - master
+ - develop
+ - feature/**
+
+env:
+ JAVA_HOME: /usr/lib/jvm/java-17-openjdk-amd64
+ UBSAN_OPTIONS: print_stacktrace=1
+ ASAN_OPTIONS: detect_odr_violation=2
+ CCACHE_DIR: /home/runner/.cache/ccache
+ CCACHE_NOHASHDIR: true
+ CPM_SOURCE_CACHE: /home/runner/.cache/CPM
+ REDIS_SLEEP_WORKAROUND_SECONDS: 60
+
+jobs:
+ posix:
+ strategy:
+ fail-fast: false
+ env:
+ CMAKE_FLAGS: >-
+ -DCMAKE_BUILD_TYPE=Debug
+ -DCMAKE_CXX_STANDARD=17
+ -DUSERVER_SANITIZE="ub addr"
+ -DUSERVER_BUILD_SAMPLES=1
+ -DUSERVER_BUILD_TESTS=1
+
+ name: ubuntu (minimal installation)
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Restore cached directories
+ id: restore-cache
+ uses: actions/cache/restore@v4
+ with:
+ path: |
+ ${{env.CCACHE_DIR}}
+ ${{env.CPM_SOURCE_CACHE}}
+ key: 'ubuntu-cache-dir ${{matrix.id}} ${{github.ref}} run-${{github.run_number}}'
+ restore-keys: |
+ ubuntu-cache-dir ${{matrix.id}} ${{github.ref}}
+ ubuntu-cache-dir ${{matrix.id}}
+
+ - name: Setup ramdrive for testsuites
+ run: |
+ sudo mkdir -p "/mnt/ramdisk/$USER"
+ sudo chmod 777 "/mnt/ramdisk/$USER"
+ sudo mount -t tmpfs -o size=2048M tmpfs "/mnt/ramdisk/$USER"
+
+ - name: Free disk space
+ run: |
+ df -h
+ # See https://stackoverflow.com/questions/75536771/github-runner-out-of-disk-space-after-building-docker-image
+ sudo rm -rf /usr/share/dotnet /usr/local/lib/android /usr/lib/php* /opt/ghc \
+ /usr/local/share/powershell /usr/share/swift /usr/local/.ghcup \
+ /opt/hostedtoolcache/CodeQL || true
+ sudo docker image prune --all --force
+ df -h
+
+ - name: Install common deps
+ run: |
+ sudo apt update
+ sudo apt install build-essential clang cmake ccache libjemalloc-dev
+ sudo apt install \
+ libssl-dev \
+ libboost-context1.83-dev \
+ libboost-coroutine1.83-dev \
+ libboost-filesystem1.83-dev \
+ libboost-iostreams1.83-dev \
+ libboost-locale1.83-dev \
+ libboost-program-options1.83-dev \
+ libboost-stacktrace1.83-dev \
+ libbenchmark-dev
+
+ - name: Setup ccache
+ run: |
+ ccache -M 2.0GB
+ ccache -s -v
+
+ - name: Run cmake
+ run: |
+ cmake -S . -B build_debug
+
+ - name: Compile
+ run: |
+ pwd
+ cd build_debug
+ cmake --build . -j $(nproc)
+
+ - name: Save cached directories
+ uses: actions/cache/save@v4
+ with:
+ path: |
+ ${{env.CCACHE_DIR}}
+ ${{env.CPM_SOURCE_CACHE}}
+ key: ${{ steps.restore-cache.outputs.cache-primary-key }}
+
+ - name: Show cache stats
+ run: |
+ du -h -d 1 ${{env.CCACHE_DIR}}
+ du -h -d 1 ${{env.CPM_SOURCE_CACHE}}
+ ccache -s -v
diff --git a/.gitignore b/.gitignore
index ac32cfefe46c..a509f672dd89 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,3 +22,5 @@ static-analyzer-report
.clangd
.vscode
scripts/docs/en/dynamic_configs
+scripts/docs/en/versions.md
+scripts/docs/scripts/versions.js
diff --git a/.mapping.json b/.mapping.json
index 6ad8348b6e82..ff9450d10596 100644
--- a/.mapping.json
+++ b/.mapping.json
@@ -11,14 +11,18 @@
".github/docker-compose.yml":"taxi/uservices/userver/.github/docker-compose.yml",
".github/pull_request_template.md":"taxi/uservices/userver/.github/pull_request_template.md",
".github/workflows/alpine.yml":"taxi/uservices/userver/.github/workflows/alpine.yml",
+ ".github/workflows/archlinux.yml":"taxi/uservices/userver/.github/workflows/archlinux.yml",
".github/workflows/ci-conan.yml":"taxi/uservices/userver/.github/workflows/ci-conan.yml",
".github/workflows/ci.yml":"taxi/uservices/userver/.github/workflows/ci.yml",
".github/workflows/codeql-analysis.yml":"taxi/uservices/userver/.github/workflows/codeql-analysis.yml",
+ ".github/workflows/debian.yml":"taxi/uservices/userver/.github/workflows/debian.yml",
".github/workflows/docker.yaml":"taxi/uservices/userver/.github/workflows/docker.yaml",
+ ".github/workflows/fedora.yml":"taxi/uservices/userver/.github/workflows/fedora.yml",
".github/workflows/macos.yml":"taxi/uservices/userver/.github/workflows/macos.yml",
".github/workflows/publish-ubuntu-22.04-base-images.yaml":"taxi/uservices/userver/.github/workflows/publish-ubuntu-22.04-base-images.yaml",
".github/workflows/publish-ubuntu-22.04-images.yaml":"taxi/uservices/userver/.github/workflows/publish-ubuntu-22.04-images.yaml",
".github/workflows/publish-ubuntu-24.04-images.yaml":"taxi/uservices/userver/.github/workflows/publish-ubuntu-24.04-images.yaml",
+ ".github/workflows/ubuntu-minimal.yml":"taxi/uservices/userver/.github/workflows/ubuntu-minimal.yml",
".gitignore":"taxi/uservices/userver/.gitignore",
"AUTHORS":"taxi/uservices/userver/AUTHORS",
"CMakeLists.txt":"taxi/uservices/userver/CMakeLists.txt",
@@ -36,6 +40,7 @@
"chaotic-openapi/chaotic_openapi/__init__.py":"taxi/uservices/userver/chaotic-openapi/chaotic_openapi/__init__.py",
"chaotic-openapi/chaotic_openapi/back/__init__.py":"taxi/uservices/userver/chaotic-openapi/chaotic_openapi/back/__init__.py",
"chaotic-openapi/chaotic_openapi/back/cpp_client/__init__.py":"taxi/uservices/userver/chaotic-openapi/chaotic_openapi/back/cpp_client/__init__.py",
+ "chaotic-openapi/chaotic_openapi/back/cpp_client/middleware.py":"taxi/uservices/userver/chaotic-openapi/chaotic_openapi/back/cpp_client/middleware.py",
"chaotic-openapi/chaotic_openapi/back/cpp_client/output.py":"taxi/uservices/userver/chaotic-openapi/chaotic_openapi/back/cpp_client/output.py",
"chaotic-openapi/chaotic_openapi/back/cpp_client/renderer.py":"taxi/uservices/userver/chaotic-openapi/chaotic_openapi/back/cpp_client/renderer.py",
"chaotic-openapi/chaotic_openapi/back/cpp_client/templates/client.cpp.jinja":"taxi/uservices/userver/chaotic-openapi/chaotic_openapi/back/cpp_client/templates/client.cpp.jinja",
@@ -105,9 +110,15 @@
"chaotic-openapi/include/userver/chaotic/openapi/parameters_read.hpp":"taxi/uservices/userver/chaotic-openapi/include/userver/chaotic/openapi/parameters_read.hpp",
"chaotic-openapi/include/userver/chaotic/openapi/parameters_write.hpp":"taxi/uservices/userver/chaotic-openapi/include/userver/chaotic/openapi/parameters_write.hpp",
"chaotic-openapi/integration_tests/CMakeLists.txt":"taxi/uservices/userver/chaotic-openapi/integration_tests/CMakeLists.txt",
+ "chaotic-openapi/integration_tests/clients/body-with-array/openapi.yaml":"taxi/uservices/userver/chaotic-openapi/integration_tests/clients/body-with-array/openapi.yaml",
+ "chaotic-openapi/integration_tests/clients/empty-openapi/openapi.yaml":"taxi/uservices/userver/chaotic-openapi/integration_tests/clients/empty-openapi/openapi.yaml",
"chaotic-openapi/integration_tests/clients/external-refs/one.yaml":"taxi/uservices/userver/chaotic-openapi/integration_tests/clients/external-refs/one.yaml",
"chaotic-openapi/integration_tests/clients/external-refs/two.yaml":"taxi/uservices/userver/chaotic-openapi/integration_tests/clients/external-refs/two.yaml",
+ "chaotic-openapi/integration_tests/clients/handler-tag/openapi.yaml":"taxi/uservices/userver/chaotic-openapi/integration_tests/clients/handler-tag/openapi.yaml",
"chaotic-openapi/integration_tests/clients/multiple-content-types/openapi.yaml":"taxi/uservices/userver/chaotic-openapi/integration_tests/clients/multiple-content-types/openapi.yaml",
+ "chaotic-openapi/integration_tests/clients/non-alphanum/openapi.yaml":"taxi/uservices/userver/chaotic-openapi/integration_tests/clients/non-alphanum/openapi.yaml",
+ "chaotic-openapi/integration_tests/clients/operation/non_std_type_reason.yaml":"taxi/uservices/userver/chaotic-openapi/integration_tests/clients/operation/non_std_type_reason.yaml",
+ "chaotic-openapi/integration_tests/clients/operation/openapi.yaml":"taxi/uservices/userver/chaotic-openapi/integration_tests/clients/operation/openapi.yaml",
"chaotic-openapi/integration_tests/clients/parameters/openapi.yaml":"taxi/uservices/userver/chaotic-openapi/integration_tests/clients/parameters/openapi.yaml",
"chaotic-openapi/integration_tests/clients/response-headers/openapi.yaml":"taxi/uservices/userver/chaotic-openapi/integration_tests/clients/response-headers/openapi.yaml",
"chaotic-openapi/integration_tests/clients/swagger/swagger.yaml":"taxi/uservices/userver/chaotic-openapi/integration_tests/clients/swagger/swagger.yaml",
@@ -115,6 +126,8 @@
"chaotic-openapi/integration_tests/clients/test-middleware/openapi.yaml":"taxi/uservices/userver/chaotic-openapi/integration_tests/clients/test-middleware/openapi.yaml",
"chaotic-openapi/integration_tests/clients/test-object/openapi.yaml":"taxi/uservices/userver/chaotic-openapi/integration_tests/clients/test-object/openapi.yaml",
"chaotic-openapi/integration_tests/src/middleware_test.cpp":"taxi/uservices/userver/chaotic-openapi/integration_tests/src/middleware_test.cpp",
+ "chaotic-openapi/integration_tests/src/operation_test.cpp":"taxi/uservices/userver/chaotic-openapi/integration_tests/src/operation_test.cpp",
+ "chaotic-openapi/integration_tests/src/parameters_test.cpp":"taxi/uservices/userver/chaotic-openapi/integration_tests/src/parameters_test.cpp",
"chaotic-openapi/integration_tests/src/requests_test.cpp":"taxi/uservices/userver/chaotic-openapi/integration_tests/src/requests_test.cpp",
"chaotic-openapi/integration_tests/src/responses_test.cpp":"taxi/uservices/userver/chaotic-openapi/integration_tests/src/responses_test.cpp",
"chaotic-openapi/integration_tests/src/struct_test.cpp":"taxi/uservices/userver/chaotic-openapi/integration_tests/src/struct_test.cpp",
@@ -180,6 +193,7 @@
"chaotic/chaotic/error.py":"taxi/uservices/userver/chaotic/chaotic/error.py",
"chaotic/chaotic/front/__init__.py":"taxi/uservices/userver/chaotic/chaotic/front/__init__.py",
"chaotic/chaotic/front/parser.py":"taxi/uservices/userver/chaotic/chaotic/front/parser.py",
+ "chaotic/chaotic/front/ref.py":"taxi/uservices/userver/chaotic/chaotic/front/ref.py",
"chaotic/chaotic/front/ref_resolver.py":"taxi/uservices/userver/chaotic/chaotic/front/ref_resolver.py",
"chaotic/chaotic/front/types.py":"taxi/uservices/userver/chaotic/chaotic/front/types.py",
"chaotic/chaotic/jinja_env.py":"taxi/uservices/userver/chaotic/chaotic/jinja_env.py",
@@ -231,6 +245,8 @@
"chaotic/include/userver/chaotic/io/std/chrono/minutes.hpp":"taxi/uservices/userver/chaotic/include/userver/chaotic/io/std/chrono/minutes.hpp",
"chaotic/include/userver/chaotic/io/std/chrono/seconds.hpp":"taxi/uservices/userver/chaotic/include/userver/chaotic/io/std/chrono/seconds.hpp",
"chaotic/include/userver/chaotic/io/std/chrono/years.hpp":"taxi/uservices/userver/chaotic/include/userver/chaotic/io/std/chrono/years.hpp",
+ "chaotic/include/userver/chaotic/io/std/int32_t.hpp":"taxi/uservices/userver/chaotic/include/userver/chaotic/io/std/int32_t.hpp",
+ "chaotic/include/userver/chaotic/io/std/int64_t.hpp":"taxi/uservices/userver/chaotic/include/userver/chaotic/io/std/int64_t.hpp",
"chaotic/include/userver/chaotic/io/std/map.hpp":"taxi/uservices/userver/chaotic/include/userver/chaotic/io/std/map.hpp",
"chaotic/include/userver/chaotic/io/std/set.hpp":"taxi/uservices/userver/chaotic/include/userver/chaotic/io/std/set.hpp",
"chaotic/include/userver/chaotic/io/std/size_t.hpp":"taxi/uservices/userver/chaotic/include/userver/chaotic/io/std/size_t.hpp",
@@ -273,6 +289,7 @@
"chaotic/integration_tests/schemas/all_of.yaml":"taxi/uservices/userver/chaotic/integration_tests/schemas/all_of.yaml",
"chaotic/integration_tests/schemas/array.yaml":"taxi/uservices/userver/chaotic/integration_tests/schemas/array.yaml",
"chaotic/integration_tests/schemas/array_of_xcpptype.yaml":"taxi/uservices/userver/chaotic/integration_tests/schemas/array_of_xcpptype.yaml",
+ "chaotic/integration_tests/schemas/container_format.yaml":"taxi/uservices/userver/chaotic/integration_tests/schemas/container_format.yaml",
"chaotic/integration_tests/schemas/custom_cpp_type.yaml":"taxi/uservices/userver/chaotic/integration_tests/schemas/custom_cpp_type.yaml",
"chaotic/integration_tests/schemas/date.yaml":"taxi/uservices/userver/chaotic/integration_tests/schemas/date.yaml",
"chaotic/integration_tests/schemas/external_ref.yaml":"taxi/uservices/userver/chaotic/integration_tests/schemas/external_ref.yaml",
@@ -486,6 +503,7 @@
"cmake/Sanitizers.cmake":"taxi/uservices/userver/cmake/Sanitizers.cmake",
"cmake/SetupAbseil.cmake":"taxi/uservices/userver/cmake/SetupAbseil.cmake",
"cmake/SetupAmqpCPP.cmake":"taxi/uservices/userver/cmake/SetupAmqpCPP.cmake",
+ "cmake/SetupBoost.cmake":"taxi/uservices/userver/cmake/SetupBoost.cmake",
"cmake/SetupBrotli.cmake":"taxi/uservices/userver/cmake/SetupBrotli.cmake",
"cmake/SetupCAres.cmake":"taxi/uservices/userver/cmake/SetupCAres.cmake",
"cmake/SetupCCTZ.cmake":"taxi/uservices/userver/cmake/SetupCCTZ.cmake",
@@ -502,6 +520,7 @@
"cmake/SetupLTO.cmake":"taxi/uservices/userver/cmake/SetupLTO.cmake",
"cmake/SetupLinker.cmake":"taxi/uservices/userver/cmake/SetupLinker.cmake",
"cmake/SetupMongoDeps.cmake":"taxi/uservices/userver/cmake/SetupMongoDeps.cmake",
+ "cmake/SetupOpenssl.cmake":"taxi/uservices/userver/cmake/SetupOpenssl.cmake",
"cmake/SetupOpentelemetryProto.cmake":"taxi/uservices/userver/cmake/SetupOpentelemetryProto.cmake",
"cmake/SetupPGO.cmake":"taxi/uservices/userver/cmake/SetupPGO.cmake",
"cmake/SetupPostgresqlDeps.cmake":"taxi/uservices/userver/cmake/SetupPostgresqlDeps.cmake",
@@ -526,8 +545,11 @@
"cmake/UserverSql.cmake":"taxi/uservices/userver/cmake/UserverSql.cmake",
"cmake/UserverTestsuite.cmake":"taxi/uservices/userver/cmake/UserverTestsuite.cmake",
"cmake/UserverVenv.cmake":"taxi/uservices/userver/cmake/UserverVenv.cmake",
+ "cmake/abseil_pr_1707.patch":"taxi/uservices/userver/cmake/abseil_pr_1707.patch",
+ "cmake/abseil_pr_1739.patch":"taxi/uservices/userver/cmake/abseil_pr_1739.patch",
"cmake/embedded_config.cmake":"taxi/uservices/userver/cmake/embedded_config.cmake",
"cmake/get_cpm.cmake":"taxi/uservices/userver/cmake/get_cpm.cmake",
+ "cmake/grpc_pr_36805.patch":"taxi/uservices/userver/cmake/grpc_pr_36805.patch",
"cmake/install/Config.cmake.in":"taxi/uservices/userver/cmake/install/Config.cmake.in",
"cmake/install/userver-chaotic-config.cmake":"taxi/uservices/userver/cmake/install/userver-chaotic-config.cmake",
"cmake/install/userver-chaotic-openapi-config.cmake":"taxi/uservices/userver/cmake/install/userver-chaotic-openapi-config.cmake",
@@ -588,6 +610,8 @@
"core/benchmarks/main.cpp":"taxi/uservices/userver/core/benchmarks/main.cpp",
"core/build_config.hpp.in":"taxi/uservices/userver/core/build_config.hpp.in",
"core/dynamic_configs/BAGGAGE_SETTINGS.yaml":"taxi/uservices/userver/core/dynamic_configs/BAGGAGE_SETTINGS.yaml",
+ "core/dynamic_configs/EGRESS_HTTP_PROXY_ENABLED.yaml":"taxi/uservices/userver/core/dynamic_configs/EGRESS_HTTP_PROXY_ENABLED.yaml",
+ "core/dynamic_configs/EGRESS_NO_PROXY_TARGETS.yaml":"taxi/uservices/userver/core/dynamic_configs/EGRESS_NO_PROXY_TARGETS.yaml",
"core/dynamic_configs/HTTP_CLIENT_CONNECTION_POOL_SIZE.yaml":"taxi/uservices/userver/core/dynamic_configs/HTTP_CLIENT_CONNECTION_POOL_SIZE.yaml",
"core/dynamic_configs/HTTP_CLIENT_CONNECT_THROTTLE.yaml":"taxi/uservices/userver/core/dynamic_configs/HTTP_CLIENT_CONNECT_THROTTLE.yaml",
"core/dynamic_configs/USERVER_BAGGAGE_ENABLED.yaml":"taxi/uservices/userver/core/dynamic_configs/USERVER_BAGGAGE_ENABLED.yaml",
@@ -725,6 +749,12 @@
"core/functional_tests/tracing/static_config.yaml":"taxi/uservices/userver/core/functional_tests/tracing/static_config.yaml",
"core/functional_tests/tracing/tests/conftest.py":"taxi/uservices/userver/core/functional_tests/tracing/tests/conftest.py",
"core/functional_tests/tracing/tests/test_trace_headers_propagation.py":"taxi/uservices/userver/core/functional_tests/tracing/tests/test_trace_headers_propagation.py",
+ "core/functional_tests/trx_tracker/CMakeLists.txt":"taxi/uservices/userver/core/functional_tests/trx_tracker/CMakeLists.txt",
+ "core/functional_tests/trx_tracker/main.cpp":"taxi/uservices/userver/core/functional_tests/trx_tracker/main.cpp",
+ "core/functional_tests/trx_tracker/src/handler.hpp":"taxi/uservices/userver/core/functional_tests/trx_tracker/src/handler.hpp",
+ "core/functional_tests/trx_tracker/static_config.yaml":"taxi/uservices/userver/core/functional_tests/trx_tracker/static_config.yaml",
+ "core/functional_tests/trx_tracker/tests/conftest.py":"taxi/uservices/userver/core/functional_tests/trx_tracker/tests/conftest.py",
+ "core/functional_tests/trx_tracker/tests/test_ignore_testpoint.py":"taxi/uservices/userver/core/functional_tests/trx_tracker/tests/test_ignore_testpoint.py",
"core/functional_tests/uctl/CMakeLists.txt":"taxi/uservices/userver/core/functional_tests/uctl/CMakeLists.txt",
"core/functional_tests/uctl/config_vars.yaml":"taxi/uservices/userver/core/functional_tests/uctl/config_vars.yaml",
"core/functional_tests/uctl/secure_data.json":"taxi/uservices/userver/core/functional_tests/uctl/secure_data.json",
@@ -1101,6 +1131,7 @@
"core/include/userver/utils/statistics/system_statistics_collector.hpp":"taxi/uservices/userver/core/include/userver/utils/statistics/system_statistics_collector.hpp",
"core/include/userver/utils/statistics/writer.hpp":"taxi/uservices/userver/core/include/userver/utils/statistics/writer.hpp",
"core/include/userver/utils/text.hpp":"taxi/uservices/userver/core/include/userver/utils/text.hpp",
+ "core/include/userver/utils/trx_tracker.hpp":"taxi/uservices/userver/core/include/userver/utils/trx_tracker.hpp",
"core/include/userver/utils/userver_info.hpp":"taxi/uservices/userver/core/include/userver/utils/userver_info.hpp",
"core/internal/include/userver/engine/task/task_processor_utils.hpp":"taxi/uservices/userver/core/internal/include/userver/engine/task/task_processor_utils.hpp",
"core/internal/include/userver/internal/net/net_listener.hpp":"taxi/uservices/userver/core/internal/include/userver/internal/net/net_listener.hpp",
@@ -1560,6 +1591,7 @@
"core/src/fs/read.cpp":"taxi/uservices/userver/core/src/fs/read.cpp",
"core/src/fs/read_test.cpp":"taxi/uservices/userver/core/src/fs/read_test.cpp",
"core/src/fs/temp_file.cpp":"taxi/uservices/userver/core/src/fs/temp_file.cpp",
+ "core/src/fs/temp_file_test.cpp":"taxi/uservices/userver/core/src/fs/temp_file_test.cpp",
"core/src/fs/write.cpp":"taxi/uservices/userver/core/src/fs/write.cpp",
"core/src/fs/write_test.cpp":"taxi/uservices/userver/core/src/fs/write_test.cpp",
"core/src/logging/component.cpp":"taxi/uservices/userver/core/src/logging/component.cpp",
@@ -1957,6 +1989,8 @@
"core/src/utils/sys_info.hpp":"taxi/uservices/userver/core/src/utils/sys_info.hpp",
"core/src/utils/text.cpp":"taxi/uservices/userver/core/src/utils/text.cpp",
"core/src/utils/text_test.cpp":"taxi/uservices/userver/core/src/utils/text_test.cpp",
+ "core/src/utils/trx_tracker.cpp":"taxi/uservices/userver/core/src/utils/trx_tracker.cpp",
+ "core/src/utils/trx_tracker_test.cpp":"taxi/uservices/userver/core/src/utils/trx_tracker_test.cpp",
"core/src/utils/userver_info.cpp":"taxi/uservices/userver/core/src/utils/userver_info.cpp",
"core/static_configs/dns_client.yaml":"taxi/uservices/userver/core/static_configs/dns_client.yaml",
"core/sys_coro/include/coroutines/coroutine.hpp":"taxi/uservices/userver/core/sys_coro/include/coroutines/coroutine.hpp",
@@ -1994,6 +2028,7 @@
"grpc/benchmarks/format_log_message.cpp":"taxi/uservices/userver/grpc/benchmarks/format_log_message.cpp",
"grpc/benchmarks/logging.cpp":"taxi/uservices/userver/grpc/benchmarks/logging.cpp",
"grpc/benchmarks/ya.make":"taxi/uservices/userver/grpc/benchmarks/ya.make",
+ "grpc/dynamic_configs/EGRESS_GRPC_PROXY_ENABLED.yaml":"taxi/uservices/userver/grpc/dynamic_configs/EGRESS_GRPC_PROXY_ENABLED.yaml",
"grpc/dynamic_configs/USERVER_GRPC_CLIENT_ENABLE_DEADLINE_PROPAGATION.yaml":"taxi/uservices/userver/grpc/dynamic_configs/USERVER_GRPC_CLIENT_ENABLE_DEADLINE_PROPAGATION.yaml",
"grpc/dynamic_configs/USERVER_GRPC_SERVER_CANCEL_TASK_BY_DEADLINE.yaml":"taxi/uservices/userver/grpc/dynamic_configs/USERVER_GRPC_SERVER_CANCEL_TASK_BY_DEADLINE.yaml",
"grpc/functional_tests/CMakeLists.txt":"taxi/uservices/userver/grpc/functional_tests/CMakeLists.txt",
@@ -2030,6 +2065,7 @@
"grpc/functional_tests/basic_chaos/tests-grpcserver/test_network_smaller_parts.py":"taxi/uservices/userver/grpc/functional_tests/basic_chaos/tests-grpcserver/test_network_smaller_parts.py",
"grpc/functional_tests/basic_chaos/tests-grpcserver/test_server_client_bytes.py":"taxi/uservices/userver/grpc/functional_tests/basic_chaos/tests-grpcserver/test_server_client_bytes.py",
"grpc/functional_tests/basic_chaos/tests-grpcserver/test_server_smaller_parts.py":"taxi/uservices/userver/grpc/functional_tests/basic_chaos/tests-grpcserver/test_server_smaller_parts.py",
+ "grpc/functional_tests/basic_chaos/tests-grpcserver/test_tracing_metadata.py":"taxi/uservices/userver/grpc/functional_tests/basic_chaos/tests-grpcserver/test_tracing_metadata.py",
"grpc/functional_tests/basic_server/CMakeLists.txt":"taxi/uservices/userver/grpc/functional_tests/basic_server/CMakeLists.txt",
"grpc/functional_tests/basic_server/grpc_service.cpp":"taxi/uservices/userver/grpc/functional_tests/basic_server/grpc_service.cpp",
"grpc/functional_tests/basic_server/proto/samples/greeter.proto":"taxi/uservices/userver/grpc/functional_tests/basic_server/proto/samples/greeter.proto",
@@ -2041,6 +2077,9 @@
"grpc/functional_tests/basic_server/tests-unix-socket/conftest.py":"taxi/uservices/userver/grpc/functional_tests/basic_server/tests-unix-socket/conftest.py",
"grpc/functional_tests/basic_server/tests-unix-socket/test_grpc.py":"taxi/uservices/userver/grpc/functional_tests/basic_server/tests-unix-socket/test_grpc.py",
"grpc/functional_tests/basic_server/tests-unix-socket/test_header_propagation.py":"taxi/uservices/userver/grpc/functional_tests/basic_server/tests-unix-socket/test_header_propagation.py",
+ "grpc/functional_tests/basic_server/unused_proto/CMakeLists.txt":"taxi/uservices/userver/grpc/functional_tests/basic_server/unused_proto/CMakeLists.txt",
+ "grpc/functional_tests/basic_server/unused_proto/proto/samples/unused.proto":"taxi/uservices/userver/grpc/functional_tests/basic_server/unused_proto/proto/samples/unused.proto",
+ "grpc/functional_tests/basic_server/unused_proto/proto/ya.make":"taxi/uservices/userver/grpc/functional_tests/basic_server/unused_proto/proto/ya.make",
"grpc/functional_tests/lowlevel/CMakeLists.txt":"taxi/uservices/userver/grpc/functional_tests/lowlevel/CMakeLists.txt",
"grpc/functional_tests/lowlevel/main.cpp":"taxi/uservices/userver/grpc/functional_tests/lowlevel/main.cpp",
"grpc/functional_tests/lowlevel/proto/samples/greeter.proto":"taxi/uservices/userver/grpc/functional_tests/lowlevel/proto/samples/greeter.proto",
@@ -2121,26 +2160,34 @@
"grpc/include/userver/ugrpc/client/impl/async_method_invocation.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/client/impl/async_method_invocation.hpp",
"grpc/include/userver/ugrpc/client/impl/async_methods.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/client/impl/async_methods.hpp",
"grpc/include/userver/ugrpc/client/impl/async_stream_methods.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/client/impl/async_stream_methods.hpp",
+ "grpc/include/userver/ugrpc/client/impl/async_unary_call_adapter.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/client/impl/async_unary_call_adapter.hpp",
"grpc/include/userver/ugrpc/client/impl/call_kind.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/client/impl/call_kind.hpp",
"grpc/include/userver/ugrpc/client/impl/call_params.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/client/impl/call_params.hpp",
"grpc/include/userver/ugrpc/client/impl/call_state.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/client/impl/call_state.hpp",
- "grpc/include/userver/ugrpc/client/impl/channel_arguments_builder.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/client/impl/channel_arguments_builder.hpp",
+ "grpc/include/userver/ugrpc/client/impl/channel_argument_utils.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/client/impl/channel_argument_utils.hpp",
"grpc/include/userver/ugrpc/client/impl/channel_factory.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/client/impl/channel_factory.hpp",
"grpc/include/userver/ugrpc/client/impl/client_data.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/client/impl/client_data.hpp",
+ "grpc/include/userver/ugrpc/client/impl/client_data_accessor.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/client/impl/client_data_accessor.hpp",
"grpc/include/userver/ugrpc/client/impl/client_internals.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/client/impl/client_internals.hpp",
"grpc/include/userver/ugrpc/client/impl/codegen_declarations.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/client/impl/codegen_declarations.hpp",
"grpc/include/userver/ugrpc/client/impl/codegen_definitions.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/client/impl/codegen_definitions.hpp",
+ "grpc/include/userver/ugrpc/client/impl/compat/channel_arguments_builder.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/client/impl/compat/channel_arguments_builder.hpp",
"grpc/include/userver/ugrpc/client/impl/completion_queue_pool.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/client/impl/completion_queue_pool.hpp",
+ "grpc/include/userver/ugrpc/client/impl/fwd.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/client/impl/fwd.hpp",
"grpc/include/userver/ugrpc/client/impl/graceful_stream_finish.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/client/impl/graceful_stream_finish.hpp",
"grpc/include/userver/ugrpc/client/impl/middleware_hooks.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/client/impl/middleware_hooks.hpp",
"grpc/include/userver/ugrpc/client/impl/middleware_pipeline.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/client/impl/middleware_pipeline.hpp",
"grpc/include/userver/ugrpc/client/impl/perform_unary_call.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/client/impl/perform_unary_call.hpp",
"grpc/include/userver/ugrpc/client/impl/prepare_call.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/client/impl/prepare_call.hpp",
+ "grpc/include/userver/ugrpc/client/impl/response_future_impl_base.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/client/impl/response_future_impl_base.hpp",
+ "grpc/include/userver/ugrpc/client/impl/retry_backoff.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/client/impl/retry_backoff.hpp",
+ "grpc/include/userver/ugrpc/client/impl/retry_policy.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/client/impl/retry_policy.hpp",
"grpc/include/userver/ugrpc/client/impl/rpc.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/client/impl/rpc.hpp",
"grpc/include/userver/ugrpc/client/impl/stub_any.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/client/impl/stub_any.hpp",
"grpc/include/userver/ugrpc/client/impl/stub_handle.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/client/impl/stub_handle.hpp",
"grpc/include/userver/ugrpc/client/impl/stub_pool.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/client/impl/stub_pool.hpp",
"grpc/include/userver/ugrpc/client/impl/tracing.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/client/impl/tracing.hpp",
+ "grpc/include/userver/ugrpc/client/impl/unary_call.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/client/impl/unary_call.hpp",
"grpc/include/userver/ugrpc/client/middlewares/baggage/component.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/client/middlewares/baggage/component.hpp",
"grpc/include/userver/ugrpc/client/middlewares/baggage/middleware.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/client/middlewares/baggage/middleware.hpp",
"grpc/include/userver/ugrpc/client/middlewares/base.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/client/middlewares/base.hpp",
@@ -2154,6 +2201,7 @@
"grpc/include/userver/ugrpc/client/middlewares/testsuite/middleware.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/client/middlewares/testsuite/middleware.hpp",
"grpc/include/userver/ugrpc/client/qos.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/client/qos.hpp",
"grpc/include/userver/ugrpc/client/response_future.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/client/response_future.hpp",
+ "grpc/include/userver/ugrpc/client/retry_config.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/client/retry_config.hpp",
"grpc/include/userver/ugrpc/client/simple_client_component.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/client/simple_client_component.hpp",
"grpc/include/userver/ugrpc/client/stream.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/client/stream.hpp",
"grpc/include/userver/ugrpc/client/stream_read_future.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/client/stream_read_future.hpp",
@@ -2165,12 +2213,14 @@
"grpc/include/userver/ugrpc/impl/maybe_owned_string.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/impl/maybe_owned_string.hpp",
"grpc/include/userver/ugrpc/impl/protobuf_collector.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/impl/protobuf_collector.hpp",
"grpc/include/userver/ugrpc/impl/queue_runner.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/impl/queue_runner.hpp",
+ "grpc/include/userver/ugrpc/impl/rpc_type.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/impl/rpc_type.hpp",
"grpc/include/userver/ugrpc/impl/static_service_metadata.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/impl/static_service_metadata.hpp",
"grpc/include/userver/ugrpc/impl/statistics.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/impl/statistics.hpp",
"grpc/include/userver/ugrpc/impl/statistics_scope.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/impl/statistics_scope.hpp",
"grpc/include/userver/ugrpc/impl/statistics_storage.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/impl/statistics_storage.hpp",
"grpc/include/userver/ugrpc/impl/to_string.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/impl/to_string.hpp",
"grpc/include/userver/ugrpc/proto_json.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/proto_json.hpp",
+ "grpc/include/userver/ugrpc/protobuf_logging.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/protobuf_logging.hpp",
"grpc/include/userver/ugrpc/protobuf_visit.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/protobuf_visit.hpp",
"grpc/include/userver/ugrpc/server/call_context.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/server/call_context.hpp",
"grpc/include/userver/ugrpc/server/component_list.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/server/component_list.hpp",
@@ -2195,6 +2245,7 @@
"grpc/include/userver/ugrpc/server/impl/stream_adapter.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/server/impl/stream_adapter.hpp",
"grpc/include/userver/ugrpc/server/metadata_utils.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/server/metadata_utils.hpp",
"grpc/include/userver/ugrpc/server/middlewares/access_log/component.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/server/middlewares/access_log/component.hpp",
+ "grpc/include/userver/ugrpc/server/middlewares/access_log/log_extra.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/server/middlewares/access_log/log_extra.hpp",
"grpc/include/userver/ugrpc/server/middlewares/baggage/component.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/server/middlewares/baggage/component.hpp",
"grpc/include/userver/ugrpc/server/middlewares/baggage/middleware.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/server/middlewares/baggage/middleware.hpp",
"grpc/include/userver/ugrpc/server/middlewares/base.hpp":"taxi/uservices/userver/grpc/include/userver/ugrpc/server/middlewares/base.hpp",
@@ -2244,24 +2295,25 @@
"grpc/src/ugrpc/client/exceptions.cpp":"taxi/uservices/userver/grpc/src/ugrpc/client/exceptions.cpp",
"grpc/src/ugrpc/client/generic_client.cpp":"taxi/uservices/userver/grpc/src/ugrpc/client/generic_client.cpp",
"grpc/src/ugrpc/client/impl/async_method_invocation.cpp":"taxi/uservices/userver/grpc/src/ugrpc/client/impl/async_method_invocation.cpp",
- "grpc/src/ugrpc/client/impl/async_methods.cpp":"taxi/uservices/userver/grpc/src/ugrpc/client/impl/async_methods.cpp",
"grpc/src/ugrpc/client/impl/async_stream_methods.cpp":"taxi/uservices/userver/grpc/src/ugrpc/client/impl/async_stream_methods.cpp",
"grpc/src/ugrpc/client/impl/call_options_accessor.cpp":"taxi/uservices/userver/grpc/src/ugrpc/client/impl/call_options_accessor.cpp",
"grpc/src/ugrpc/client/impl/call_options_accessor.hpp":"taxi/uservices/userver/grpc/src/ugrpc/client/impl/call_options_accessor.hpp",
"grpc/src/ugrpc/client/impl/call_params.cpp":"taxi/uservices/userver/grpc/src/ugrpc/client/impl/call_params.cpp",
"grpc/src/ugrpc/client/impl/call_state.cpp":"taxi/uservices/userver/grpc/src/ugrpc/client/impl/call_state.cpp",
- "grpc/src/ugrpc/client/impl/channel_arguments_builder.cpp":"taxi/uservices/userver/grpc/src/ugrpc/client/impl/channel_arguments_builder.cpp",
+ "grpc/src/ugrpc/client/impl/channel_argument_utils.cpp":"taxi/uservices/userver/grpc/src/ugrpc/client/impl/channel_argument_utils.cpp",
"grpc/src/ugrpc/client/impl/channel_factory.cpp":"taxi/uservices/userver/grpc/src/ugrpc/client/impl/channel_factory.cpp",
"grpc/src/ugrpc/client/impl/client_data.cpp":"taxi/uservices/userver/grpc/src/ugrpc/client/impl/client_data.cpp",
"grpc/src/ugrpc/client/impl/client_factory_config.cpp":"taxi/uservices/userver/grpc/src/ugrpc/client/impl/client_factory_config.cpp",
"grpc/src/ugrpc/client/impl/client_factory_config.hpp":"taxi/uservices/userver/grpc/src/ugrpc/client/impl/client_factory_config.hpp",
"grpc/src/ugrpc/client/impl/client_qos.cpp":"taxi/uservices/userver/grpc/src/ugrpc/client/impl/client_qos.cpp",
+ "grpc/src/ugrpc/client/impl/compat/channel_arguments_builder.cpp":"taxi/uservices/userver/grpc/src/ugrpc/client/impl/compat/channel_arguments_builder.cpp",
+ "grpc/src/ugrpc/client/impl/compat/retry_policy.cpp":"taxi/uservices/userver/grpc/src/ugrpc/client/impl/compat/retry_policy.cpp",
+ "grpc/src/ugrpc/client/impl/compat/retry_policy.hpp":"taxi/uservices/userver/grpc/src/ugrpc/client/impl/compat/retry_policy.hpp",
"grpc/src/ugrpc/client/impl/completion_queue_pool.cpp":"taxi/uservices/userver/grpc/src/ugrpc/client/impl/completion_queue_pool.cpp",
"grpc/src/ugrpc/client/impl/middleware_hooks.cpp":"taxi/uservices/userver/grpc/src/ugrpc/client/impl/middleware_hooks.cpp",
"grpc/src/ugrpc/client/impl/middleware_pipeline.cpp":"taxi/uservices/userver/grpc/src/ugrpc/client/impl/middleware_pipeline.cpp",
+ "grpc/src/ugrpc/client/impl/retry_backoff.cpp":"taxi/uservices/userver/grpc/src/ugrpc/client/impl/retry_backoff.cpp",
"grpc/src/ugrpc/client/impl/retry_policy.cpp":"taxi/uservices/userver/grpc/src/ugrpc/client/impl/retry_policy.cpp",
- "grpc/src/ugrpc/client/impl/retry_policy.hpp":"taxi/uservices/userver/grpc/src/ugrpc/client/impl/retry_policy.hpp",
- "grpc/src/ugrpc/client/impl/rpc.cpp":"taxi/uservices/userver/grpc/src/ugrpc/client/impl/rpc.cpp",
"grpc/src/ugrpc/client/impl/stub_handle.cpp":"taxi/uservices/userver/grpc/src/ugrpc/client/impl/stub_handle.cpp",
"grpc/src/ugrpc/client/impl/stub_pool.cpp":"taxi/uservices/userver/grpc/src/ugrpc/client/impl/stub_pool.cpp",
"grpc/src/ugrpc/client/impl/tracing.cpp":"taxi/uservices/userver/grpc/src/ugrpc/client/impl/tracing.cpp",
@@ -2278,6 +2330,7 @@
"grpc/src/ugrpc/client/middlewares/testsuite/component.cpp":"taxi/uservices/userver/grpc/src/ugrpc/client/middlewares/testsuite/component.cpp",
"grpc/src/ugrpc/client/middlewares/testsuite/middleware.cpp":"taxi/uservices/userver/grpc/src/ugrpc/client/middlewares/testsuite/middleware.cpp",
"grpc/src/ugrpc/client/qos.cpp":"taxi/uservices/userver/grpc/src/ugrpc/client/qos.cpp",
+ "grpc/src/ugrpc/client/retry_config.cpp":"taxi/uservices/userver/grpc/src/ugrpc/client/retry_config.cpp",
"grpc/src/ugrpc/client/secdist.hpp":"taxi/uservices/userver/grpc/src/ugrpc/client/secdist.hpp",
"grpc/src/ugrpc/client/simple_client_component.cpp":"taxi/uservices/userver/grpc/src/ugrpc/client/simple_client_component.cpp",
"grpc/src/ugrpc/datetime_utils.cpp":"taxi/uservices/userver/grpc/src/ugrpc/datetime_utils.cpp",
@@ -2290,8 +2343,6 @@
"grpc/src/ugrpc/impl/logging.cpp":"taxi/uservices/userver/grpc/src/ugrpc/impl/logging.cpp",
"grpc/src/ugrpc/impl/logging.hpp":"taxi/uservices/userver/grpc/src/ugrpc/impl/logging.hpp",
"grpc/src/ugrpc/impl/protobuf_collector.cpp":"taxi/uservices/userver/grpc/src/ugrpc/impl/protobuf_collector.cpp",
- "grpc/src/ugrpc/impl/protobuf_utils.cpp":"taxi/uservices/userver/grpc/src/ugrpc/impl/protobuf_utils.cpp",
- "grpc/src/ugrpc/impl/protobuf_utils.hpp":"taxi/uservices/userver/grpc/src/ugrpc/impl/protobuf_utils.hpp",
"grpc/src/ugrpc/impl/queue_runner.cpp":"taxi/uservices/userver/grpc/src/ugrpc/impl/queue_runner.cpp",
"grpc/src/ugrpc/impl/rpc_metadata.cpp":"taxi/uservices/userver/grpc/src/ugrpc/impl/rpc_metadata.cpp",
"grpc/src/ugrpc/impl/rpc_metadata.hpp":"taxi/uservices/userver/grpc/src/ugrpc/impl/rpc_metadata.hpp",
@@ -2300,6 +2351,7 @@
"grpc/src/ugrpc/impl/statistics_scope.cpp":"taxi/uservices/userver/grpc/src/ugrpc/impl/statistics_scope.cpp",
"grpc/src/ugrpc/impl/statistics_storage.cpp":"taxi/uservices/userver/grpc/src/ugrpc/impl/statistics_storage.cpp",
"grpc/src/ugrpc/proto_json.cpp":"taxi/uservices/userver/grpc/src/ugrpc/proto_json.cpp",
+ "grpc/src/ugrpc/protobuf_logging.cpp":"taxi/uservices/userver/grpc/src/ugrpc/protobuf_logging.cpp",
"grpc/src/ugrpc/protobuf_visit.cpp":"taxi/uservices/userver/grpc/src/ugrpc/protobuf_visit.cpp",
"grpc/src/ugrpc/server/call_context.cpp":"taxi/uservices/userver/grpc/src/ugrpc/server/call_context.cpp",
"grpc/src/ugrpc/server/component_list.cpp":"taxi/uservices/userver/grpc/src/ugrpc/server/component_list.cpp",
@@ -2322,6 +2374,7 @@
"grpc/src/ugrpc/server/impl/service_worker.cpp":"taxi/uservices/userver/grpc/src/ugrpc/server/impl/service_worker.cpp",
"grpc/src/ugrpc/server/impl/service_worker_impl.cpp":"taxi/uservices/userver/grpc/src/ugrpc/server/impl/service_worker_impl.cpp",
"grpc/src/ugrpc/server/middlewares/access_log/component.cpp":"taxi/uservices/userver/grpc/src/ugrpc/server/middlewares/access_log/component.cpp",
+ "grpc/src/ugrpc/server/middlewares/access_log/log_extra.cpp":"taxi/uservices/userver/grpc/src/ugrpc/server/middlewares/access_log/log_extra.cpp",
"grpc/src/ugrpc/server/middlewares/access_log/middleware.cpp":"taxi/uservices/userver/grpc/src/ugrpc/server/middlewares/access_log/middleware.cpp",
"grpc/src/ugrpc/server/middlewares/access_log/middleware.hpp":"taxi/uservices/userver/grpc/src/ugrpc/server/middlewares/access_log/middleware.hpp",
"grpc/src/ugrpc/server/middlewares/baggage/middleware.cpp":"taxi/uservices/userver/grpc/src/ugrpc/server/middlewares/baggage/middleware.cpp",
@@ -2350,12 +2403,12 @@
"grpc/tests/baggage_test.cpp":"taxi/uservices/userver/grpc/tests/baggage_test.cpp",
"grpc/tests/base_test.cpp":"taxi/uservices/userver/grpc/tests/base_test.cpp",
"grpc/tests/cancel_test.cpp":"taxi/uservices/userver/grpc/tests/cancel_test.cpp",
- "grpc/tests/channel_arguments_builder_test.cpp":"taxi/uservices/userver/grpc/tests/channel_arguments_builder_test.cpp",
"grpc/tests/channels_test.cpp":"taxi/uservices/userver/grpc/tests/channels_test.cpp",
"grpc/tests/client_cancel_test.cpp":"taxi/uservices/userver/grpc/tests/client_cancel_test.cpp",
"grpc/tests/client_factory_test.cpp":"taxi/uservices/userver/grpc/tests/client_factory_test.cpp",
"grpc/tests/client_middleware_hooks_test.cpp":"taxi/uservices/userver/grpc/tests/client_middleware_hooks_test.cpp",
"grpc/tests/client_qos_test.cpp":"taxi/uservices/userver/grpc/tests/client_qos_test.cpp",
+ "grpc/tests/compat/channel_arguments_builder_test.cpp":"taxi/uservices/userver/grpc/tests/compat/channel_arguments_builder_test.cpp",
"grpc/tests/congestion_control_test.cpp":"taxi/uservices/userver/grpc/tests/congestion_control_test.cpp",
"grpc/tests/datetime_utils_test.cpp":"taxi/uservices/userver/grpc/tests/datetime_utils_test.cpp",
"grpc/tests/deadline_metrics_test.cpp":"taxi/uservices/userver/grpc/tests/deadline_metrics_test.cpp",
@@ -2365,8 +2418,10 @@
"grpc/tests/generic_client_test.cpp":"taxi/uservices/userver/grpc/tests/generic_client_test.cpp",
"grpc/tests/generic_server_test.cpp":"taxi/uservices/userver/grpc/tests/generic_server_test.cpp",
"grpc/tests/logging_test.cpp":"taxi/uservices/userver/grpc/tests/logging_test.cpp",
+ "grpc/tests/metadata_test.cpp":"taxi/uservices/userver/grpc/tests/metadata_test.cpp",
"grpc/tests/proto_json.cpp":"taxi/uservices/userver/grpc/tests/proto_json.cpp",
"grpc/tests/protobuf_collector_test.cpp":"taxi/uservices/userver/grpc/tests/protobuf_collector_test.cpp",
+ "grpc/tests/protobuf_logging_test.cpp":"taxi/uservices/userver/grpc/tests/protobuf_logging_test.cpp",
"grpc/tests/protobuf_utils_test.cpp":"taxi/uservices/userver/grpc/tests/protobuf_utils_test.cpp",
"grpc/tests/protobuf_visit_test.cpp":"taxi/uservices/userver/grpc/tests/protobuf_visit_test.cpp",
"grpc/tests/retry_test.cpp":"taxi/uservices/userver/grpc/tests/retry_test.cpp",
@@ -2507,6 +2562,10 @@
"libraries/easy/samples/6_pg_service_template_no_http_with/testsuite/test_basic.py":"taxi/uservices/userver/libraries/easy/samples/6_pg_service_template_no_http_with/testsuite/test_basic.py",
"libraries/easy/samples/CMakeLists.txt":"taxi/uservices/userver/libraries/easy/samples/CMakeLists.txt",
"libraries/easy/src/easy.cpp":"taxi/uservices/userver/libraries/easy/src/easy.cpp",
+ "libraries/grpc-proto-structs/include/userver/grpc-proto-structs/client/impl/codegen_declarations.hpp":"taxi/uservices/userver/libraries/grpc-proto-structs/include/userver/grpc-proto-structs/client/impl/codegen_declarations.hpp",
+ "libraries/grpc-proto-structs/include/userver/grpc-proto-structs/server/impl/codegen_declarations.hpp":"taxi/uservices/userver/libraries/grpc-proto-structs/include/userver/grpc-proto-structs/server/impl/codegen_declarations.hpp",
+ "libraries/grpc-proto-structs/include/userver/grpc-proto-structs/server/stream.hpp":"taxi/uservices/userver/libraries/grpc-proto-structs/include/userver/grpc-proto-structs/server/stream.hpp",
+ "libraries/grpc-proto-structs/src/grpc-proto-structs.cpp":"taxi/uservices/userver/libraries/grpc-proto-structs/src/grpc-proto-structs.cpp",
"libraries/grpc-protovalidate/CMakeLists.txt":"taxi/uservices/userver/libraries/grpc-protovalidate/CMakeLists.txt",
"libraries/grpc-protovalidate/include/userver/grpc-protovalidate/buf_validate.hpp":"taxi/uservices/userver/libraries/grpc-protovalidate/include/userver/grpc-protovalidate/buf_validate.hpp",
"libraries/grpc-protovalidate/include/userver/grpc-protovalidate/client/component.hpp":"taxi/uservices/userver/libraries/grpc-protovalidate/include/userver/grpc-protovalidate/client/component.hpp",
@@ -2545,13 +2604,128 @@
"libraries/grpc-reflection/src/grpc-reflection/proto_server_reflection.cpp":"taxi/uservices/userver/libraries/grpc-reflection/src/grpc-reflection/proto_server_reflection.cpp",
"libraries/grpc-reflection/src/grpc-reflection/proto_server_reflection.hpp":"taxi/uservices/userver/libraries/grpc-reflection/src/grpc-reflection/proto_server_reflection.hpp",
"libraries/grpc-reflection/src/grpc-reflection/reflection_service_component.cpp":"taxi/uservices/userver/libraries/grpc-reflection/src/grpc-reflection/reflection_service_component.cpp",
+ "libraries/proto-structs/codegen-tests/proto/box/autobox/cycles.proto":"taxi/uservices/userver/libraries/proto-structs/codegen-tests/proto/box/autobox/cycles.proto",
+ "libraries/proto-structs/codegen-tests/proto/box/autobox/dependency_on_nested.proto":"taxi/uservices/userver/libraries/proto-structs/codegen-tests/proto/box/autobox/dependency_on_nested.proto",
+ "libraries/proto-structs/codegen-tests/proto/box/autobox/dependency_on_self.proto":"taxi/uservices/userver/libraries/proto-structs/codegen-tests/proto/box/autobox/dependency_on_self.proto",
+ "libraries/proto-structs/codegen-tests/proto/box/autobox/unbreakable_cycle.proto":"taxi/uservices/userver/libraries/proto-structs/codegen-tests/proto/box/autobox/unbreakable_cycle.proto",
+ "libraries/proto-structs/codegen-tests/proto/box/options/cycles.proto":"taxi/uservices/userver/libraries/proto-structs/codegen-tests/proto/box/options/cycles.proto",
+ "libraries/proto-structs/codegen-tests/proto/box/options/initialization.proto":"taxi/uservices/userver/libraries/proto-structs/codegen-tests/proto/box/options/initialization.proto",
+ "libraries/proto-structs/codegen-tests/proto/enums/names.proto":"taxi/uservices/userver/libraries/proto-structs/codegen-tests/proto/enums/names.proto",
+ "libraries/proto-structs/codegen-tests/proto/maps/basic.proto":"taxi/uservices/userver/libraries/proto-structs/codegen-tests/proto/maps/basic.proto",
+ "libraries/proto-structs/codegen-tests/proto/not_recommended_field_names/basic.proto":"taxi/uservices/userver/libraries/proto-structs/codegen-tests/proto/not_recommended_field_names/basic.proto",
+ "libraries/proto-structs/codegen-tests/proto/oneof/basic.proto":"taxi/uservices/userver/libraries/proto-structs/codegen-tests/proto/oneof/basic.proto",
+ "libraries/proto-structs/codegen-tests/proto/oneof/custom_oneof_type_name.proto":"taxi/uservices/userver/libraries/proto-structs/codegen-tests/proto/oneof/custom_oneof_type_name.proto",
+ "libraries/proto-structs/codegen-tests/proto/oneof/proto2.proto":"taxi/uservices/userver/libraries/proto-structs/codegen-tests/proto/oneof/proto2.proto",
+ "libraries/proto-structs/codegen-tests/proto/simple/base.proto":"taxi/uservices/userver/libraries/proto-structs/codegen-tests/proto/simple/base.proto",
+ "libraries/proto-structs/codegen-tests/proto/simple/subdirectory/separate_enum.proto":"taxi/uservices/userver/libraries/proto-structs/codegen-tests/proto/simple/subdirectory/separate_enum.proto",
+ "libraries/proto-structs/codegen-tests/proto/simple/subdirectory/subdirectory.proto":"taxi/uservices/userver/libraries/proto-structs/codegen-tests/proto/simple/subdirectory/subdirectory.proto",
+ "libraries/proto-structs/codegen-tests/src/box/autobox/cycles_test.cpp":"taxi/uservices/userver/libraries/proto-structs/codegen-tests/src/box/autobox/cycles_test.cpp",
+ "libraries/proto-structs/codegen-tests/src/box/autobox/dependency_on_nested_test.cpp":"taxi/uservices/userver/libraries/proto-structs/codegen-tests/src/box/autobox/dependency_on_nested_test.cpp",
+ "libraries/proto-structs/codegen-tests/src/box/autobox/dependency_on_self_test.cpp":"taxi/uservices/userver/libraries/proto-structs/codegen-tests/src/box/autobox/dependency_on_self_test.cpp",
+ "libraries/proto-structs/codegen-tests/src/box/autobox/unbreakable_cycle_test.cpp":"taxi/uservices/userver/libraries/proto-structs/codegen-tests/src/box/autobox/unbreakable_cycle_test.cpp",
+ "libraries/proto-structs/codegen-tests/src/box/options/cycles_test.cpp":"taxi/uservices/userver/libraries/proto-structs/codegen-tests/src/box/options/cycles_test.cpp",
+ "libraries/proto-structs/codegen-tests/src/box/options/initialization_test.cpp":"taxi/uservices/userver/libraries/proto-structs/codegen-tests/src/box/options/initialization_test.cpp",
+ "libraries/proto-structs/codegen-tests/src/enums/names_test.cpp":"taxi/uservices/userver/libraries/proto-structs/codegen-tests/src/enums/names_test.cpp",
+ "libraries/proto-structs/codegen-tests/src/maps/basic_test.cpp":"taxi/uservices/userver/libraries/proto-structs/codegen-tests/src/maps/basic_test.cpp",
+ "libraries/proto-structs/codegen-tests/src/not_recommended_field_names/basic_test.cpp":"taxi/uservices/userver/libraries/proto-structs/codegen-tests/src/not_recommended_field_names/basic_test.cpp",
+ "libraries/proto-structs/codegen-tests/src/oneof/basic_test.cpp":"taxi/uservices/userver/libraries/proto-structs/codegen-tests/src/oneof/basic_test.cpp",
+ "libraries/proto-structs/codegen-tests/src/oneof/custom_oneof_type_name_test.cpp":"taxi/uservices/userver/libraries/proto-structs/codegen-tests/src/oneof/custom_oneof_type_name_test.cpp",
+ "libraries/proto-structs/codegen-tests/src/oneof/proto2_test.cpp":"taxi/uservices/userver/libraries/proto-structs/codegen-tests/src/oneof/proto2_test.cpp",
+ "libraries/proto-structs/codegen-tests/src/simple/simple_test.cpp":"taxi/uservices/userver/libraries/proto-structs/codegen-tests/src/simple/simple_test.cpp",
+ "libraries/proto-structs/codegen-tests/src/test_utils/type_assertions.hpp":"taxi/uservices/userver/libraries/proto-structs/codegen-tests/src/test_utils/type_assertions.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/any.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/any.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/convert.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/convert.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/exceptions.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/exceptions.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/hash_map.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/hash_map.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/impl/bundles/structs_cpp.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/impl/bundles/structs_cpp.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/impl/bundles/structs_hpp.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/impl/bundles/structs_hpp.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/impl/oneof_codegen.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/impl/oneof_codegen.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/impl/traits_light.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/impl/traits_light.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/context.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/context.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/context_base.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/context_base.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/fwd.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/fwd.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/impl/field_accessor.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/impl/field_accessor.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/impl/read.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/impl/read.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/impl/std/any_map_conv.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/impl/std/any_map_conv.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/impl/write.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/impl/write.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/std/chrono/duration.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/std/chrono/duration.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/std/chrono/duration_conv.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/std/chrono/duration_conv.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/std/chrono/hh_mm_ss.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/std/chrono/hh_mm_ss.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/std/chrono/hh_mm_ss_conv.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/std/chrono/hh_mm_ss_conv.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/std/chrono/time_point.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/std/chrono/time_point.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/std/chrono/time_point_conv.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/std/chrono/time_point_conv.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/std/chrono/year_month_day.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/std/chrono/year_month_day.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/std/chrono/year_month_day_conv.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/std/chrono/year_month_day_conv.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/std/int32_t.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/std/int32_t.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/std/int32_t_conv.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/std/int32_t_conv.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/std/int64_t.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/std/int64_t.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/std/int64_t_conv.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/std/int64_t_conv.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/std/map.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/std/map.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/std/map_conv.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/std/map_conv.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/std/optional.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/std/optional.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/std/optional_conv.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/std/optional_conv.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/std/scalar.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/std/scalar.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/std/scalar_conv.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/std/scalar_conv.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/std/size_t.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/std/size_t.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/std/size_t_conv.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/std/size_t_conv.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/std/string.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/std/string.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/std/string_conv.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/std/string_conv.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/std/uint32_t.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/std/uint32_t.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/std/uint32_t_conv.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/std/uint32_t_conv.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/std/uint64_t.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/std/uint64_t.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/std/uint64_t_conv.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/std/uint64_t_conv.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/std/unordered_map.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/std/unordered_map.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/std/unordered_map_conv.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/std/unordered_map_conv.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/std/vector.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/std/vector.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/std/vector_conv.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/std/vector_conv.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/supported_types.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/supported_types.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/supported_types_conv.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/supported_types_conv.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/userver/decimal64/decimal.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/userver/decimal64/decimal.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/userver/decimal64/decimal_conv.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/userver/decimal64/decimal_conv.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/userver/proto_structs/any.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/userver/proto_structs/any.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/userver/proto_structs/any_conv.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/userver/proto_structs/any_conv.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/userver/proto_structs/hash_map.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/userver/proto_structs/hash_map.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/userver/proto_structs/hash_map_conv.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/userver/proto_structs/hash_map_conv.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/userver/proto_structs/oneof.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/userver/proto_structs/oneof.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/userver/proto_structs/oneof_conv.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/userver/proto_structs/oneof_conv.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/userver/proto_structs/unbreakable_dependency_cycle.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/userver/proto_structs/unbreakable_dependency_cycle.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/userver/proto_structs/unbreakable_dependency_cycle_conv.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/userver/proto_structs/unbreakable_dependency_cycle_conv.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/userver/utils/box.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/userver/utils/box.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/userver/utils/box_conv.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/userver/utils/box_conv.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/userver/utils/datetime/time_of_day.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/userver/utils/datetime/time_of_day.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/userver/utils/datetime/time_of_day_conv.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/userver/utils/datetime/time_of_day_conv.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/userver/utils/strong_typedef.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/userver/utils/strong_typedef.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/io/userver/utils/strong_typedef_conv.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/io/userver/utils/strong_typedef_conv.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/oneof.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/oneof.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/type_mapping.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/type_mapping.hpp",
+ "libraries/proto-structs/include/userver/proto-structs/unbreakable_dependency_cycle.hpp":"taxi/uservices/userver/libraries/proto-structs/include/userver/proto-structs/unbreakable_dependency_cycle.hpp",
+ "libraries/proto-structs/proto/userver/structs/annotations.proto":"taxi/uservices/userver/libraries/proto-structs/proto/userver/structs/annotations.proto",
+ "libraries/proto-structs/src/proto-structs/exceptions.cpp":"taxi/uservices/userver/libraries/proto-structs/src/proto-structs/exceptions.cpp",
+ "libraries/proto-structs/src/proto-structs/io/context_base.cpp":"taxi/uservices/userver/libraries/proto-structs/src/proto-structs/io/context_base.cpp",
+ "libraries/proto-structs/src/proto-structs/io/impl/field_accessor.cpp":"taxi/uservices/userver/libraries/proto-structs/src/proto-structs/io/impl/field_accessor.cpp",
+ "libraries/proto-structs/src/proto-structs/io/std/chrono/time_point.cpp":"taxi/uservices/userver/libraries/proto-structs/src/proto-structs/io/std/chrono/time_point.cpp",
+ "libraries/proto-structs/src/proto-structs/io/std/chrono/year_month_day.cpp":"taxi/uservices/userver/libraries/proto-structs/src/proto-structs/io/std/chrono/year_month_day.cpp",
+ "libraries/proto-structs/src/proto-structs/io/userver/proto_structs/any.cpp":"taxi/uservices/userver/libraries/proto-structs/src/proto-structs/io/userver/proto_structs/any.cpp",
+ "libraries/proto-structs/tests/any_test.cpp":"taxi/uservices/userver/libraries/proto-structs/tests/any_test.cpp",
+ "libraries/proto-structs/tests/exceptions_test.cpp":"taxi/uservices/userver/libraries/proto-structs/tests/exceptions_test.cpp",
+ "libraries/proto-structs/tests/message_to_struct_test.cpp":"taxi/uservices/userver/libraries/proto-structs/tests/message_to_struct_test.cpp",
+ "libraries/proto-structs/tests/oneof_test.cpp":"taxi/uservices/userver/libraries/proto-structs/tests/oneof_test.cpp",
+ "libraries/proto-structs/tests/proto/messages.proto":"taxi/uservices/userver/libraries/proto-structs/tests/proto/messages.proto",
+ "libraries/proto-structs/tests/struct_simple.cpp":"taxi/uservices/userver/libraries/proto-structs/tests/struct_simple.cpp",
+ "libraries/proto-structs/tests/struct_simple.hpp":"taxi/uservices/userver/libraries/proto-structs/tests/struct_simple.hpp",
+ "libraries/proto-structs/tests/struct_to_message_test.cpp":"taxi/uservices/userver/libraries/proto-structs/tests/struct_to_message_test.cpp",
+ "libraries/proto-structs/tests/structs.cpp":"taxi/uservices/userver/libraries/proto-structs/tests/structs.cpp",
+ "libraries/proto-structs/tests/structs.hpp":"taxi/uservices/userver/libraries/proto-structs/tests/structs.hpp",
"libraries/s3api/CMakeLists.txt":"taxi/uservices/userver/libraries/s3api/CMakeLists.txt",
"libraries/s3api/include/userver/s3api/authenticators/access_key.hpp":"taxi/uservices/userver/libraries/s3api/include/userver/s3api/authenticators/access_key.hpp",
"libraries/s3api/include/userver/s3api/authenticators/interface.hpp":"taxi/uservices/userver/libraries/s3api/include/userver/s3api/authenticators/interface.hpp",
"libraries/s3api/include/userver/s3api/authenticators/utils.hpp":"taxi/uservices/userver/libraries/s3api/include/userver/s3api/authenticators/utils.hpp",
"libraries/s3api/include/userver/s3api/clients/fwd.hpp":"taxi/uservices/userver/libraries/s3api/include/userver/s3api/clients/fwd.hpp",
"libraries/s3api/include/userver/s3api/clients/s3api.hpp":"taxi/uservices/userver/libraries/s3api/include/userver/s3api/clients/s3api.hpp",
+ "libraries/s3api/include/userver/s3api/models/errors.hpp":"taxi/uservices/userver/libraries/s3api/include/userver/s3api/models/errors.hpp",
"libraries/s3api/include/userver/s3api/models/fwd.hpp":"taxi/uservices/userver/libraries/s3api/include/userver/s3api/models/fwd.hpp",
+ "libraries/s3api/include/userver/s3api/models/multipart_upload/requests.hpp":"taxi/uservices/userver/libraries/s3api/include/userver/s3api/models/multipart_upload/requests.hpp",
+ "libraries/s3api/include/userver/s3api/models/multipart_upload/responses.hpp":"taxi/uservices/userver/libraries/s3api/include/userver/s3api/models/multipart_upload/responses.hpp",
"libraries/s3api/include/userver/s3api/models/request.hpp":"taxi/uservices/userver/libraries/s3api/include/userver/s3api/models/request.hpp",
"libraries/s3api/include/userver/s3api/models/s3api_connection_type.hpp":"taxi/uservices/userver/libraries/s3api/include/userver/s3api/models/s3api_connection_type.hpp",
"libraries/s3api/include/userver/s3api/models/secret.hpp":"taxi/uservices/userver/libraries/s3api/include/userver/s3api/models/secret.hpp",
@@ -2561,6 +2735,8 @@
"libraries/s3api/src/s3api/authenticators/utils_test.cpp":"taxi/uservices/userver/libraries/s3api/src/s3api/authenticators/utils_test.cpp",
"libraries/s3api/src/s3api/clients/client.cpp":"taxi/uservices/userver/libraries/s3api/src/s3api/clients/client.cpp",
"libraries/s3api/src/s3api/clients/client.hpp":"taxi/uservices/userver/libraries/s3api/src/s3api/clients/client.hpp",
+ "libraries/s3api/src/s3api/models/multipart_upload/responses.cpp":"taxi/uservices/userver/libraries/s3api/src/s3api/models/multipart_upload/responses.cpp",
+ "libraries/s3api/src/s3api/models/multipart_upload/responses_test.cpp":"taxi/uservices/userver/libraries/s3api/src/s3api/models/multipart_upload/responses_test.cpp",
"libraries/s3api/src/s3api/models/s3api_connection_type.cpp":"taxi/uservices/userver/libraries/s3api/src/s3api/models/s3api_connection_type.cpp",
"libraries/s3api/src/s3api/models/secret.cpp":"taxi/uservices/userver/libraries/s3api/src/s3api/models/secret.cpp",
"libraries/s3api/src/s3api/s3_connection.cpp":"taxi/uservices/userver/libraries/s3api/src/s3api/s3_connection.cpp",
@@ -2577,8 +2753,8 @@
"mongo/dynamic_configs/MONGO_CONGESTION_CONTROL_ENABLED.yaml":"taxi/uservices/userver/mongo/dynamic_configs/MONGO_CONGESTION_CONTROL_ENABLED.yaml",
"mongo/dynamic_configs/MONGO_CONGESTION_CONTROL_SETTINGS.yaml":"taxi/uservices/userver/mongo/dynamic_configs/MONGO_CONGESTION_CONTROL_SETTINGS.yaml",
"mongo/dynamic_configs/MONGO_CONNECTION_POOL_SETTINGS.yaml":"taxi/uservices/userver/mongo/dynamic_configs/MONGO_CONNECTION_POOL_SETTINGS.yaml",
- "mongo/dynamic_configs/MONGO_DEADLINE_PROPAGATION_ENABLED_V2.yaml":"taxi/uservices/userver/mongo/dynamic_configs/MONGO_DEADLINE_PROPAGATION_ENABLED_V2.yaml",
"mongo/dynamic_configs/MONGO_DEFAULT_MAX_TIME_MS.yaml":"taxi/uservices/userver/mongo/dynamic_configs/MONGO_DEFAULT_MAX_TIME_MS.yaml",
+ "mongo/dynamic_configs/USERVER_DEADLINE_PROPAGATION_ENABLED.yaml":"taxi/uservices/userver/mongo/dynamic_configs/USERVER_DEADLINE_PROPAGATION_ENABLED.yaml",
"mongo/functional_tests/CMakeLists.txt":"taxi/uservices/userver/mongo/functional_tests/CMakeLists.txt",
"mongo/functional_tests/basic_chaos/CMakeLists.txt":"taxi/uservices/userver/mongo/functional_tests/basic_chaos/CMakeLists.txt",
"mongo/functional_tests/basic_chaos/mongo_service.cpp":"taxi/uservices/userver/mongo/functional_tests/basic_chaos/mongo_service.cpp",
@@ -3312,6 +3488,7 @@
"redis/include/userver/storages/redis/request_data_base.hpp":"taxi/uservices/userver/redis/include/userver/storages/redis/request_data_base.hpp",
"redis/include/userver/storages/redis/request_eval.hpp":"taxi/uservices/userver/redis/include/userver/storages/redis/request_eval.hpp",
"redis/include/userver/storages/redis/request_evalsha.hpp":"taxi/uservices/userver/redis/include/userver/storages/redis/request_evalsha.hpp",
+ "redis/include/userver/storages/redis/request_generic.hpp":"taxi/uservices/userver/redis/include/userver/storages/redis/request_generic.hpp",
"redis/include/userver/storages/redis/scan_tag.hpp":"taxi/uservices/userver/redis/include/userver/storages/redis/scan_tag.hpp",
"redis/include/userver/storages/redis/subscribe_client.hpp":"taxi/uservices/userver/redis/include/userver/storages/redis/subscribe_client.hpp",
"redis/include/userver/storages/redis/subscription_token.hpp":"taxi/uservices/userver/redis/include/userver/storages/redis/subscription_token.hpp",
@@ -3711,11 +3888,11 @@
"samples/postgres_cache_order_by/tests/static/test_data.sql":"taxi/uservices/userver/samples/postgres_cache_order_by/tests/static/test_data.sql",
"samples/postgres_cache_order_by/tests/test_cache_order_by.py":"taxi/uservices/userver/samples/postgres_cache_order_by/tests/test_cache_order_by.py",
"samples/postgres_service/CMakeLists.txt":"taxi/uservices/userver/samples/postgres_service/CMakeLists.txt",
- "samples/postgres_service/delete_value.sql":"taxi/uservices/userver/samples/postgres_service/delete_value.sql",
- "samples/postgres_service/insert_value.sql":"taxi/uservices/userver/samples/postgres_service/insert_value.sql",
"samples/postgres_service/main.cpp":"taxi/uservices/userver/samples/postgres_service/main.cpp",
+ "samples/postgres_service/queries/read/select_value.sql":"taxi/uservices/userver/samples/postgres_service/queries/read/select_value.sql",
+ "samples/postgres_service/queries/write/delete_value.sql":"taxi/uservices/userver/samples/postgres_service/queries/write/delete_value.sql",
+ "samples/postgres_service/queries/write/insert_value.sql":"taxi/uservices/userver/samples/postgres_service/queries/write/insert_value.sql",
"samples/postgres_service/schemas/postgresql/admin.sql":"taxi/uservices/userver/samples/postgres_service/schemas/postgresql/admin.sql",
- "samples/postgres_service/select_value.sql":"taxi/uservices/userver/samples/postgres_service/select_value.sql",
"samples/postgres_service/static_config.yaml":"taxi/uservices/userver/samples/postgres_service/static_config.yaml",
"samples/postgres_service/tests/conftest.py":"taxi/uservices/userver/samples/postgres_service/tests/conftest.py",
"samples/postgres_service/tests/test_postgres.py":"taxi/uservices/userver/samples/postgres_service/tests/test_postgres.py",
@@ -3836,6 +4013,7 @@
"scripts/chaotic/requirements.txt":"taxi/uservices/userver/scripts/chaotic/requirements.txt",
"scripts/clickhouse/ubuntu-install-clickhouse.sh":"taxi/uservices/userver/scripts/clickhouse/ubuntu-install-clickhouse.sh",
"scripts/create-service-test-helper":"taxi/uservices/userver/scripts/create-service-test-helper",
+ "scripts/debian-rules":"taxi/uservices/userver/scripts/debian-rules",
"scripts/docker/Readme.md":"taxi/uservices/userver/scripts/docker/Readme.md",
"scripts/docker/base-debian-11.dockerfile":"taxi/uservices/userver/scripts/docker/base-debian-11.dockerfile",
"scripts/docker/base-ubuntu-22.04-ci.dockerfile":"taxi/uservices/userver/scripts/docker/base-ubuntu-22.04-ci.dockerfile",
@@ -3868,16 +4046,41 @@
"scripts/docs/en/deps/arch.md":"taxi/uservices/userver/scripts/docs/en/deps/arch.md",
"scripts/docs/en/deps/debian-11.md":"taxi/uservices/userver/scripts/docs/en/deps/debian-11.md",
"scripts/docs/en/deps/debian-12.md":"taxi/uservices/userver/scripts/docs/en/deps/debian-12.md",
- "scripts/docs/en/deps/fedora-35.md":"taxi/uservices/userver/scripts/docs/en/deps/fedora-35.md",
- "scripts/docs/en/deps/fedora-36.md":"taxi/uservices/userver/scripts/docs/en/deps/fedora-36.md",
+ "scripts/docs/en/deps/debian-13.md":"taxi/uservices/userver/scripts/docs/en/deps/debian-13.md",
+ "scripts/docs/en/deps/fedora-42.md":"taxi/uservices/userver/scripts/docs/en/deps/fedora-42.md",
"scripts/docs/en/deps/gentoo.md":"taxi/uservices/userver/scripts/docs/en/deps/gentoo.md",
"scripts/docs/en/deps/macos.md":"taxi/uservices/userver/scripts/docs/en/deps/macos.md",
"scripts/docs/en/deps/ubuntu-21.10.md":"taxi/uservices/userver/scripts/docs/en/deps/ubuntu-21.10.md",
"scripts/docs/en/deps/ubuntu-22.04.md":"taxi/uservices/userver/scripts/docs/en/deps/ubuntu-22.04.md",
+ "scripts/docs/en/deps/ubuntu-22.04/chaotic":"taxi/uservices/userver/scripts/docs/en/deps/ubuntu-22.04/chaotic",
+ "scripts/docs/en/deps/ubuntu-22.04/core":"taxi/uservices/userver/scripts/docs/en/deps/ubuntu-22.04/core",
+ "scripts/docs/en/deps/ubuntu-22.04/grpc":"taxi/uservices/userver/scripts/docs/en/deps/ubuntu-22.04/grpc",
+ "scripts/docs/en/deps/ubuntu-22.04/kafka":"taxi/uservices/userver/scripts/docs/en/deps/ubuntu-22.04/kafka",
+ "scripts/docs/en/deps/ubuntu-22.04/mongo":"taxi/uservices/userver/scripts/docs/en/deps/ubuntu-22.04/mongo",
+ "scripts/docs/en/deps/ubuntu-22.04/mysql":"taxi/uservices/userver/scripts/docs/en/deps/ubuntu-22.04/mysql",
+ "scripts/docs/en/deps/ubuntu-22.04/odbc":"taxi/uservices/userver/scripts/docs/en/deps/ubuntu-22.04/odbc",
+ "scripts/docs/en/deps/ubuntu-22.04/postgresql":"taxi/uservices/userver/scripts/docs/en/deps/ubuntu-22.04/postgresql",
+ "scripts/docs/en/deps/ubuntu-22.04/redis":"taxi/uservices/userver/scripts/docs/en/deps/ubuntu-22.04/redis",
+ "scripts/docs/en/deps/ubuntu-22.04/rocks":"taxi/uservices/userver/scripts/docs/en/deps/ubuntu-22.04/rocks",
+ "scripts/docs/en/deps/ubuntu-22.04/sqlite":"taxi/uservices/userver/scripts/docs/en/deps/ubuntu-22.04/sqlite",
+ "scripts/docs/en/deps/ubuntu-22.04/universal":"taxi/uservices/userver/scripts/docs/en/deps/ubuntu-22.04/universal",
+ "scripts/docs/en/deps/ubuntu-22.04/ydb":"taxi/uservices/userver/scripts/docs/en/deps/ubuntu-22.04/ydb",
"scripts/docs/en/deps/ubuntu-24.04.md":"taxi/uservices/userver/scripts/docs/en/deps/ubuntu-24.04.md",
+ "scripts/docs/en/deps/ubuntu-24.04/chaotic":"taxi/uservices/userver/scripts/docs/en/deps/ubuntu-24.04/chaotic",
+ "scripts/docs/en/deps/ubuntu-24.04/core":"taxi/uservices/userver/scripts/docs/en/deps/ubuntu-24.04/core",
+ "scripts/docs/en/deps/ubuntu-24.04/grpc":"taxi/uservices/userver/scripts/docs/en/deps/ubuntu-24.04/grpc",
+ "scripts/docs/en/deps/ubuntu-24.04/kafka":"taxi/uservices/userver/scripts/docs/en/deps/ubuntu-24.04/kafka",
+ "scripts/docs/en/deps/ubuntu-24.04/mongo":"taxi/uservices/userver/scripts/docs/en/deps/ubuntu-24.04/mongo",
+ "scripts/docs/en/deps/ubuntu-24.04/mysql":"taxi/uservices/userver/scripts/docs/en/deps/ubuntu-24.04/mysql",
+ "scripts/docs/en/deps/ubuntu-24.04/odbc":"taxi/uservices/userver/scripts/docs/en/deps/ubuntu-24.04/odbc",
+ "scripts/docs/en/deps/ubuntu-24.04/postgresql":"taxi/uservices/userver/scripts/docs/en/deps/ubuntu-24.04/postgresql",
+ "scripts/docs/en/deps/ubuntu-24.04/redis":"taxi/uservices/userver/scripts/docs/en/deps/ubuntu-24.04/redis",
+ "scripts/docs/en/deps/ubuntu-24.04/rocks":"taxi/uservices/userver/scripts/docs/en/deps/ubuntu-24.04/rocks",
+ "scripts/docs/en/deps/ubuntu-24.04/sqlite":"taxi/uservices/userver/scripts/docs/en/deps/ubuntu-24.04/sqlite",
+ "scripts/docs/en/deps/ubuntu-24.04/universal":"taxi/uservices/userver/scripts/docs/en/deps/ubuntu-24.04/universal",
+ "scripts/docs/en/deps/ubuntu-24.04/ydb":"taxi/uservices/userver/scripts/docs/en/deps/ubuntu-24.04/ydb",
"scripts/docs/en/index.md":"taxi/uservices/userver/scripts/docs/en/index.md",
"scripts/docs/en/landing.md":"taxi/uservices/userver/scripts/docs/en/landing.md",
- "scripts/docs/en/schemas/dynamic_configs.md":"taxi/uservices/userver/scripts/docs/en/schemas/dynamic_configs.md",
"scripts/docs/en/userver/404.md":"taxi/uservices/userver/scripts/docs/en/userver/404.md",
"scripts/docs/en/userver/build/build.md":"taxi/uservices/userver/scripts/docs/en/userver/build/build.md",
"scripts/docs/en/userver/build/dependencies.md":"taxi/uservices/userver/scripts/docs/en/userver/build/dependencies.md",
@@ -3895,6 +4098,7 @@
"scripts/docs/en/userver/deploy_env.md":"taxi/uservices/userver/scripts/docs/en/userver/deploy_env.md",
"scripts/docs/en/userver/development/releases.md":"taxi/uservices/userver/scripts/docs/en/userver/development/releases.md",
"scripts/docs/en/userver/development/stability.md":"taxi/uservices/userver/scripts/docs/en/userver/development/stability.md",
+ "scripts/docs/en/userver/distro_maintainers.md":"taxi/uservices/userver/scripts/docs/en/userver/distro_maintainers.md",
"scripts/docs/en/userver/dns_control.md":"taxi/uservices/userver/scripts/docs/en/userver/dns_control.md",
"scripts/docs/en/userver/driver_guide.md":"taxi/uservices/userver/scripts/docs/en/userver/driver_guide.md",
"scripts/docs/en/userver/dynamic_config.md":"taxi/uservices/userver/scripts/docs/en/userver/dynamic_config.md",
@@ -3911,6 +4115,7 @@
"scripts/docs/en/userver/grpc/middlewares_toggle.md":"taxi/uservices/userver/scripts/docs/en/userver/grpc/middlewares_toggle.md",
"scripts/docs/en/userver/grpc/server_middleware_implementation.md":"taxi/uservices/userver/scripts/docs/en/userver/grpc/server_middleware_implementation.md",
"scripts/docs/en/userver/grpc/server_middlewares.md":"taxi/uservices/userver/scripts/docs/en/userver/grpc/server_middlewares.md",
+ "scripts/docs/en/userver/grpc/timeouts_retries.md":"taxi/uservices/userver/scripts/docs/en/userver/grpc/timeouts_retries.md",
"scripts/docs/en/userver/http_server.md":"taxi/uservices/userver/scripts/docs/en/userver/http_server.md",
"scripts/docs/en/userver/http_server_middlewares.md":"taxi/uservices/userver/scripts/docs/en/userver/http_server_middlewares.md",
"scripts/docs/en/userver/intro.md":"taxi/uservices/userver/scripts/docs/en/userver/intro.md",
@@ -3986,6 +4191,7 @@
"scripts/docs/fontello/font/fontello.woff":"taxi/uservices/userver/scripts/docs/fontello/font/fontello.woff",
"scripts/docs/fontello/font/fontello.woff2":"taxi/uservices/userver/scripts/docs/fontello/font/fontello.woff2",
"scripts/docs/footer.html":"taxi/uservices/userver/scripts/docs/footer.html",
+ "scripts/docs/generate_versions.py":"taxi/uservices/userver/scripts/docs/generate_versions.py",
"scripts/docs/header.html":"taxi/uservices/userver/scripts/docs/header.html",
"scripts/docs/highlight.js/LICENSE":"taxi/uservices/userver/scripts/docs/highlight.js/LICENSE",
"scripts/docs/highlight.js/README.md":"taxi/uservices/userver/scripts/docs/highlight.js/README.md",
@@ -4028,6 +4234,7 @@
"scripts/docs/img/plane.svg":"taxi/uservices/userver/scripts/docs/img/plane.svg",
"scripts/docs/img/sample_static_service.png":"taxi/uservices/userver/scripts/docs/img/sample_static_service.png",
"scripts/docs/img/slack_logo.svg":"taxi/uservices/userver/scripts/docs/img/slack_logo.svg",
+ "scripts/docs/img/slugkit.svg":"taxi/uservices/userver/scripts/docs/img/slugkit.svg",
"scripts/docs/img/square_logo.svg":"taxi/uservices/userver/scripts/docs/img/square_logo.svg",
"scripts/docs/img/telegram_logo.svg":"taxi/uservices/userver/scripts/docs/img/telegram_logo.svg",
"scripts/docs/img/title.svg":"taxi/uservices/userver/scripts/docs/img/title.svg",
@@ -4070,12 +4277,14 @@
"scripts/gdb/tests/src/formats/json/gdb_test_json.cpp":"taxi/uservices/userver/scripts/gdb/tests/src/formats/json/gdb_test_json.cpp",
"scripts/gdb/tests/test_printers.py":"taxi/uservices/userver/scripts/gdb/tests/test_printers.py",
"scripts/gdb/update_gdbinit.py":"taxi/uservices/userver/scripts/gdb/update_gdbinit.py",
+ "scripts/generate-debian-directory.sh":"taxi/uservices/userver/scripts/generate-debian-directory.sh",
"scripts/grpc/__init__.py":"taxi/uservices/userver/scripts/grpc/__init__.py",
"scripts/grpc/generator.py":"taxi/uservices/userver/scripts/grpc/generator.py",
"scripts/grpc/protoc_usrv_plugin.sh":"taxi/uservices/userver/scripts/grpc/protoc_usrv_plugin.sh",
"scripts/grpc/requirements-3.txt":"taxi/uservices/userver/scripts/grpc/requirements-3.txt",
"scripts/grpc/requirements-4.txt":"taxi/uservices/userver/scripts/grpc/requirements-4.txt",
"scripts/grpc/requirements-5.txt":"taxi/uservices/userver/scripts/grpc/requirements-5.txt",
+ "scripts/grpc/requirements-6.txt":"taxi/uservices/userver/scripts/grpc/requirements-6.txt",
"scripts/grpc/templates/client.usrv.cpp.jinja":"taxi/uservices/userver/scripts/grpc/templates/client.usrv.cpp.jinja",
"scripts/grpc/templates/client.usrv.hpp.jinja":"taxi/uservices/userver/scripts/grpc/templates/client.usrv.hpp.jinja",
"scripts/grpc/templates/service.usrv.cpp.jinja":"taxi/uservices/userver/scripts/grpc/templates/service.usrv.cpp.jinja",
@@ -4088,12 +4297,35 @@
"scripts/perf-blocking-syscall":"taxi/uservices/userver/scripts/perf-blocking-syscall",
"scripts/postgres/pg_sql_codes.py":"taxi/uservices/userver/scripts/postgres/pg_sql_codes.py",
"scripts/postgres/ubuntu-install-postgresql-includes.sh":"taxi/uservices/userver/scripts/postgres/ubuntu-install-postgresql-includes.sh",
+ "scripts/proto_structs/README.md":"taxi/uservices/userver/scripts/proto_structs/README.md",
"scripts/proto_structs/__init__.py":"taxi/uservices/userver/scripts/proto_structs/__init__.py",
+ "scripts/proto_structs/descriptors/__init__.py":"taxi/uservices/userver/scripts/proto_structs/descriptors/__init__.py",
+ "scripts/proto_structs/descriptors/descriptor_proto.py":"taxi/uservices/userver/scripts/proto_structs/descriptors/descriptor_proto.py",
+ "scripts/proto_structs/descriptors/node_parsers.py":"taxi/uservices/userver/scripts/proto_structs/descriptors/node_parsers.py",
+ "scripts/proto_structs/descriptors/option_parsers.py":"taxi/uservices/userver/scripts/proto_structs/descriptors/option_parsers.py",
+ "scripts/proto_structs/descriptors/type_mapping.py":"taxi/uservices/userver/scripts/proto_structs/descriptors/type_mapping.py",
"scripts/proto_structs/generator.py":"taxi/uservices/userver/scripts/proto_structs/generator.py",
+ "scripts/proto_structs/models/__init__.py":"taxi/uservices/userver/scripts/proto_structs/models/__init__.py",
+ "scripts/proto_structs/models/forward_decls.py":"taxi/uservices/userver/scripts/proto_structs/models/forward_decls.py",
+ "scripts/proto_structs/models/gen_node.py":"taxi/uservices/userver/scripts/proto_structs/models/gen_node.py",
+ "scripts/proto_structs/models/includes.py":"taxi/uservices/userver/scripts/proto_structs/models/includes.py",
+ "scripts/proto_structs/models/includes_bundles.py":"taxi/uservices/userver/scripts/proto_structs/models/includes_bundles.py",
+ "scripts/proto_structs/models/io.py":"taxi/uservices/userver/scripts/proto_structs/models/io.py",
+ "scripts/proto_structs/models/names.py":"taxi/uservices/userver/scripts/proto_structs/models/names.py",
+ "scripts/proto_structs/models/options.py":"taxi/uservices/userver/scripts/proto_structs/models/options.py",
+ "scripts/proto_structs/models/reserved_identifiers.py":"taxi/uservices/userver/scripts/proto_structs/models/reserved_identifiers.py",
+ "scripts/proto_structs/models/sort_dependencies.py":"taxi/uservices/userver/scripts/proto_structs/models/sort_dependencies.py",
+ "scripts/proto_structs/models/toposort.py":"taxi/uservices/userver/scripts/proto_structs/models/toposort.py",
+ "scripts/proto_structs/models/type_ref.py":"taxi/uservices/userver/scripts/proto_structs/models/type_ref.py",
+ "scripts/proto_structs/models/type_ref_consts.py":"taxi/uservices/userver/scripts/proto_structs/models/type_ref_consts.py",
+ "scripts/proto_structs/models/vanilla.py":"taxi/uservices/userver/scripts/proto_structs/models/vanilla.py",
+ "scripts/proto_structs/templates/io.inc.jinja":"taxi/uservices/userver/scripts/proto_structs/templates/io.inc.jinja",
+ "scripts/proto_structs/templates/io_forward_declarations.inc.jinja":"taxi/uservices/userver/scripts/proto_structs/templates/io_forward_declarations.inc.jinja",
"scripts/proto_structs/templates/structs.usrv.cpp.jinja":"taxi/uservices/userver/scripts/proto_structs/templates/structs.usrv.cpp.jinja",
"scripts/proto_structs/templates/structs.usrv.hpp.jinja":"taxi/uservices/userver/scripts/proto_structs/templates/structs.usrv.hpp.jinja",
+ "scripts/proto_structs/templates/types.inc.jinja":"taxi/uservices/userver/scripts/proto_structs/templates/types.inc.jinja",
+ "scripts/proto_structs/templates/types_forward_declarations.inc.jinja":"taxi/uservices/userver/scripts/proto_structs/templates/types_forward_declarations.inc.jinja",
"scripts/proto_structs/templates/utils.inc.jinja":"taxi/uservices/userver/scripts/proto_structs/templates/utils.inc.jinja",
- "scripts/proto_structs/ya.make":"taxi/uservices/userver/scripts/proto_structs/ya.make",
"scripts/rabbitmq/ubuntu_install_rabbitmq_dev.sh":"taxi/uservices/userver/scripts/rabbitmq/ubuntu_install_rabbitmq_dev.sh",
"scripts/rabbitmq/ubuntu_install_rabbitmq_server.sh":"taxi/uservices/userver/scripts/rabbitmq/ubuntu_install_rabbitmq_server.sh",
"scripts/sql/__init__.py":"taxi/uservices/userver/scripts/sql/__init__.py",
@@ -4308,6 +4540,7 @@
"testsuite/requirements-grpc-3.txt":"taxi/uservices/userver/testsuite/requirements-grpc-3.txt",
"testsuite/requirements-grpc-4.txt":"taxi/uservices/userver/testsuite/requirements-grpc-4.txt",
"testsuite/requirements-grpc-5.txt":"taxi/uservices/userver/testsuite/requirements-grpc-5.txt",
+ "testsuite/requirements-grpc-6.txt":"taxi/uservices/userver/testsuite/requirements-grpc-6.txt",
"testsuite/requirements-internal-tests.txt":"taxi/uservices/userver/testsuite/requirements-internal-tests.txt",
"testsuite/requirements-mongo.txt":"taxi/uservices/userver/testsuite/requirements-mongo.txt",
"testsuite/requirements-postgres.txt":"taxi/uservices/userver/testsuite/requirements-postgres.txt",
@@ -4603,6 +4836,7 @@
"universal/include/userver/crypto/public_key.hpp":"taxi/uservices/userver/universal/include/userver/crypto/public_key.hpp",
"universal/include/userver/crypto/random.hpp":"taxi/uservices/userver/universal/include/userver/crypto/random.hpp",
"universal/include/userver/crypto/signers.hpp":"taxi/uservices/userver/universal/include/userver/crypto/signers.hpp",
+ "universal/include/userver/crypto/ssl_ctx.hpp":"taxi/uservices/userver/universal/include/userver/crypto/ssl_ctx.hpp",
"universal/include/userver/crypto/verifiers.hpp":"taxi/uservices/userver/universal/include/userver/crypto/verifiers.hpp",
"universal/include/userver/decimal64/decimal64.hpp":"taxi/uservices/userver/universal/include/userver/decimal64/decimal64.hpp",
"universal/include/userver/decimal64/format_options.hpp":"taxi/uservices/userver/universal/include/userver/decimal64/format_options.hpp",
@@ -4696,6 +4930,7 @@
"universal/include/userver/logging/format.hpp":"taxi/uservices/userver/universal/include/userver/logging/format.hpp",
"universal/include/userver/logging/fwd.hpp":"taxi/uservices/userver/universal/include/userver/logging/fwd.hpp",
"universal/include/userver/logging/impl/formatters/base.hpp":"taxi/uservices/userver/universal/include/userver/logging/impl/formatters/base.hpp",
+ "universal/include/userver/logging/impl/log_extra_tskv_formatter.hpp":"taxi/uservices/userver/universal/include/userver/logging/impl/log_extra_tskv_formatter.hpp",
"universal/include/userver/logging/impl/logger_base.hpp":"taxi/uservices/userver/universal/include/userver/logging/impl/logger_base.hpp",
"universal/include/userver/logging/impl/mem_logger.hpp":"taxi/uservices/userver/universal/include/userver/logging/impl/mem_logger.hpp",
"universal/include/userver/logging/impl/tag_writer.hpp":"taxi/uservices/userver/universal/include/userver/logging/impl/tag_writer.hpp",
@@ -4791,6 +5026,7 @@
"universal/include/userver/utils/string_literal.hpp":"taxi/uservices/userver/universal/include/userver/utils/string_literal.hpp",
"universal/include/userver/utils/string_to_duration.hpp":"taxi/uservices/userver/universal/include/userver/utils/string_to_duration.hpp",
"universal/include/userver/utils/strong_typedef.hpp":"taxi/uservices/userver/universal/include/userver/utils/strong_typedef.hpp",
+ "universal/include/userver/utils/strong_typedef_fwd.hpp":"taxi/uservices/userver/universal/include/userver/utils/strong_typedef_fwd.hpp",
"universal/include/userver/utils/struct_subsets.hpp":"taxi/uservices/userver/universal/include/userver/utils/struct_subsets.hpp",
"universal/include/userver/utils/swappingsmart.hpp":"taxi/uservices/userver/universal/include/userver/utils/swappingsmart.hpp",
"universal/include/userver/utils/text_light.hpp":"taxi/uservices/userver/universal/include/userver/utils/text_light.hpp",
@@ -4848,6 +5084,7 @@
"universal/src/crypto/random.cpp":"taxi/uservices/userver/universal/src/crypto/random.cpp",
"universal/src/crypto/signature_test.cpp":"taxi/uservices/userver/universal/src/crypto/signature_test.cpp",
"universal/src/crypto/signers.cpp":"taxi/uservices/userver/universal/src/crypto/signers.cpp",
+ "universal/src/crypto/ssl_ctx.cpp":"taxi/uservices/userver/universal/src/crypto/ssl_ctx.cpp",
"universal/src/crypto/verifiers.cpp":"taxi/uservices/userver/universal/src/crypto/verifiers.cpp",
"universal/src/decimal64/decimal64.cpp":"taxi/uservices/userver/universal/src/decimal64/decimal64.cpp",
"universal/src/decimal64/decimal64_errors_test.cpp":"taxi/uservices/userver/universal/src/decimal64/decimal64_errors_test.cpp",
@@ -4975,6 +5212,7 @@
"universal/src/logging/impl/formatters/struct.hpp":"taxi/uservices/userver/universal/src/logging/impl/formatters/struct.hpp",
"universal/src/logging/impl/formatters/tskv.cpp":"taxi/uservices/userver/universal/src/logging/impl/formatters/tskv.cpp",
"universal/src/logging/impl/formatters/tskv.hpp":"taxi/uservices/userver/universal/src/logging/impl/formatters/tskv.hpp",
+ "universal/src/logging/impl/log_extra_tskv_formatter.cpp":"taxi/uservices/userver/universal/src/logging/impl/log_extra_tskv_formatter.cpp",
"universal/src/logging/impl/logger_base.cpp":"taxi/uservices/userver/universal/src/logging/impl/logger_base.cpp",
"universal/src/logging/impl/mem_logger.cpp":"taxi/uservices/userver/universal/src/logging/impl/mem_logger.cpp",
"universal/src/logging/impl/tag_writer.cpp":"taxi/uservices/userver/universal/src/logging/impl/tag_writer.cpp",
@@ -5140,6 +5378,7 @@
"universal/utest/src/utest/current_process_open_files_test.cpp":"taxi/uservices/userver/universal/utest/src/utest/current_process_open_files_test.cpp",
"universal/utest/src/utest/log_capture_fixture.cpp":"taxi/uservices/userver/universal/utest/src/utest/log_capture_fixture.cpp",
"universal/utest/src/utest/parameter_names_test.cpp":"taxi/uservices/userver/universal/utest/src/utest/parameter_names_test.cpp",
+ "version.txt":"taxi/uservices/userver/version.txt",
"ydb/CMakeLists.txt":"taxi/uservices/userver/ydb/CMakeLists.txt",
"ydb/dynamic_configs/YDB_DEADLINE_PROPAGATION_VERSION.yaml":"taxi/uservices/userver/ydb/dynamic_configs/YDB_DEADLINE_PROPAGATION_VERSION.yaml",
"ydb/dynamic_configs/YDB_QUERIES_COMMAND_CONTROL.yaml":"taxi/uservices/userver/ydb/dynamic_configs/YDB_QUERIES_COMMAND_CONTROL.yaml",
diff --git a/.roorules b/.roorules
new file mode 100644
index 000000000000..8109bc724043
--- /dev/null
+++ b/.roorules
@@ -0,0 +1,107 @@
+# Roo Rules (.roorules)
+#
+# Basic guide for AI agents working with userver service development.
+# Contains essential project structure, examples location, and workflow commands.
+
+project_info:
+ description: "Basic guide for userver service development"
+ purpose: "Provides essential structure and workflow for creating userver services"
+
+# Project structure for userver services
+project_structure:
+ description: "Standard directory structure for userver services"
+ service_template_location: "service_template/ - Use this as base for new services"
+ examples_location: "samples/ - Find working examples of various userver features here"
+
+ standard_service_structure:
+ - "CMakeLists.txt # Build configuration"
+ - "configs/ # Configuration files"
+ - " └── static_config.yaml # Static service configuration"
+ - "src/ # Source code"
+ - " ├── main.cpp # Service entry point"
+ - " ├── handlers/ # HTTP handlers"
+ - " │ ├── handler_name.hpp # Handler declaration"
+ - " │ └── handler_name.cpp # Handler implementation"
+ - " └── components/ # Custom components (optional)"
+ - "tests/ # Tests (optional)"
+
+# Key files and their purposes
+key_files:
+ main_cpp:
+ description: "Service entry point - registers components and starts service"
+ cmake_lists_txt:
+ description: "Build configuration - links userver components"
+ static_config_yaml:
+ description: "Configuration - defines task processors, components, URL mappings"
+
+# Basic workflow commands for service development
+workflow:
+ description: "Essential commands for userver service development"
+
+ create_service:
+ description: "Create new service from template"
+ commands:
+ - "cp -r service_template/ my_new_service/"
+ - "cd my_new_service/"
+ - "# Edit CMakeLists.txt to change project name"
+ - "# Edit src/main.cpp to register your handlers"
+ - "# Edit configs/static_config.yaml to configure service"
+
+ build_service:
+ description: "Build the service"
+ commands:
+ - "mkdir build && cd build"
+ - "cmake .."
+ - "make -j$(nproc)"
+
+ run_service:
+ description: "Run the service"
+ commands:
+ - "./my_service --config ../configs/static_config.yaml"
+
+ run_tests:
+ description: "Run tests"
+ commands:
+ - "make test"
+ - "# For functional tests with pytest:"
+ - "cd tests && python3 -m pytest"
+
+# Where to find examples
+examples:
+ description: "Look in samples/ directory for working examples"
+ basic_examples:
+ - "samples/hello_service/ - Simple HTTP service"
+ - "samples/postgres_service/ - Service with PostgreSQL"
+ - "samples/redis_service/ - Service with Redis"
+ - "samples/grpc_service/ - gRPC service example"
+
+# Stubs for future expansion
+http_client_usage:
+ description: "TODO: Add HTTP client patterns"
+
+caching_strategies:
+ description: "TODO: Add caching patterns"
+
+middleware_development:
+ description: "TODO: Add middleware patterns"
+
+error_handling_logging:
+ description: "TODO: Add error handling patterns"
+
+security_best_practices:
+ description: "TODO: Add security guidelines"
+
+debugging_profiling:
+ description: "TODO: Add debugging workflows"
+
+deployment_release:
+ description: "TODO: Add deployment processes"
+
+monitoring_observability:
+ description: "TODO: Add monitoring setup"
+
+api_design_principles:
+ description: "TODO: Add API design guidelines"
+
+code_review_standards:
+ description: "TODO: Add code review standards"
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 221aec71706a..26e86dcafeac 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -56,8 +56,8 @@ if(USERVER_BUILD_TESTS AND NOT USERVER_FEATURE_UTEST)
message(FATAL_ERROR "Running unit tests for userver requires utest, disabling it is meaningless")
endif()
-if(USERVER_INSTALL AND (USERVER_BUILD_TESTS OR USERVER_BUILD_SAMPLES))
- message(FATAL_ERROR "For USERVER_INSTALL, please turn off USERVER_BUILD_TESTS and USERVER_BUILD_SAMPLES "
+if(USERVER_INSTALL AND USERVER_BUILD_TESTS)
+ message(FATAL_ERROR "For USERVER_INSTALL, please turn off USERVER_BUILD_TESTS "
"to avoid accidentally installing them"
)
endif()
@@ -175,6 +175,9 @@ include(UserverSetupEnvironment)
userver_setup_environment()
include(PrepareInstall)
+
+include(GetUserverVersion)
+
include(UserverModule)
if(USERVER_INSTALL)
@@ -182,7 +185,6 @@ if(USERVER_INSTALL)
endif()
include(ModuleHelpers)
-include(GetUserverVersion)
include(AddGoogleTests)
include(FindPackageRequired)
include(IncludeWhatYouUse)
@@ -192,6 +194,9 @@ include(UserverGenerateDynamicConfigsDocs)
include(CheckCompileFlags)
include(CMakePackageConfigHelpers)
+_userver_macos_set_default_dir(OPENSSL_ROOT_DIR "brew;--prefix;openssl")
+include(SetupOpenssl)
+
set(USERVER_THIRD_PARTY_DIRS
${USERVER_ROOT_DIR}/third_party
CACHE INTERNAL ""
@@ -199,6 +204,7 @@ set(USERVER_THIRD_PARTY_DIRS
init_debian_depends()
+include(SetupBoost)
include(SetupGTest)
if(USERVER_FEATURE_GRPC)
@@ -324,10 +330,6 @@ if(USERVER_BUILD_SAMPLES AND USERVER_FEATURE_CORE)
add_subdirectory(samples)
endif()
-if(USERVER_INSTALL)
- include(cmake/UserverPack.cmake)
-endif()
-
if(USERVER_FEATURE_CORE)
_userver_directory_install(
COMPONENT core
@@ -367,3 +369,11 @@ endif()
_userver_add_target_gen_dynamic_configs_docs()
_userver_print_features_list()
+if(CPM_PACKAGES)
+ include(DownloadUsingCPM)
+ _userver_print_cpm_packages()
+endif()
+
+if(USERVER_INSTALL)
+ include(cmake/UserverPack.cmake)
+endif()
diff --git a/Makefile b/Makefile
index 7162fe89b4e9..146530bbb36b 100644
--- a/Makefile
+++ b/Makefile
@@ -75,4 +75,13 @@ docker-kill:
# clean build folders
.PHONY: dist-clean
dist-clean:
- rm -rf build_*
+ rm -rf build_*/
+ rm -rf debian/
+ rm -rf .ruff_cache/
+ rm -rf _CPack_Packages/
+ find -name .mypy_cache | xargs rm -rf
+ find -name __pycache__ | xargs rm -rf
+
+.PHONY: gen-debian-directory
+gen-debian-directory:
+ scripts/generate-debian-directory.sh
diff --git a/README.md b/README.md
index f24126ff9686..4f03ba8e9bfc 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,8 @@
# userver [
](https://userver.tech/)
[](https://github.com/userver-framework/userver/actions/workflows/ci.yml)
+[](https://github.com/userver-framework/userver/actions/workflows/fedora.yml)
+[](https://github.com/userver-framework/userver/actions/workflows/debian.yml)
[](https://github.com/userver-framework/userver/actions/workflows/macos.yml)
[](https://github.com/userver-framework/userver/actions/workflows/alpine.yml)
[](https://github.com/userver-framework/userver/actions/workflows/docker.yaml)
diff --git a/chaotic-openapi/CMakeLists.txt b/chaotic-openapi/CMakeLists.txt
index b6ada59033e8..185bb249c759 100644
--- a/chaotic-openapi/CMakeLists.txt
+++ b/chaotic-openapi/CMakeLists.txt
@@ -8,6 +8,7 @@ userver_module(
LINK_LIBRARIES userver-core userver-chaotic
UTEST_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/*_test.cpp"
UTEST_LINK_LIBRARIES userver-core userver-chaotic
+ DEPENDS core
)
if(USERVER_BUILD_TESTS)
@@ -25,7 +26,9 @@ if(USERVER_BUILD_TESTS)
)
add_subdirectory(integration_tests)
- add_subdirectory(golden_tests)
+ if (USERVER_CHAOTIC_GOLDEN_TESTS)
+ add_subdirectory(golden_tests)
+ endif()
endif()
_userver_directory_install(
diff --git a/chaotic-openapi/chaotic_openapi/back/cpp_client/middleware.py b/chaotic-openapi/chaotic_openapi/back/cpp_client/middleware.py
new file mode 100644
index 000000000000..5ee1b421d054
--- /dev/null
+++ b/chaotic-openapi/chaotic_openapi/back/cpp_client/middleware.py
@@ -0,0 +1,96 @@
+import abc
+
+
+class Middleware(abc.ABC):
+ """
+ Per-operation middleware.
+ It generates a single struct member for `Request` type.
+ If we want to represent the structure as a jinja template,
+ it will look something like that:
+
+ struct Request {
+ {{ middleware.request_member_cpp_type }} {{ middleware.request_member_name }};
+ };
+
+ void SerializeRequest(
+ const Request& request,
+ const std::string& base_url,
+ USERVER_NAMESPACE::clients::http::Request& http_request
+ )
+ {
+ openapi::ParameterSinkHttpClient sink(
+ http_request,
+ base_url + "{{ op.path }}"
+ );
+ ...
+ {{ middleware.write_member_command('request', 'sink', 'http_request') }}
+ }
+ """
+
+ @property
+ @abc.abstractmethod
+ def request_member_name(self) -> str:
+ """
+ Generated `Request` member name for middleware data.
+ """
+ pass
+
+ @property
+ @abc.abstractmethod
+ def request_member_cpp_type(self) -> str:
+ """
+ C++ type of `request_member_name`.
+ """
+ pass
+
+ @property
+ @abc.abstractmethod
+ def requests_hpp_includes(self) -> list[str]:
+ """
+ C++ include path used to access `request_member_cpp_type`.
+ """
+ pass
+
+ @abc.abstractmethod
+ def write_member_command(
+ self,
+ request: str,
+ sink: str,
+ http_request: str,
+ ) -> str:
+ """
+ C++ code that will be called as part of `Request` serialization.
+
+ request - variable name of `Request` type
+ sink - variable name of `ParameterSinkHttpClient` type
+ http_request - variable name of `clients::http::Request` type
+ """
+ pass
+
+
+class MiddlewarePlugin(abc.ABC):
+ """
+ Implement `MiddlewarePlugin` if you want to extend OpenAPI schema
+ with your middleware. The schema is extended with
+ `x-usrv-middleware.` + MiddlewarePlugin.field object member.
+
+ A plugin is created per-parser.
+ """
+
+ @property
+ @abc.abstractmethod
+ def field(self) -> str:
+ """
+ Returns `x-usrv-middleware` property name that is responsible for
+ middleware activation and parameters storage.
+ """
+ pass
+
+ @abc.abstractmethod
+ def create(self, args: dict) -> Middleware:
+ """
+ The method is called every time a HTTP operation with
+ "x-usrv-middleware." + field property is discovered.
+ It creates a per-operation `Middleware` object.
+ """
+ pass
diff --git a/chaotic-openapi/chaotic_openapi/back/cpp_client/output.py b/chaotic-openapi/chaotic_openapi/back/cpp_client/output.py
index 6987bb990995..f290b0280477 100644
--- a/chaotic-openapi/chaotic_openapi/back/cpp_client/output.py
+++ b/chaotic-openapi/chaotic_openapi/back/cpp_client/output.py
@@ -1,11 +1,15 @@
import collections
+import dataclasses
+import os
import pathlib
-from typing import Dict
-from typing import List
+from typing import Any
from typing import Optional
import yaml
+from chaotic.back.cpp import types as cpp_types
+from chaotic.front import parser as chaotic_parser
+from chaotic.front import ref
from . import renderer
TYPES_INCLUDES = [
@@ -16,7 +20,11 @@
]
-def _get_template_includes(name: str, client_name: str, graph: Dict[str, List[str]]) -> List[str]:
+def filepath_with_stem(fpath: str) -> str:
+ return fpath.rsplit('.', 1)[0]
+
+
+def _get_template_includes(name: str, client_name: str, graph: dict[str, list[str]]) -> list[str]:
includes = {
'client.cpp': [
f'clients/{client_name}/client.hpp',
@@ -77,7 +85,6 @@ def _get_template_includes(name: str, client_name: str, graph: Dict[str, List[st
'string',
'variant',
*[f'clients/{client_name}/{dep}' for dep in graph],
- # TODO
],
'responses.cpp': [
f'clients/{client_name}/responses.hpp',
@@ -92,63 +99,95 @@ def _get_template_includes(name: str, client_name: str, graph: Dict[str, List[st
f'clients/{client_name}/exceptions.hpp',
'userver/chaotic/openapi/client/exceptions.hpp',
*[f'clients/{client_name}/{dep}' for dep in graph],
- # TODO
],
}
return includes[name]
-def trim_suffix(string: str, suffix: str) -> Optional[str]:
- if string.endswith(suffix):
- return string[: -len(suffix)]
- return None
-
-
-def extract_includes(name: str, path: pathlib.Path) -> Optional[List[str]]:
+def extract_includes(name: str, path: pathlib.Path, schemas_dir: pathlib.Path) -> Optional[list[str]]:
with open(path) as ifile:
content = yaml.safe_load(ifile)
- includes: List[str] = []
+ includes: list[str] = []
+ is_file_produced = False
+
+ relpath = os.path.relpath(os.path.dirname(path), schemas_dir)
def visit(data) -> None:
+ nonlocal is_file_produced
if isinstance(data, dict):
for v in data.values():
visit(v)
if '$ref' in data:
- ref = data['$ref']
- ref = ref.split('#')[0]
- ref_fname = ref.rsplit('/', 1)[-1]
- if ref_fname:
- stem = ref_fname.rsplit('.', 1)[0]
+ ref_ = data['$ref']
+ filename = ref.Ref(ref_).file
+ if filename:
+ stem = os.path.join(relpath, filepath_with_stem(filename))
+ stem = chaotic_parser.SchemaParser._normalize_ref(stem)
includes.append(f'clients/{name}/{stem}.hpp')
- if 'x-taxi-cpp-type' in data:
- pass
- if 'x-usrv-cpp-type' in data:
- pass
+
+ for field in ('x-taxi-cpp-type', 'x-usrv-cpp-type'):
+ if field not in data:
+ continue
+ header = extract_cpp_type_header(data[field])
+ if header:
+ includes.append(header)
+
+ if is_file_produced_feature(data):
+ is_file_produced = True
elif isinstance(data, list):
for v in data:
visit(v)
- if 'definitions' in content or 'components' in content or 'paths' in content:
- visit(content)
+ visit(content)
+ if content.get('definitions') or content.get('components', {}).get('schemas'):
+ is_file_produced = True
+
+ if includes or is_file_produced:
return includes
- else:
+
+ # No schemas in file, it will generate no .hpp/.cpp
+ return None
+
+
+def is_file_produced_feature(data: dict[str, Any]) -> bool:
+ if data.get('type') == 'object':
+ return True
+ if data.get('oneOf'):
+ return True
+ if data.get('allOf'):
+ return True
+ return False
+
+
+def extract_cpp_type_header(cpp_type: str) -> Optional[str]:
+ parts = cpp_type.split('::')
+ if len(parts) < 2:
+ return None
+ library = parts[0].replace('_', '-')
+ if library == 'std':
return None
+ filename = cpp_types.camel_to_snake_case(parts[1])
+ return '{}/{}.hpp'.format(library, filename)
-def include_graph(name: str, schemas_dir: pathlib.Path) -> Dict[str, List[str]]:
+def include_graph(name: str, schemas_dir: pathlib.Path) -> dict[str, list[str]]:
result = {}
for root, _, filenames in schemas_dir.walk():
for filename in filenames:
+ if not filename.endswith('.yaml'):
+ continue
filepath = pathlib.Path(root) / filename
if filepath == schemas_dir / 'client.yaml' or filename == 'a.yaml':
continue
- result[filepath.stem + '.hpp'] = extract_includes(name, filepath)
+
+ stem = filepath_with_stem(os.path.relpath(filepath, schemas_dir))
+ result[stem + '.hpp'] = extract_includes(name, filepath, schemas_dir)
return {key: result[key] for key in result if result[key] is not None} # type: ignore
-def get_includes(client_name: str, schemas_dir: str) -> Dict[str, List[str]]:
+def get_includes(client_name: str, schemas_dir: str) -> dict[str, list[str]]:
graph = include_graph(client_name, pathlib.Path(schemas_dir))
output = collections.defaultdict(list)
@@ -160,7 +199,7 @@ def get_includes(client_name: str, schemas_dir: str) -> Dict[str, List[str]]:
output[rel_path] = _get_template_includes(name, client_name, graph)
for file in graph:
- stem = pathlib.Path(file).stem
+ stem = filepath_with_stem(file)
output[f'include/clients/{client_name}/{stem}_fwd.hpp'] = []
output[f'include/clients/{client_name}/{stem}.hpp'] = [
f'clients/{client_name}/{stem}_fwd.hpp',
@@ -182,9 +221,19 @@ def get_includes(client_name: str, schemas_dir: str) -> Dict[str, List[str]]:
return output
-def external_libraries(schemas_dir: str) -> List[str]:
+@dataclasses.dataclass
+class External:
+ libraries: list[str]
+ userver_modules: list[str]
+
+
+# For Yandex uservices only, not for OSS
+def external_libraries(schemas_dir: str) -> External:
types = set()
+ libraries = []
+ userver_modules = []
+
def visit(data) -> None:
if isinstance(data, dict):
for v in data.values():
@@ -193,6 +242,18 @@ def visit(data) -> None:
types.add(data['x-taxi-cpp-type'])
if 'x-usrv-cpp-type' in data:
types.add(data['x-usrv-cpp-type'])
+ if 'x-taxi-cpp-typedef-tag' in data:
+ types.add(data['x-taxi-cpp-typedef-tag'])
+ if 'x-usrv-cpp-typedef-tag' in data:
+ types.add(data['x-usrv-cpp-typedef-tag'])
+
+ if data.get('x-taxi-middlewares', {}).get('api-4.0'):
+ libraries.append('passenger-authorizer-backend')
+ if data.get('x-taxi-middlewares', {}).get('eats') == 'v1':
+ libraries.append('eats-authproxy-backend')
+ if data.get('x-taxi-middlewares', {}).get('bank-authproxy'):
+ libraries.append('bank-authproxy-backend')
+
elif isinstance(data, list):
for v in data:
visit(v)
@@ -202,10 +263,14 @@ def visit(data) -> None:
content = yaml.safe_load(ifile)
visit(content)
- libraries = []
for type_ in types:
+ if type_.startswith('::'):
+ type_ = type_[2:]
library = type_.split('::')[0].replace('_', '-')
# special namespaces (and unsigned) which are defined in userver/, not in libraries/
if library not in {'std', 'storages', 'decimal64', 'unsigned'}:
libraries.append(library)
- return libraries
+ if type_.startswith('storages::postgres::'):
+ userver_modules.append('postgresql')
+
+ return External(libraries=libraries, userver_modules=userver_modules)
diff --git a/chaotic-openapi/chaotic_openapi/back/cpp_client/renderer.py b/chaotic-openapi/chaotic_openapi/back/cpp_client/renderer.py
index 51fa73fdb543..060ce66a4f73 100644
--- a/chaotic-openapi/chaotic_openapi/back/cpp_client/renderer.py
+++ b/chaotic-openapi/chaotic_openapi/back/cpp_client/renderer.py
@@ -1,7 +1,6 @@
import dataclasses
import os
import pathlib
-from typing import List
import jinja2
@@ -45,7 +44,7 @@ class CppOutput:
content: str
@staticmethod
- def save(outputs: List['CppOutput'], prefix: str) -> None:
+ def save(outputs: list['CppOutput'], prefix: str) -> None:
for output in outputs:
path = os.path.join(prefix, output.rel_path)
content = THIS_FILE_IS_AUTOGENERATED + output.content
@@ -81,7 +80,7 @@ def make_env() -> jinja2.Environment:
JINJA_ENV = make_env()
-def render(spec: types.ClientSpec, context: Context) -> List[CppOutput]:
+def render(spec: types.ClientSpec, context: Context) -> list[CppOutput]:
assert '-' not in spec.cpp_namespace
env = {
'spec': spec,
@@ -113,7 +112,7 @@ def render(spec: types.ClientSpec, context: Context) -> List[CppOutput]:
filepath = cpp_type.json_schema.source_location().filepath
vfilepath_map[filepath] = 'clients/{}/{}'.format(
spec.client_name,
- filepath.split('/')[-1],
+ filepath,
)
# C++ types files
diff --git a/chaotic-openapi/chaotic_openapi/back/cpp_client/templates/client_impl.cpp.jinja b/chaotic-openapi/chaotic_openapi/back/cpp_client/templates/client_impl.cpp.jinja
index 9baab975dfd9..ac3719233454 100644
--- a/chaotic-openapi/chaotic_openapi/back/cpp_client/templates/client_impl.cpp.jinja
+++ b/chaotic-openapi/chaotic_openapi/back/cpp_client/templates/client_impl.cpp.jinja
@@ -21,6 +21,9 @@ ClientImpl::ClientImpl(const USERVER_NAMESPACE::chaotic::openapi::client::Config
const USERVER_NAMESPACE::chaotic::openapi::client::CommandControl& command_control
) {
auto r = http_client_.CreateRequest();
+ if (plugins_) {
+ r.SetPluginsList(*plugins_);
+ }
ApplyConfig(r, command_control, config_);
{% if not op.empty_request() -%}
diff --git a/chaotic-openapi/chaotic_openapi/back/cpp_client/templates/client_impl.hpp.jinja b/chaotic-openapi/chaotic_openapi/back/cpp_client/templates/client_impl.hpp.jinja
index 85ae34f81813..26ee612ee375 100644
--- a/chaotic-openapi/chaotic_openapi/back/cpp_client/templates/client_impl.hpp.jinja
+++ b/chaotic-openapi/chaotic_openapi/back/cpp_client/templates/client_impl.hpp.jinja
@@ -47,11 +47,16 @@ public:
middleware_manager_.RegisterMiddleware(middleware);
}
+ void SetPlugins(std::optional>> plugins) {
+ plugins_ = std::move(plugins);
+ }
+
private:
USERVER_NAMESPACE::chaotic::openapi::client::Config config_;
USERVER_NAMESPACE::clients::http::Client& http_client_;
USERVER_NAMESPACE::chaotic::openapi::MiddlewareManager middleware_manager_;
std::unordered_map> middlewares_;
+ std::optional>> plugins_;
};
}
diff --git a/chaotic-openapi/chaotic_openapi/back/cpp_client/templates/component.cpp.jinja b/chaotic-openapi/chaotic_openapi/back/cpp_client/templates/component.cpp.jinja
index fed655854bd3..a30c75de6073 100644
--- a/chaotic-openapi/chaotic_openapi/back/cpp_client/templates/component.cpp.jinja
+++ b/chaotic-openapi/chaotic_openapi/back/cpp_client/templates/component.cpp.jinja
@@ -2,6 +2,8 @@
#include
#include
+#include
+#include
#include
#include
@@ -26,11 +28,23 @@ Component::Component(const USERVER_NAMESPACE::components::ComponentConfig& confi
if (config.HasMember("middlewares")) {
const auto& mw_config = config["middlewares"];
- for (const auto& [name, config] : Items(mw_config)) {
+ for (const auto& [name, config] : USERVER_NAMESPACE::formats::common::Items(mw_config)) {
auto &factory = context.FindComponent("chaotic-client-middleware-" + name);
auto middleware = factory.Create(config);
client_.RegisterMiddleware(middleware);
}
+
+ const auto& names = config["plugins"].As>>(std::nullopt);
+ std::optional>> plugins;
+ if (names) {
+ plugins.emplace();
+ for (const auto& name : *names) {
+ auto& component =
+ context.FindComponent(std::string{"http-client-plugin-"} + name);
+ plugins->emplace_back(&component.GetPlugin());
+ }
+ }
+ client_.SetPlugins(std::move(plugins));
}
}
diff --git a/chaotic-openapi/chaotic_openapi/back/cpp_client/templates/exceptions.hpp.jinja b/chaotic-openapi/chaotic_openapi/back/cpp_client/templates/exceptions.hpp.jinja
index 8f432cd5e31d..ec5f62ff28ed 100644
--- a/chaotic-openapi/chaotic_openapi/back/cpp_client/templates/exceptions.hpp.jinja
+++ b/chaotic-openapi/chaotic_openapi/back/cpp_client/templates/exceptions.hpp.jinja
@@ -38,7 +38,7 @@ class TimeoutException: public USERVER_NAMESPACE::chaotic::openapi::client::Time
namespace {{ op.cpp_namespace() }} {
/// @brief Base exception class for all client {{ op.method }} operations with URL '{{ op.path }}'
-class Exception: public {{ namespace }}::Exception {
+class Exception: public ::{{ namespace }}::Exception {
public:
const char* what() const noexcept override;
@@ -50,17 +50,17 @@ class Exception: public {{ namespace }}::Exception {
/// @brief Error response with ErrorKind for all client {{ op.method }} operations with URL '{{ op.path }}'
class HttpException
: public Exception
- , public {{ namespace }}::HttpException
+ , public ::{{ namespace }}::HttpException
{
public:
- using {{ namespace }}::HttpException::HttpException;
+ using ::{{ namespace }}::HttpException::HttpException;
~HttpException();
};
/// @brief Timeout exception class for all client {{ op.method }} operations with URL '{{ op.path }}'
class TimeoutException
: public HttpException
- , public {{ namespace }}::TimeoutException
+ , public ::{{ namespace }}::TimeoutException
{
public:
TimeoutException();
@@ -70,10 +70,10 @@ class TimeoutException
/// @brief Error response with HTTP status code for all client {{ op.method }} operations with URL '{{ op.path }}'
class ExceptionWithStatusCode
: public Exception
- , public {{ namespace }}::ExceptionWithStatusCode
+ , public ::{{ namespace }}::ExceptionWithStatusCode
{
public:
- using {{ namespace }}::ExceptionWithStatusCode::ExceptionWithStatusCode;
+ using ::{{ namespace }}::ExceptionWithStatusCode::ExceptionWithStatusCode;
~ExceptionWithStatusCode();
};
diff --git a/chaotic-openapi/chaotic_openapi/back/cpp_client/templates/requests.cpp.jinja b/chaotic-openapi/chaotic_openapi/back/cpp_client/templates/requests.cpp.jinja
index 772147b235b5..d0cc7d4e1a6e 100644
--- a/chaotic-openapi/chaotic_openapi/back/cpp_client/templates/requests.cpp.jinja
+++ b/chaotic-openapi/chaotic_openapi/back/cpp_client/templates/requests.cpp.jinja
@@ -8,6 +8,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -25,6 +26,22 @@ namespace openapi = USERVER_NAMESPACE::chaotic::openapi;
static constexpr openapi::Name k{{ parameter.cpp_name }} = "{{ parameter.raw_name }}";
{% endfor %}
+{% if op.has_any_hidden_query_parameters() %}
+ constexpr utils::TrivialSet kHiddenQueries = [](auto selector) {
+ return selector()
+ {% for parameter in op.parameters %}
+ {% if parameter.query_log_mode_hide %}
+ .Case(k{{ parameter.cpp_name }})
+ {% endif %}
+ {% endfor %}
+ ;
+ };
+
+ bool IsHiddenQueryArg(std::string_view str) {
+ return kHiddenQueries.Contains(str);
+ }
+{% endif %}
+
void SerializeRequest(const Request& request, const std::string& base_url, USERVER_NAMESPACE::clients::http::Request& http_request)
{
{# parameters #}
@@ -32,6 +49,11 @@ void SerializeRequest(const Request& request, const std::string& base_url, USERV
http_request,
base_url + "{{ op.path }}"
);
+
+ {% if op.has_any_hidden_query_parameters() %}
+ sink.SetHiddenQueryArgNamesFunc(&IsHiddenQueryArg);
+ {% endif %}
+
{% for parameter in op.parameters %}
{% if parameter.required %}
openapi::WriteParameter<{{ parameter.parser }}>(request.{{ parameter.cpp_name}}, sink);
@@ -71,6 +93,12 @@ void SerializeRequest(const Request& request, const std::string& base_url, USERV
{% endif %}
sink.Flush();
+
+ {# middlewares #}
+ {% for mw in op.middlewares %}
+ {{ mw.write_member_command('request', 'sink', 'http_request') }}
+ {% endfor %}
+
}
} // namespace
diff --git a/chaotic-openapi/chaotic_openapi/back/cpp_client/templates/requests.hpp.jinja b/chaotic-openapi/chaotic_openapi/back/cpp_client/templates/requests.hpp.jinja
index 5119ff7189ef..fbf4d4b4534a 100644
--- a/chaotic-openapi/chaotic_openapi/back/cpp_client/templates/requests.hpp.jinja
+++ b/chaotic-openapi/chaotic_openapi/back/cpp_client/templates/requests.hpp.jinja
@@ -5,6 +5,9 @@
#pragma once
#include
+{% if spec.has_array_in_request_body() %}
+#include
+{% endif %}
{% if spec.has_multiple_content_type_request() %}
#include
{% endif %}
@@ -46,6 +49,7 @@ namespace {{ namespace }} {
{% endfor %}
>;
{% endif %}
+
struct Request {
{% for parameter in op.parameters %}
{% if parameter.cpp_type.nullable %}
@@ -55,6 +59,11 @@ namespace {{ namespace }} {
{% endif -%}
{{ parameter.cpp_name }};
{% endfor %}
+
+ {% for mw in op.middlewares %}
+ {{ mw.request_member_cpp_type }} {{ mw.request_member_name }};
+ {% endfor %}
+
{% if op.request_bodies %}
Body body;
{% endif %}
diff --git a/chaotic-openapi/chaotic_openapi/back/cpp_client/templates/responses.cpp.jinja b/chaotic-openapi/chaotic_openapi/back/cpp_client/templates/responses.cpp.jinja
index d5d7728be1b9..78d74ad335cb 100644
--- a/chaotic-openapi/chaotic_openapi/back/cpp_client/templates/responses.cpp.jinja
+++ b/chaotic-openapi/chaotic_openapi/back/cpp_client/templates/responses.cpp.jinja
@@ -5,6 +5,7 @@
#include
#include
#include
+#include
{% for include in spec.responses_definitions_includes() %}
#include <{{ include }}>
@@ -67,7 +68,10 @@ namespace {{ namespace }} {
static const USERVER_NAMESPACE::http::headers::PredefinedHeader kHeader("{{ header.raw_name }}");
auto it = http_response.headers().find(kHeader);
if (it != http_response.headers().end()) {
- r.{{ header.cpp_name }} = it->second;
+ namespace openapi = chaotic::openapi;
+ static constexpr openapi::Name k{{ header.cpp_name }} = "{{ header.raw_name }}";
+ using Header = {{ header.parser }};
+ r.{{ header.cpp_name }} = openapi::ParseParameter::Parse(std::string{it->second});
}
}
{% endfor %}
diff --git a/chaotic-openapi/chaotic_openapi/back/cpp_client/translator.py b/chaotic-openapi/chaotic_openapi/back/cpp_client/translator.py
index 745e78c7c6b8..f9747de6d204 100644
--- a/chaotic-openapi/chaotic_openapi/back/cpp_client/translator.py
+++ b/chaotic-openapi/chaotic_openapi/back/cpp_client/translator.py
@@ -1,6 +1,7 @@
import re
+import sys
+import traceback
import typing
-from typing import List
from typing import Union
from chaotic import cpp_names
@@ -9,7 +10,9 @@
from chaotic.back.cpp import types as cpp_types
from chaotic.front import ref_resolver
from chaotic.front import types as chaotic_types
+from chaotic_openapi.back.cpp_client import middleware
from chaotic_openapi.back.cpp_client import types
+from chaotic_openapi.front import base_model
from chaotic_openapi.front import model
@@ -20,7 +23,8 @@ def __init__(
*,
cpp_namespace: str,
dynamic_config: str,
- include_dirs: List[str],
+ include_dirs: list[str],
+ middleware_plugins: list[middleware.MiddlewarePlugin],
) -> None:
self._spec = types.ClientSpec(
client_name=service.name,
@@ -30,7 +34,26 @@ def __init__(
operations=[],
schemas={},
)
+ self._include_dirs = include_dirs
+ self._middleware_plugins = middleware_plugins
+
+ try:
+ self.translate(service)
+ except chaotic_error.BaseError:
+ raise
+ except BaseException as exc:
+ print(traceback.format_exc(), file=sys.stderr)
+ raise chaotic_error.BaseError(
+ full_filepath=f'<{service.name}>',
+ infile_path='',
+ schema_type='openapi/swagger',
+ msg=str(exc),
+ )
+ def translate(
+ self,
+ service: model.Service,
+ ) -> None:
# components/schemas
parsed_schemas = chaotic_types.ParsedSchemas(
schemas={str(schema.source_location()): schema for schema in service.schemas.values()},
@@ -40,7 +63,7 @@ def __init__(
chaotic_translator.GeneratorConfig(
namespaces={schema.source_location().filepath: '' for schema in service.schemas.values()},
infile_to_name_func=self.map_infile_path_to_cpp_type,
- include_dirs=include_dirs,
+ include_dirs=self._include_dirs,
)
)
self._spec.schemas = gen.generate_types(resolved_schemas)
@@ -63,10 +86,12 @@ def __init__(
op = types.Operation(
method=operation.method.upper(),
path=operation.path,
+ operation_id=operation.operationId,
description=operation.description,
parameters=[self._translate_parameter(parameter) for parameter in operation.parameters],
request_bodies=request_bodies,
responses=[self._translate_response(r, status) for status, r in operation.responses.items()],
+ middlewares=self._translate_middlewares(operation.x_middlewares),
)
self._spec.operations.append(op)
@@ -77,13 +102,21 @@ def map_infile_path_to_cpp_type(self, name: str, stem: str) -> str:
if name.startswith('/components/schemas/'):
return '{}::{}'.format(
self._spec.cpp_namespace,
- name.split('/')[-1],
+ cpp_names.cpp_identifier(name.split('/')[-1]),
)
if name.startswith('/definitions/'):
return '{}::{}'.format(
self._spec.cpp_namespace,
- name.split('/')[-1],
+ cpp_names.cpp_identifier(name.split('/')[-1]),
+ )
+
+ match = re.fullmatch('/paths/\\[([^\\]]*)\\]/([a-zA-Z]*)/requestBody(/schema)?', name)
+ if match:
+ return '{}::{}::{}::Body'.format(
+ self._spec.cpp_namespace,
+ cpp_names.namespace(match.group(1)),
+ types.map_method(match.group(2)),
)
match = re.fullmatch('/paths/\\[([^\\]]*)\\]/([a-zA-Z]*)/requestBody/content/\\[([^\\]]*)\\]/schema', name)
@@ -121,6 +154,16 @@ def map_infile_path_to_cpp_type(self, name: str, stem: str) -> str:
),
)
+ match = re.fullmatch('/components/responses/([-a-zA-Z_0-9]*)/headers/([^/]*)/schema', name)
+ if match:
+ return '{}::Response{}Header{}'.format(
+ self._spec.cpp_namespace,
+ match.group(1),
+ cpp_names.camel_case(
+ cpp_names.cpp_identifier(match.group(2)),
+ ),
+ )
+
match = re.fullmatch('/paths/\\[([^\\]]*)\\]/([a-zA-Z]*)/parameters/([0-9]*)(/schema)?', name)
if match:
return '{}::{}::{}::Parameter{}'.format(
@@ -205,6 +248,7 @@ def _translate_single_schema(self, schema: chaotic_types.Schema) -> cpp_types.Cp
chaotic_translator.GeneratorConfig(
namespaces={schema.source_location().filepath: ''},
infile_to_name_func=self.map_infile_path_to_cpp_type,
+ include_dirs=self._include_dirs,
)
)
gen_types = gen.generate_types(
@@ -231,13 +275,14 @@ def _translate_response(
for name, header in response.headers.items():
headers.append(self._translate_parameter(header))
+ body = {}
+ for content_type, schema in response.content.items():
+ assert isinstance(schema.schema, chaotic_types.Schema)
+ body[content_type] = self._translate_single_schema(schema.schema)
return types.Response(
status=status,
headers=headers,
- body={
- content_type: self._translate_single_schema(schema.schema)
- for content_type, schema in response.content.items()
- },
+ body=body,
)
def _translate_request_body(self, request_body: model.RequestBody) -> types.Body:
@@ -274,7 +319,9 @@ def _validate_primitive_object(self, schema: cpp_types.CppType) -> None:
def _translate_parameter(self, parameter: model.Parameter) -> types.Parameter:
in_ = parameter.in_
in_str = in_[0].title() + in_[1:]
- cpp_name = cpp_names.cpp_identifier(parameter.name)
+ cpp_name = cpp_names.cpp_identifier(
+ parameter.x_cpp_name or parameter.name,
+ )
if isinstance(parameter.schema, chaotic_types.Array):
delimiter = {
@@ -309,7 +356,26 @@ def _translate_parameter(self, parameter: model.Parameter) -> types.Parameter:
cpp_type=cpp_type,
parser=parser,
required=parameter.required,
+ query_log_mode_hide=parameter.x_query_log_mode_hide,
)
+ def _translate_middlewares(self, middlewares: base_model.XMiddlewares) -> list[middleware.Middleware]:
+ extra_members = middlewares.__pydantic_extra__ or {}
+
+ result = []
+ for plugin in self._middleware_plugins:
+ field = plugin.field
+ if field not in extra_members:
+ continue
+
+ mw = plugin.create({field: extra_members[field]})
+ result.append(mw)
+ del extra_members[field]
+
+ if extra_members:
+ raise Exception(f'Unknown member(s) in x-taxi-middlewares: {extra_members}')
+
+ return result
+
def spec(self) -> types.ClientSpec:
return self._spec
diff --git a/chaotic-openapi/chaotic_openapi/back/cpp_client/types.py b/chaotic-openapi/chaotic_openapi/back/cpp_client/types.py
index 3f13c23230d2..be4e6d5a99a8 100644
--- a/chaotic-openapi/chaotic_openapi/back/cpp_client/types.py
+++ b/chaotic-openapi/chaotic_openapi/back/cpp_client/types.py
@@ -1,13 +1,12 @@
import dataclasses
-from typing import Dict
from typing import Generator
-from typing import List
from typing import Optional
-from typing import Set
+from typing import Union
from chaotic import cpp_names
from chaotic import error
from chaotic.back.cpp import types as cpp_types
+from . import middleware
@dataclasses.dataclass
@@ -19,14 +18,15 @@ class Parameter:
parser: str
required: bool
- def declaration_includes(self) -> List[str]:
+ query_log_mode_hide: bool
+
+ def declaration_includes(self) -> list[str]:
# TODO
if not self.required:
return ['optional']
return []
- @classmethod
- def _validate_schema(cls, schema: cpp_types.CppType, *, is_array_allowed: bool) -> None:
+ def _validate_schema(self, schema: cpp_types.CppType, *, is_array_allowed: bool) -> None:
assert schema.json_schema
if not is_array_allowed and isinstance(schema, cpp_types.CppArray):
source_location = schema.json_schema.source_location()
@@ -53,17 +53,28 @@ def _validate_schema(cls, schema: cpp_types.CppType, *, is_array_allowed: bool)
full_filepath=source_location.filepath,
infile_path=source_location.location,
schema_type='jsonschema',
- msg='Unsupported parameter type',
+ msg=f'Unsupported parameter type for parameter "{self.raw_name}"',
)
if isinstance(schema, cpp_types.CppRef):
- cls._validate_schema(schema.orig_cpp_type, is_array_allowed=is_array_allowed)
+ self._validate_schema(schema.orig_cpp_type, is_array_allowed=is_array_allowed)
if isinstance(schema, cpp_types.CppArray):
- cls._validate_schema(schema.items, is_array_allowed=False)
+ self._validate_schema(schema.items, is_array_allowed=False)
def __post_init__(self) -> None:
self._validate_schema(self.cpp_type, is_array_allowed=True)
+ # TODO: for handler only
+ def span_value(self) -> str:
+ if self.required:
+ arg = self.cpp_name
+ else:
+ arg = '*' + self.cpp_name
+ if self.cpp_type.cpp_user_name() == 'std::string':
+ return arg
+ else:
+ return f'::fmt::format("{{}}", {arg})'
+
@dataclasses.dataclass
class Body:
@@ -79,8 +90,8 @@ def cpp_name(self) -> str:
@dataclasses.dataclass
class Response:
status: int
- body: Dict[str, cpp_types.CppType]
- headers: List[Parameter] = dataclasses.field(default_factory=list)
+ body: dict[str, cpp_types.CppType]
+ headers: list[Parameter] = dataclasses.field(default_factory=list)
def is_error(self) -> bool:
return self.status >= 400
@@ -108,18 +119,25 @@ def map_method(method: str) -> str:
class Operation:
method: str
path: str
+ operation_id: Union[str, None]
description: str = ''
- parameters: List[Parameter] = dataclasses.field(default_factory=list)
- request_bodies: List[Body] = dataclasses.field(default_factory=list)
- responses: List[Response] = dataclasses.field(default_factory=list)
+ parameters: list[Parameter] = dataclasses.field(default_factory=list)
+ request_bodies: list[Body] = dataclasses.field(default_factory=list)
+ responses: list[Response] = dataclasses.field(default_factory=list)
client_generate: bool = True
+ middlewares: list[middleware.Middleware] = dataclasses.field(default_factory=list)
+
def cpp_namespace(self) -> str:
+ if self.operation_id:
+ return cpp_names.namespace(self.operation_id)
return cpp_names.namespace(self.path) + '::' + map_method(self.method)
def cpp_method_name(self) -> str:
+ if self.operation_id:
+ return self.operation_id
return cpp_names.camel_case(
cpp_names.namespace(self.path) + '_' + map_method(self.method),
)
@@ -148,6 +166,12 @@ def iter_error_responses(self) -> Generator[Response, None, None]:
if response.is_error():
yield response
+ def has_any_hidden_query_parameters(self) -> bool:
+ for parameter in self.parameters:
+ if parameter.query_log_mode_hide:
+ return True
+ return False
+
@dataclasses.dataclass
class ClientSpec:
@@ -155,12 +179,12 @@ class ClientSpec:
cpp_namespace: str
dynamic_config: str
description: str = ''
- operations: List[Operation] = dataclasses.field(default_factory=list)
+ operations: list[Operation] = dataclasses.field(default_factory=list)
# Internal types which cannot be referred to
- internal_schemas: Dict[str, cpp_types.CppType] = dataclasses.field(default_factory=dict)
+ internal_schemas: dict[str, cpp_types.CppType] = dataclasses.field(default_factory=dict)
# Types which can be referred to
- schemas: Dict[str, cpp_types.CppType] = dataclasses.field(default_factory=dict)
+ schemas: dict[str, cpp_types.CppType] = dataclasses.field(default_factory=dict)
def has_multiple_content_type_request(self) -> bool:
for op in self.operations:
@@ -168,8 +192,15 @@ def has_multiple_content_type_request(self) -> bool:
return True
return False
- def requests_declaration_includes(self) -> List[str]:
- includes: Set[str] = set()
+ def has_array_in_request_body(self) -> bool:
+ for op in self.operations:
+ for body in op.request_bodies:
+ if isinstance(body.schema, cpp_types.CppArray):
+ return True
+ return False
+
+ def requests_declaration_includes(self) -> list[str]:
+ includes: set[str] = set()
for op in self.operations:
if not op.client_generate:
continue
@@ -180,10 +211,13 @@ def requests_declaration_includes(self) -> List[str]:
for param in op.parameters:
includes.update(param.declaration_includes())
+ for mw in op.middlewares:
+ includes.update(mw.requests_hpp_includes)
+
return sorted(includes)
- def responses_declaration_includes(self) -> List[str]:
- includes: Set[str] = set()
+ def responses_declaration_includes(self) -> list[str]:
+ includes: set[str] = set()
for op in self.operations:
if not op.client_generate:
continue
@@ -195,8 +229,8 @@ def responses_declaration_includes(self) -> List[str]:
includes.update(header.declaration_includes())
return sorted(includes)
- def responses_definitions_includes(self) -> List[str]:
- includes: Set[str] = set()
+ def responses_definitions_includes(self) -> list[str]:
+ includes: set[str] = set()
for op in self.operations:
if not op.client_generate:
continue
@@ -206,7 +240,7 @@ def responses_definitions_includes(self) -> List[str]:
includes.update(body.definition_includes())
return sorted(includes)
- def cpp_includes(self) -> List[str]:
+ def cpp_includes(self) -> list[str]:
includes = []
for cpp_type in self.extract_cpp_types().values():
assert cpp_type.json_schema
@@ -214,12 +248,12 @@ def cpp_includes(self) -> List[str]:
includes.append(
'clients/{}/{}.hpp'.format(
self.client_name,
- filepath.split('/')[-1].split('.')[0],
+ filepath.rsplit('.', 1)[0],
),
)
return includes
- def extract_cpp_types(self) -> Dict[str, cpp_types.CppType]:
+ def extract_cpp_types(self) -> dict[str, cpp_types.CppType]:
types = self.schemas.copy()
types.update(self.internal_schemas)
diff --git a/chaotic-openapi/chaotic_openapi/back/linter/validators.py b/chaotic-openapi/chaotic_openapi/back/linter/validators.py
index e0ae6d6ee1c3..10df136ec8a5 100644
--- a/chaotic-openapi/chaotic_openapi/back/linter/validators.py
+++ b/chaotic-openapi/chaotic_openapi/back/linter/validators.py
@@ -26,7 +26,6 @@ def validate_nonobject_body(service: model.Service) -> None:
if not isinstance(
schema, (types.SchemaObject, types.OneOfWithDiscriminator, types.OneOfWithoutDiscriminator)
):
- print(schema, file=sys.stderr)
report_error('non-object-body', schema.source_location(), 'Non-object type in body root is forbidden')
diff --git a/chaotic-openapi/chaotic_openapi/front/base_model.py b/chaotic-openapi/chaotic_openapi/front/base_model.py
index 527cfbe141e7..fd147d5c1a81 100644
--- a/chaotic-openapi/chaotic_openapi/front/base_model.py
+++ b/chaotic-openapi/chaotic_openapi/front/base_model.py
@@ -1,12 +1,11 @@
from typing import Any
-from typing import List
import pydantic
class BaseModel(pydantic.BaseModel):
model_config = pydantic.ConfigDict(extra='allow')
- _model_userver_tags: List[str] = []
+ _model_userver_tags: list[str] = []
def model_post_init(self, context: Any) -> None:
super().model_post_init(context)
@@ -14,7 +13,9 @@ def model_post_init(self, context: Any) -> None:
if not self.__pydantic_extra__:
return
for field in self.__pydantic_extra__:
- if field.startswith('x-taxi-py3-'):
+ if field.startswith('x-taxi-py3'):
+ continue
+ if field.startswith('x-taxi-go-'):
continue
if field.startswith('x-taxi-') or field.startswith('x-usrv-'):
assert field in self._model_userver_tags, f'Field {field} is not allowed in this context'
@@ -23,3 +24,11 @@ def model_post_init(self, context: Any) -> None:
if field.startswith('x-'):
continue
raise Exception(f'Unknown field "{field}"')
+
+
+class XMiddlewares(pydantic.BaseModel):
+ model_config = pydantic.ConfigDict(extra='allow')
+
+ tvm: bool = True
+
+ # TODO: validate field set
diff --git a/chaotic-openapi/chaotic_openapi/front/model.py b/chaotic-openapi/chaotic_openapi/front/model.py
index 73799c1b9fbd..b6f17b696438 100644
--- a/chaotic-openapi/chaotic_openapi/front/model.py
+++ b/chaotic-openapi/chaotic_openapi/front/model.py
@@ -1,12 +1,11 @@
import dataclasses
import enum
from typing import Any
-from typing import Dict
-from typing import List
from typing import Optional
from typing import Union
from chaotic.front import types
+from . import base_model
class In(str, enum.Enum):
@@ -32,7 +31,7 @@ class Parameter:
name: str
in_: In
description: str
- examples: Dict[str, Any]
+ examples: dict[str, Any]
deprecated: bool
required: bool
@@ -41,18 +40,21 @@ class Parameter:
style: Style
schema: types.Schema
+ x_cpp_name: Optional[str]
+ x_query_log_mode_hide: bool
+
@dataclasses.dataclass
class MediaType:
- schema: Union[types.Schema, types.Ref]
- examples: Dict[str, Any]
+ schema: Union[types.Schema, types.Ref, None]
+ examples: dict[str, Any]
@dataclasses.dataclass
class Response:
description: str
- headers: Dict[str, Parameter]
- content: Dict[str, MediaType]
+ headers: dict[str, Parameter]
+ content: dict[str, MediaType]
@dataclasses.dataclass
@@ -87,7 +89,7 @@ class ApiKeySecurity(Security):
@dataclasses.dataclass
class Flow:
refreshUrl: str
- scopes: Dict[str, str]
+ scopes: dict[str, str]
@dataclasses.dataclass
@@ -113,7 +115,7 @@ class AuthCodeFlow(Flow):
@dataclasses.dataclass
class OAuthSecurity(Security):
- flows: List[Flow]
+ flows: list[Flow]
@dataclasses.dataclass
@@ -132,22 +134,25 @@ class Operation:
description: str
path: str
method: str
- operationId: str
- parameters: List[Parameter]
- requestBody: Union[List[RequestBody], Ref]
- responses: Dict[int, Union[Response, Ref]]
- security: List[Security]
+ operationId: Union[str, None]
+ parameters: list[Parameter]
+ requestBody: Union[list[RequestBody], Ref]
+ responses: dict[int, Union[Response, Ref]]
+ security: list[Security]
+
+ x_client_codegen: bool
+ x_middlewares: base_model.XMiddlewares
@dataclasses.dataclass
class Service:
name: str
description: str = ''
- operations: List[Operation] = dataclasses.field(default_factory=list)
-
- schemas: Dict[str, types.Schema] = dataclasses.field(default_factory=dict)
- responses: Dict[str, Response] = dataclasses.field(default_factory=dict)
- parameters: Dict[str, Parameter] = dataclasses.field(default_factory=dict)
- headers: Dict[str, Parameter] = dataclasses.field(default_factory=dict)
- requestBodies: Dict[str, List[RequestBody]] = dataclasses.field(default_factory=dict)
- security: Dict[str, Security] = dataclasses.field(default_factory=dict)
+ operations: list[Operation] = dataclasses.field(default_factory=list)
+
+ schemas: dict[str, types.Schema] = dataclasses.field(default_factory=dict)
+ responses: dict[str, Response] = dataclasses.field(default_factory=dict)
+ parameters: dict[str, Parameter] = dataclasses.field(default_factory=dict)
+ headers: dict[str, Parameter] = dataclasses.field(default_factory=dict)
+ requestBodies: dict[str, list[RequestBody]] = dataclasses.field(default_factory=dict)
+ security: dict[str, Security] = dataclasses.field(default_factory=dict)
diff --git a/chaotic-openapi/chaotic_openapi/front/openapi.py b/chaotic-openapi/chaotic_openapi/front/openapi.py
index 9e8216aa8dd2..718f8d6f60b3 100644
--- a/chaotic-openapi/chaotic_openapi/front/openapi.py
+++ b/chaotic-openapi/chaotic_openapi/front/openapi.py
@@ -1,7 +1,5 @@
import enum
from typing import Any
-from typing import Dict
-from typing import List
from typing import Optional
from typing import Union
@@ -25,7 +23,7 @@ class Info(base_model.BaseModel):
class Server(base_model.BaseModel):
url: str
description: Optional[str] = None
- variables: Dict[str, Any] = pydantic.Field(default_factory=dict)
+ variables: dict[str, Any] = pydantic.Field(default_factory=dict)
Schema = Any
@@ -54,15 +52,20 @@ class Header(base_model.BaseModel):
allowReserved: bool = False
schema_: Schema = pydantic.Field(alias='schema')
example: Any = None
- examples: Dict[str, Any] = pydantic.Field(default_factory=dict)
+ examples: dict[str, Any] = pydantic.Field(default_factory=dict)
# https://spec.openapis.org/oas/v3.0.0.html#media-type-object
class MediaType(base_model.BaseModel):
schema_: Schema = pydantic.Field(alias='schema', default=None)
example: Any = None
- examples: Dict[str, Any] = pydantic.Field(default_factory=dict)
- # encoding: Dict[str, Encoding] = {}
+ examples: dict[str, Any] = pydantic.Field(default_factory=dict)
+ # encoding: dict[str, Encoding] = {}
+
+ _model_userver_tags: list[str] = [
+ 'x-taxi-non-std-type-reason',
+ 'x-usrv-non-std-type-reason',
+ ]
# https://spec.openapis.org/oas/v3.0.0.html#reference-object
@@ -73,10 +76,15 @@ class Ref(base_model.BaseModel):
# https://spec.openapis.org/oas/v3.0.0.html#responses-object
class Response(base_model.BaseModel):
description: str
- headers: Dict[str, Union[Header, Ref]] = pydantic.Field(default_factory=dict)
- content: Dict[str, MediaType] = pydantic.Field(default_factory=dict)
+ headers: dict[str, Union[Header, Ref]] = pydantic.Field(default_factory=dict)
+ content: dict[str, MediaType] = pydantic.Field(default_factory=dict)
# TODO: links
+ def model_post_init(self, context: Any, /) -> None:
+ if 'application/json' in self.content and not self.content['application/json'].schema_:
+ # empty application/json means the same "no body"
+ del self.content['application/json']
+
class In(str, enum.Enum):
path = 'path'
@@ -85,6 +93,11 @@ class In(str, enum.Enum):
cookie = 'cookie'
+class QueryLogMode(str, enum.Enum):
+ show = 'show'
+ hide = 'hide'
+
+
# https://spec.openapis.org/oas/v3.0.0.html#parameter-object
class Parameter(base_model.BaseModel):
name: str
@@ -99,9 +112,26 @@ class Parameter(base_model.BaseModel):
allowReserved: bool = False
schema_: Schema = pydantic.Field(alias='schema')
example: Any = None
- examples: Dict[str, Any] = pydantic.Field(default_factory=dict)
+ examples: dict[str, Any] = pydantic.Field(default_factory=dict)
- # content: Dict[str, MediaType] = {}
+ # content: dict[str, MediaType] = {}
+
+ x_handler_tag: Optional[str] = pydantic.Field(
+ default=None,
+ validation_alias=pydantic.AliasChoices('x-taxi-handler-tag', 'x-usrv-handler-tag'),
+ )
+ x_cpp_name: Optional[str] = pydantic.Field(
+ default=None,
+ validation_alias=pydantic.AliasChoices('x-taxi-cpp-name', 'x-usrv-cpp-name'),
+ )
+ x_query_log_mode: QueryLogMode = pydantic.Field(
+ default=QueryLogMode.show,
+ validation_alias=pydantic.AliasChoices('x-taxi-query-log-mode', 'x-usrv-query-log-mode'),
+ )
+ x_explode_true_reason: str = pydantic.Field(
+ default='',
+ validation_alias=pydantic.AliasChoices('x-taxi-explode-true-reason', 'x-usrv-explode-true-reason'),
+ )
def model_post_init(self, context: Any, /) -> None:
super().model_post_init(context)
@@ -119,7 +149,7 @@ def model_post_init(self, context: Any, /) -> None:
# https://spec.openapis.org/oas/v3.0.0.html#request-body-object
class RequestBody(base_model.BaseModel):
description: Optional[str] = None
- content: Dict[str, MediaType]
+ content: dict[str, MediaType]
required: bool = False
@@ -138,25 +168,25 @@ class SecurityIn(str, enum.Enum):
class ImplicitFlow(base_model.BaseModel):
refreshUrl: Optional[str] = None
- scopes: Dict[str, str] = pydantic.Field(default_factory=dict)
+ scopes: dict[str, str] = pydantic.Field(default_factory=dict)
authorizationUrl: str
class PasswordFlow(base_model.BaseModel):
refreshUrl: Optional[str] = None
- scopes: Dict[str, str] = pydantic.Field(default_factory=dict)
+ scopes: dict[str, str] = pydantic.Field(default_factory=dict)
tokenUrl: str
class ClientCredFlow(base_model.BaseModel):
refreshUrl: Optional[str] = None
- scopes: Dict[str, str] = pydantic.Field(default_factory=dict)
+ scopes: dict[str, str] = pydantic.Field(default_factory=dict)
tokenUrl: str
class AuthCodeFlow(base_model.BaseModel):
refreshUrl: Optional[str] = None
- scopes: Dict[str, str] = pydantic.Field(default_factory=dict)
+ scopes: dict[str, str] = pydantic.Field(default_factory=dict)
authorizationUrl: str
tokenUrl: str
@@ -199,47 +229,65 @@ def model_post_init(self, context: Any, /) -> None:
raise ValueError(errors.missing_field_msg('openIdConnectUrl'))
-SecuritySchemes = Dict[str, Union[SecurityScheme, Ref]]
+SecuritySchemes = dict[str, Union[SecurityScheme, Ref]]
# https://spec.openapis.org/oas/v3.0.0.html#security-requirement-object
-Security = Dict[str, List[str]]
+Security = dict[str, list[str]]
# https://spec.openapis.org/oas/v3.0.0.html#components-object
class Components(base_model.BaseModel):
- schemas: Dict[str, Schema] = pydantic.Field(default_factory=dict)
- requests: Dict[str, Any] = pydantic.Field(default_factory=dict) # TODO
- responses: Dict[str, Response] = pydantic.Field(default_factory=dict)
- parameters: Dict[str, Parameter] = pydantic.Field(default_factory=dict)
- headers: Dict[str, Header] = pydantic.Field(default_factory=dict)
- requestBodies: Dict[str, RequestBody] = pydantic.Field(default_factory=dict)
+ schemas: dict[str, Schema] = pydantic.Field(default_factory=dict)
+ requests: dict[str, Any] = pydantic.Field(default_factory=dict) # TODO
+ responses: dict[str, Response] = pydantic.Field(default_factory=dict)
+ parameters: dict[str, Parameter] = pydantic.Field(default_factory=dict)
+ headers: dict[str, Header] = pydantic.Field(default_factory=dict)
+ requestBodies: dict[str, RequestBody] = pydantic.Field(default_factory=dict)
securitySchemes: SecuritySchemes = pydantic.Field(default_factory=dict)
-class XTaxiMiddlewares(base_model.BaseModel):
- tvm: bool = True
-
-
# https://spec.openapis.org/oas/v3.0.0.html#operation-object
class Operation(base_model.BaseModel):
- tags: List[str] = pydantic.Field(default_factory=list)
+ tags: list[str] = pydantic.Field(default_factory=list)
summary: Optional[str] = None
description: str = ''
externalDocs: Any = None
operationId: Optional[str] = None
- parameters: List[Union[Parameter, Ref]] = pydantic.Field(default_factory=list)
+ parameters: list[Union[Parameter, Ref]] = pydantic.Field(default_factory=list)
requestBody: Optional[Union[RequestBody, Ref]] = None
- responses: Dict[Union[str, int], Union[Response, Ref]]
+ responses: dict[Union[str, int], Union[Response, Ref]]
deprecated: bool = False
security: Optional[Security] = None
- servers: List[Server] = pydantic.Field(default_factory=list)
+ servers: list[Server] = pydantic.Field(default_factory=list)
- x_taxi_middlewares: Optional[XTaxiMiddlewares] = pydantic.Field(
+ x_taxi_middlewares: Optional[base_model.XMiddlewares] = pydantic.Field(
default=None,
- alias='x-taxi-middlewares',
+ validation_alias=pydantic.AliasChoices('x-taxi-middlewares', 'x-usrv-middlewares'),
+ )
+ x_taxi_handler_codegen: bool = pydantic.Field(
+ default=True,
+ validation_alias=pydantic.AliasChoices('x-taxi-handler-codegen', 'x-usrv-handler-codegen'),
+ )
+ x_query_log_mode: QueryLogMode = pydantic.Field(
+ default=QueryLogMode.show,
+ validation_alias=pydantic.AliasChoices('x-taxi-query-log-mode', 'x-usrv-query-log-mode'),
)
+ x_client_codegen: bool = pydantic.Field(
+ default=True,
+ validation_alias=pydantic.AliasChoices('x-taxi-client-codegen', 'x-usrv-client-codegen'),
+ )
+
+ def model_post_init(self, context: Any, /) -> None:
+ super().model_post_init(context)
+
+ if self.x_query_log_mode == QueryLogMode.hide:
+ for parameter in self.parameters:
+ if not isinstance(parameter, Parameter):
+ continue
+ if parameter.in_ == In.query:
+ parameter.x_query_log_mode = QueryLogMode.hide
# https://spec.openapis.org/oas/v3.0.0.html#path-item-object
@@ -256,8 +304,8 @@ class Path(base_model.BaseModel):
patch: Optional[Operation] = None
trace: Optional[Operation] = None
- servers: List[Server] = pydantic.Field(default_factory=list)
- parameters: List[Union[Parameter, Ref]] = pydantic.Field(default_factory=list)
+ servers: list[Server] = pydantic.Field(default_factory=list)
+ parameters: list[Union[Parameter, Ref]] = pydantic.Field(default_factory=list)
class XTaxiClientQos(base_model.BaseModel):
@@ -268,20 +316,20 @@ class XTaxiClientQos(base_model.BaseModel):
class OpenApi(base_model.BaseModel):
openapi: str = '3.0.0'
info: Optional[Info] = None
- servers: List[Server] = pydantic.Field(default_factory=list)
- paths: Dict[str, Path] = pydantic.Field(default_factory=dict)
+ servers: list[Server] = pydantic.Field(default_factory=list)
+ paths: dict[str, Path] = pydantic.Field(default_factory=dict)
components: Components = Components()
security: Security = pydantic.Field(default_factory=dict)
- tags: List[Any] = pydantic.Field(default_factory=list)
+ tags: list[Any] = pydantic.Field(default_factory=list)
externalDocs: Any = None
x_taxi_client_qos: Optional[XTaxiClientQos] = pydantic.Field(
default=None,
- alias='x-taxi-client-qos',
+ validation_alias=pydantic.AliasChoices('x-taxi-client-qos', 'x-usrv-client-qos'),
)
- x_taxi_middlewares: Optional[XTaxiMiddlewares] = pydantic.Field(
+ x_taxi_middlewares: Optional[base_model.XMiddlewares] = pydantic.Field(
default=None,
- alias='x-taxi-middlewares',
+ validation_alias=pydantic.AliasChoices('x-taxi-middlewares', 'x-usrv-middlewares'),
)
def validate_security(self, security: Optional[Security]) -> None:
diff --git a/chaotic-openapi/chaotic_openapi/front/parser.py b/chaotic-openapi/chaotic_openapi/front/parser.py
index fecc7696d962..caa831714e29 100644
--- a/chaotic-openapi/chaotic_openapi/front/parser.py
+++ b/chaotic-openapi/chaotic_openapi/front/parser.py
@@ -3,17 +3,16 @@
import typing
from typing import Any
from typing import Callable
-from typing import Dict
-from typing import List
from typing import Optional
-from typing import Tuple
from typing import Union
import pydantic
-from chaotic import cpp_names
+from chaotic import error as chaotic_error
from chaotic.front import parser as chaotic_parser
+from chaotic.front import ref
from chaotic.front import types
+from . import base_model
from . import errors
from . import model
from . import openapi
@@ -24,7 +23,10 @@
@dataclasses.dataclass
class ParserState:
service: model.Service
+ # Full filepath in filesystem as-is
full_filepath: str = ''
+ # Virtual filepath for .cpp/.hpp file generation (e.g. relative paths)
+ full_vfilepath: str = ''
class Parser:
@@ -34,8 +36,9 @@ def __init__(
) -> None:
self._state = ParserState(service=model.Service(name=name))
- def parse_schema(self, schema: dict, full_filepath: str) -> None:
+ def parse_schema(self, schema: dict, full_filepath: str, full_vfilepath: str) -> None:
self._state.full_filepath = full_filepath
+ self._state.full_vfilepath = full_vfilepath
parser = self._guess_parser(schema)
try:
parsed = parser(**schema)
@@ -82,6 +85,8 @@ def _convert_swagger_header(self, name: str, header: swagger.Header, infile_path
allowEmptyValue=False,
style=model.Style.simple,
schema=self._parse_schema(header_dict, infile_path + '/schema'),
+ x_cpp_name=None,
+ x_query_log_mode_hide=False,
)
def _convert_media_type(
@@ -99,11 +104,11 @@ def _convert_media_type(
RIGHT_SLASH_RE = re.compile('/[^/]*$')
- def _locate_ref(self, ref: str) -> str:
- if ref.startswith('#'):
- return self._state.full_filepath + ref
+ def _locate_ref(self, ref_: str) -> str:
+ if not ref.Ref(ref_).file:
+ return self._state.full_filepath + ref_
cur = re.sub(self.RIGHT_SLASH_RE, '/', self._state.full_filepath)
- return chaotic_parser.SchemaParser._normalize_ref(cur + ref)
+ return chaotic_parser.SchemaParser._normalize_ref(cur + ref_)
def _convert_openapi_parameter(
self,
@@ -122,13 +127,15 @@ def _convert_openapi_parameter(
allowEmptyValue=parameter.allowEmptyValue,
style=model.Style(parameter.style),
schema=self._parse_schema(parameter.schema_, infile_path + '/schema'),
+ x_cpp_name=parameter.x_cpp_name,
+ x_query_log_mode_hide=(parameter.x_query_log_mode == openapi.QueryLogMode.hide),
)
return p
def _is_swagger_request_body(
self,
parameter: Union[swagger.Parameter, swagger.Ref],
- global_params: Dict[str, Union[model.Parameter, List[model.RequestBody]]],
+ global_params: dict[str, Union[model.Parameter, list[model.RequestBody]]],
) -> bool:
if isinstance(parameter, swagger.Ref):
return isinstance(global_params[self._locate_ref(parameter.ref)], list)
@@ -139,14 +146,14 @@ def _convert_swagger_request_body(
self,
request_body: Union[swagger.Parameter, swagger.Ref],
infile_path: str,
- consumes: List[str] = [],
- ) -> Union[List[model.RequestBody], model.Ref]:
+ consumes: list[str] = [],
+ ) -> Union[list[model.RequestBody], model.Ref]:
if isinstance(request_body, swagger.Ref):
- ref = ref_resolver.normalize_ref(
+ ref_ = ref_resolver.normalize_ref(
self._state.full_filepath,
request_body.ref,
)
- return model.Ref(ref)
+ return model.Ref(ref_)
if request_body.in_ == swagger.In.body:
return [
@@ -160,6 +167,15 @@ def _convert_swagger_request_body(
assert request_body.in_ == swagger.In.formData
+ if request_body.type == 'file':
+ if 'multipart/form-data' not in consumes and 'application/x-www-form-urlencoded' not in consumes:
+ raise chaotic_error.BaseError(
+ full_filepath=self._state.full_filepath,
+ infile_path=infile_path,
+ schema_type='swagger',
+ msg='"consumes" must be either "multipart/form-data" or "application/x-www-form-urlencoded" for "type: file"',
+ )
+
schema = self._parse_schema(
request_body.model_dump(
by_alias=True,
@@ -167,6 +183,7 @@ def _convert_swagger_request_body(
exclude_unset=True,
),
infile_path,
+ allow_file=True,
)
return [
model.RequestBody(
@@ -190,6 +207,7 @@ def _convert_swagger_parameter(
exclude_unset=True,
),
infile_path,
+ allow_file=True,
)
style: model.Style
@@ -208,6 +226,8 @@ def _convert_swagger_parameter(
allowEmptyValue=parameter.allowEmptyValue,
style=style,
schema=schema,
+ x_cpp_name=parameter.x_cpp_name,
+ x_query_log_mode_hide=False,
)
return p
@@ -220,12 +240,15 @@ def _convert_openapi_response(
assert infile_path.count('#') <= 1
if isinstance(response, openapi.Ref):
- ref = ref_resolver.normalize_ref(
+ ref_ = ref_resolver.normalize_ref(
self._state.full_filepath,
response.ref,
)
- assert ref.count('#') == 1, ref
- return model.Ref(ref)
+
+ # validate
+ ref.Ref(ref_)
+
+ return model.Ref(ref_)
content = {}
for content_type, openapi_content in response.content.items():
@@ -243,17 +266,20 @@ def _convert_openapi_response(
)
def _convert_swagger_response(
- self, response: Union[swagger.Response, swagger.Ref], produces: List[str], infile_path: str
+ self, response: Union[swagger.Response, swagger.Ref], produces: list[str], infile_path: str
) -> Union[model.Response, model.Ref]:
assert infile_path.count('#') <= 1
if isinstance(response, swagger.Ref):
- ref = ref_resolver.normalize_ref(
+ ref_ = ref_resolver.normalize_ref(
self._state.full_filepath,
response.ref,
)
- assert ref.count('#') == 1, ref
- return model.Ref(ref)
+
+ # validate
+ ref.Ref(ref_)
+
+ return model.Ref(ref_)
if response.schema_:
schema = self._parse_schema(response.schema_, infile_path + '/schema')
@@ -275,13 +301,13 @@ def _convert_openapi_request_body(
self,
request_body: Union[openapi.RequestBody, openapi.Ref],
infile_path: str,
- ) -> Union[List[model.RequestBody], model.Ref]:
+ ) -> Union[list[model.RequestBody], model.Ref]:
if isinstance(request_body, openapi.Ref):
- ref = ref_resolver.normalize_ref(
+ ref_ = ref_resolver.normalize_ref(
self._state.full_filepath,
request_body.ref,
)
- return model.Ref(ref)
+ return model.Ref(ref_)
requestBody = []
for content_type, media_type in request_body.content.items():
@@ -298,8 +324,8 @@ def _convert_openapi_request_body(
)
return requestBody
- def _convert_openapi_flows(self, flows: openapi.OAuthFlows) -> List[model.Flow]:
- model_flows: List[model.Flow] = []
+ def _convert_openapi_flows(self, flows: openapi.OAuthFlows) -> list[model.Flow]:
+ model_flows: list[model.Flow] = []
if flows.implicit:
implicit = flows.implicit
refreshUrl = implicit.refreshUrl or ''
@@ -322,7 +348,7 @@ def _convert_openapi_flows(self, flows: openapi.OAuthFlows) -> List[model.Flow]:
return model_flows
def _convert_openapi_securuty(
- self, security_scheme: Union[openapi.SecurityScheme, openapi.Ref], flows_scopes: Optional[List[str]] = None
+ self, security_scheme: Union[openapi.SecurityScheme, openapi.Ref], flows_scopes: Optional[list[str]] = None
) -> model.Security:
if isinstance(security_scheme, openapi.Ref):
return self._state.service.security[self._locate_ref(security_scheme.ref)]
@@ -350,7 +376,7 @@ def _convert_openapi_securuty(
assert False
def _convert_swagger_security(
- self, security_def: swagger.SecurityDef, flows_scopes: Optional[List[str]] = None
+ self, security_def: swagger.SecurityDef, flows_scopes: Optional[list[str]] = None
) -> model.Security:
description = security_def.description or ''
if security_def.type == swagger.SecurityType.basic:
@@ -388,7 +414,7 @@ def _append_schema(
self,
parsed: Union[openapi.OpenApi, swagger.Swagger],
) -> None:
- components_schemas: Dict[str, Any] = {}
+ components_schemas: dict[str, Any] = {}
components_schemas_path = ''
if isinstance(parsed, openapi.OpenApi):
parsed = typing.cast(openapi.OpenApi, parsed)
@@ -414,11 +440,11 @@ def _append_schema(
security_scheme = self._convert_openapi_securuty(sec_scheme)
self._state.service.security[self._state.full_filepath + '#' + infile_path] = security_scheme
- def _convert_op_security(security: Optional[openapi.Security]) -> List[model.Security]:
+ def _convert_op_security(security: Optional[openapi.Security]) -> list[model.Security]:
if not security:
security = default_security
- securities: List[model.Security] = []
+ securities: list[model.Security] = []
for name, scopes in security.items():
securities.append(self._convert_openapi_securuty(security_schemas[name], scopes))
@@ -451,7 +477,7 @@ def _convert_op_security(security: Optional[openapi.Security]) -> List[model.Sec
# paths
for path, path_item in parsed.paths.items():
infile_path = f'/paths/[{path}]'
- path_params: Dict[Tuple[str, model.In], model.Parameter] = {}
+ path_params: dict[tuple[str, model.In], model.Parameter] = {}
for i, path_parameter in enumerate(path_item.parameters):
param = self._convert_openapi_parameter(path_parameter, infile_path + f'/parameters/{i}')
path_params[(param.name, param.in_)] = param
@@ -474,7 +500,7 @@ def _convert_op_security(security: Optional[openapi.Security]) -> List[model.Sec
consumes = parsed.consumes
# parameters
- global_params: Dict[str, Union[model.Parameter, List[model.RequestBody]]] = {}
+ global_params: dict[str, Union[model.Parameter, list[model.RequestBody]]] = {}
for name, sw_parameter in parsed.parameters.items():
if self._is_swagger_request_body(sw_parameter, global_params):
infile_path = '/requestBodies/' + name
@@ -506,11 +532,11 @@ def _convert_op_security(security: Optional[openapi.Security]) -> List[model.Sec
security_def = self._convert_swagger_security(sec_def)
self._state.service.security[self._state.full_filepath + '#' + infile_path] = security_def
- def _convert_op_security(security: Optional[swagger.Security]) -> List[model.Security]:
+ def _convert_op_security(security: Optional[swagger.Security]) -> list[model.Security]:
if not security:
security = default_security
- securities: List[model.Security] = []
+ securities: list[model.Security] = []
for name, scopes in security.items():
securities.append(self._convert_swagger_security(security_defs[name], scopes))
@@ -519,8 +545,8 @@ def _convert_op_security(security: Optional[swagger.Security]) -> List[model.Sec
# paths
for sw_path, sw_path_item in parsed.paths.items():
infile_path = f'/paths/[{sw_path}]'
- sw_path_params: Dict[Tuple[str, model.In], model.Parameter] = {}
- sw_path_body: Union[List[model.RequestBody], model.Ref] = []
+ sw_path_params: dict[tuple[str, model.In], model.Parameter] = {}
+ sw_path_body: Union[list[model.RequestBody], model.Ref] = []
for i, sw_path_parameter in enumerate(sw_path_item.parameters):
if self._is_swagger_request_body(sw_path_parameter, global_params):
sw_path_body = self._convert_swagger_request_body(
@@ -533,8 +559,8 @@ def _convert_op_security(security: Optional[swagger.Security]) -> List[model.Sec
def _convert_op_params(
op_params: swagger.Parameters,
infile_path: str,
- consumes: List[str],
- ) -> Tuple[List[model.Parameter], Union[List[model.RequestBody], model.Ref]]:
+ consumes: list[str],
+ ) -> tuple[list[model.Parameter], Union[list[model.RequestBody], model.Ref]]:
params = sw_path_params.copy()
body = sw_path_body
for i, sw_parameter in enumerate(op_params):
@@ -577,7 +603,7 @@ def _convert_op_params(
parser = chaotic_parser.SchemaParser(
config=chaotic_parser.ParserConfig(erase_prefix=''),
full_filepath=self._state.full_filepath,
- full_vfilepath=self._state.full_filepath,
+ full_vfilepath=self._state.full_vfilepath,
)
for name, schema in components_schemas.items():
infile_path = components_schemas_path + '/' + name
@@ -594,15 +620,14 @@ def _make_sure_operations_are_unique(self) -> None:
raise Exception(f'Operation {operation.method.upper()} {operation.path} is duplicated')
seen.add(new)
- @staticmethod
- def _gen_operation_id(path: str, method: str) -> str:
- return cpp_names.camel_case((path + '_' + method).replace('/', '_'))
-
- def _parse_schema(self, schema: Any, infile_path: str) -> Union[types.Schema, types.Ref]:
+ def _parse_schema(self, schema: Any, infile_path: str, allow_file=False) -> Union[types.Schema, types.Ref]:
parser = chaotic_parser.SchemaParser(
- config=chaotic_parser.ParserConfig(erase_prefix=''),
+ config=chaotic_parser.ParserConfig(
+ erase_prefix='',
+ allow_file=allow_file,
+ ),
full_filepath=self._state.full_filepath,
- full_vfilepath=self._state.full_filepath,
+ full_vfilepath=self._state.full_vfilepath,
)
parser.parse_schema(infile_path, schema)
parsed_schemas = parser.parsed_schemas()
@@ -610,13 +635,13 @@ def _parse_schema(self, schema: Any, infile_path: str) -> Union[types.Schema, ty
schema_ref = list(parsed_schemas.schemas.values())[0]
if isinstance(schema_ref, types.Ref):
- ref = types.Ref(
+ ref_ = types.Ref(
chaotic_parser.SchemaParser._normalize_ref(schema_ref.ref),
indirect=schema_ref.indirect,
self_ref=schema_ref.self_ref,
)
- ref._source_location = schema_ref._source_location # type: ignore
- return ref
+ ref_._source_location = schema_ref._source_location # type: ignore
+ return ref_
else:
return schema_ref
@@ -625,8 +650,8 @@ def _append_openapi_operation(
path: str,
method: str,
operation: Optional[openapi.Operation],
- security_converter: Callable[[Optional[openapi.Security]], List[model.Security]],
- path_params: Dict[Tuple[str, model.In], model.Parameter],
+ security_converter: Callable[[Optional[openapi.Security]], list[model.Security]],
+ path_params: dict[tuple[str, model.In], model.Parameter],
) -> None:
if not operation:
return
@@ -651,7 +676,7 @@ def _append_openapi_operation(
description=operation.description,
path=path,
method=method,
- operationId=(operation.operationId or self._gen_operation_id(path, method)),
+ operationId=operation.operationId,
parameters=list(params.values()),
requestBody=requestBody,
responses={
@@ -659,6 +684,8 @@ def _append_openapi_operation(
for status, response in operation.responses.items()
},
security=security_converter(operation.security),
+ x_middlewares=operation.x_taxi_middlewares or base_model.XMiddlewares(tvm=True),
+ x_client_codegen=operation.x_client_codegen,
)
)
@@ -667,10 +694,10 @@ def _append_swagger_operation(
path: str,
method: str,
operation: Optional[swagger.Operation],
- security_converter: Callable[[Optional[swagger.Security]], List[model.Security]],
+ security_converter: Callable[[Optional[swagger.Security]], list[model.Security]],
params_converter: Callable[
- [swagger.Parameters, str, List[str]],
- Tuple[List[model.Parameter], Union[List[model.RequestBody], model.Ref]],
+ [swagger.Parameters, str, list[str]],
+ tuple[list[model.Parameter], Union[list[model.RequestBody], model.Ref]],
],
) -> None:
if not operation:
@@ -685,7 +712,7 @@ def _append_swagger_operation(
description=operation.description,
path=path,
method=method,
- operationId=(operation.operationId or self._gen_operation_id(path, method)),
+ operationId=operation.operationId,
parameters=params,
requestBody=body,
responses={
@@ -695,6 +722,8 @@ def _append_swagger_operation(
for status, response in operation.responses.items()
},
security=security_converter(operation.security),
+ x_middlewares=operation.x_taxi_middlewares or base_model.XMiddlewares(tvm=True),
+ x_client_codegen=operation.x_client_codegen,
)
)
diff --git a/chaotic-openapi/chaotic_openapi/front/ref_resolver.py b/chaotic-openapi/chaotic_openapi/front/ref_resolver.py
index 997976c152f8..1893aa58197c 100644
--- a/chaotic-openapi/chaotic_openapi/front/ref_resolver.py
+++ b/chaotic-openapi/chaotic_openapi/front/ref_resolver.py
@@ -1,31 +1,26 @@
import collections
-import re
from typing import Any
-from typing import Dict
-from typing import List
-from typing import Tuple
from chaotic.front import parser as chaotic_parser
+from chaotic.front import ref
from chaotic.front import ref_resolver
-REF_SHRINK_RE = re.compile('/[^/]+/../')
-
-def normalize_ref(filepath: str, ref: str) -> str:
- if ref.startswith('#'):
- return filepath + ref
+def normalize_ref(filepath: str, ref_: str) -> str:
+ if not ref.Ref(ref_).file:
+ return filepath + ref_
return chaotic_parser.SchemaParser._normalize_ref(
'{}/{}'.format(
filepath.rsplit('/', 1)[0],
- ref,
+ ref_,
)
)
# Extracts list of external (non-'#ref') $refs in global form
# (e.g. 'other.yaml#ref' becomes '/path/to/other.yaml#ref')
-def _extract_refs(filepath: str, content: Any) -> List[str]:
+def _extract_refs(filepath: str, content: Any) -> list[str]:
refs = []
def visit(value: Any) -> None:
@@ -36,15 +31,16 @@ def visit(value: Any) -> None:
for item in value.values():
visit(item)
if '$ref' in value:
- ref = value['$ref']
- if not ref.startswith('#'):
- refs.append(normalize_ref(filepath, ref).split('#')[0])
+ ref_ = value['$ref']
+ reference = ref.Ref(ref_)
+ if reference.file:
+ refs.append(ref.Ref(normalize_ref(filepath, ref_)).file)
visit(content)
return refs
-def sort_openapis(contents: Dict[str, Any]) -> List[Tuple[str, Any]]:
+def sort_openapis(contents: dict[str, Any]) -> list[tuple[str, Any]]:
nodes = set()
edges = collections.defaultdict(list)
@@ -52,8 +48,8 @@ def sort_openapis(contents: Dict[str, Any]) -> List[Tuple[str, Any]]:
nodes.add(filepath)
refs = _extract_refs(filepath, content)
- for ref in refs:
- edges[filepath].append(ref)
+ for ref_ in refs:
+ edges[filepath].append(ref_)
sorted_nodes = ref_resolver.sort_dfs(nodes, edges)
return [(filepath, contents[filepath]) for filepath in sorted_nodes]
diff --git a/chaotic-openapi/chaotic_openapi/front/swagger.py b/chaotic-openapi/chaotic_openapi/front/swagger.py
index 2692ff83c229..2ce02e4c1b6a 100644
--- a/chaotic-openapi/chaotic_openapi/front/swagger.py
+++ b/chaotic-openapi/chaotic_openapi/front/swagger.py
@@ -1,7 +1,5 @@
import enum
from typing import Any
-from typing import Dict
-from typing import List
from typing import Optional
from typing import Union
@@ -44,12 +42,17 @@ class Parameter(base_model.BaseModel):
type: Optional[str] = None
format: Optional[str] = None
allowEmptyValue: bool = False
- items: Optional[Dict] = None
+ items: Optional[dict] = None
collectionFormat: Optional[str] = None
default: Any = None
# TODO: validators
+ x_cpp_name: Optional[str] = pydantic.Field(
+ default=None,
+ validation_alias=pydantic.AliasChoices('x-taxi-cpp-name', 'x-usrv-cpp-name'),
+ )
+
def model_post_init(self, context: Any, /) -> None:
if self.in_ == In.body:
if not self.schema_:
@@ -67,7 +70,7 @@ class Header(base_model.BaseModel):
description: Optional[str] = None
type: str
format: Optional[str] = None
- items: Optional[Dict] = None
+ items: Optional[dict] = None
collectionFormat: Optional[str] = None
default: Any = None
@@ -88,11 +91,11 @@ def model_post_init(self, context: Any, /) -> None:
class Response(base_model.BaseModel):
description: str
schema_: Schema = pydantic.Field(alias='schema', default=None)
- headers: Dict[str, Header] = pydantic.Field(default_factory=dict)
- examples: Dict[str, Any] = pydantic.Field(default_factory=dict)
+ headers: dict[str, Header] = pydantic.Field(default_factory=dict)
+ examples: dict[str, Any] = pydantic.Field(default_factory=dict)
-Responses = Dict[Union[str, int], Union[Response, Ref]]
+Responses = dict[Union[str, int], Union[Response, Ref]]
class SecurityType(str, enum.Enum):
@@ -122,7 +125,7 @@ class SecurityDef(base_model.BaseModel):
flow: Optional[OAuthFlow] = None
authorizationUrl: Optional[str] = None
tokenUrl: Optional[str] = None
- scopes: Dict[str, str] = pydantic.Field(default_factory=dict)
+ scopes: dict[str, str] = pydantic.Field(default_factory=dict)
def model_post_init(self, context: Any, /) -> None:
if self.type == SecurityType.apiKey:
@@ -150,26 +153,39 @@ def model_post_init(self, context: Any, /) -> None:
# https://spec.openapis.org/oas/v2.0.html#security-requirement-object
-Security = Dict[str, List[str]]
+Security = dict[str, list[str]]
-Parameters = List[Union[Parameter, Ref]]
+Parameters = list[Union[Parameter, Ref]]
# https://spec.openapis.org/oas/v2.0.html#operation-object
class Operation(base_model.BaseModel):
- tags: Optional[List[str]] = None
+ tags: Optional[list[str]] = None
summary: Optional[str] = None
description: str = ''
- externalDocs: Optional[Dict] = None
+ externalDocs: Optional[dict] = None
operationId: Optional[str] = None
- consumes: List[str] = pydantic.Field(default_factory=list)
- produces: List[str] = pydantic.Field(default_factory=list)
+ consumes: list[str] = pydantic.Field(default_factory=list)
+ produces: list[str] = pydantic.Field(default_factory=list)
parameters: Parameters = pydantic.Field(default_factory=list)
responses: Responses
- schemes: List[str] = pydantic.Field(default_factory=list)
+ schemes: list[str] = pydantic.Field(default_factory=list)
deprecated: bool = False
security: Optional[Security] = None
+ x_taxi_middlewares: Optional[base_model.XMiddlewares] = pydantic.Field(
+ default=None,
+ validation_alias=pydantic.AliasChoices('x-taxi-middlewares', 'x-usrv-middlewares'),
+ )
+ x_taxi_handler_codegen: bool = pydantic.Field(
+ default=True,
+ validation_alias=pydantic.AliasChoices('x-taxi-handler-codegen', 'x-usrv-handler-codegen'),
+ )
+ x_client_codegen: bool = pydantic.Field(
+ default=True,
+ validation_alias=pydantic.AliasChoices('x-taxi-client-codegen', 'x-usrv-client-codegen'),
+ )
+
# https://spec.openapis.org/oas/v2.0.html#paths-object
class Path(base_model.BaseModel):
@@ -183,7 +199,7 @@ class Path(base_model.BaseModel):
parameters: Parameters = pydantic.Field(default_factory=list)
-Paths = Dict[str, Path]
+Paths = dict[str, Path]
# https://spec.openapis.org/oas/v2.0.html#schema
@@ -192,14 +208,14 @@ class Swagger(base_model.BaseModel):
info: Optional[Info] = None
host: Optional[str] = None
basePath: str = ''
- schemes: List[str] = pydantic.Field(default_factory=list)
- consumes: List[str] = pydantic.Field(default_factory=list)
- produces: List[str] = pydantic.Field(default_factory=list)
+ schemes: list[str] = pydantic.Field(default_factory=list)
+ consumes: list[str] = pydantic.Field(default_factory=list)
+ produces: list[str] = pydantic.Field(default_factory=list)
paths: Paths = pydantic.Field(default_factory=dict)
- definitions: Dict[str, Schema] = pydantic.Field(default_factory=dict)
- parameters: Dict[str, Parameter] = pydantic.Field(default_factory=dict)
- responses: Dict[str, Response] = pydantic.Field(default_factory=dict)
- securityDefinitions: Dict[str, SecurityDef] = pydantic.Field(default_factory=dict)
+ definitions: dict[str, Schema] = pydantic.Field(default_factory=dict)
+ parameters: dict[str, Parameter] = pydantic.Field(default_factory=dict)
+ responses: dict[str, Response] = pydantic.Field(default_factory=dict)
+ securityDefinitions: dict[str, SecurityDef] = pydantic.Field(default_factory=dict)
security: Security = pydantic.Field(default_factory=dict)
def validate_security(self, security: Optional[Security]) -> None:
diff --git a/chaotic-openapi/chaotic_openapi/main.py b/chaotic-openapi/chaotic_openapi/main.py
index 89a524aea959..6e003096da52 100644
--- a/chaotic-openapi/chaotic_openapi/main.py
+++ b/chaotic-openapi/chaotic_openapi/main.py
@@ -1,4 +1,5 @@
import argparse
+import os
import sys
import yaml
@@ -23,7 +24,7 @@ def do_main():
# sort
contents = {}
- for file in args.file:
+ for file in args.files:
with open(file) as ifile:
content = yaml.safe_load(ifile)
contents[file] = content
@@ -32,14 +33,15 @@ def do_main():
# parse
parser = front_parser.Parser(args.name)
for file, content in sorted_contents:
- parser.parse_schema(content, file)
+ parser.parse_schema(content, file, os.path.basename(file))
# translate
spec = translator.Translator(
parser.service(),
dynamic_config=args.dynamic_config,
cpp_namespace=(args.namespace or f'clients::{args.name}'),
- include_dirs=[],
+ include_dirs=args.include_dirs or [],
+ middleware_plugins=[],
).spec()
# render
@@ -67,7 +69,14 @@ def parse_args() -> argparse.Namespace:
),
)
parser.add_argument(
- 'file',
+ '-I',
+ '--include-dir',
+ dest='include_dirs',
+ action='append',
+ help='Path to search for include files for x-usrv-cpp-type',
+ )
+ parser.add_argument(
+ 'files',
nargs='+',
help='openapi/swagger yaml/json schemas',
)
diff --git a/chaotic-openapi/include/userver/chaotic/openapi/form.hpp b/chaotic-openapi/include/userver/chaotic/openapi/form.hpp
index 537ff5997dfa..5d8b933f2ce1 100644
--- a/chaotic-openapi/include/userver/chaotic/openapi/form.hpp
+++ b/chaotic-openapi/include/userver/chaotic/openapi/form.hpp
@@ -1,5 +1,6 @@
#pragma once
+#include
#include
USERVER_NAMESPACE_BEGIN
diff --git a/chaotic-openapi/include/userver/chaotic/openapi/parameters.hpp b/chaotic-openapi/include/userver/chaotic/openapi/parameters.hpp
index e72578e00424..1ff3d86a6d21 100644
--- a/chaotic-openapi/include/userver/chaotic/openapi/parameters.hpp
+++ b/chaotic-openapi/include/userver/chaotic/openapi/parameters.hpp
@@ -29,41 +29,41 @@ inline constexpr bool kIsTrivialRawType = true;
template <>
inline constexpr bool kIsTrivialRawType = true;
-template
+template
struct TrivialParameterBase {
- using RawType = RawType_;
- using UserType = UserType_;
+ using RawType = TRawType;
+ using UserType = TUserType;
static_assert(kIsTrivialRawType);
};
-template
+template
struct TrivialParameter {
- static constexpr auto kIn = In_;
- static constexpr auto& kName = Name_;
+ static constexpr auto kIn = TIn;
+ static constexpr auto& kName = Name;
- using Base = TrivialParameterBase;
+ using Base = TrivialParameterBase;
static_assert(kIn != In::kQueryExplode);
};
-template
+template
struct ArrayParameterBase {
- static constexpr char kDelimiter = Delimiter_;
- static constexpr auto kIn = In_;
+ static constexpr char kDelimiter = Delimiter;
+ static constexpr auto kIn = TIn;
using RawType = std::vector;
- using RawItemType = RawItemType_;
- using UserType = std::vector;
- using UserItemType = UserItemType_;
+ using RawItemType = TRawItemType;
+ using UserType = std::vector;
+ using UserItemType = TUserItemType;
};
-template
+template
struct ArrayParameter {
- static constexpr auto& kName = Name_;
- static constexpr auto kIn = In_;
+ static constexpr auto& kName = Name;
+ static constexpr auto kIn = TIn;
- using Base = ArrayParameterBase;
+ using Base = ArrayParameterBase;
};
} // namespace chaotic::openapi
diff --git a/chaotic-openapi/include/userver/chaotic/openapi/parameters_read.hpp b/chaotic-openapi/include/userver/chaotic/openapi/parameters_read.hpp
index c21911619773..34d1f14abe2b 100644
--- a/chaotic-openapi/include/userver/chaotic/openapi/parameters_read.hpp
+++ b/chaotic-openapi/include/userver/chaotic/openapi/parameters_read.hpp
@@ -24,18 +24,18 @@ namespace chaotic::openapi {
*
*/
-template
+template
auto GetParameter(std::string_view name, const server::http::HttpRequest& source) {
- if constexpr (In_ == In::kPath) {
+ if constexpr (TIn == In::kPath) {
return source.GetPathArg(name);
- } else if constexpr (In_ == In::kCookie) {
+ } else if constexpr (TIn == In::kCookie) {
return source.GetCookie(std::string{name});
- } else if constexpr (In_ == In::kHeader) {
+ } else if constexpr (TIn == In::kHeader) {
return source.GetHeader(name);
- } else if constexpr (In_ == In::kQuery) {
+ } else if constexpr (TIn == In::kQuery) {
return source.GetArg(name);
} else {
- static_assert(In_ == In::kQueryExplode, "Unknown 'In'");
+ static_assert(TIn == In::kQueryExplode, "Unknown 'In'");
return source.GetArgVector(name);
}
}
@@ -78,8 +78,8 @@ void SplitByDelimiter(std::string_view str, char delimiter, utils::function_ref<
}
-template
-struct ParseParameter> {
+template
+struct ParseParameter> {
static auto Parse(std::string&& str_value) {
openapi::ParseParameter> parser;
diff --git a/chaotic-openapi/include/userver/chaotic/openapi/parameters_write.hpp b/chaotic-openapi/include/userver/chaotic/openapi/parameters_write.hpp
index 81e16cad89c6..f2ec49e854d4 100644
--- a/chaotic-openapi/include/userver/chaotic/openapi/parameters_write.hpp
+++ b/chaotic-openapi/include/userver/chaotic/openapi/parameters_write.hpp
@@ -54,32 +54,39 @@ class ParameterSinkHttpClient final : public ParameterSinkBase {
void Flush();
+ const clients::http::Headers& GetHeaders() const;
+
+ using HiddenQueryArgNamesFunc = bool (*)(std::string_view);
+
+ void SetHiddenQueryArgNamesFunc(HiddenQueryArgNamesFunc func);
+
private:
std::string url_pattern_;
clients::http::Request& request_;
clients::http::Headers headers_;
http::MultiArgs query_args_;
+ HiddenQueryArgNamesFunc hidden_query_arg_names_func_{};
std::unordered_map cookies_;
fmt::dynamic_format_arg_store path_vars_;
};
void ValidatePathVariableValue(std::string_view name, std::string_view value);
-template
+template
void SetParameter(Name& name, StrType&& str_value, ParameterSinkBase& dest) {
static_assert(std::is_same_v || std::is_same_v>);
- if constexpr (In == In::kPath) {
+ if constexpr (TIn == In::kPath) {
USERVER_NAMESPACE::chaotic::openapi::ValidatePathVariableValue(name, str_value);
dest.SetPath(name, std::forward(str_value));
- } else if constexpr (In == In::kCookie) {
+ } else if constexpr (TIn == In::kCookie) {
dest.SetCookie(name, std::forward(str_value));
- } else if constexpr (In == In::kHeader) {
+ } else if constexpr (TIn == In::kHeader) {
dest.SetHeader(name, std::forward(str_value));
- } else if constexpr (In == In::kQuery) {
+ } else if constexpr (TIn == In::kQuery) {
dest.SetQuery(name, std::forward(str_value));
} else {
- static_assert(In == In::kQueryExplode, "Unknown 'In'");
+ static_assert(TIn == In::kQueryExplode, "Unknown 'In'");
dest.SetMultiQuery(name, std::forward(str_value));
}
}
@@ -145,9 +152,9 @@ struct SerializeParameter
}
};
-template
-struct SerializeParameter> {
- static_assert(In_ != In::kQueryExplode);
+template
+struct SerializeParameter> {
+ static_assert(TIn != In::kQueryExplode);
std::string operator()(const UserType& item) const { return ToStrParameter(Convert(item, convert::To{})); }
diff --git a/chaotic-openapi/integration_tests/clients/body-with-array/openapi.yaml b/chaotic-openapi/integration_tests/clients/body-with-array/openapi.yaml
new file mode 100644
index 000000000000..4f47e43b77d1
--- /dev/null
+++ b/chaotic-openapi/integration_tests/clients/body-with-array/openapi.yaml
@@ -0,0 +1,18 @@
+openapi: 3.0.0
+info:
+ title: test-object client
+ version: '1.0'
+
+paths:
+ /test1:
+ post:
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ type: string
+ responses:
+ '200':
+ description: OK
diff --git a/chaotic-openapi/integration_tests/clients/empty-openapi/openapi.yaml b/chaotic-openapi/integration_tests/clients/empty-openapi/openapi.yaml
new file mode 100644
index 000000000000..30776fa4e2a5
--- /dev/null
+++ b/chaotic-openapi/integration_tests/clients/empty-openapi/openapi.yaml
@@ -0,0 +1,12 @@
+openapi: 3.0.0
+info:
+ title: test-object client
+ version: '1.0'
+
+paths:
+ /test1:
+ post:
+ responses:
+ '200':
+ description: OK
+components: {}
diff --git a/chaotic-openapi/integration_tests/clients/handler-tag/openapi.yaml b/chaotic-openapi/integration_tests/clients/handler-tag/openapi.yaml
new file mode 100644
index 000000000000..e094a1e0279f
--- /dev/null
+++ b/chaotic-openapi/integration_tests/clients/handler-tag/openapi.yaml
@@ -0,0 +1,51 @@
+openapi: 3.0.0
+info:
+ title: test-object client
+ version: '1.0'
+
+paths:
+ /test1:
+ get:
+ parameters:
+ - name: param1
+ in: query
+ required: true
+ x-usrv-handler-tag: param1
+ schema:
+ type: integer
+ - name: param2
+ in: query
+ required: true
+ x-usrv-handler-tag: param2
+ schema:
+ type: number
+ - name: param3
+ in: query
+ required: true
+ x-usrv-handler-tag: param3
+ schema:
+ type: string
+
+ - name: param4
+ in: query
+ required: false
+ x-usrv-handler-tag: param4
+ schema:
+ type: integer
+ - name: param5
+ in: query
+ required: false
+ x-usrv-handler-tag: param5
+ schema:
+ type: number
+ - name: param6
+ in: query
+ required: false
+ x-usrv-handler-tag: param6
+ schema:
+ type: string
+
+ responses:
+ '200':
+ description: OK
+components: {}
diff --git a/chaotic-openapi/integration_tests/clients/non-alphanum/openapi.yaml b/chaotic-openapi/integration_tests/clients/non-alphanum/openapi.yaml
new file mode 100644
index 000000000000..a7eadf154ba7
--- /dev/null
+++ b/chaotic-openapi/integration_tests/clients/non-alphanum/openapi.yaml
@@ -0,0 +1,24 @@
+openapi: 3.0.0
+info:
+ title: test-object client
+ version: '1.0'
+
+paths:
+ /some.method-name:
+ x-tag: test
+ post:
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: object
+ additionalProperties: false
+ properties: {}
+components:
+ schemas:
+ My.Super-Object:
+ type: object
+ additionalProperties: false
+ properties: {}
diff --git a/chaotic-openapi/integration_tests/clients/operation/non_std_type_reason.yaml b/chaotic-openapi/integration_tests/clients/operation/non_std_type_reason.yaml
new file mode 100644
index 000000000000..d9646f7d3e72
--- /dev/null
+++ b/chaotic-openapi/integration_tests/clients/operation/non_std_type_reason.yaml
@@ -0,0 +1,26 @@
+openapi: 3.0.0
+info:
+ title: test-object client
+ version: '1.0'
+
+paths:
+ /test2:
+ get:
+ operationId: MyTest2
+ requestBody:
+ content:
+ application/json:
+ x-usrv-non-std-type-reason: bla bla
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ x-usrv-non-std-type-reason: bla bla bla
+ schema:
+ type: object
+ properties: {}
+ additionalProperties: false
+components: {}
diff --git a/chaotic-openapi/integration_tests/clients/operation/openapi.yaml b/chaotic-openapi/integration_tests/clients/operation/openapi.yaml
new file mode 100644
index 000000000000..3d4e8a3b7e2a
--- /dev/null
+++ b/chaotic-openapi/integration_tests/clients/operation/openapi.yaml
@@ -0,0 +1,26 @@
+openapi: 3.0.0
+info:
+ title: test-object client
+ version: '1.0'
+
+paths:
+ /test1:
+ get:
+ operationId: MyTest
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: object
+ properties: {}
+ additionalProperties: false
+ /test3:
+ get:
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json: {}
+components: {}
diff --git a/chaotic-openapi/integration_tests/clients/parameters/openapi.yaml b/chaotic-openapi/integration_tests/clients/parameters/openapi.yaml
index 8813f946e46f..b27cf61542a5 100644
--- a/chaotic-openapi/integration_tests/clients/parameters/openapi.yaml
+++ b/chaotic-openapi/integration_tests/clients/parameters/openapi.yaml
@@ -15,6 +15,12 @@ paths:
- one
- two
- three
+ - name: class
+ x-usrv-cpp-name: myclass
+ in: query
+ required: false
+ schema:
+ type: string
requestBody:
content:
application/json:
@@ -41,4 +47,41 @@ paths:
description: Some error
'500':
description: Some error
+
+ /test1/query-log-mode:
+ get:
+ x-taxi-query-log-mode: hide
+ parameters:
+ - in: query
+ name: password
+ required: true
+ schema:
+ type: string
+ - in: query
+ name: secret
+ required: false
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+
+ /test1/query-log-mode/parameter:
+ get:
+ parameters:
+ - in: query
+ name: password
+ required: true
+ x-taxi-query-log-mode: hide
+ schema:
+ type: string
+ - in: query
+ name: secret
+ required: false
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+
components: {}
diff --git a/chaotic-openapi/integration_tests/clients/response-headers/openapi.yaml b/chaotic-openapi/integration_tests/clients/response-headers/openapi.yaml
index 1ece697b68a4..fd4161df5be7 100644
--- a/chaotic-openapi/integration_tests/clients/response-headers/openapi.yaml
+++ b/chaotic-openapi/integration_tests/clients/response-headers/openapi.yaml
@@ -19,4 +19,18 @@ paths:
X-Header:
schema:
type: string
-components: {}
+ X-Integer:
+ schema:
+ type: integer
+ X-Seconds:
+ schema:
+ type: integer
+ x-taxi-cpp-type: std::chrono::seconds
+components:
+ responses:
+ ResponseX:
+ description: OK
+ headers:
+ X-Header:
+ schema:
+ type: string
diff --git a/chaotic-openapi/integration_tests/clients/swagger/swagger.yaml b/chaotic-openapi/integration_tests/clients/swagger/swagger.yaml
index 137e8471f8b4..a7a1fd19e3c3 100644
--- a/chaotic-openapi/integration_tests/clients/swagger/swagger.yaml
+++ b/chaotic-openapi/integration_tests/clients/swagger/swagger.yaml
@@ -21,6 +21,42 @@ paths:
'404':
$ref: '#/responses/Response404'
+ /test1/no-tvm:
+ post:
+ x-usrv-middlewares:
+ tvm: false
+ responses:
+ '200':
+ description: OK
+
+ /test1/body:
+ get:
+ consumes:
+ - application/json
+ produces:
+ - application/json
+ parameters:
+ - in: body
+ name: body
+ required: true
+ schema:
+ type: string
+ responses:
+ '200':
+ description: OK
+
+ /test1/file:
+ post:
+ consumes:
+ - multipart/form-data
+ parameters:
+ - in: formData
+ name: file
+ required: true
+ type: file
+ responses:
+ '200':
+ description: OK
parameters:
X-Header-456:
diff --git a/chaotic-openapi/integration_tests/clients/test-object/openapi.yaml b/chaotic-openapi/integration_tests/clients/test-object/openapi.yaml
index df35b5aaf772..becc3c8269c6 100644
--- a/chaotic-openapi/integration_tests/clients/test-object/openapi.yaml
+++ b/chaotic-openapi/integration_tests/clients/test-object/openapi.yaml
@@ -37,6 +37,9 @@ components:
Type:
type: object
properties:
+ bar:
+ type: integer
+ x-usrv-cpp-type: unsigned
baz:
type: string
additionalProperties: false
diff --git a/chaotic-openapi/integration_tests/src/operation_test.cpp b/chaotic-openapi/integration_tests/src/operation_test.cpp
new file mode 100644
index 000000000000..4368fd3d190e
--- /dev/null
+++ b/chaotic-openapi/integration_tests/src/operation_test.cpp
@@ -0,0 +1,28 @@
+#include
+
+#include
+
+#include
+
+USERVER_NAMESPACE_BEGIN
+
+namespace {
+
+UTEST(Operation, OperationId) {
+ auto http_client = utest::CreateHttpClient();
+ ::clients::operation::ClientImpl client(
+ {
+ "",
+ },
+ *http_client
+ );
+
+ if (false) {
+ // Dead code intentionally
+ [[maybe_unused]] ::clients::operation::mytest::Response200 response = client.MyTest({});
+ }
+}
+
+} // namespace
+
+USERVER_NAMESPACE_END
diff --git a/chaotic-openapi/integration_tests/src/parameters_test.cpp b/chaotic-openapi/integration_tests/src/parameters_test.cpp
new file mode 100644
index 000000000000..1b347ebca618
--- /dev/null
+++ b/chaotic-openapi/integration_tests/src/parameters_test.cpp
@@ -0,0 +1,20 @@
+#include
+
+#include
+
+USERVER_NAMESPACE_BEGIN
+
+namespace {
+
+namespace client = ::clients::parameters::test1::post;
+
+UTEST(Parameters, CppName) {
+ client::Request request;
+
+ // compilation test
+ request.myclass = "123";
+}
+
+} // namespace
+
+USERVER_NAMESPACE_END
diff --git a/chaotic-openapi/integration_tests/src/requests_test.cpp b/chaotic-openapi/integration_tests/src/requests_test.cpp
index 632802933cab..99980d1ac1c0 100644
--- a/chaotic-openapi/integration_tests/src/requests_test.cpp
+++ b/chaotic-openapi/integration_tests/src/requests_test.cpp
@@ -5,8 +5,11 @@
#include
#include
#include
+#include
+#include
#include
+#include
USERVER_NAMESPACE_BEGIN
@@ -96,6 +99,43 @@ UTEST(RequestsMultipleContentTypes, OctetStream) {
auto response = request.perform();
EXPECT_EQ(response->status_code(), 200);
}
+
+class RequestsQueryLogMode : public utest::LogCaptureFixture<> {};
+
+UTEST_F(RequestsQueryLogMode, HideOperation) {
+ const utest::HttpServerMock http_server([&](const utest::HttpServerMock::HttpRequest&) {
+ return utest::HttpServerMock::HttpResponse{200};
+ });
+ auto http_client_ptr = utest::CreateHttpClient();
+ auto request = http_client_ptr->CreateRequest();
+
+ namespace client = ::clients::parameters::test1_query_log_mode::get;
+ client::SerializeRequest({client::Request{"foo", "bar"}}, http_server.GetBaseUrl(), request);
+ auto response = request.perform();
+
+ EXPECT_EQ(response->status_code(), 200);
+
+ auto text = GetLogCapture().GetAll().back().GetTag("http_url");
+ EXPECT_TRUE(utils::text::EndsWith(text, "test1/query-log-mode?password=***&secret=***"));
+}
+
+UTEST_F(RequestsQueryLogMode, HideParameter) {
+ const utest::HttpServerMock http_server([&](const utest::HttpServerMock::HttpRequest&) {
+ return utest::HttpServerMock::HttpResponse{200};
+ });
+ auto http_client_ptr = utest::CreateHttpClient();
+ auto request = http_client_ptr->CreateRequest();
+
+ namespace client = ::clients::parameters::test1_query_log_mode_parameter::get;
+ client::SerializeRequest({client::Request{"foo", "bar"}}, http_server.GetBaseUrl(), request);
+ auto response = request.perform();
+
+ EXPECT_EQ(response->status_code(), 200);
+
+ auto text = GetLogCapture().GetAll().back().GetTag("http_url");
+ EXPECT_TRUE(utils::text::EndsWith(text, "test1/query-log-mode/parameter?password=***&secret=bar")) << text;
+}
+
} // namespace
USERVER_NAMESPACE_END
diff --git a/chaotic-openapi/integration_tests/src/responses_test.cpp b/chaotic-openapi/integration_tests/src/responses_test.cpp
index 2a67d547f5bf..6e0372f49f54 100644
--- a/chaotic-openapi/integration_tests/src/responses_test.cpp
+++ b/chaotic-openapi/integration_tests/src/responses_test.cpp
@@ -190,6 +190,8 @@ UTEST(ResponsesMultipleContentType, HeaderParse) {
r.body = R"({})";
r.headers[std::string{"Content-Type"}] = "application/json";
r.headers[std::string{"X-Header"}] = "string";
+ r.headers[std::string{"X-Integer"}] = "42";
+ r.headers[std::string{"X-Seconds"}] = "100";
return r;
});
auto http_client = utest::CreateHttpClient();
@@ -198,6 +200,8 @@ UTEST(ResponsesMultipleContentType, HeaderParse) {
namespace client = ::clients::response_headers::test1::post;
auto response = client::ParseResponse(*http_response);
EXPECT_EQ(response.X_Header, "string");
+ EXPECT_EQ(response.X_Integer, 42);
+ EXPECT_EQ(response.X_Seconds, 100);
}
} // namespace
diff --git a/chaotic-openapi/src/chaotic/openapi/parameters_write.cpp b/chaotic-openapi/src/chaotic/openapi/parameters_write.cpp
index 2d36488072be..a198cde0fdf7 100644
--- a/chaotic-openapi/src/chaotic/openapi/parameters_write.cpp
+++ b/chaotic-openapi/src/chaotic/openapi/parameters_write.cpp
@@ -1,11 +1,34 @@
#include
+#include
+
#include
+#include
+
USERVER_NAMESPACE_BEGIN
namespace chaotic::openapi {
+namespace {
+
+constexpr std::string_view kMask = "***";
+
+auto MaskQueryMultiArgs(const http::MultiArgs& args, ParameterSinkHttpClient::HiddenQueryArgNamesFunc func) {
+ using Pair = std::pair;
+
+ auto masked = args | boost::adaptors::transformed([&func](const auto& pair) {
+ const auto& [name, value] = pair;
+ if (func(name))
+ return Pair(name, kMask);
+ else
+ return Pair(name, value);
+ });
+ return utils::AsContainer>(std::move(masked));
+}
+
+} // namespace
+
ParameterSinkHttpClient::ParameterSinkHttpClient(clients::http::Request& request, std::string&& url_pattern)
: url_pattern_(std::move(url_pattern)), request_(request) {}
@@ -35,10 +58,21 @@ void ParameterSinkHttpClient::SetMultiQuery(std::string_view name, std::vector')
+ parser.parse_schema(schema, '', '')
service = parser.service()
tr = translator.Translator(
@@ -15,6 +15,7 @@ def func(schema):
dynamic_config='',
cpp_namespace='test_namespace',
include_dirs=[],
+ middleware_plugins=[],
)
return tr.spec()
diff --git a/chaotic-openapi/tests/back/test_headers.py b/chaotic-openapi/tests/back/test_headers.py
index 89931db57b26..8e1279c1ef31 100644
--- a/chaotic-openapi/tests/back/test_headers.py
+++ b/chaotic-openapi/tests/back/test_headers.py
@@ -37,6 +37,7 @@ def test_headers(translate_single_schema):
types.Operation(
method='GET',
path='/',
+ operation_id=None,
request_bodies=[],
responses=[
types.Response(
@@ -59,6 +60,7 @@ def test_headers(translate_single_schema):
),
parser='openapi::TrivialParameter',
required=False,
+ query_log_mode_hide=False,
)
],
body={},
@@ -108,6 +110,7 @@ def test_header_ref(translate_single_schema):
types.Operation(
method='GET',
path='/',
+ operation_id=None,
request_bodies=[],
responses=[
types.Response(
@@ -129,6 +132,7 @@ def test_header_ref(translate_single_schema):
),
parser='openapi::TrivialParameter',
required=False,
+ query_log_mode_hide=False,
),
],
)
diff --git a/chaotic-openapi/tests/back/test_parameters.py b/chaotic-openapi/tests/back/test_parameters.py
index 0fdfaec0c239..d8af7a024076 100644
--- a/chaotic-openapi/tests/back/test_parameters.py
+++ b/chaotic-openapi/tests/back/test_parameters.py
@@ -36,6 +36,7 @@ def test_parameters(translate_single_schema):
types.Operation(
method='GET',
path='/',
+ operation_id=None,
parameters=[
types.Parameter(
description='parameter description',
@@ -54,6 +55,7 @@ def test_parameters(translate_single_schema):
),
parser='openapi::TrivialParameter',
required=False,
+ query_log_mode_hide=False,
)
],
request_bodies=[],
@@ -100,6 +102,7 @@ def test_parameters_ref(translate_single_schema):
types.Operation(
method='GET',
path='/',
+ operation_id=None,
parameters=[
types.Parameter(
description='parameter description',
@@ -118,6 +121,7 @@ def test_parameters_ref(translate_single_schema):
),
parser='openapi::TrivialParameter',
required=False,
+ query_log_mode_hide=False,
)
],
request_bodies=[],
@@ -164,6 +168,7 @@ def test_parameters_schemas_ref(translate_single_schema):
types.Operation(
method='GET',
path='/',
+ operation_id=None,
parameters=[
types.Parameter(
description='parameter description',
@@ -198,6 +203,7 @@ def test_parameters_schemas_ref(translate_single_schema):
),
parser='openapi::TrivialParameter',
required=False,
+ query_log_mode_hide=False,
)
],
request_bodies=[],
@@ -309,6 +315,6 @@ def test_parameters_too_complex_schema(translate_single_schema):
Unhandled error while processing
Path "/paths/[/]/get/parameters/0/schema", Format "jsonschema"
Error:
-Unsupported parameter type
+Unsupported parameter type for parameter "param"
==============================================================="""
)
diff --git a/chaotic-openapi/tests/back/test_request_body.py b/chaotic-openapi/tests/back/test_request_body.py
index 948931f40ebe..871deac6603d 100644
--- a/chaotic-openapi/tests/back/test_request_body.py
+++ b/chaotic-openapi/tests/back/test_request_body.py
@@ -35,6 +35,7 @@ def test_request_body(translate_single_schema):
types.Operation(
method='GET',
path='/',
+ operation_id=None,
request_bodies=[
types.Body(
content_type='application/json',
@@ -91,6 +92,7 @@ def test_request_body_ref(translate_single_schema):
types.Operation(
method='GET',
path='/',
+ operation_id=None,
request_bodies=[
types.Body(
content_type='application/json',
@@ -140,6 +142,7 @@ def test_request_body_nonrequired(translate_single_schema):
types.Operation(
method='GET',
path='/',
+ operation_id=None,
request_bodies=[
types.Body(
content_type='application/json',
diff --git a/chaotic-openapi/tests/back/test_responses.py b/chaotic-openapi/tests/back/test_responses.py
index cf2264ae88bf..f1f85b61fa31 100644
--- a/chaotic-openapi/tests/back/test_responses.py
+++ b/chaotic-openapi/tests/back/test_responses.py
@@ -36,6 +36,7 @@ def test_response(translate_single_schema):
types.Operation(
method='GET',
path='/',
+ operation_id=None,
request_bodies=[],
responses=[
types.Response(
@@ -97,6 +98,7 @@ def test_response_ref(translate_single_schema):
types.Operation(
method='GET',
path='/',
+ operation_id=None,
request_bodies=[],
responses=[
types.Response(
diff --git a/chaotic-openapi/tests/front/conftest.py b/chaotic-openapi/tests/front/conftest.py
index 58561e15e228..1a662dfaec78 100644
--- a/chaotic-openapi/tests/front/conftest.py
+++ b/chaotic-openapi/tests/front/conftest.py
@@ -6,7 +6,7 @@
def simple_parser():
def _simple_parser(schema):
parser = front_parser.Parser('test')
- parser.parse_schema(schema, '')
+ parser.parse_schema(schema, '', '')
return parser.service()
return _simple_parser
diff --git a/chaotic-openapi/tests/front/test_openapi.py b/chaotic-openapi/tests/front/test_openapi.py
index a1d0677d471d..fb18a7aa760b 100644
--- a/chaotic-openapi/tests/front/test_openapi.py
+++ b/chaotic-openapi/tests/front/test_openapi.py
@@ -1,3 +1,4 @@
+from chaotic_openapi.front import base_model
from chaotic_openapi.front import model
import pytest
@@ -42,7 +43,7 @@ def test_openapi_body_schema(simple_parser):
description='',
path='/',
method='get',
- operationId='Get',
+ operationId=None,
parameters=[],
requestBody=[
model.RequestBody(
@@ -53,6 +54,8 @@ def test_openapi_body_schema(simple_parser):
],
responses={},
security=[],
+ x_middlewares=base_model.XMiddlewares(tvm=True),
+ x_client_codegen=True,
)
],
)
@@ -140,7 +143,7 @@ def test_openapi_security(simple_parser):
description='',
path='/',
method='get',
- operationId='Get',
+ operationId=None,
parameters=[],
responses={},
requestBody=[],
@@ -163,12 +166,14 @@ def test_openapi_security(simple_parser):
],
),
],
+ x_middlewares=base_model.XMiddlewares(tvm=True),
+ x_client_codegen=True,
),
model.Operation(
description='',
path='/',
method='post',
- operationId='Post',
+ operationId=None,
parameters=[],
responses={},
requestBody=[],
@@ -191,12 +196,14 @@ def test_openapi_security(simple_parser):
],
),
],
+ x_middlewares=base_model.XMiddlewares(tvm=True),
+ x_client_codegen=True,
),
model.Operation(
description='',
path='/',
method='put',
- operationId='Put',
+ operationId=None,
parameters=[],
responses={},
requestBody=[],
@@ -219,6 +226,8 @@ def test_openapi_security(simple_parser):
],
),
],
+ x_middlewares=base_model.XMiddlewares(tvm=True),
+ x_client_codegen=True,
),
],
)
@@ -309,6 +318,8 @@ def test_openapi_parameters(simple_parser):
examples={},
deprecated=False,
allowEmptyValue=False,
+ x_cpp_name=None,
+ x_query_log_mode_hide=False,
)
},
operations=[
@@ -316,10 +327,12 @@ def test_openapi_parameters(simple_parser):
description='',
path='/',
method='get',
- operationId='Get',
+ operationId=None,
responses={},
requestBody=[],
security=[],
+ x_middlewares=base_model.XMiddlewares(tvm=True),
+ x_client_codegen=True,
parameters=[
model.Parameter(
name='pamparam1',
@@ -331,6 +344,8 @@ def test_openapi_parameters(simple_parser):
examples={},
deprecated=False,
allowEmptyValue=False,
+ x_cpp_name=None,
+ x_query_log_mode_hide=False,
),
model.Parameter(
name='pamparam2',
@@ -342,6 +357,8 @@ def test_openapi_parameters(simple_parser):
examples={},
deprecated=False,
allowEmptyValue=False,
+ x_cpp_name=None,
+ x_query_log_mode_hide=False,
),
model.Parameter(
name='pamparam2',
@@ -353,6 +370,8 @@ def test_openapi_parameters(simple_parser):
examples={},
deprecated=False,
allowEmptyValue=False,
+ x_cpp_name=None,
+ x_query_log_mode_hide=False,
),
],
)
diff --git a/chaotic-openapi/tests/front/test_swagger.py b/chaotic-openapi/tests/front/test_swagger.py
index 353949c529e3..47d3180638e9 100644
--- a/chaotic-openapi/tests/front/test_swagger.py
+++ b/chaotic-openapi/tests/front/test_swagger.py
@@ -1,5 +1,8 @@
+from chaotic_openapi.front import base_model
from chaotic_openapi.front import model
+import pytest
+from chaotic import error
from chaotic.front import types
@@ -43,7 +46,7 @@ def test_swagger_body_schema(simple_parser):
description='',
path='/',
method='get',
- operationId='Get',
+ operationId=None,
parameters=[],
requestBody=[
model.RequestBody(
@@ -54,6 +57,8 @@ def test_swagger_body_schema(simple_parser):
],
responses={},
security=[],
+ x_middlewares=base_model.XMiddlewares(tvm=True),
+ x_client_codegen=True,
)
],
)
@@ -118,6 +123,8 @@ def test_swagger_responses(simple_parser):
allowEmptyValue=False,
style=model.Style.simple,
schema=types.String(),
+ x_cpp_name=None,
+ x_query_log_mode_hide=False,
)
},
content={
@@ -137,7 +144,7 @@ def test_swagger_responses(simple_parser):
description='',
path='/',
method='get',
- operationId='Get',
+ operationId=None,
parameters=[],
requestBody=[],
responses={
@@ -154,6 +161,8 @@ def test_swagger_responses(simple_parser):
allowEmptyValue=False,
style=model.Style.simple,
schema=types.String(),
+ x_cpp_name=None,
+ x_query_log_mode_hide=False,
),
'X-Header-Name-2': model.Parameter(
name='X-Header-Name-2',
@@ -165,6 +174,8 @@ def test_swagger_responses(simple_parser):
allowEmptyValue=False,
style=model.Style.simple,
schema=types.Array(items=types.Integer()),
+ x_cpp_name=None,
+ x_query_log_mode_hide=False,
),
},
content={
@@ -181,6 +192,8 @@ def test_swagger_responses(simple_parser):
500: model.Ref('#/responses/500'),
},
security=[],
+ x_middlewares=base_model.XMiddlewares(tvm=True),
+ x_client_codegen=True,
)
],
)
@@ -274,7 +287,7 @@ def test_swagger_securuty(simple_parser):
description='',
path='/',
method='get',
- operationId='Get',
+ operationId=None,
parameters=[],
responses={},
requestBody=[],
@@ -302,12 +315,14 @@ def test_swagger_securuty(simple_parser):
],
),
],
+ x_middlewares=base_model.XMiddlewares(tvm=True),
+ x_client_codegen=True,
),
model.Operation(
description='',
path='/',
method='post',
- operationId='Post',
+ operationId=None,
parameters=[],
responses={},
requestBody=[],
@@ -335,12 +350,14 @@ def test_swagger_securuty(simple_parser):
],
),
],
+ x_middlewares=base_model.XMiddlewares(tvm=True),
+ x_client_codegen=True,
),
model.Operation(
description='',
path='/',
method='put',
- operationId='Put',
+ operationId=None,
parameters=[],
responses={},
requestBody=[],
@@ -368,6 +385,8 @@ def test_swagger_securuty(simple_parser):
],
),
],
+ x_middlewares=base_model.XMiddlewares(tvm=True),
+ x_client_codegen=True,
),
],
)
@@ -449,13 +468,15 @@ def test_swagger_parameters(simple_parser):
description='',
path='/',
method='get',
- operationId='Get',
+ operationId=None,
responses={},
requestBody=[
model.RequestBody(content_type='text/plain; charset=utf-8', required=True, schema=types.Number()),
model.RequestBody(content_type='application/json', required=True, schema=types.Number()),
],
security=[],
+ x_middlewares=base_model.XMiddlewares(tvm=True),
+ x_client_codegen=True,
parameters=[
model.Parameter(
name='pamparam2',
@@ -467,6 +488,8 @@ def test_swagger_parameters(simple_parser):
examples={},
deprecated=False,
allowEmptyValue=False,
+ x_cpp_name=None,
+ x_query_log_mode_hide=False,
),
model.Parameter(
name='pamparam2',
@@ -478,8 +501,73 @@ def test_swagger_parameters(simple_parser):
examples={},
deprecated=False,
allowEmptyValue=False,
+ x_cpp_name=None,
+ x_query_log_mode_hide=False,
),
],
)
],
)
+
+
+@pytest.mark.parametrize(
+ 'consumes',
+ [
+ 'multipart/form-data',
+ 'application/x-www-form-urlencoded',
+ ],
+)
+def test_swagger_file_in_body_ok(simple_parser, consumes):
+ simple_parser(
+ {
+ 'swagger': '2.0',
+ 'info': {},
+ 'paths': {
+ '/': {
+ 'get': {
+ 'consumes': [consumes],
+ 'parameters': [
+ {
+ 'name': 'pamparam',
+ 'in': 'formData',
+ 'required': True,
+ 'type': 'file',
+ },
+ ],
+ 'responses': {},
+ },
+ },
+ },
+ },
+ )
+
+
+def test_swagger_file_in_body_wrong_consumes(simple_parser):
+ with pytest.raises(error.BaseError) as exc_info:
+ simple_parser(
+ {
+ 'swagger': '2.0',
+ 'info': {},
+ 'paths': {
+ '/': {
+ 'get': {
+ 'consumes': ['application/json'],
+ 'parameters': [
+ {
+ 'name': 'pamparam',
+ 'in': 'formData',
+ 'required': True,
+ 'type': 'file',
+ },
+ ],
+ 'responses': {},
+ },
+ },
+ },
+ },
+ )
+
+ assert (
+ exc_info.value.msg
+ == '"consumes" must be either "multipart/form-data" or "application/x-www-form-urlencoded" for "type: file"'
+ )
diff --git a/chaotic/CMakeLists.txt b/chaotic/CMakeLists.txt
index 475eceb28abd..b8b749c24c7f 100644
--- a/chaotic/CMakeLists.txt
+++ b/chaotic/CMakeLists.txt
@@ -7,6 +7,7 @@ userver_module(
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}"
UTEST_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/*_test.cpp"
LINK_LIBRARIES userver::universal
+ DEPENDS universal
NO_CORE_LINK
)
@@ -24,7 +25,11 @@ if(USERVER_BUILD_TESTS)
)
add_subdirectory(integration_tests)
- add_subdirectory(golden_tests)
+
+ option(USERVER_CHAOTIC_GOLDEN_TESTS "Whether to run golden tests of chaotic" ON)
+ if (USERVER_CHAOTIC_GOLDEN_TESTS)
+ add_subdirectory(golden_tests)
+ endif()
endif()
_userver_directory_install(
diff --git a/chaotic/chaotic/back/cpp/renderer.py b/chaotic/chaotic/back/cpp/renderer.py
index c05fa3229bec..88a898b4db69 100644
--- a/chaotic/chaotic/back/cpp/renderer.py
+++ b/chaotic/chaotic/back/cpp/renderer.py
@@ -2,8 +2,6 @@
import dataclasses
import os
import pathlib
-from typing import Dict
-from typing import List
from typing import Optional
import jinja2
@@ -26,7 +24,7 @@ class CppOutputFile:
@dataclasses.dataclass
class CppOutput:
filepath_wo_ext: str
- files: List[CppOutputFile]
+ files: list[CppOutputFile]
current_namespace = '' # pylint: disable=invalid-name
@@ -76,14 +74,14 @@ def cpp_type(name: str) -> str:
return name_part
-def declaration_includes(types: List[cpp_types.CppType]) -> List[str]:
+def declaration_includes(types: list[cpp_types.CppType]) -> list[str]:
includes = set()
for type_ in types:
includes.update(set(type_.declaration_includes()))
return sorted(includes)
-def definition_includes(types: List[cpp_types.CppType]) -> List[str]:
+def definition_includes(types: list[cpp_types.CppType]) -> list[str]:
includes = set()
for type_ in types:
includes.update(set(type_.definition_includes()))
@@ -144,7 +142,7 @@ def __init__(
self,
*,
relative_to: str,
- vfilepath_to_relfilepath: Dict[str, str],
+ vfilepath_to_relfilepath: dict[str, str],
clang_format_bin: str,
parse_extra_formats: bool = False,
generate_serializer: bool = False,
@@ -165,9 +163,9 @@ def _vfilepath_to_relfilepath(self, vfilepath: str) -> str:
def extract_external_includes(
self,
- types_cpp: Dict[str, cpp_types.CppType],
+ types_cpp: dict[str, cpp_types.CppType],
ignore_filepath_wo_ext: str,
- ) -> List[str]:
+ ) -> list[str]:
result = set()
def visitor(
@@ -200,11 +198,11 @@ def filepath_to_include(self, filepath_wo_ext: str) -> str:
def render(
self,
- types: Dict[str, cpp_types.CppType],
+ types: dict[str, cpp_types.CppType],
local_pair_header=True,
pair_header: Optional[str] = None,
- ) -> List[CppOutput]:
- files: Dict[str, Dict[str, cpp_types.CppType]] = collections.defaultdict(dict)
+ ) -> list[CppOutput]:
+ files: dict[str, dict[str, cpp_types.CppType]] = collections.defaultdict(dict)
for name, type_ in types.items():
assert type_.json_schema
@@ -297,7 +295,7 @@ def render(
return output
@staticmethod
- def get_output_files(stem: str, path: str) -> List[str]:
+ def get_output_files(stem: str, path: str) -> list[str]:
return [
f'include/{path}/{stem}_fwd.hpp',
f'include/{path}/{stem}_parsers.ipp',
diff --git a/chaotic/chaotic/back/cpp/translator.py b/chaotic/chaotic/back/cpp/translator.py
index fefa06cbcc3e..4bea33ef8212 100644
--- a/chaotic/chaotic/back/cpp/translator.py
+++ b/chaotic/chaotic/back/cpp/translator.py
@@ -4,27 +4,25 @@
import pathlib
import re
from typing import Callable
-from typing import Dict
-from typing import List
from typing import NoReturn
from typing import Optional
-from typing import Set
from chaotic import cpp_names
from chaotic import error
from chaotic.back.cpp import type_name
from chaotic.back.cpp import types as cpp_types
+from chaotic.front import ref
from chaotic.front import types
@dataclasses.dataclass
class GeneratorConfig:
# vfull -> namespace
- namespaces: Dict[str, str]
+ namespaces: dict[str, str]
# infile_path -> cpp type
infile_to_name_func: Callable
# type: ignore
- include_dirs: Optional[List[str]] = dataclasses.field(
+ include_dirs: Optional[list[str]] = dataclasses.field(
# type: ignore
default_factory=list,
)
@@ -34,11 +32,11 @@ class GeneratorConfig:
@dataclasses.dataclass
class GeneratorState:
- types: Dict[str, cpp_types.CppType]
- refs: Dict[types.Schema, str] # type: ignore
- ref_objects: List[cpp_types.CppRef]
- external_types: Dict[types.Schema, cpp_types.CppType] # type: ignore
- seen_includes: Set[str]
+ types: dict[str, cpp_types.CppType]
+ refs: dict[types.Schema, str] # type: ignore
+ ref_objects: list[cpp_types.CppRef]
+ external_types: dict[types.Schema, cpp_types.CppType] # type: ignore
+ seen_includes: set[str]
NON_NAME_SYMBOL_RE = re.compile('[^_0-9a-zA-Z]')
@@ -47,11 +45,11 @@ class GeneratorState:
class FormatChooser:
- def __init__(self, types: List[cpp_types.CppType]) -> None:
+ def __init__(self, types: list[cpp_types.CppType]) -> None:
self.types = types
- self.parent: Dict[
+ self.parent: dict[
cpp_types.CppType,
- List[Optional[cpp_types.CppType]],
+ list[Optional[cpp_types.CppType]],
] = collections.defaultdict(list)
def check_for_json_onlyness(self) -> None:
@@ -117,8 +115,8 @@ def __init__(self, config: GeneratorConfig) -> None:
def generate_types(
self,
schemas: types.ResolvedSchemas,
- external_schemas: Dict[str, cpp_types.CppType] = {},
- ) -> Dict[str, cpp_types.CppType]:
+ external_schemas: dict[str, cpp_types.CppType] = {},
+ ) -> dict[str, cpp_types.CppType]:
self._state.seen_includes = set()
for cpp_type in external_schemas.values():
@@ -128,6 +126,16 @@ def generate_types(
for name, schema in schemas.schemas.items():
fq_cpp_name = self._gen_fq_cpp_name(name)
+ if fq_cpp_name in self._state.types:
+ sl1 = schema.source_location()
+ path1 = f'{sl1.filepath}#{sl1.location}'
+
+ existing_schema = self._state.types[fq_cpp_name].json_schema
+ assert existing_schema
+ sl2 = existing_schema.source_location()
+ path2 = f'{sl2.filepath}#{sl2.location}'
+ raise Exception(f'Duplicate type name: {fq_cpp_name}, generated from {path1} and {path2}')
+
self._state.refs[schema] = fq_cpp_name
self._state.types[fq_cpp_name] = self._generate_type(
type_name.TypeName(fq_cpp_name),
@@ -140,7 +148,7 @@ def generate_types(
return self._state.types
@property
- def seen_includes(self) -> Set[str]:
+ def seen_includes(self) -> set[str]:
return self._state.seen_includes
def _validate_type(self, type_: cpp_types.CppType) -> None:
@@ -149,11 +157,15 @@ def _validate_type(self, type_: cpp_types.CppType) -> None:
if type_.has_generated_user_cpp_type():
return
+ user_includes = type_.get_includes_by_cpp_type(type_.user_cpp_type)
+ for user_include in user_includes:
+ self._validate_user_include_exists(type_, user_include)
+
+ def _validate_user_include_exists(self, type_: cpp_types.CppType, user_include: str) -> None:
if self._config.include_dirs is None:
# no check at all
return
- user_include = type_.cpp_type_to_user_include_path(type_.user_cpp_type)
for include_dir in self._config.include_dirs:
path = os.path.join(include_dir, user_include)
if os.path.exists(path):
@@ -217,19 +229,19 @@ def _extract_container(self, schema: types.Schema) -> str:
return container
def fixup_refs(self) -> None:
- for ref in self._state.ref_objects:
- assert isinstance(ref.json_schema, types.Ref)
- schema = ref.json_schema.schema
+ for ref_ in self._state.ref_objects:
+ assert isinstance(ref_.json_schema, types.Ref)
+ schema = ref_.json_schema.schema
name = self._state.refs.get(schema)
if name:
orig_cpp_type = self._state.types[name]
else:
orig_cpp_type = self._state.external_types[schema]
- ref.orig_cpp_type = orig_cpp_type
- ref.indirect = ref.json_schema.indirect
- ref.self_ref = ref.json_schema.self_ref
- ref.cpp_name = name
+ ref_.orig_cpp_type = orig_cpp_type
+ ref_.indirect = ref_.json_schema.indirect
+ ref_.self_ref = ref_.json_schema.self_ref
+ ref_.cpp_name = name
def fixup_formats(self) -> None:
chooser = FormatChooser(list(self._state.types.values()))
@@ -247,9 +259,9 @@ def _generate_type(
return cpp_type
def _gen_fq_cpp_name(self, jsonschema_name: str) -> str:
- vfile, infile = jsonschema_name.split('#')
- name = self._config.infile_to_name_func(infile, pathlib.Path(vfile).stem)
- namespace = self._config.namespaces[vfile]
+ reference = ref.Ref(jsonschema_name)
+ name = self._config.infile_to_name_func(reference.fragment, pathlib.Path(reference.file).stem)
+ namespace = self._config.namespaces[reference.file]
if namespace:
return '::' + namespace + '::' + name
else:
@@ -285,7 +297,7 @@ def _gen_integer(
if 'x-enum-varnames' in schema.x_properties:
enum_names = schema.x_properties['x-enum-varnames']
- emum_items: List[cpp_types.CppIntEnumItem] = []
+ emum_items: list[cpp_types.CppIntEnumItem] = []
def to_camel_case(text: str) -> str:
words = SPLIT_RE.findall(text)
@@ -607,8 +619,8 @@ def _gen_one_of_with_discriminator(
mapping_values = schema.mapping.as_ints()
for field_value, refs in zip(schema.oneOf, mapping_values):
- for ref in refs:
- variants[ref] = self._gen_ref(
+ for ref_ in refs:
+ variants[ref_] = self._gen_ref(
type_name.TypeName(''),
field_value,
)
@@ -698,7 +710,7 @@ def _gen_ref(
name: type_name.TypeName,
schema: types.Ref,
) -> cpp_types.CppType:
- ref = cpp_types.CppRef(
+ ref_ = cpp_types.CppRef(
json_schema=schema,
nullable=False,
# stub
@@ -713,8 +725,8 @@ def _gen_ref(
indirect=False,
self_ref=False,
)
- self._state.ref_objects.append(ref)
- return ref
+ self._state.ref_objects.append(ref_)
+ return ref_
# pylint: disable=protected-access
diff --git a/chaotic/chaotic/back/cpp/type_name.py b/chaotic/chaotic/back/cpp/type_name.py
index dd7071be73b4..21970a445912 100644
--- a/chaotic/chaotic/back/cpp/type_name.py
+++ b/chaotic/chaotic/back/cpp/type_name.py
@@ -1,9 +1,8 @@
-from typing import List
from typing import Union
class TypeName:
- def __init__(self, name: Union[str, List[str]]) -> None:
+ def __init__(self, name: Union[str, list[str]]) -> None:
if isinstance(name, str):
self._components = name.split('::')
else:
diff --git a/chaotic/chaotic/back/cpp/types.py b/chaotic/chaotic/back/cpp/types.py
index 5b65341098ab..016c7092947b 100644
--- a/chaotic/chaotic/back/cpp/types.py
+++ b/chaotic/chaotic/back/cpp/types.py
@@ -2,8 +2,6 @@
import dataclasses
import itertools
from typing import Any
-from typing import Dict
-from typing import List
from typing import Optional
from typing import Union
@@ -45,13 +43,13 @@ def without_json_schema(self) -> 'CppType':
# Should return only direct subtypes, not recursively because
# jinja's generate_*() is called recursively.
- def subtypes(self) -> List['CppType']:
+ def subtypes(self) -> list['CppType']:
return []
- def declaration_includes(self) -> List[str]:
+ def declaration_includes(self) -> list[str]:
raise NotImplementedError()
- def definition_includes(self) -> List[str]:
+ def definition_includes(self) -> list[str]:
raise NotImplementedError()
def parser_type(self, ns: str, name: str) -> str:
@@ -123,26 +121,32 @@ def cpp_comment(self) -> str:
assert self.json_schema
kwargs = self.json_schema.x_properties
- description = kwargs.get('description')
+ description = (kwargs.get('title', '') + '\n' + kwargs.get('description', '')).strip()
if description:
return '// ' + description.replace('\n', '\n//')
else:
return ''
@classmethod
- def cpp_type_to_user_include_path(cls, cpp_type: str) -> str:
+ def get_includes_by_cpp_type(cls, cpp_type: str) -> list[str]:
+ includes = []
+
parts = cpp_type.split('<', 1)
cpp_type = parts[0]
if cpp_type == 'userver::utils::StrongTypedef' and parts[1][-1] == '>':
cpp_type = parts[1].split(', ', 1)[0]
+ includes.append('userver/utils/strong_typedef.hpp')
+ elif cpp_type == 'utils::StrongTypedef' and parts[1][-1] == '>':
+ # legacy uservices
+ cpp_type = parts[1].split(', ', 1)[0]
+ includes.append('userver/utils/strong_typedef.hpp')
if cpp_type.startswith('::'):
cpp_type = cpp_type[2:]
- return 'userver/chaotic/io/' + camel_to_snake_case(cpp_type.replace('::', '/')) + '.hpp'
- @classmethod
- def get_include_by_cpp_type(cls, cpp_type: str) -> List[str]:
- return [cls.cpp_type_to_user_include_path(cpp_type)]
+ includes.append('userver/chaotic/io/' + camel_to_snake_case(cpp_type.replace('::', '/')) + '.hpp')
+
+ return includes
def _primitive_parser_type(self) -> str:
raw_cpp_type = f'USERVER_NAMESPACE::chaotic::Primitive<{self.raw_cpp_type.in_global_scope()}>'
@@ -247,10 +251,10 @@ class CppPrimitiveType(CppType):
__hash__ = CppType.__hash__
- def declaration_includes(self) -> List[str]:
+ def declaration_includes(self) -> list[str]:
includes = []
if self.user_cpp_type:
- includes += self.get_include_by_cpp_type(self.user_cpp_type)
+ includes += self.get_includes_by_cpp_type(self.user_cpp_type)
assert self.json_schema
type_ = self.json_schema.type # type: ignore
@@ -258,13 +262,13 @@ def declaration_includes(self) -> List[str]:
includes.append('cstdint')
elif type_ in ('number', 'boolean'):
pass
- elif type_ == 'string':
+ elif type_ in ('string', 'file'):
includes.append('string')
else:
raise NotImplementedError(type_)
return includes
- def definition_includes(self) -> List[str]:
+ def definition_includes(self) -> list[str]:
includes = ['userver/chaotic/primitive.hpp']
if not self.validators.is_none():
includes.append('userver/chaotic/validators.hpp')
@@ -344,16 +348,16 @@ def cpp_user_name(self) -> str:
cpp_type = 'USERVER_NAMESPACE::' + cpp_type[len(USERVER_COLONCOLON) :]
return cpp_type
- def declaration_includes(self) -> List[str]:
+ def declaration_includes(self) -> list[str]:
includes = []
if self.user_cpp_type:
- includes += self.get_include_by_cpp_type(self.user_cpp_type)
- includes += self.get_include_by_cpp_type(self.format_cpp_type)
+ includes += self.get_includes_by_cpp_type(self.user_cpp_type)
+ includes += self.get_includes_by_cpp_type(self.format_cpp_type)
includes.append('string')
return includes
- def definition_includes(self) -> List[str]:
+ def definition_includes(self) -> list[str]:
includes = ['userver/chaotic/primitive.hpp']
includes.append('userver/chaotic/with_type.hpp')
return includes
@@ -406,7 +410,7 @@ def cpp_user_name(self) -> str:
else:
return self.cpp_name
- def declaration_includes(self) -> List[str]:
+ def declaration_includes(self) -> list[str]:
if self.indirect:
return ['userver/utils/box.hpp']
if self.self_ref:
@@ -414,7 +418,7 @@ def declaration_includes(self) -> List[str]:
else:
return self.orig_cpp_type.declaration_includes()
- def definition_includes(self) -> List[str]:
+ def definition_includes(self) -> list[str]:
if self.indirect:
return ['userver/chaotic/ref.hpp']
if self.self_ref:
@@ -444,21 +448,21 @@ class CppIntEnumItem:
raw_name: str
cpp_name: str
- def declaration_includes(self) -> List[str]:
+ def declaration_includes(self) -> list[str]:
return ['fmt/core.h']
@dataclasses.dataclass
class CppIntEnum(CppType):
name: str
- enums: List[CppIntEnumItem]
+ enums: list[CppIntEnumItem]
__hash__ = CppType.__hash__
- def declaration_includes(self) -> List[str]:
+ def declaration_includes(self) -> list[str]:
return ['fmt/core.h']
- def definition_includes(self) -> List[str]:
+ def definition_includes(self) -> list[str]:
return [
'cstdint',
'userver/chaotic/exception.hpp',
@@ -484,22 +488,22 @@ class CppStringEnumItem:
raw_name: str
cpp_name: str
- def declaration_includes(self) -> List[str]:
+ def declaration_includes(self) -> list[str]:
return ['fmt/core.h']
@dataclasses.dataclass
class CppStringEnum(CppType):
name: str
- enums: List[CppStringEnumItem]
+ enums: list[CppStringEnumItem]
default: Optional[EnumItemName]
__hash__ = CppType.__hash__
- def declaration_includes(self) -> List[str]:
+ def declaration_includes(self) -> list[str]:
return ['string', 'fmt/core.h']
- def definition_includes(self) -> List[str]:
+ def definition_includes(self) -> list[str]:
return [
'userver/chaotic/exception.hpp',
'userver/chaotic/primitive.hpp',
@@ -604,7 +608,7 @@ def cpp_field_parse_type(self) -> str:
@dataclasses.dataclass
class CppStruct(CppType):
- fields: Dict[str, CppStructField]
+ fields: dict[str, CppStructField]
# 'None' means 'do not generate extra member'
extra_type: Union[CppType, bool, None] = False
autodiscover_default_dict: bool = False
@@ -665,7 +669,7 @@ def parser_type(self, ns: str, name: str) -> str:
return f'USERVER_NAMESPACE::chaotic::WithType<{parser_type}, {dict_type}>'
return parser_type
- def subtypes(self) -> List[CppType]:
+ def subtypes(self) -> list[CppType]:
types = [field.schema for field in self.fields.values()]
if isinstance(self.extra_type, CppType) and not self._is_default_dict():
types.append(self.extra_type)
@@ -679,10 +683,10 @@ def extra_container(self) -> str:
kwargs.get('x-taxi-cpp-extra-type', 'std::unordered_map'),
)
- def declaration_includes(self) -> List[str]:
+ def declaration_includes(self) -> list[str]:
includes = ['optional']
if self.user_cpp_type:
- includes += self.get_include_by_cpp_type(self.user_cpp_type)
+ includes += self.get_includes_by_cpp_type(self.user_cpp_type)
for field in self.fields.values():
includes.extend(field.schema.declaration_includes())
@@ -690,7 +694,7 @@ def declaration_includes(self) -> List[str]:
includes.append('string')
if isinstance(self.extra_type, CppType):
extra_container = self.extra_container()
- includes += self.get_include_by_cpp_type(extra_container)
+ includes += self.get_includes_by_cpp_type(extra_container)
includes.extend(self.extra_type.declaration_includes())
else:
includes.append('userver/formats/json/value.hpp')
@@ -699,7 +703,7 @@ def declaration_includes(self) -> List[str]:
includes.append('userver/utils/default_dict.hpp')
return includes
- def definition_includes(self) -> List[str]:
+ def definition_includes(self) -> list[str]:
includes = [
'userver/formats/parse/common_containers.hpp',
'userver/formats/serialize/common_containers.hpp',
@@ -718,7 +722,7 @@ def definition_includes(self) -> List[str]:
if isinstance(self.extra_type, CppType):
includes.extend(self.extra_type.definition_includes())
if self._is_default_dict():
- includes += self.get_include_by_cpp_type(
+ includes += self.get_includes_by_cpp_type(
'userver::utils::DefaultDict<>',
)
return includes
@@ -767,7 +771,7 @@ def without_json_schema(self) -> 'CppArray':
tmp.items = self.items.without_json_schema()
return tmp
- def subtypes(self) -> List[CppType]:
+ def subtypes(self) -> list[CppType]:
return [self.items]
def parser_type(self, ns: str, name: str) -> str:
@@ -787,13 +791,13 @@ def parser_type(self, ns: str, name: str) -> str:
parser_type = f'USERVER_NAMESPACE::chaotic::WithType<{parser_type}, {user_cpp_type}>'
return parser_type
- def declaration_includes(self) -> List[str]:
- includes = self.get_include_by_cpp_type(self.container) + self.items.declaration_includes()
+ def declaration_includes(self) -> list[str]:
+ includes = self.get_includes_by_cpp_type(self.container) + self.items.declaration_includes()
if self.user_cpp_type:
- includes += self.get_include_by_cpp_type(self.user_cpp_type)
+ includes += self.get_includes_by_cpp_type(self.user_cpp_type)
return includes
- def definition_includes(self) -> List[str]:
+ def definition_includes(self) -> list[str]:
return [
'userver/chaotic/array.hpp',
'userver/chaotic/with_type.hpp',
@@ -812,20 +816,20 @@ def flatten(data: list) -> list:
@dataclasses.dataclass
class CppStructAllOf(CppType):
- parents: List[CppType]
+ parents: list[CppType]
KNOWN_X_PROPERTIES = ['x-usrv-cpp-type', 'x-taxi-cpp-type']
__hash__ = CppType.__hash__
- def declaration_includes(self) -> List[str]:
+ def declaration_includes(self) -> list[str]:
includes = []
if self.user_cpp_type:
- includes += self.get_include_by_cpp_type(self.user_cpp_type)
+ includes += self.get_includes_by_cpp_type(self.user_cpp_type)
includes += flatten([item.declaration_includes() for item in self.parents])
return includes
- def definition_includes(self) -> List[str]:
+ def definition_includes(self) -> list[str]:
return [
'userver/formats/common/merge.hpp',
'userver/chaotic/primitive.hpp',
@@ -834,7 +838,7 @@ def definition_includes(self) -> List[str]:
def parser_type(self, ns: str, name: str) -> str:
return self._primitive_parser_type()
- def subtypes(self) -> List[CppType]:
+ def subtypes(self) -> list[CppType]:
return self.parents
def need_dom_parser(self) -> bool:
@@ -849,16 +853,16 @@ def need_operator_eq(self) -> bool:
@dataclasses.dataclass
class CppVariant(CppType):
- variants: List[CppType]
+ variants: list[CppType]
KNOWN_X_PROPERTIES = ['x-usrv-cpp-type', 'x-taxi-cpp-type']
__hash__ = CppType.__hash__
- def declaration_includes(self) -> List[str]:
+ def declaration_includes(self) -> list[str]:
includes = []
if self.user_cpp_type:
- includes += self.get_include_by_cpp_type(self.user_cpp_type)
+ includes += self.get_includes_by_cpp_type(self.user_cpp_type)
return (
includes
+ [
@@ -869,7 +873,7 @@ def declaration_includes(self) -> List[str]:
+ flatten([item.declaration_includes() for item in self.variants])
)
- def definition_includes(self) -> List[str]:
+ def definition_includes(self) -> list[str]:
return [
'userver/chaotic/primitive.hpp',
'userver/chaotic/variant.hpp',
@@ -885,7 +889,7 @@ def parser_type(self, ns: str, name: str) -> str:
else:
return parser_type
- def subtypes(self) -> List[CppType]:
+ def subtypes(self) -> list[CppType]:
return self.variants
def need_operator_lshift(self) -> bool:
@@ -895,21 +899,21 @@ def need_operator_lshift(self) -> bool:
@dataclasses.dataclass
class CppVariantWithDiscriminator(CppType):
property_name: str
- variants: Dict[str, CppType]
+ variants: dict[str, CppType]
mapping_type: types.MappingType = types.MappingType.STR
KNOWN_X_PROPERTIES = ['x-usrv-cpp-type', 'x-taxi-cpp-type']
__hash__ = CppType.__hash__
- def declaration_includes(self) -> List[str]:
+ def declaration_includes(self) -> list[str]:
includes = ['variant', 'userver/chaotic/oneof_with_discriminator.hpp']
if self.user_cpp_type:
- includes += self.get_include_by_cpp_type(self.user_cpp_type)
+ includes += self.get_includes_by_cpp_type(self.user_cpp_type)
return includes + flatten([item.declaration_includes() for item in self.variants.values()])
- def definition_includes(self) -> List[str]:
+ def definition_includes(self) -> list[str]:
return ['userver/formats/json/serialize_variant.hpp'] + flatten([
item.definition_includes() for item in self.variants.values()
])
diff --git a/chaotic/chaotic/compilers/dynamic_config.py b/chaotic/chaotic/compilers/dynamic_config.py
index 6e37d5294174..ce6e7e0300d7 100644
--- a/chaotic/chaotic/compilers/dynamic_config.py
+++ b/chaotic/chaotic/compilers/dynamic_config.py
@@ -3,10 +3,6 @@
import pathlib
import subprocess
from typing import Any
-from typing import Dict
-from typing import List
-from typing import Set
-from typing import Tuple
import jinja2
import yaml
@@ -16,12 +12,16 @@
from chaotic.back.cpp import renderer
from chaotic.back.cpp import translator
from chaotic.back.cpp import types
+from chaotic.front import ref
from chaotic.front.types import ResolvedSchemas
from chaotic.main import generate_cpp_name_func
from chaotic.main import NameMapItem
from chaotic.main import read_schemas
-INCLUDE_DIR = str(pathlib.Path(__file__).parent.parent.parent / 'include')
+INCLUDE_DIRS = [
+ str(pathlib.Path(__file__).parent.parent.parent / 'include'),
+ str(pathlib.Path(__file__).parent.parent.parent.parent / 'universal' / 'include'),
+]
TEMPLATE_DIR = str(pathlib.Path(__file__).parent / 'dynamic_config' / 'templates')
CLANG_FORMAT_BIN = None
@@ -80,25 +80,25 @@ def enrich_jinja_env(env: jinja2.Environment) -> None:
class CompilerBase:
def __init__(self) -> None:
- self._variables_types: Dict[str, Dict[str, types.CppType]] = {}
- self._definitions: Dict[
+ self._variables_types: dict[str, dict[str, types.CppType]] = {}
+ self._definitions: dict[
str,
- Tuple[ResolvedSchemas, Dict[str, types.CppType]],
+ tuple[ResolvedSchemas, dict[str, types.CppType]],
] = {}
- self._defaults: Dict[str, Any] = {}
- self.seen_includes: Dict[str, Set[str]] = {}
+ self._defaults: dict[str, Any] = {}
+ self.seen_includes: dict[str, set[str]] = {}
- def extract_definition_names(self, filepath: str) -> List[str]:
+ def extract_definition_names(self, filepath: str) -> list[str]:
with open(filepath, 'r') as ifile:
content = yaml.load(ifile, Loader=yaml.CLoader)
return list(set(self._extract_definition_names(content)) - set(['']))
- def _extract_definition_names(self, content: Any) -> List[str]:
+ def _extract_definition_names(self, content: Any) -> list[str]:
if isinstance(content, dict):
refs = []
- ref = content.get('$ref')
- if isinstance(ref, str):
- filename = ref.split('#')[0]
+ ref_ = content.get('$ref')
+ if isinstance(ref_, str):
+ filename = ref.Ref(ref_).file
refs.append(filename)
for value in content.values():
@@ -130,7 +130,7 @@ def parse_definition(
self,
filepath: str,
name: str,
- include_dirs: List[str] = [],
+ include_dirs: list[str] = [],
) -> None:
name_lower = self.format_ns_name(name)
name_map = [NameMapItem('/([^/]+)/={0}')]
@@ -148,16 +148,16 @@ def parse_definition(
self._definitions[name] = (schemas, types)
self.seen_includes[filepath] = seen_includes
- def definitions_includes_hpp(self) -> List[str]:
+ def definitions_includes_hpp(self) -> list[str]:
types = self._collect_types()
- includes: List[str] = []
+ includes: list[str] = []
for type_ in types.values():
includes += type_.declaration_includes()
return sorted(set(includes))
- def definitions_includes_cpp(self) -> List[str]:
+ def definitions_includes_cpp(self) -> list[str]:
types = self._collect_types()
- includes: List[str] = []
+ includes: list[str] = []
for type_ in types.values():
includes += type_.definition_includes()
return sorted(set(includes))
@@ -167,7 +167,7 @@ def parse_variable(
filepath: str,
name: str,
namespace: str,
- include_dirs: List[str] = [],
+ include_dirs: list[str] = [],
) -> None:
name_lower = self.format_ns_name(name)
name_map = [
@@ -193,13 +193,13 @@ def parse_variable(
self._defaults[name] = self._read_default(filepath)
self.seen_includes[filepath] = seen_includes
- def _collect_schemas(self) -> List[ResolvedSchemas]:
+ def _collect_schemas(self) -> list[ResolvedSchemas]:
schemas = []
for def_schemas, _definitions in self._definitions.values():
schemas.append(def_schemas)
return schemas
- def _collect_types(self) -> Dict[str, types.CppType]:
+ def _collect_types(self) -> dict[str, types.CppType]:
types = {}
for _def_schemas, definitions in self._definitions.values():
types.update(definitions)
@@ -212,8 +212,8 @@ def _generate_types(
erase_prefix: str,
name_map,
fname: str,
- include_dirs: List[str],
- ) -> Tuple[ResolvedSchemas, Dict[str, types.CppType], Set[str]]:
+ include_dirs: list[str],
+ ) -> tuple[ResolvedSchemas, dict[str, types.CppType], set[str]]:
schemas = read_schemas(
erase_path_prefix=erase_prefix,
filepaths=[filepath],
@@ -227,7 +227,7 @@ def _generate_types(
)
gen = translator.Generator(
config=translator.GeneratorConfig(
- include_dirs=include_dirs + [INCLUDE_DIR],
+ include_dirs=include_dirs + INCLUDE_DIRS,
namespaces={fname: namespace},
infile_to_name_func=cpp_name_func,
autodiscover_default_dict=True,
@@ -240,22 +240,22 @@ def _generate_types(
)
return schemas, types, gen.seen_includes
- def variables_includes_hpp(self, name: str) -> List[str]:
+ def variables_includes_hpp(self, name: str) -> list[str]:
types = self._variables_types[name]
- includes: List[str] = []
+ includes: list[str] = []
for type_ in types.values():
includes += type_.declaration_includes()
return sorted(set(includes))
- def variables_includes_cpp(self, name: str) -> List[str]:
+ def variables_includes_cpp(self, name: str) -> list[str]:
types = self._variables_types[name]
- includes: List[str] = []
+ includes: list[str] = []
for type_ in types.values():
includes += type_.definition_includes()
return sorted(set(includes))
- def variables_external_includes_hpp(self, name: str) -> List[str]:
+ def variables_external_includes_hpp(self, name: str) -> list[str]:
types = self._variables_types[name]
return self.renderer_for_variable(
name,
@@ -273,8 +273,8 @@ def _jinja(self) -> jinja2.Environment:
def create_aliases(
self,
- types: Dict[str, types.CppType],
- ) -> List[Tuple[str, str]]:
+ types: dict[str, types.CppType],
+ ) -> list[tuple[str, str]]:
return []
def renderer_for_variable(
diff --git a/chaotic/chaotic/front/parser.py b/chaotic/chaotic/front/parser.py
index b7200b2dd8b4..a4592ff9448a 100644
--- a/chaotic/chaotic/front/parser.py
+++ b/chaotic/chaotic/front/parser.py
@@ -3,20 +3,22 @@
import dataclasses
import os
import re
-from typing import Dict
+from typing import Any
from typing import Generator
-from typing import List
from typing import NoReturn
from typing import Optional
from typing import Union
from chaotic import error
+from chaotic.front import ref
from chaotic.front import types
@dataclasses.dataclass(frozen=True)
class ParserConfig:
erase_prefix: str
+ # Allow type: file
+ allow_file: bool = False
@dataclasses.dataclass
@@ -24,7 +26,7 @@ class ParserState:
# Current location in file
infile_path: str
- schemas: Dict[str, types.Schema]
+ schemas: dict[str, types.Schema]
schema_type: str = 'jsonschema'
@@ -89,7 +91,7 @@ def do_parse_schema(self, input__: dict) -> Union[types.Schema, types.Ref]:
def _parse_allof(self, variants: list, input__: dict) -> types.AllOf:
raw = types.AllOfRaw(**input__)
- variables: List[types.Schema] = []
+ variables: list[types.Schema] = []
with self._path_enter('allOf') as _:
for i, variant in enumerate(variants):
with self._path_enter(str(i)) as _:
@@ -113,7 +115,10 @@ def _parse_oneof(self, variants: list, input__: dict) -> types.Schema:
with self._path_enter(str(i)) as _:
type_ = self._parse_schema(variant)
variables.append(type_)
- obj = types.OneOfWithoutDiscriminator(oneOf=variables)
+ obj = types.OneOfWithoutDiscriminator(
+ oneOf=variables,
+ nullable=raw.nullable,
+ )
obj.x_properties = raw.x_properties # type:ignore
return obj
@@ -129,7 +134,10 @@ def _parse_oneof_i(
self._raise('Not a $ref in oneOf with discriminator')
assert self._state
- dest_type = self._state.schemas.get(type_.ref)
+ dest_type: Any = type_
+ while isinstance(dest_type, types.Ref):
+ dest_type = self._state.schemas.get(dest_type.ref)
+
# TODO: fix in TAXICOMMON-8910
#
# if not dest_type:
@@ -139,7 +147,7 @@ def _parse_oneof_i(
# )
if dest_type:
if not isinstance(dest_type, types.SchemaObject):
- self._raise('oneOf $ref to non-object')
+ self._raise(f'oneOf $ref to non-object ({type(dest_type).__name__})')
if discriminator_property not in (dest_type.properties or {}):
self._raise(
f'No discriminator property "{discriminator_property}"',
@@ -150,7 +158,7 @@ def _parse_oneof_i(
def _parse_oneof_disc_mapping(
self,
user_mapping: dict,
- variables: List[types.Ref],
+ variables: list[types.Ref],
) -> types.DiscMapping:
with self._path_enter('discriminator/mapping') as _:
idx_mapping = collections.defaultdict(list)
@@ -171,11 +179,11 @@ def _parse_oneof_disc_mapping(
idx_mapping[abs_ref].append(key)
- for ref in variables:
- assert isinstance(ref, types.Ref)
- map_value = idx_mapping.pop(ref.ref, None)
+ for ref_ in variables:
+ assert isinstance(ref_, types.Ref)
+ map_value = idx_mapping.pop(ref_.ref, None)
if map_value is None:
- self._raise(f'Missing $ref in mapping: {ref.ref}')
+ self._raise(f'Missing $ref in mapping: {ref_.ref}')
mapping.append(map_value)
@@ -195,7 +203,7 @@ def _parse_oneof_w_discriminator(
discriminator_property = input_['discriminator']['propertyName']
- variables: List[types.Ref] = []
+ variables: list[types.Ref] = []
with self._path_enter('oneOf') as _:
for i, variant in enumerate(variants):
with self._path_enter(str(i)) as _:
@@ -209,11 +217,12 @@ def _parse_oneof_w_discriminator(
if user_mapping is not None:
mapping = self._parse_oneof_disc_mapping(user_mapping, variables)
else:
- mapping = types.DiscMapping(str_values=[[ref.ref.split('/')[-1]] for ref in variables], int_values=None)
+ mapping = types.DiscMapping(str_values=[[ref_.ref.split('/')[-1]] for ref_ in variables], int_values=None)
obj = types.OneOfWithDiscriminator(
oneOf=variables,
discriminator_property=discriminator_property,
mapping=mapping,
+ nullable=input_.get('nullable', False),
)
del input_['discriminator']
@@ -229,7 +238,6 @@ def _path_enter(self, path_component: str) -> Generator[None, None, None]:
if self._state.infile_path:
self._state.infile_path += '/'
self._state.infile_path += path_component
- # print(f'enter => {self._state.infile_path}')
try:
yield
@@ -240,7 +248,7 @@ def _path_enter(self, path_component: str) -> Generator[None, None, None]:
except ParserError:
raise
except Exception as exc: # pylint: disable=broad-exception-caught
- self._raise(exc.__repr__())
+ self._raise(str(exc))
else:
self._state.infile_path = old
@@ -328,30 +336,40 @@ def _parse_string(self, input_: dict) -> types.String:
fmt = None
return types.String(**input_, format=fmt)
+ def _parse_file(self, input_: dict) -> types.String:
+ if not self._config.allow_file:
+ with self._path_enter('type') as _:
+ self._raise('"file" type is not allowed')
+
+ return self._parse_string(input_)
+
REF_SHRINK_RE = re.compile('/[^/]+/\\.\\./')
REF_SHRINK_DOT_RE = re.compile('/\\./')
@staticmethod
- def _normalize_ref(ref: str) -> str:
- while SchemaParser.REF_SHRINK_RE.search(ref):
- ref = re.sub(SchemaParser.REF_SHRINK_RE, '/', ref)
- while SchemaParser.REF_SHRINK_DOT_RE.search(ref):
- ref = re.sub(SchemaParser.REF_SHRINK_DOT_RE, '/', ref)
- return ref
-
- def _make_abs_ref(self, ref: str) -> str:
+ def _normalize_ref(ref_: str) -> str:
+ ref_ = '/' + ref_ # for regex simplicity
+
+ while SchemaParser.REF_SHRINK_RE.search(ref_):
+ ref_ = re.sub(SchemaParser.REF_SHRINK_RE, '/', ref_)
+ while SchemaParser.REF_SHRINK_DOT_RE.search(ref_):
+ ref_ = re.sub(SchemaParser.REF_SHRINK_DOT_RE, '/', ref_)
+
+ return ref_[1:]
+
+ def _make_abs_ref(self, ref_: str) -> str:
assert self._state
- if ref.startswith('#'):
+ if not ref.Ref(ref_).file:
# Local $ref
- return self.full_vfilepath + ref
+ return self.full_vfilepath + ref_
else:
my_ref = '/'.join(self.full_vfilepath.split('/')[:-1])
- file, infile = ref.split('#')
- out_file = self._normalize_ref(os.path.join(my_ref, file))
- return out_file + '#' + infile
+ reference = ref.Ref(ref_)
+ out_file = self._normalize_ref(os.path.join(my_ref, reference.file))
+ return out_file + '#' + reference.fragment
- def _parse_ref(self, ref: str, input_: dict) -> types.Ref:
+ def _parse_ref(self, ref_: str, input_: dict) -> types.Ref:
assert self._state
fields = set(input_.keys())
@@ -376,7 +394,7 @@ def _parse_ref(self, ref: str, input_: dict) -> types.Ref:
self._raise(f'Unknown field(s) {list(fields)}')
with self._path_enter('$ref') as _:
- abs_ref = self._make_abs_ref(ref)
+ abs_ref = self._make_abs_ref(ref_)
ref_value = types.Ref(
ref=abs_ref,
indirect=indirect,
@@ -399,6 +417,7 @@ def parsed_schemas(self) -> types.ParsedSchemas:
'integer': SchemaParser._parse_int,
'number': SchemaParser._parse_number,
'string': SchemaParser._parse_string,
+ 'file': SchemaParser._parse_file,
'array': SchemaParser._parse_array,
'object': SchemaParser._parse_object,
}
diff --git a/chaotic/chaotic/front/ref.py b/chaotic/chaotic/front/ref.py
new file mode 100644
index 000000000000..a56548c7b9b6
--- /dev/null
+++ b/chaotic/chaotic/front/ref.py
@@ -0,0 +1,10 @@
+class Ref:
+ def __init__(self, ref: str) -> None:
+ self._ref = ref
+
+ parts = ref.split('#')
+ if len(parts) != 2:
+ raise Exception(f'Error in $ref ({ref}): there should be exactly one "#" inside')
+
+ self.file = parts[0]
+ self.fragment = parts[1]
diff --git a/chaotic/chaotic/front/ref_resolver.py b/chaotic/chaotic/front/ref_resolver.py
index 9166b82b965c..4498d97b62f5 100644
--- a/chaotic/chaotic/front/ref_resolver.py
+++ b/chaotic/chaotic/front/ref_resolver.py
@@ -1,9 +1,6 @@
import collections
from typing import Any
-from typing import Dict
-from typing import List
from typing import Optional
-from typing import Set
from chaotic import error
from chaotic.front import types
@@ -13,9 +10,9 @@ class ResolverError(Exception):
pass
-def sort_dfs(nodes: Set[str], edges: Dict[str, List[str]]) -> List[str]:
+def sort_dfs(nodes: set[str], edges: dict[str, list[str]]) -> list[str]:
visited = set()
- visiting: List[str] = []
+ visiting: list[str] = []
sorted_nodes = []
def do_node(node: str):
@@ -152,9 +149,9 @@ def _search_refs(cls, data: Any, *, inside_items: bool):
def sort_json_types(
self,
- types: Dict[str, Any],
+ types: dict[str, Any],
erase_path_prefix: str = '',
- ) -> Dict[str, Any]:
+ ) -> dict[str, Any]:
"""
Sorts not-yet-parsed schemas. Required for correct allOf/oneOf parsing.
"""
diff --git a/chaotic/chaotic/front/types.py b/chaotic/chaotic/front/types.py
index 65d36ae5ca7e..777acfa1241e 100644
--- a/chaotic/chaotic/front/types.py
+++ b/chaotic/chaotic/front/types.py
@@ -4,8 +4,6 @@
import typing
from typing import Any
from typing import Callable
-from typing import Dict
-from typing import List
from typing import Optional
from typing import TypeVar
from typing import Union
@@ -22,7 +20,7 @@ def __init__(self, field: str, msg: str) -> None:
def is_ignored_prefix(arg: str) -> bool:
if arg.startswith('x-'):
return True
- if arg in ('description', 'example'):
+ if arg in ('description', 'example', 'title'):
return True
return False
@@ -77,7 +75,7 @@ def validate_type(field_name: str, value, type_) -> None:
f'field "{field_name}" has wrong type',
)
except TypeError:
- # TODO: type=List[str]
+ # TODO: type=list[str]
pass
@@ -105,7 +103,7 @@ def __post_init__(self) -> None:
@dataclasses.dataclass
class Schema(Base):
- x_properties: Dict[str, Any] = dataclasses.field(
+ x_properties: dict[str, Any] = dataclasses.field(
init=False,
default_factory=dict,
)
@@ -170,7 +168,7 @@ class Ref(Schema):
schema: Schema = _NOT_IMPL
def __post_init__(self):
- assert self.ref.find('/../') == -1
+ assert self.ref.find('/../') == -1, self.ref
__hash__ = Schema.__hash__
@@ -181,6 +179,7 @@ class Boolean(Schema):
type: str = 'boolean'
default: Optional[bool] = None
nullable: bool = False
+ deprecated: bool = False
__hash__ = Schema.__hash__
@@ -217,8 +216,9 @@ class Integer(Schema):
exclusiveMinimum: Optional[int] = None
exclusiveMaximum: Optional[int] = None
# TODO: multipleOf
- enum: Optional[List[int]] = None
+ enum: Optional[list[int]] = None
format: Optional[IntegerFormat] = None
+ deprecated: bool = False
def __post_init__(self) -> None:
super().__post_init__()
@@ -244,6 +244,7 @@ class Number(Schema):
exclusiveMinimum: Optional[Union[float, int]] = None
exclusiveMaximum: Optional[Union[float, int]] = None
format: Optional[str] = None
+ deprecated: bool = False
# TODO: multipleOf
__hash__ = Schema.__hash__
@@ -285,11 +286,12 @@ class String(Schema):
type: str = 'string'
default: Optional[str] = None
nullable: bool = False
- enum: Optional[List[str]] = None
+ enum: Optional[list[str]] = None
pattern: Optional[str] = None
format: Optional[StringFormat] = None
minLength: Optional[int] = None
maxLength: Optional[int] = None
+ deprecated: bool = False
def __post_init__(self) -> None:
super().__post_init__()
@@ -313,6 +315,7 @@ class Array(Schema):
nullable: bool = False
minItems: Optional[int] = None
maxItems: Optional[int] = None
+ deprecated: bool = False
def visit_children(self, cb: Callable[[Schema, Schema], None]) -> None:
cb(self.items, self)
@@ -327,16 +330,17 @@ class SchemaObjectRaw:
type: str
additionalProperties: Any
properties: Optional[dict] = None
- required: Optional[List[str]] = None
+ required: Optional[list[str]] = None
nullable: bool = False
+ deprecated: bool = False
@smart_fields
@dataclasses.dataclass
class SchemaObject(Schema):
additionalProperties: Union[Schema, bool]
- properties: Dict[str, Schema]
- required: Optional[List[str]] = None
+ properties: dict[str, Schema]
+ required: Optional[list[str]] = None
nullable: bool = False
def __post_init__(self) -> None:
@@ -364,7 +368,7 @@ def visit_children(self, cb: Callable[[Schema, Schema], None]) -> None:
@smart_fields
@dataclasses.dataclass
class AllOf(Schema):
- allOf: List[Schema] # type: ignore
+ allOf: list[Schema] # type: ignore
nullable: bool = False
def visit_children(self, cb: Callable[[Schema, Schema], None]) -> None:
@@ -380,6 +384,7 @@ def visit_children(self, cb: Callable[[Schema, Schema], None]) -> None:
class AllOfRaw:
allOf: list # type:ignore
nullable: bool = False
+ deprecated: bool = False
def __post_init__(self) -> None:
if not self.allOf:
@@ -389,7 +394,7 @@ def __post_init__(self) -> None:
@smart_fields
@dataclasses.dataclass
class OneOfWithoutDiscriminator(Schema):
- oneOf: List[Schema] # type:ignore
+ oneOf: list[Schema] # type:ignore
nullable: bool = False
def visit_children(self, cb: Callable[[Schema, Schema], None]) -> None:
@@ -408,14 +413,14 @@ class MappingType(enum.Enum):
@dataclasses.dataclass
class DiscMapping:
# only one list must be not none
- str_values: Optional[List[List[str]]] = None
- int_values: Optional[List[List[int]]] = None
+ str_values: Optional[list[list[str]]] = None
+ int_values: Optional[list[list[int]]] = None
def append(self, value: list):
if self.str_values is not None:
- self.str_values.append(typing.cast(List[str], value))
+ self.str_values.append(typing.cast(list[str], value))
elif self.int_values is not None:
- self.int_values.append(typing.cast(List[int], value))
+ self.int_values.append(typing.cast(list[int], value))
def enable_str(self):
self.str_values = []
@@ -431,11 +436,11 @@ def get_type(self) -> MappingType:
return MappingType.STR
- def as_strs(self) -> List[List[str]]:
- return typing.cast(List[List[str]], self.str_values)
+ def as_strs(self) -> list[list[str]]:
+ return typing.cast(list[list[str]], self.str_values)
- def as_ints(self) -> List[List[int]]:
- return typing.cast(List[List[int]], self.int_values)
+ def as_ints(self) -> list[list[int]]:
+ return typing.cast(list[list[int]], self.int_values)
def is_int(self):
return self.int_values is not None
@@ -447,7 +452,7 @@ def is_str(self):
@smart_fields
@dataclasses.dataclass
class OneOfWithDiscriminator(Schema):
- oneOf: List[Ref] # type:ignore
+ oneOf: list[Ref] # type:ignore
discriminator_property: Optional[str] = None
mapping: DiscMapping = dataclasses.field(default_factory=DiscMapping)
nullable: bool = False
@@ -464,7 +469,7 @@ def visit_children(self, cb: Callable[[Schema, Schema], None]) -> None:
@dataclasses.dataclass
class OneOfDiscriminatorRaw:
propertyName: str # type:ignore
- mapping: Optional[Dict[str, str]] = None
+ mapping: Optional[dict[str, str]] = None
@smart_fields
@@ -472,6 +477,8 @@ class OneOfDiscriminatorRaw:
class OneOfRaw:
oneOf: list # type:ignore
discriminator: Optional[dict] = None
+ nullable: bool = False
+ deprecated: bool = False
def __post_init__(self) -> None:
if not self.oneOf:
@@ -480,12 +487,12 @@ def __post_init__(self) -> None:
@dataclasses.dataclass
class ParsedSchemas:
- schemas: Dict[str, Schema] = dataclasses.field(
+ schemas: dict[str, Schema] = dataclasses.field(
default_factory=collections.OrderedDict,
)
@staticmethod
- def merge(schemas: List['ParsedSchemas']) -> 'ParsedSchemas':
+ def merge(schemas: list['ParsedSchemas']) -> 'ParsedSchemas':
result = ParsedSchemas()
for schema in schemas:
result.schemas.update(schema.schemas)
@@ -494,4 +501,4 @@ def merge(schemas: List['ParsedSchemas']) -> 'ParsedSchemas':
@dataclasses.dataclass
class ResolvedSchemas:
- schemas: Dict[str, Schema]
+ schemas: dict[str, Schema]
diff --git a/chaotic/chaotic/main.py b/chaotic/chaotic/main.py
index 271779187382..c8c56bc473c8 100644
--- a/chaotic/chaotic/main.py
+++ b/chaotic/chaotic/main.py
@@ -6,8 +6,6 @@
import sys
from typing import Any
from typing import Callable
-from typing import Dict
-from typing import List
from typing import Optional
import yaml
@@ -115,7 +113,7 @@ def parse_args() -> argparse.Namespace:
def generate_cpp_name_func(
- name_map: List[NameMapItem],
+ name_map: list[NameMapItem],
erase_prefix: str,
) -> Callable:
def cpp_name_func(schema_name: str, stem: str) -> str:
@@ -129,7 +127,7 @@ def cpp_name_func(schema_name: str, stem: str) -> str:
return cpp_name_func
-def vfilepath_from_filepath(filepath: str, file_map: List[NameMapItem]) -> str:
+def vfilepath_from_filepath(filepath: str, file_map: list[NameMapItem]) -> str:
for item in file_map:
vfilepath = item.match(filepath, stem=pathlib.Path(filepath).stem)
if vfilepath:
@@ -168,8 +166,8 @@ def traverse_dfs(path: str, data: Any):
def extract_schemas_to_scan(
inp: dict,
- name_map: List[NameMapItem],
-) -> Dict[str, Any]:
+ name_map: list[NameMapItem],
+) -> dict[str, Any]:
schemas = []
gen = traverse_dfs('/', inp)
@@ -192,10 +190,10 @@ def extract_schemas_to_scan(
def read_schemas(
erase_path_prefix: str,
- filepaths: List[str],
+ filepaths: list[str],
name_map,
file_map,
- dependencies: List[types.ResolvedSchemas] = [],
+ dependencies: list[types.ResolvedSchemas] = [],
) -> types.ResolvedSchemas:
config = front_parser.ParserConfig(erase_prefix=erase_path_prefix)
rr = ref_resolver.RefResolver()
diff --git a/chaotic/include/userver/chaotic/io/boost/uuids/uuid.hpp b/chaotic/include/userver/chaotic/io/boost/uuids/uuid.hpp
index 8365631f7e34..167ffac411e4 100644
--- a/chaotic/include/userver/chaotic/io/boost/uuids/uuid.hpp
+++ b/chaotic/include/userver/chaotic/io/boost/uuids/uuid.hpp
@@ -1,6 +1,7 @@
#pragma once
#include
+#include
#include
diff --git a/chaotic/include/userver/chaotic/io/std/int32_t.hpp b/chaotic/include/userver/chaotic/io/std/int32_t.hpp
new file mode 100644
index 000000000000..d0879af92bfb
--- /dev/null
+++ b/chaotic/include/userver/chaotic/io/std/int32_t.hpp
@@ -0,0 +1,26 @@
+#pragma once
+
+#include
+
+#include
+
+#include
+#include
+
+USERVER_NAMESPACE_BEGIN
+
+namespace chaotic::convert {
+
+template
+std::enable_if_t, T> Convert(const std::int32_t& value, To) {
+ return utils::numeric_cast(value);
+}
+
+template
+std::enable_if_t, std::int32_t> Convert(const T& value, To) {
+ return utils::numeric_cast(value);
+}
+
+} // namespace chaotic::convert
+
+USERVER_NAMESPACE_END
diff --git a/chaotic/include/userver/chaotic/io/std/int64_t.hpp b/chaotic/include/userver/chaotic/io/std/int64_t.hpp
new file mode 100644
index 000000000000..d0fc56a02095
--- /dev/null
+++ b/chaotic/include/userver/chaotic/io/std/int64_t.hpp
@@ -0,0 +1,26 @@
+#pragma once
+
+#include
+
+#include
+
+#include
+#include
+
+USERVER_NAMESPACE_BEGIN
+
+namespace chaotic::convert {
+
+template
+std::enable_if_t, T> Convert(const std::int64_t& value, To) {
+ return utils::numeric_cast(value);
+}
+
+template
+std::enable_if_t, std::int64_t> Convert(const T& value, To) {
+ return utils::numeric_cast(value);
+}
+
+} // namespace chaotic::convert
+
+USERVER_NAMESPACE_END
diff --git a/chaotic/integration_tests/CMakeLists.txt b/chaotic/integration_tests/CMakeLists.txt
index 34c7fa4f01f8..91ea8afcf578 100644
--- a/chaotic/integration_tests/CMakeLists.txt
+++ b/chaotic/integration_tests/CMakeLists.txt
@@ -18,6 +18,7 @@ userver_target_generate_chaotic(
INCLUDE_DIRS
${CMAKE_CURRENT_SOURCE_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}/../include
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../universal/include
LAYOUT
"/definitions/([^/]*)/=ns::{0}"
OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/src
diff --git a/chaotic/integration_tests/schemas/container_format.yaml b/chaotic/integration_tests/schemas/container_format.yaml
new file mode 100644
index 000000000000..54b60d13074d
--- /dev/null
+++ b/chaotic/integration_tests/schemas/container_format.yaml
@@ -0,0 +1,11 @@
+definitions:
+ ContainerWithFormatItem:
+ type: object
+ additionalProperties: false
+ properties:
+ my_field:
+ type: array
+ x-usrv-cpp-container: std::unordered_set
+ items:
+ type: string
+ format: uuid
diff --git a/chaotic/integration_tests/schemas/custom_cpp_type.yaml b/chaotic/integration_tests/schemas/custom_cpp_type.yaml
index 118bc7eeec1e..e8545b6498e4 100644
--- a/chaotic/integration_tests/schemas/custom_cpp_type.yaml
+++ b/chaotic/integration_tests/schemas/custom_cpp_type.yaml
@@ -115,3 +115,10 @@ definitions:
type: string
x-usrv-cpp-type: my::CustomString
x-usrv-cpp-type: userver::utils::DefaultDict
+ StrongTypedefObject:
+ type: object
+ additionalProperties: false
+ properties:
+ strong_typedef:
+ type: string
+ x-usrv-cpp-type: userver::utils::StrongTypedef
diff --git a/chaotic/integration_tests/schemas/oneofdiscriminator.yaml b/chaotic/integration_tests/schemas/oneofdiscriminator.yaml
index 41392f447a4d..6eb30a291281 100644
--- a/chaotic/integration_tests/schemas/oneofdiscriminator.yaml
+++ b/chaotic/integration_tests/schemas/oneofdiscriminator.yaml
@@ -56,3 +56,12 @@ definitions:
properties:
version:
type: integer
+
+ OneOfToRefToRef:
+ oneOf:
+ - $ref: '#/definitions/RefToC'
+ discriminator:
+ propertyName: version
+
+ RefToC:
+ $ref: '#/definitions/C'
diff --git a/chaotic/tests/back/cpp/conftest.py b/chaotic/tests/back/cpp/conftest.py
index 3347723e616b..206b8ba47189 100644
--- a/chaotic/tests/back/cpp/conftest.py
+++ b/chaotic/tests/back/cpp/conftest.py
@@ -1,5 +1,4 @@
from collections import OrderedDict
-from typing import Dict
import pytest
@@ -33,7 +32,7 @@ def func(input_: dict):
@pytest.fixture(name='clean')
def _clean():
- def func(ordered_dict: OrderedDict) -> Dict[str, CppType]:
+ def func(ordered_dict: OrderedDict) -> dict[str, CppType]:
res = {}
for key, val in ordered_dict.items():
res[key] = val.without_json_schema()
diff --git a/chaotic/tests/back/cpp/test_external.py b/chaotic/tests/back/cpp/test_external.py
index a06f4a962279..c1ce55acd35a 100644
--- a/chaotic/tests/back/cpp/test_external.py
+++ b/chaotic/tests/back/cpp/test_external.py
@@ -1,3 +1,5 @@
+import pytest
+
from chaotic.back.cpp import type_name
from chaotic.back.cpp import types as cpp_types
from chaotic.back.cpp.translator import Generator
@@ -32,16 +34,14 @@ def parse(path, input_, external_schemas, external_types, cpp_name_func):
return resolved_schemas, types
-def test_import(cpp_name_func):
+def test_import(cpp_name_func, cpp_primitive_type):
ext_schemas, ext_types = parse('/type1', {'type': 'string'}, types.ResolvedSchemas(schemas={}), {}, cpp_name_func)
assert ext_schemas.schemas == {'vfull#/type1': types.String(type='string')}
assert ext_types == {
- '::type1': cpp_types.CppPrimitiveType(
- raw_cpp_type=type_name.TypeName('std::string'),
- nullable=False,
- user_cpp_type=None,
- json_schema=types.String(type='string'),
+ '::type1': cpp_primitive_type(
validators=cpp_types.CppPrimitiveValidator(prefix='type1'),
+ raw_cpp_type_str='std::string',
+ json_schema=types.String(type='string'),
),
}
@@ -65,3 +65,42 @@ def test_import(cpp_name_func):
json_schema=new_schemas.schemas['vfull#/type2'],
),
}
+
+
+def test_duplicate_name(cpp_name_func):
+ config = parser.ParserConfig(erase_prefix='')
+
+ schema_parser = parser.SchemaParser(
+ config=config,
+ full_filepath='full',
+ full_vfilepath='vfull',
+ )
+ schema_parser.parse_schema('/type1', {'type': 'string'})
+ schemas1 = schema_parser.parsed_schemas()
+
+ schema_parser = parser.SchemaParser(
+ config=config,
+ full_filepath='full2',
+ full_vfilepath='vfull2',
+ )
+ schema_parser.parse_schema('/type1', {'type': 'integer'})
+ schemas2 = schema_parser.parsed_schemas()
+
+ schemas = types.ParsedSchemas.merge([schemas1, schemas2])
+ rr = ref_resolver.RefResolver()
+ resolved_schemas = rr.sort_schemas(
+ schemas,
+ )
+
+ gen = Generator(
+ config=GeneratorConfig(
+ namespaces={'vfull': '', 'vfull2': ''}, include_dirs=None, infile_to_name_func=cpp_name_func
+ ),
+ )
+
+ with pytest.raises(
+ BaseException, match='Duplicate type name: ::type1, generated from vfull2#/type1 and vfull#/type1'
+ ):
+ gen.generate_types(
+ resolved_schemas,
+ )
diff --git a/chaotic/tests/back/cpp/test_tr_array.py b/chaotic/tests/back/cpp/test_tr_array.py
index 697923168028..0b0574679df7 100644
--- a/chaotic/tests/back/cpp/test_tr_array.py
+++ b/chaotic/tests/back/cpp/test_tr_array.py
@@ -1,11 +1,10 @@
from chaotic.back.cpp import type_name
from chaotic.back.cpp.types import CppArray
from chaotic.back.cpp.types import CppArrayValidator
-from chaotic.back.cpp.types import CppPrimitiveType
from chaotic.back.cpp.types import CppPrimitiveValidator
-def test_array_int(simple_gen):
+def test_array_int(simple_gen, cpp_primitive_type):
types = simple_gen({'type': 'array', 'items': {'type': 'integer'}})
assert types == {
'::type': CppArray(
@@ -13,12 +12,9 @@ def test_array_int(simple_gen):
user_cpp_type=None,
json_schema=None,
nullable=False,
- items=CppPrimitiveType(
- raw_cpp_type=type_name.TypeName('int'),
- user_cpp_type=None,
- json_schema=None,
- nullable=False,
+ items=cpp_primitive_type(
validators=CppPrimitiveValidator(prefix='typeA'),
+ raw_cpp_type_str='int',
),
container='std::vector',
validators=CppArrayValidator(),
@@ -26,7 +22,7 @@ def test_array_int(simple_gen):
}
-def test_array_array_with_validators(simple_gen):
+def test_array_array_with_validators(simple_gen, cpp_primitive_type):
types = simple_gen({
'type': 'array',
'items': {'type': 'array', 'items': {'type': 'integer', 'minimum': 1}},
@@ -42,15 +38,12 @@ def test_array_array_with_validators(simple_gen):
user_cpp_type=None,
json_schema=None,
nullable=False,
- items=CppPrimitiveType(
- raw_cpp_type=type_name.TypeName('int'),
- user_cpp_type=None,
- json_schema=None,
- nullable=False,
+ items=cpp_primitive_type(
validators=CppPrimitiveValidator(
min=1,
prefix='typeAA',
),
+ raw_cpp_type_str='int',
),
container='std::vector',
validators=CppArrayValidator(),
diff --git a/chaotic/tests/back/cpp/test_tr_int.py b/chaotic/tests/back/cpp/test_tr_int.py
index d383c33bf08a..2fa1c64cc23a 100644
--- a/chaotic/tests/back/cpp/test_tr_int.py
+++ b/chaotic/tests/back/cpp/test_tr_int.py
@@ -2,19 +2,15 @@
from chaotic.back.cpp import type_name
from chaotic.back.cpp.types import CppIntEnum
from chaotic.back.cpp.types import CppIntEnumItem
-from chaotic.back.cpp.types import CppPrimitiveType
from chaotic.back.cpp.types import CppPrimitiveValidator
-def test_int(simple_gen):
+def test_int(simple_gen, cpp_primitive_type):
types = simple_gen({'type': 'integer'})
assert types == {
- '::type': CppPrimitiveType(
- raw_cpp_type=type_name.TypeName('int'),
- user_cpp_type=None,
- json_schema=None,
- nullable=False,
+ '::type': cpp_primitive_type(
validators=CppPrimitiveValidator(prefix='type'),
+ raw_cpp_type_str='int',
),
}
@@ -30,118 +26,99 @@ def test_wrong_type_x(simple_gen):
assert exc.msg == 'Non-string x- property "x-usrv-cpp-type"'
-def test_int_nullable(simple_gen):
+def test_int_nullable(simple_gen, cpp_primitive_type):
types = simple_gen({'type': 'integer', 'nullable': True})
assert types == {
- '::type': CppPrimitiveType(
- raw_cpp_type=type_name.TypeName('int'),
- user_cpp_type=None,
- json_schema=None,
- nullable=True,
+ '::type': cpp_primitive_type(
validators=CppPrimitiveValidator(prefix='type'),
+ raw_cpp_type_str='int',
+ nullable=True,
),
}
-def test_int_cpp_type(simple_gen):
+def test_int_cpp_type(simple_gen, cpp_primitive_type):
types = simple_gen({'type': 'integer', 'x-usrv-cpp-type': 'X'})
assert types == {
- '::type': CppPrimitiveType(
- raw_cpp_type=type_name.TypeName('int'),
- user_cpp_type='X',
- json_schema=None,
- nullable=False,
+ '::type': cpp_primitive_type(
validators=CppPrimitiveValidator(prefix='type'),
+ raw_cpp_type_str='int',
+ user_cpp_type='X',
),
}
-def test_int_default(simple_gen):
+def test_int_default(simple_gen, cpp_primitive_type):
types = simple_gen({'type': 'integer', 'default': 42})
assert types == {
- '::type': CppPrimitiveType(
- raw_cpp_type=type_name.TypeName('int'),
- user_cpp_type=None,
- default=42,
- json_schema=None,
- nullable=False,
+ '::type': cpp_primitive_type(
validators=CppPrimitiveValidator(prefix='type'),
+ raw_cpp_type_str='int',
+ default=42,
),
}
-def test_int_min(simple_gen):
+def test_int_min(simple_gen, cpp_primitive_type):
types = simple_gen({'type': 'integer', 'minimum': 1})
assert types == {
- '::type': CppPrimitiveType(
- raw_cpp_type=type_name.TypeName('int'),
- user_cpp_type=None,
- json_schema=None,
- nullable=False,
+ '::type': cpp_primitive_type(
validators=CppPrimitiveValidator(
min=1,
prefix='type',
),
+ raw_cpp_type_str='int',
),
}
-def test_int_min_max(simple_gen):
+def test_int_min_max(simple_gen, cpp_primitive_type):
types = simple_gen({'type': 'integer', 'minimum': 1, 'maximum': 10})
assert types == {
- '::type': CppPrimitiveType(
- raw_cpp_type=type_name.TypeName('int'),
- user_cpp_type=None,
- json_schema=None,
+ '::type': cpp_primitive_type(
validators=CppPrimitiveValidator(
min=1,
max=10,
prefix='type',
),
- nullable=False,
+ raw_cpp_type_str='int',
),
}
-def test_int_min_max_exclusive(simple_gen):
+def test_int_min_max_exclusive(simple_gen, cpp_primitive_type):
types = simple_gen({
'type': 'integer',
'exclusiveMinimum': 1,
'exclusiveMaximum': 10,
})
assert types == {
- '::type': CppPrimitiveType(
- raw_cpp_type=type_name.TypeName('int'),
- user_cpp_type=None,
- json_schema=None,
+ '::type': cpp_primitive_type(
validators=CppPrimitiveValidator(
exclusiveMin=1,
exclusiveMax=10,
prefix='type',
),
- nullable=False,
+ raw_cpp_type_str='int',
),
}
-def test_int_min_max_exclusive_false(simple_gen):
+def test_int_min_max_exclusive_false(simple_gen, cpp_primitive_type):
types = simple_gen({
'type': 'integer',
'exclusiveMinimum': False,
'exclusiveMaximum': False,
})
assert types == {
- '::type': CppPrimitiveType(
- raw_cpp_type=type_name.TypeName('int'),
- user_cpp_type=None,
- json_schema=None,
+ '::type': cpp_primitive_type(
validators=CppPrimitiveValidator(prefix='type'),
- nullable=False,
+ raw_cpp_type_str='int',
),
}
-def test_int_min_max_exclusive_legacy(simple_gen):
+def test_int_min_max_exclusive_legacy(simple_gen, cpp_primitive_type):
types = simple_gen({
'type': 'integer',
'exclusiveMinimum': True,
@@ -150,42 +127,33 @@ def test_int_min_max_exclusive_legacy(simple_gen):
'maximum': 10,
})
assert types == {
- '::type': CppPrimitiveType(
- raw_cpp_type=type_name.TypeName('int'),
- user_cpp_type=None,
- json_schema=None,
+ '::type': cpp_primitive_type(
validators=CppPrimitiveValidator(
exclusiveMin=2,
exclusiveMax=10,
prefix='type',
),
- nullable=False,
+ raw_cpp_type_str='int',
),
}
-def test_int_format_int32(simple_gen):
+def test_int_format_int32(simple_gen, cpp_primitive_type):
types = simple_gen({'type': 'integer', 'format': 'int32'})
assert types == {
- '::type': CppPrimitiveType(
- raw_cpp_type=type_name.TypeName('std::int32_t'),
- user_cpp_type=None,
- json_schema=None,
- nullable=False,
+ '::type': cpp_primitive_type(
validators=CppPrimitiveValidator(prefix='type'),
+ raw_cpp_type_str='std::int32_t',
),
}
-def test_int_format_int64(simple_gen):
+def test_int_format_int64(simple_gen, cpp_primitive_type):
types = simple_gen({'type': 'integer', 'format': 'int64'})
assert types == {
- '::type': CppPrimitiveType(
- raw_cpp_type=type_name.TypeName('std::int64_t'),
- user_cpp_type=None,
- json_schema=None,
- nullable=False,
+ '::type': cpp_primitive_type(
validators=CppPrimitiveValidator(prefix='type'),
+ raw_cpp_type_str='std::int64_t',
),
}
diff --git a/chaotic/tests/back/cpp/test_tr_object.py b/chaotic/tests/back/cpp/test_tr_object.py
index 28d8d330c10c..587aa6be1976 100644
--- a/chaotic/tests/back/cpp/test_tr_object.py
+++ b/chaotic/tests/back/cpp/test_tr_object.py
@@ -6,7 +6,6 @@
from chaotic.back.cpp.types import CppPrimitiveValidator
from chaotic.back.cpp.types import CppStruct
from chaotic.back.cpp.types import CppStructField
-from chaotic.back.cpp.types import CppStructPrimitiveField
def test_empty(simple_gen):
@@ -26,7 +25,7 @@ def test_empty(simple_gen):
}
-def test_additional_properties_simple(simple_gen):
+def test_additional_properties_simple(simple_gen, cpp_primitive_type):
schemas = simple_gen({
'type': 'object',
'properties': {},
@@ -39,22 +38,19 @@ def test_additional_properties_simple(simple_gen):
nullable=False,
user_cpp_type=None,
fields={},
- extra_type=CppPrimitiveType(
- raw_cpp_type=type_name.TypeName('int'),
- json_schema=None,
- nullable=False,
- user_cpp_type=None,
+ extra_type=cpp_primitive_type(
validators=CppPrimitiveValidator(
namespace='::type',
prefix='Extra',
),
+ raw_cpp_type_str='int',
),
),
}
@pytest.mark.skip(reason='see comment in translator.py: _gen_field()')
-def test_field_external(simple_gen):
+def test_field_external(simple_gen, cpp_primitive_type):
schemas = simple_gen({
'type': 'object',
'properties': {'field': {'type': 'integer'}},
@@ -71,8 +67,12 @@ def test_field_external(simple_gen):
'field': CppStructField(
name='field',
required=False,
- external_schema=CppStructPrimitiveField(
- raw_cpp_type=type_name.TypeName('int'),
+ schema=cpp_primitive_type(
+ validators=CppPrimitiveValidator(
+ namespace='::type',
+ prefix='Field',
+ ),
+ raw_cpp_type_str='int',
),
),
},
@@ -80,7 +80,7 @@ def test_field_external(simple_gen):
}
-def test_field_with_default(simple_gen):
+def test_field_with_default(simple_gen, cpp_primitive_type):
schemas = simple_gen({
'type': 'object',
'properties': {'field': {'type': 'integer', 'default': 1}},
@@ -97,16 +97,13 @@ def test_field_with_default(simple_gen):
'field': CppStructField(
name='field',
required=False,
- schema=CppPrimitiveType(
- raw_cpp_type=type_name.TypeName('int'),
- json_schema=None,
- default=1,
- nullable=False,
- user_cpp_type=None,
+ schema=cpp_primitive_type(
validators=CppPrimitiveValidator(
namespace='::type',
prefix='Field',
),
+ raw_cpp_type_str='int',
+ default=1,
),
),
},
@@ -114,7 +111,7 @@ def test_field_with_default(simple_gen):
}
-def test_field_inplace(simple_gen):
+def test_field_inplace(simple_gen, cpp_primitive_type):
schemas = simple_gen({
'type': 'object',
'properties': {'field': {'type': 'integer', 'minimum': 1}},
@@ -131,16 +128,13 @@ def test_field_inplace(simple_gen):
'field': CppStructField(
name='field',
required=False,
- schema=CppPrimitiveType(
- raw_cpp_type=type_name.TypeName('int'),
- json_schema=None,
- nullable=False,
- user_cpp_type=None,
+ schema=cpp_primitive_type(
validators=CppPrimitiveValidator(
min=1,
namespace='::type',
prefix='Field',
),
+ raw_cpp_type_str='int',
),
),
},
diff --git a/chaotic/tests/back/cpp/test_tr_oneof.py b/chaotic/tests/back/cpp/test_tr_oneof.py
index 7ae6111ce156..8b217246b174 100644
--- a/chaotic/tests/back/cpp/test_tr_oneof.py
+++ b/chaotic/tests/back/cpp/test_tr_oneof.py
@@ -2,8 +2,6 @@
from chaotic.back.cpp.translator import Generator
from chaotic.back.cpp.translator import GeneratorConfig
from chaotic.front import ref_resolver
-from chaotic.front.parser import ParserConfig
-from chaotic.front.parser import SchemaParser
from chaotic.front.types import MappingType
from chaotic.main import generate_cpp_name_func
from chaotic.main import NameMapItem
@@ -36,13 +34,8 @@ def test_simple(simple_gen):
assert foo_schema.variants[2].raw_cpp_type == type_name.TypeName('double')
-def test_empty_mapping(clean):
- config = ParserConfig(erase_prefix='')
- parser = SchemaParser(
- config=config,
- full_filepath='full',
- full_vfilepath='vfull',
- )
+def test_empty_mapping(clean, schema_parser):
+ parser = schema_parser
parser.parse_schema(
'/definitions/A',
@@ -101,13 +94,8 @@ def test_empty_mapping(clean):
assert list(foo_schema.variants.keys()) == ['A', 'B']
-def test_str_mapping(clean):
- config = ParserConfig(erase_prefix='')
- parser = SchemaParser(
- config=config,
- full_filepath='full',
- full_vfilepath='vfull',
- )
+def test_str_mapping(clean, schema_parser):
+ parser = schema_parser
parser.parse_schema(
'/definitions/A',
@@ -170,13 +158,8 @@ def test_str_mapping(clean):
assert foo_schema.variants['bbb'].cpp_name == '::B'
-def test_int_mapping(clean):
- config = ParserConfig(erase_prefix='')
- parser = SchemaParser(
- config=config,
- full_filepath='full',
- full_vfilepath='vfull',
- )
+def test_int_mapping(clean, schema_parser):
+ parser = schema_parser
parser.parse_schema(
'/definitions/A',
diff --git a/chaotic/tests/back/cpp/test_tr_ref.py b/chaotic/tests/back/cpp/test_tr_ref.py
index ac14e1a1a02c..c0741d515350 100644
--- a/chaotic/tests/back/cpp/test_tr_ref.py
+++ b/chaotic/tests/back/cpp/test_tr_ref.py
@@ -4,18 +4,11 @@
from chaotic.back.cpp.types import CppRef
from chaotic.back.cpp.types import CppStruct
from chaotic.front import ref_resolver
-from chaotic.front.parser import ParserConfig
-from chaotic.front.parser import SchemaParser
from chaotic.front.types import SchemaObject
-def test_simple_ref(clean, cpp_name_func):
- config = ParserConfig(erase_prefix='')
- parser = SchemaParser(
- config=config,
- full_filepath='full',
- full_vfilepath='vfull',
- )
+def test_simple_ref(clean, cpp_name_func, schema_parser):
+ parser = schema_parser
parser.parse_schema(
'/definitions/Type',
diff --git a/chaotic/tests/back/cpp/test_tr_string.py b/chaotic/tests/back/cpp/test_tr_string.py
index 28617124637d..de27623edfde 100644
--- a/chaotic/tests/back/cpp/test_tr_string.py
+++ b/chaotic/tests/back/cpp/test_tr_string.py
@@ -2,17 +2,14 @@
from chaotic.back.cpp import types as cpp_types
-def test_simple(simple_gen):
+def test_simple(simple_gen, cpp_primitive_type):
types = simple_gen({'type': 'string'})
assert types == {
- '::type': cpp_types.CppPrimitiveType(
- raw_cpp_type=type_name.TypeName('std::string'),
- user_cpp_type=None,
- json_schema=None,
- nullable=False,
+ '::type': cpp_primitive_type(
validators=cpp_types.CppPrimitiveValidator(
prefix='type',
),
+ raw_cpp_type_str='std::string',
),
}
diff --git a/chaotic/tests/back/cpp/test_types.py b/chaotic/tests/back/cpp/test_types.py
index 8ec0c6757c3a..4cc8025ee8a4 100644
--- a/chaotic/tests/back/cpp/test_types.py
+++ b/chaotic/tests/back/cpp/test_types.py
@@ -7,13 +7,13 @@ def test_camel_to_snake_case_smoke():
assert types.camel_to_snake_case('unsigned') == 'unsigned'
-def test_get_include_by_cpp_type_smoke():
- assert types.CppType.get_include_by_cpp_type('geometry::Distance') == [
+def test_get_includes_by_cpp_type_smoke():
+ assert types.CppType.get_includes_by_cpp_type('geometry::Distance') == [
'userver/chaotic/io/geometry/distance.hpp',
]
- assert types.CppType.get_include_by_cpp_type('std::vector') == [
+ assert types.CppType.get_includes_by_cpp_type('std::vector') == [
'userver/chaotic/io/std/vector.hpp',
]
- assert types.CppType.get_include_by_cpp_type(
+ assert types.CppType.get_includes_by_cpp_type(
'userver::utils::StrongTypedef',
- ) == ['userver/chaotic/io/xxx.hpp']
+ ) == ['userver/utils/strong_typedef.hpp', 'userver/chaotic/io/xxx.hpp']
diff --git a/chaotic/tests/compilers/test_dynamic_configs.py b/chaotic/tests/compilers/test_dynamic_configs.py
index b22cb91d6dc2..249a7d3bf996 100644
--- a/chaotic/tests/compilers/test_dynamic_configs.py
+++ b/chaotic/tests/compilers/test_dynamic_configs.py
@@ -3,7 +3,6 @@
from typing import Any
from chaotic import error
-from chaotic.back.cpp import type_name
from chaotic.back.cpp import types
from chaotic.compilers import dynamic_config
@@ -21,17 +20,14 @@ def parse_variable_content(
return compiler.extract_variable_type()
-def test_smoke():
+def test_smoke(cpp_primitive_type):
var = parse_variable_content({'schema': {'type': 'integer'}, 'default': 1})
- expected = types.CppPrimitiveType(
- raw_cpp_type=type_name.TypeName('int'),
- nullable=False,
- user_cpp_type=None,
- json_schema=None,
+ expected = cpp_primitive_type(
validators=types.CppPrimitiveValidator(
namespace='::taxi_config::var',
prefix='VariableTypeRaw',
),
+ raw_cpp_type_str='int',
)
assert var.without_json_schema() == expected
@@ -89,7 +85,10 @@ def test_strong_typedef_dependencies():
})
assert False
except error.BaseError as exc:
- assert 'Include file "userver/chaotic/io/xxx.hpp" not found' in exc.msg
+ assert (
+ 'Include file "userver/utils/strong_typedef.hpp" not found' in exc.msg
+ or 'Include file "userver/chaotic/io/xxx.hpp" not found' in exc.msg
+ )
def test_default_isomorphic():
diff --git a/chaotic/tests/conftest.py b/chaotic/tests/conftest.py
index 7544f7e704b8..5e69870315b6 100644
--- a/chaotic/tests/conftest.py
+++ b/chaotic/tests/conftest.py
@@ -1,6 +1,23 @@
+from typing import Any
+from typing import Optional
+
import pytest
+from chaotic.back.cpp import type_name
+from chaotic.back.cpp.types import CppPrimitiveType
+from chaotic.back.cpp.types import CppPrimitiveValidator
from chaotic.front import parser
+from chaotic.front.types import Schema
+
+
+@pytest.fixture
+def schema_parser():
+ config = parser.ParserConfig(erase_prefix='')
+ return parser.SchemaParser(
+ config=config,
+ full_filepath='full',
+ full_vfilepath='vfull',
+ )
@pytest.fixture
@@ -16,3 +33,31 @@ def func(input_: dict):
return schema_parser.parsed_schemas()
return func
+
+
+@pytest.fixture
+def cpp_primitive_type():
+ """Factory fixture for creating CppPrimitiveType instances with common defaults."""
+
+ def create(
+ validators: CppPrimitiveValidator,
+ raw_cpp_type_str: str,
+ user_cpp_type: Optional[str] = None,
+ json_schema: Optional[Schema] = None,
+ nullable: bool = False,
+ default: Any = None,
+ ):
+ kwargs = {
+ 'raw_cpp_type': type_name.TypeName(raw_cpp_type_str),
+ 'user_cpp_type': user_cpp_type,
+ 'json_schema': json_schema,
+ 'nullable': nullable,
+ 'validators': validators,
+ }
+
+ if default is not None:
+ kwargs['default'] = default
+
+ return CppPrimitiveType(**kwargs)
+
+ return create
diff --git a/chaotic/tests/front/test_all_of.py b/chaotic/tests/front/test_all_of.py
index e8feefddaee3..666a12da842c 100644
--- a/chaotic/tests/front/test_all_of.py
+++ b/chaotic/tests/front/test_all_of.py
@@ -1,15 +1,15 @@
+import pytest
+
from chaotic.front.parser import ParserError
from chaotic.front.types import AllOf
from chaotic.front.types import SchemaObject
def test_of_none(simple_parse):
- try:
+ with pytest.raises(ParserError) as exc:
simple_parse({'allOf': []})
- assert False
- except ParserError as exc:
- assert exc.infile_path == '/definitions/type/allOf'
- assert exc.msg == 'Empty allOf'
+ assert exc.value.infile_path == '/definitions/type/allOf'
+ assert exc.value.msg == 'Empty allOf'
# stupid, but valid
@@ -52,9 +52,7 @@ def test_2_empty(simple_parse):
def test_non_object(simple_parse):
- try:
+ with pytest.raises(ParserError) as exc:
simple_parse({'allOf': [{'type': 'integer'}]})
- assert False
- except ParserError as exc:
- assert exc.infile_path == '/definitions/type/allOf/0'
- assert exc.msg == 'Non-object type in allOf: integer'
+ assert exc.value.infile_path == '/definitions/type/allOf/0'
+ assert exc.value.msg == 'Non-object type in allOf: integer'
diff --git a/chaotic/tests/front/test_array.py b/chaotic/tests/front/test_array.py
index 3904f405e36c..693426f8e8ac 100644
--- a/chaotic/tests/front/test_array.py
+++ b/chaotic/tests/front/test_array.py
@@ -1,13 +1,13 @@
+import pytest
+
from chaotic.front.parser import ParserError
def test_array_missing_items(simple_parse):
- try:
+ with pytest.raises(ParserError) as exc:
simple_parse({'type': 'array'})
- assert False
- except ParserError as exc:
- assert exc.infile_path == '/definitions/type/items'
- assert exc.msg == '"items" is missing'
+ assert exc.value.infile_path == '/definitions/type/items'
+ assert exc.value.msg == '"items" is missing'
def test_int_array(simple_parse):
diff --git a/chaotic/tests/front/test_basic.py b/chaotic/tests/front/test_basic.py
index 5b835b085d06..bb6c859e617d 100644
--- a/chaotic/tests/front/test_basic.py
+++ b/chaotic/tests/front/test_basic.py
@@ -1,45 +1,34 @@
+import pytest
+
from chaotic.front import ref_resolver
-from chaotic.front.parser import ParserConfig
from chaotic.front.parser import ParserError
-from chaotic.front.parser import SchemaParser
def test_generic_error(simple_parse):
- try:
+ with pytest.raises(ParserError) as exc:
simple_parse({'type': 'integer', 'unknown_field': '1'})
- assert False
- except ParserError as exc:
- assert exc.full_filepath == 'full'
- assert exc.schema_type == 'jsonschema'
+ assert exc.value.full_filepath == 'full'
+ assert exc.value.schema_type == 'jsonschema'
def test_unknown_field(simple_parse):
- try:
+ with pytest.raises(ParserError) as exc:
simple_parse({'type': 'integer', 'unknown_field': '1'})
- assert False
- except ParserError as exc:
- assert exc.infile_path == '/definitions/type/unknown_field'
- assert exc.msg == (
- 'Unknown field: "unknown_field", known fields: '
- '["default", "enum", "exclusiveMaximum", "exclusiveMinimum", '
- '"format", "maximum", "minimum", "nullable", "type"]'
- )
+ assert exc.value.infile_path == '/definitions/type/unknown_field'
+ assert exc.value.msg == (
+ 'Unknown field: "unknown_field", known fields: '
+ '["default", "deprecated", "enum", "exclusiveMaximum", "exclusiveMinimum", '
+ '"format", "maximum", "minimum", "nullable", "type"]'
+ )
-def test_duplicate_path(simple_parse):
- try:
- config = ParserConfig(erase_prefix='')
- parser = SchemaParser(
- config=config,
- full_filepath='full',
- full_vfilepath='vfull',
- )
+def test_duplicate_path(schema_parser):
+ with pytest.raises(ParserError) as exc:
+ parser = schema_parser
parser.parse_schema('/definitions/type', {'type': 'integer'})
parser.parse_schema('/definitions/type', {'type': 'number'})
- assert False
- except ParserError as exc:
- assert exc.infile_path == '/definitions/type'
- assert exc.msg == 'Duplicate path: vfull#/definitions/type'
+ assert exc.value.infile_path == '/definitions/type'
+ assert exc.value.msg == 'Duplicate path: vfull#/definitions/type'
def test_x_unknown_field(simple_parse):
@@ -51,41 +40,37 @@ def test_x_known_field(simple_parse):
def test_wrong_field_type(simple_parse):
- try:
+ with pytest.raises(ParserError) as exc:
simple_parse({'type': 'intxxxx'})
- assert False
- except ParserError as exc:
- assert exc.infile_path == '/definitions/type/type'
- assert exc.msg == 'Unknown type "intxxxx"'
+ assert exc.value.infile_path == '/definitions/type/type'
+ assert exc.value.msg == 'Unknown type "intxxxx"'
def test_no_schema_type(simple_parse):
- try:
+ with pytest.raises(ParserError) as exc:
simple_parse({'enum': [1, 2, 3]})
- assert False
- except ParserError as exc:
- assert exc.infile_path == '/definitions/type'
- assert exc.msg == '"type" is missing'
+ assert exc.value.infile_path == '/definitions/type'
+ assert exc.value.msg == '"type" is missing'
def test_schema_type_string(simple_parse):
simple_parse({'type': 'string'})
-def test_ref(simple_parse):
- try:
- config = ParserConfig(erase_prefix='')
- parser = SchemaParser(
- config=config,
- full_filepath='full',
- full_vfilepath='vfull',
- )
+def test_title(simple_parse):
+ simple_parse({'type': 'string', 'title': 'something'})
+
+
+def test_ref(schema_parser):
+ with pytest.raises(Exception) as exc_info:
+ parser = schema_parser
parser.parse_schema(
'/definitions/type1',
{'$ref': '#/definitions/type'},
)
rr = ref_resolver.RefResolver()
rr.sort_schemas(parser.parsed_schemas())
- assert False
- except Exception as exc: # pylint: disable=broad-exception-caught
- assert str(exc) == ('$ref to unknown type "vfull#/definitions/type", known refs:\n- vfull#/definitions/type1')
+
+ assert str(exc_info.value) == (
+ '$ref to unknown type "vfull#/definitions/type", known refs:\n- vfull#/definitions/type1'
+ )
diff --git a/chaotic/tests/front/test_number.py b/chaotic/tests/front/test_number.py
index f1ed67d45738..a47e2d73b0a3 100644
--- a/chaotic/tests/front/test_number.py
+++ b/chaotic/tests/front/test_number.py
@@ -45,12 +45,10 @@ def test_number_minmax_exclusive(simple_parse):
def test_number_extra_enum(simple_parse):
- try:
+ with pytest.raises(ParserError) as exc:
simple_parse({'type': 'number', 'enum': [1.0]})
- assert False
- except ParserError as exc:
- assert exc.infile_path == '/definitions/type/enum'
- assert 'Unknown field: "enum"' in exc.msg
+ assert exc.value.infile_path == '/definitions/type/enum'
+ assert 'Unknown field: "enum"' in exc.value.msg
def test_integer_min_max(simple_parse):
@@ -75,12 +73,10 @@ def test_integer_minmax_exclusive(simple_parse):
def test_integer_min_max_number(simple_parse):
- try:
+ with pytest.raises(ParserError) as exc:
simple_parse({'type': 'integer', 'minimum': 1.1})
- assert False
- except ParserError as exc:
- assert exc.infile_path == '/definitions/type/minimum'
- assert exc.msg == 'field "minimum" has wrong type'
+ assert exc.value.infile_path == '/definitions/type/minimum'
+ assert exc.value.msg == 'field "minimum" has wrong type'
def test_integer_enum(simple_parse):
@@ -91,18 +87,14 @@ def test_integer_enum(simple_parse):
def test_integer_enum_wrong_type(simple_parse):
- try:
+ with pytest.raises(ParserError) as exc:
simple_parse({'type': 'integer', 'enum': ['1']})
- assert False
- except ParserError as exc:
- assert exc.infile_path == '/definitions/type/enum'
- assert exc.msg == 'field "enum" contains non-integers (1)'
+ assert exc.value.infile_path == '/definitions/type/enum'
+ assert exc.value.msg == 'field "enum" contains non-integers (1)'
def test_integer_min_wrong_str(simple_parse):
- try:
+ with pytest.raises(ParserError) as exc:
simple_parse({'type': 'integer', 'minimum': '1'})
- assert False
- except ParserError as exc:
- assert exc.infile_path == '/definitions/type/minimum'
- assert exc.msg == 'field "minimum" has wrong type'
+ assert exc.value.infile_path == '/definitions/type/minimum'
+ assert exc.value.msg == 'field "minimum" has wrong type'
diff --git a/chaotic/tests/front/test_object.py b/chaotic/tests/front/test_object.py
index 615980e1b83b..9777818d76d8 100644
--- a/chaotic/tests/front/test_object.py
+++ b/chaotic/tests/front/test_object.py
@@ -1,3 +1,5 @@
+import pytest
+
from chaotic.front.parser import ParserError
from chaotic.front.types import Boolean
from chaotic.front.types import Integer
@@ -17,57 +19,49 @@ def test_very_empty(simple_parse):
def test_unknown_required(simple_parse):
- try:
+ with pytest.raises(ParserError) as exc:
simple_parse({
'type': 'object',
'properties': {},
'additionalProperties': False,
'required': ['unknown'],
})
- assert False
- except ParserError as exc:
- assert exc.infile_path == '/definitions/type/required'
- assert exc.msg == ('Field "unknown" is set in "required", but missing in "properties"')
+ assert exc.value.infile_path == '/definitions/type/required'
+ assert exc.value.msg == ('Field "unknown" is set in "required", but missing in "properties"')
def test_unknown_fields(simple_parse):
- try:
+ with pytest.raises(ParserError) as exc:
simple_parse({
'type': 'object',
'unknown_field': 'x',
'properties': {},
'additionalProperties': False,
})
- assert False
- except ParserError as exc:
- assert exc.infile_path == '/definitions/type/unknown_field'
- assert 'Unknown field: "unknown_field"' in exc.msg
+ assert exc.value.infile_path == '/definitions/type/unknown_field'
+ assert 'Unknown field: "unknown_field"' in exc.value.msg
def test_error_in_property(simple_parse):
- try:
+ with pytest.raises(ParserError) as exc:
simple_parse({
'type': 'object',
'properties': {'field': {'type': 'xxxx'}},
'additionalProperties': False,
})
- assert False
- except ParserError as exc:
- assert exc.infile_path == '/definitions/type/properties/field/type'
- assert exc.msg == 'Unknown type "xxxx"'
+ assert exc.value.infile_path == '/definitions/type/properties/field/type'
+ assert exc.value.msg == 'Unknown type "xxxx"'
def test_error_in_extra(simple_parse):
- try:
+ with pytest.raises(ParserError) as exc:
simple_parse({
'type': 'object',
'properties': {},
'additionalProperties': {'type': 'xxx'},
})
- assert False
- except ParserError as exc:
- assert exc.infile_path == '/definitions/type/additionalProperties/type'
- assert exc.msg == 'Unknown type "xxx"'
+ assert exc.value.infile_path == '/definitions/type/additionalProperties/type'
+ assert exc.value.msg == 'Unknown type "xxx"'
def test_property_and_additional(simple_parse):
diff --git a/chaotic/tests/front/test_one_of.py b/chaotic/tests/front/test_one_of.py
index f803a44b594c..fbcf6fc67fc2 100644
--- a/chaotic/tests/front/test_one_of.py
+++ b/chaotic/tests/front/test_one_of.py
@@ -1,8 +1,6 @@
import pytest
-from chaotic.front.parser import ParserConfig
from chaotic.front.parser import ParserError
-from chaotic.front.parser import SchemaParser
from chaotic.front.types import Boolean
from chaotic.front.types import DiscMapping
from chaotic.front.types import Integer
@@ -15,14 +13,9 @@
@pytest.fixture(name='parse_after_refs')
-def _parse_after_refs():
+def _parse_after_refs(schema_parser):
def func(input_: dict):
- config = ParserConfig(erase_prefix='')
- parser = SchemaParser(
- config=config,
- full_filepath='full',
- full_vfilepath='vfull',
- )
+ parser = schema_parser
parser.parse_schema(
'/definitions/type1',
{
@@ -108,12 +101,10 @@ def func(input_: dict):
def test_of_none(simple_parse):
- try:
+ with pytest.raises(ParserError) as exc:
simple_parse({'oneOf': []})
- assert False
- except ParserError as exc:
- assert exc.infile_path == '/definitions/type/oneOf'
- assert exc.msg == 'Empty oneOf'
+ assert exc.value.infile_path == '/definitions/type/oneOf'
+ assert exc.value.msg == 'Empty oneOf'
# stupid, but valid
@@ -145,8 +136,20 @@ def test_wo_discriminator_2(simple_parse):
}
+def test_wo_discriminator_nullable(simple_parse):
+ parsed = simple_parse({'oneOf': [{'type': 'integer'}], 'nullable': True})
+ assert parsed.schemas['vfull#/definitions/type'].nullable
+
+
+def test_wo_discriminator_nullable_wrong_type(simple_parse):
+ with pytest.raises(ParserError) as exc:
+ simple_parse({'oneOf': [{'type': 'integer'}], 'nullable': 1})
+ assert exc.value.infile_path == '/definitions/type/oneOf/nullable'
+ assert exc.value.msg == 'field "nullable" has wrong type'
+
+
def test_wd_no_ref_or_object(simple_parse):
- try:
+ with pytest.raises(ParserError) as exc:
simple_parse({
'oneOf': [
{'type': 'integer'},
@@ -158,14 +161,12 @@ def test_wd_no_ref_or_object(simple_parse):
],
'discriminator': {'propertyName': 'foo'},
})
- assert False
- except ParserError as exc:
- assert exc.infile_path == '/definitions/type/oneOf/0'
- assert exc.msg == 'Not a $ref in oneOf with discriminator'
+ assert exc.value.infile_path == '/definitions/type/oneOf/0'
+ assert exc.value.msg == 'Not a $ref in oneOf with discriminator'
def test_wd_wrong_property(simple_parse):
- try:
+ with pytest.raises(ParserError) as exc:
simple_parse({
'oneOf': [
{
@@ -176,14 +177,12 @@ def test_wd_wrong_property(simple_parse):
],
'discriminator': {'propertyName': 'foo'},
})
- assert False
- except ParserError as exc:
- assert exc.infile_path == '/definitions/type/oneOf/0'
- assert exc.msg == 'Not a $ref in oneOf with discriminator'
+ assert exc.value.infile_path == '/definitions/type/oneOf/0'
+ assert exc.value.msg == 'Not a $ref in oneOf with discriminator'
def test_wd_wrong_property2(parse_after_refs):
- try:
+ with pytest.raises(ParserError) as exc:
parse_after_refs({
'oneOf': [
{'$ref': '#/definitions/type1'},
@@ -192,22 +191,18 @@ def test_wd_wrong_property2(parse_after_refs):
],
'discriminator': {'propertyName': 'foo'},
})
- assert False
- except ParserError as exc:
- assert exc.infile_path == '/definitions/type/oneOf/2'
- assert exc.msg == 'No discriminator property "foo"'
+ assert exc.value.infile_path == '/definitions/type/oneOf/2'
+ assert exc.value.msg == 'No discriminator property "foo"'
def test_wd_wrong_type(parse_after_refs):
- try:
+ with pytest.raises(ParserError) as exc:
parse_after_refs({
'oneOf': [{'$ref': '#/definitions/type_int'}],
'discriminator': {'propertyName': 'foo'},
})
- assert False
- except ParserError as exc:
- assert exc.infile_path == '/definitions/type/oneOf/0'
- assert exc.msg == 'oneOf $ref to non-object'
+ assert exc.value.infile_path == '/definitions/type/oneOf/0'
+ assert exc.value.msg == 'oneOf $ref to non-object (Integer)'
def test_wd_ok(parse_after_refs):
@@ -311,7 +306,7 @@ def test_wd_ok_with_int_mapping(parse_after_refs):
def test_wd_non_uniform_mapping(parse_after_refs):
- try:
+ with pytest.raises(ParserError) as exc:
parse_after_refs({
'oneOf': [
{'$ref': '#/definitions/type3'},
@@ -325,13 +320,12 @@ def test_wd_non_uniform_mapping(parse_after_refs):
},
},
})
- except ParserError as exc:
- assert exc.infile_path == '/definitions/type/discriminator/mapping'
- assert exc.msg.startswith('Not uniform mapping')
+ assert exc.value.infile_path == '/definitions/type/discriminator/mapping'
+ assert exc.value.msg.startswith('Not uniform mapping')
def test_wd_ok_with_mapping_missing_ref(parse_after_refs):
- try:
+ with pytest.raises(ParserError) as exc:
parse_after_refs({
'oneOf': [
{'$ref': '#/definitions/type1'},
@@ -342,14 +336,12 @@ def test_wd_ok_with_mapping_missing_ref(parse_after_refs):
'mapping': {'t2': '#/definitions/type2'},
},
})
- assert False
- except ParserError as exc:
- assert exc.infile_path == '/definitions/type/discriminator/mapping'
- assert exc.msg == 'Missing $ref in mapping: vfull#/definitions/type1'
+ assert exc.value.infile_path == '/definitions/type/discriminator/mapping'
+ assert exc.value.msg == 'Missing $ref in mapping: vfull#/definitions/type1'
def test_wd_ok_with_mapping_invalid_ref(parse_after_refs):
- try:
+ with pytest.raises(ParserError) as exc:
parse_after_refs({
'oneOf': [
{'$ref': '#/definitions/type1'},
@@ -364,14 +356,12 @@ def test_wd_ok_with_mapping_invalid_ref(parse_after_refs):
},
},
})
- assert False
- except ParserError as exc:
- assert exc.infile_path == '/definitions/type/discriminator/mapping'
- assert exc.msg == ("$ref(s) outside of oneOf: ['vfull#/definitions/wrong']")
+ assert exc.value.infile_path == '/definitions/type/discriminator/mapping'
+ assert exc.value.msg == ("$ref(s) outside of oneOf: ['vfull#/definitions/wrong']")
def test_wd_invalidtype_mapping_value(parse_after_refs):
- try:
+ with pytest.raises(ParserError) as exc:
parse_after_refs({
'oneOf': [
{'$ref': '#/definitions/type1'},
@@ -382,14 +372,12 @@ def test_wd_invalidtype_mapping_value(parse_after_refs):
'mapping': {'t1': 1, 't2': '#/definitions/type2'},
},
})
- assert False
- except ParserError as exc:
- assert exc.infile_path == '/definitions/type/discriminator/mapping/t1'
- assert exc.msg == 'Not a string in mapping'
+ assert exc.value.infile_path == '/definitions/type/discriminator/mapping/t1'
+ assert exc.value.msg == 'Not a string in mapping'
def test_wd_extra_field(simple_parse):
- try:
+ with pytest.raises(ParserError) as exc:
simple_parse({
'oneOf': [
{
@@ -400,7 +388,17 @@ def test_wd_extra_field(simple_parse):
],
'discriminator': {'foo': 1, 'propertyName': 'foo'},
})
- assert False
- except ParserError as exc:
- assert exc.infile_path == '/definitions/type/discriminator/foo'
- assert exc.msg == ('Unknown field: "foo", known fields: ["mapping", "propertyName"]')
+ assert exc.value.infile_path == '/definitions/type/discriminator/foo'
+ assert exc.value.msg == ('Unknown field: "foo", known fields: ["mapping", "propertyName"]')
+
+
+def test_wd_nullable(parse_after_refs):
+ schema = parse_after_refs({
+ 'oneOf': [
+ {'$ref': '#/definitions/type1'},
+ {'$ref': '#/definitions/type2'},
+ ],
+ 'discriminator': {'propertyName': 'foo'},
+ 'nullable': True,
+ })
+ assert schema['vfull#/definitions/type'].nullable
diff --git a/chaotic/tests/front/test_ref.py b/chaotic/tests/front/test_ref.py
index 1ab19cadc95e..5a763b77a2af 100644
--- a/chaotic/tests/front/test_ref.py
+++ b/chaotic/tests/front/test_ref.py
@@ -1,5 +1,7 @@
import collections
+import pytest
+
from chaotic.front import ref_resolver
from chaotic.front import types
from chaotic.front.parser import ParserConfig
@@ -10,13 +12,8 @@
from chaotic.front.types import Ref
-def test_ref_ok():
- config = ParserConfig(erase_prefix='')
- parser = SchemaParser(
- config=config,
- full_filepath='full',
- full_vfilepath='vfull',
- )
+def test_ref_ok(schema_parser):
+ parser = schema_parser
parser.parse_schema('/definitions/type1', {'type': 'integer'})
parser.parse_schema('/definitions/type2', {'$ref': '#/definitions/type1'})
@@ -32,13 +29,8 @@ def test_ref_ok():
}
-def test_ref_from_items_ok():
- config = ParserConfig(erase_prefix='')
- parser = SchemaParser(
- config=config,
- full_filepath='full',
- full_vfilepath='vfull',
- )
+def test_ref_from_items_ok(schema_parser):
+ parser = schema_parser
parser.parse_schema('/definitions/type1', {'type': 'integer'})
parser.parse_schema(
@@ -59,48 +51,37 @@ def test_ref_from_items_ok():
}
-def test_ref_invalid():
- config = ParserConfig(erase_prefix='')
- parser = SchemaParser(
- config=config,
- full_filepath='full',
- full_vfilepath='vfull',
+def test_ref_invalid(schema_parser):
+ parser = schema_parser
+
+ parser.parse_schema('/definitions/type1', {'type': 'integer'})
+ parser.parse_schema(
+ '/definitions/type2',
+ {'$ref': '#/definitions/other_type'},
)
+ rr = ref_resolver.RefResolver()
- try:
- parser.parse_schema('/definitions/type1', {'type': 'integer'})
- parser.parse_schema(
- '/definitions/type2',
- {'$ref': '#/definitions/other_type'},
- )
- rr = ref_resolver.RefResolver()
+ with pytest.raises(Exception) as exc:
rr.sort_schemas(parser.parsed_schemas())
- assert False
- except Exception as exc: # pylint: disable=broad-exception-caught
- assert str(exc) == (
- '$ref to unknown type "vfull#/definitions/other_type", '
- 'known refs:\n- vfull#/definitions/type1\n'
- '- vfull#/definitions/type2'
- )
+
+ assert str(exc.value) == (
+ '$ref to unknown type "vfull#/definitions/other_type", '
+ 'known refs:\n- vfull#/definitions/type1\n'
+ '- vfull#/definitions/type2'
+ )
def test_extra_fields(simple_parse):
- try:
+ with pytest.raises(ParserError) as exc:
simple_parse({'$ref': '123', 'field': 1})
- assert False
- except ParserError as exc:
- assert exc.infile_path == '/definitions/type'
- assert exc.msg == "Unknown field(s) ['field']"
+ assert exc.value.infile_path == '/definitions/type'
+ assert exc.value.msg == "Unknown field(s) ['field']"
-def test_sibling_file():
+def test_sibling_file(schema_parser):
config = ParserConfig(erase_prefix='')
schemas = []
- parser = SchemaParser(
- config=config,
- full_filepath='full',
- full_vfilepath='vfull',
- )
+ parser = schema_parser
parser.parse_schema('/definitions/type1', {'type': 'integer'})
schemas.append(parser.parsed_schemas())
@@ -137,13 +118,8 @@ def test_sibling_file():
}
-def test_forward_reference():
- config = ParserConfig(erase_prefix='')
- parser = SchemaParser(
- config=config,
- full_filepath='full',
- full_vfilepath='vfull',
- )
+def test_forward_reference(schema_parser):
+ parser = schema_parser
parser.parse_schema('/definitions/type1', {'$ref': '#/definitions/type2'})
parser.parse_schema('/definitions/type2', {'type': 'integer'})
parser.parse_schema('/definitions/type3', {'$ref': '#/definitions/type4'})
@@ -184,20 +160,38 @@ def test_forward_reference():
})
-def test_cycle():
+def test_cycle(schema_parser):
+ parser = schema_parser
+ parser.parse_schema('/definitions/type1', {'$ref': '#/definitions/type2'})
+ parser.parse_schema('/definitions/type2', {'$ref': '#/definitions/type1'})
+
+ rr = ref_resolver.RefResolver()
+
+ with pytest.raises(ref_resolver.ResolverError) as exc:
+ rr.sort_schemas(parser.parsed_schemas())
+
+ assert str(exc.value) == '$ref cycle: vfull#/definitions/type1, vfull#/definitions/type2'
+
+
+def test_self_ref(schema_parser):
+ parser = schema_parser
+ parser.parse_schema('/definitions/type1', {'$ref': '#/definitions/type1'})
+
+ rr = ref_resolver.RefResolver()
+
+ with pytest.raises(ref_resolver.ResolverError) as exc:
+ rr.sort_schemas(parser.parsed_schemas())
+
+ assert str(exc.value) == '$ref cycle: vfull#/definitions/type1'
+
+
+def test_no_fragment():
config = ParserConfig(erase_prefix='')
parser = SchemaParser(
config=config,
full_filepath='full',
full_vfilepath='vfull',
)
- parser.parse_schema('/definitions/type1', {'$ref': '#/definitions/type2'})
- parser.parse_schema('/definitions/type2', {'$ref': '#/definitions/type1'})
-
- rr = ref_resolver.RefResolver()
- try:
- rr.sort_schemas(parser.parsed_schemas())
- except ref_resolver.ResolverError as exc:
- assert str(exc) == '$ref cycle: vfull#/definitions/type1, vfull#/definitions/type2'
- else:
- assert False
+ with pytest.raises(ParserError) as exc_info:
+ parser.parse_schema('/definitions/type1', {'$ref': '/definitions/type2'})
+ assert exc_info.value.msg == 'Error in $ref (/definitions/type2): there should be exactly one "#" inside'
diff --git a/clickhouse/CMakeLists.txt b/clickhouse/CMakeLists.txt
index ac2096b7fb45..0b609b571561 100644
--- a/clickhouse/CMakeLists.txt
+++ b/clickhouse/CMakeLists.txt
@@ -16,6 +16,7 @@ userver_module(
# HACK: common source between unittest and dbtest targets.
"${CMAKE_CURRENT_SOURCE_DIR}/src/storages/tests/utils_test.cpp"
DBTEST_DATABASES clickhouse
+ DEPENDS core
)
target_compile_options(${PROJECT_NAME} PUBLIC "-Wno-error=pedantic")
diff --git a/cmake/ChaoticGen.cmake b/cmake/ChaoticGen.cmake
index 1f7b2088efe9..3119cc15fce4 100644
--- a/cmake/ChaoticGen.cmake
+++ b/cmake/ChaoticGen.cmake
@@ -131,7 +131,6 @@ function(userver_target_generate_chaotic TARGET)
list(APPEND CHAOTIC_ARGS "-o" "${PARSE_OUTPUT_DIR}/${PARSE_OUTPUT_PREFIX}")
list(APPEND CHAOTIC_ARGS "--relative-to" "${PARSE_RELATIVE_TO}")
- list(APPEND CHAOTIC_ARGS "--clang-format" "${CLANG_FORMAT}")
_userver_initialize_codegen_flag()
@@ -150,6 +149,7 @@ function(userver_target_generate_chaotic TARGET)
OUTPUT ${SCHEMAS}
COMMAND ${CMAKE_COMMAND} -E env "USERVER_PYTHON=${USERVER_CHAOTIC_PYTHON_BINARY}" "${CHAOTIC_BIN}"
${CHAOTIC_EXTRA_ARGS} ${CHAOTIC_ARGS} ${PARSE_SCHEMAS}
+ --clang-format "${CLANG_FORMAT}"
DEPENDS ${PARSE_SCHEMAS}
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
VERBATIM ${CODEGEN}
@@ -198,23 +198,23 @@ function(userver_target_generate_openapi_client TARGET)
endif()
set(SCHEMAS
- "${PARSE_OUTPUT_DIR}/include/client/${PARSE_NAME}/client.hpp"
- "${PARSE_OUTPUT_DIR}/include/client/${PARSE_NAME}/client_impl.hpp"
- "${PARSE_OUTPUT_DIR}/include/client/${PARSE_NAME}/component.hpp"
- "${PARSE_OUTPUT_DIR}/include/client/${PARSE_NAME}/requests.hpp"
- "${PARSE_OUTPUT_DIR}/include/client/${PARSE_NAME}/responses.hpp"
- "${PARSE_OUTPUT_DIR}/include/client/${PARSE_NAME}/exceptions.hpp"
- "${PARSE_OUTPUT_DIR}/src/client/${PARSE_NAME}/client.cpp"
- "${PARSE_OUTPUT_DIR}/src/client/${PARSE_NAME}/client_impl.cpp"
- "${PARSE_OUTPUT_DIR}/src/client/${PARSE_NAME}/component.cpp"
- "${PARSE_OUTPUT_DIR}/src/client/${PARSE_NAME}/requests.cpp"
- "${PARSE_OUTPUT_DIR}/src/client/${PARSE_NAME}/responses.cpp"
- "${PARSE_OUTPUT_DIR}/src/client/${PARSE_NAME}/exceptions.cpp"
+ "${PARSE_OUTPUT_DIR}/include/clients/${PARSE_NAME}/client.hpp"
+ "${PARSE_OUTPUT_DIR}/include/clients/${PARSE_NAME}/client_impl.hpp"
+ "${PARSE_OUTPUT_DIR}/include/clients/${PARSE_NAME}/component.hpp"
+ "${PARSE_OUTPUT_DIR}/include/clients/${PARSE_NAME}/requests.hpp"
+ "${PARSE_OUTPUT_DIR}/include/clients/${PARSE_NAME}/responses.hpp"
+ "${PARSE_OUTPUT_DIR}/include/clients/${PARSE_NAME}/exceptions.hpp"
+ "${PARSE_OUTPUT_DIR}/src/clients/${PARSE_NAME}/client.cpp"
+ "${PARSE_OUTPUT_DIR}/src/clients/${PARSE_NAME}/client_impl.cpp"
+ "${PARSE_OUTPUT_DIR}/src/clients/${PARSE_NAME}/component.cpp"
+ "${PARSE_OUTPUT_DIR}/src/clients/${PARSE_NAME}/requests.cpp"
+ "${PARSE_OUTPUT_DIR}/src/clients/${PARSE_NAME}/responses.cpp"
+ "${PARSE_OUTPUT_DIR}/src/clients/${PARSE_NAME}/exceptions.cpp"
)
foreach(SCHEMA ${PARSE_SCHEMAS})
string(REGEX REPLACE "^.*/([^/]*)\\.([^.]*)\$" "\\1" SCHEMA "${SCHEMA}")
- set(SCHEMAS ${SCHEMAS} "${PARSE_OUTPUT_DIR}/include/client/${PARSE_NAME}/${SCHEMA}.hpp"
- "${PARSE_OUTPUT_DIR}/src/client/${PARSE_NAME}/${SCHEMA}.cpp"
+ set(SCHEMAS ${SCHEMAS} "${PARSE_OUTPUT_DIR}/include/clients/${PARSE_NAME}/${SCHEMA}.hpp"
+ "${PARSE_OUTPUT_DIR}/src/clients/${PARSE_NAME}/${SCHEMA}.cpp"
)
endforeach()
diff --git a/cmake/DownloadUsingCPM.cmake b/cmake/DownloadUsingCPM.cmake
index a754d0ac0be1..4f16f389076d 100644
--- a/cmake/DownloadUsingCPM.cmake
+++ b/cmake/DownloadUsingCPM.cmake
@@ -73,3 +73,10 @@ function(mark_targets_as_system directory)
endforeach()
endforeach()
endfunction()
+
+function(_userver_print_cpm_packages)
+ message(STATUS "Dependencies from CPM:")
+ foreach(PACKAGE ${CPM_PACKAGES})
+ message(STATUS "- ${PACKAGE}")
+ endforeach()
+endfunction()
diff --git a/cmake/GetUserverVersion.cmake b/cmake/GetUserverVersion.cmake
index 2826b3355477..f4e3d5edf733 100644
--- a/cmake/GetUserverVersion.cmake
+++ b/cmake/GetUserverVersion.cmake
@@ -21,8 +21,9 @@ else()
message(STATUS "Git not found")
endif()
-set(USERVER_MAJOR_VERSION 2)
-set(USERVER_MINOR_VERSION 12-rc)
+file(READ ${USERVER_ROOT_DIR}/version.txt VERSION)
+string(REGEX MATCH ^[0-9]+ USERVER_MAJOR_VERSION "${VERSION}")
+string(REGEX MATCH [-0-9a-z]+$ USERVER_MINOR_VERSION "${VERSION}")
set(USERVER_VERSION "${USERVER_MAJOR_VERSION}.${USERVER_MINOR_VERSION}")
string(REPLACE "-" "_" USERVER_VERSION_STR "${USERVER_VERSION}")
diff --git a/cmake/ModuleHelpers.cmake b/cmake/ModuleHelpers.cmake
index 740097b8e8c8..e702d1e42aca 100644
--- a/cmake/ModuleHelpers.cmake
+++ b/cmake/ModuleHelpers.cmake
@@ -3,20 +3,46 @@ include_guard(GLOBAL)
cmake_policy(SET CMP0054 NEW)
macro(_userver_module_begin)
- set(options)
+ set(options
+ CPM_DOWNLOAD_ONLY
+ )
set(oneValueArgs # Target name, also used for package name by default
NAME VERSION
)
set(multiValueArgs
DEBIAN_NAMES FORMULA_NAMES RPM_NAMES PACMAN_NAMES PKG_NAMES
# For version detection of manually installed packages and unknown package managers.
- PKG_CONFIG_NAMES
+ PKG_CONFIG_NAMES
+ # For CPM options
+ CPM_NAME
+ CPM_VERSION
+ CPM_GITHUB_REPOSITORY
+ CPM_URL
+ CPM_OPTIONS
+ CPM_SOURCE_SUBDIR
+ CPM_GIT_TAG
)
cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" "${ARGN}")
set(name "${ARG_NAME}")
+ string(TOUPPER "${ARG_CPM_NAME}" ARG_CPM_NAME)
+ string(REPLACE "-" "_" ARG_CPM_NAME "${ARG_CPM_NAME}")
+
+ if(ARG_CPM_NAME)
+ option(
+ USERVER_DOWNLOAD_PACKAGE_${ARG_CPM_NAME}
+ "Download and setup ${ARG_CPM_NAME} if no library of matching version was found"
+ ${USERVER_DOWNLOAD_PACKAGES}
+ )
+ option(
+ USERVER_FORCE_DOWNLOAD_${ARG_CPM_NAME}
+ "Download ${ARG_CPM_NAME} even if there is an installed system package"
+ ${USERVER_FORCE_DOWNLOAD_PACKAGES}
+ )
+ endif()
+
if(ARG_VERSION)
if(NOT ${name}_FIND_VERSION OR "${${name}_FIND_VERSION}" VERSION_LESS "${ARG_VERSION}")
set("${name}_FIND_VERSION" "${ARG_VERSION}")
@@ -78,6 +104,12 @@ endmacro()
macro(_userver_module_find_part)
# Also uses ARGs left over from _userver_find_module_begin
+ # TODO: return() doesn't work inside of macro
+ # if(USERVER_FORCE_DOWNLOAD_${ARG_CPM_NAME})
+ # message(STATUS "Skipping ${ARG_CPM_NAME} system package search due to USERVER_FORCE_DOWNLOAD_${ARG_CPM_NAME}=TRUE")
+ # return()
+ # endif()
+
set(options)
set(oneValueArgs PART_TYPE)
set(multiValueArgs NAMES PATHS PATH_SUFFIXES)
@@ -261,12 +293,25 @@ macro(_userver_module_end)
list(APPEND required_vars "${programs_variable}")
endif()
if(required_vars)
- find_package_handle_standard_args(
- "${current_package_name}"
- REQUIRED_VARS ${required_vars}
- FAIL_MESSAGE "${FULL_ERROR_MESSAGE}"
- )
- mark_as_advanced(${required_vars})
+ foreach(_CURRENT_VAR ${required_vars})
+ if(NOT ${_CURRENT_VAR})
+ set(NEED_CPM TRUE)
+ if(USERVER_DOWNLOAD_PACKAGE_${ARG_CPM_NAME})
+ set(${_CURRENT_VAR})
+ endif()
+ endif()
+ endforeach()
+
+ if(NEED_CPM AND USERVER_DOWNLOAD_PACKAGE_${ARG_CPM_NAME})
+ _userver_cpm_addpackage("${current_package_name}")
+ else()
+ find_package_handle_standard_args(
+ "${current_package_name}"
+ REQUIRED_VARS ${required_vars}
+ FAIL_MESSAGE "${FULL_ERROR_MESSAGE}"
+ )
+ mark_as_advanced(${required_vars})
+ endif()
else()
# Forward to another CMake module, add nice error messages if missing.
set(wrapped_package_name "${current_package_name}")
@@ -323,6 +368,29 @@ macro(_userver_module_end)
endif()
endmacro()
+macro(_userver_cpm_addpackage name)
+ include(DownloadUsingCPM)
+
+ set(EXTRA_ARGS)
+ if(ARG_CPM_DOWNLOAD_ONLY)
+ set(EXTRA_ARGS ${EXTRA_ARGS} DOWNLOAD_ONLY)
+ endif()
+ cpmaddpackage(
+ NAME ${name}
+ VERSION ${ARG_CPM_VERSION}
+ GITHUB_REPOSITORY ${ARG_CPM_GITHUB_REPOSITORY}
+ URL ${ARG_CPM_URL}
+ OPTIONS ${ARG_CPM_OPTIONS}
+ SOURCE_SUBDIR ${ARG_CPM_SOURCE_SUBDIR}
+ GIT_TAG ${ARG_CPM_GIT_TAG}
+ ${EXTRA_ARGS}
+ )
+ if(NOT ARG_CPM_DOWNLOAD_ONLY)
+ mark_targets_as_system("${${name}_SOURCE_DIR}")
+ endif()
+ set(${name}_FOUND 1)
+endmacro()
+
function(_userver_macos_set_default_dir variable command_args)
set(default_value "")
if(CMAKE_SYSTEM_NAME MATCHES "Darwin" AND NOT DEFINED ${variable})
diff --git a/cmake/PrepareInstall.cmake b/cmake/PrepareInstall.cmake
index 828e3dcd921d..3c5b3326de5c 100644
--- a/cmake/PrepareInstall.cmake
+++ b/cmake/PrepareInstall.cmake
@@ -60,6 +60,7 @@ function(_userver_export_targets)
CONFIGURATIONS RELEASE
NAMESPACE userver::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/userver/release
+ COMPONENT universal
)
install(
EXPORT userver-targets_d
@@ -67,6 +68,7 @@ function(_userver_export_targets)
CONFIGURATIONS DEBUG
NAMESPACE userver::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/userver/debug
+ COMPONENT universal
)
endfunction()
@@ -74,6 +76,7 @@ function(_userver_directory_install)
if(NOT USERVER_INSTALL)
return()
endif()
+ set(option)
set(oneValueArgs COMPONENT DESTINATION PATTERN)
set(multiValueArgs FILES DIRECTORY PROGRAMS)
cmake_parse_arguments(ARG "${option}" "${oneValueArgs}" "${multiValueArgs}" "${ARGN}")
@@ -145,3 +148,64 @@ function(_userver_make_install_config)
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/userver"
)
endfunction()
+
+function(_userver_install_component)
+ if(NOT USERVER_INSTALL)
+ return()
+ endif()
+
+ set(oneValueArgs MODULE)
+ set(multiValueArgs DEPENDS)
+ cmake_parse_arguments(ARG "${option}" "${oneValueArgs}" "${multiValueArgs}" "${ARGN}")
+
+ string(TOUPPER "${ARG_MODULE}" MODULE_UPPER)
+ if(CPACK_COMPONENTS_GROUPING STREQUAL ONE_PER_GROUP)
+ if(NOT CPACK_DEBIAN_${MODULE_UPPER}_PACKAGE_DEPENDS)
+ message(FATAL_ERROR "File with per-component dependencies is missing (component ${ARG_MODULE}). Either use CPACK_COMPONENTS_GROUPING=ALL_COMPONENTS_IN_ONE to build a single all-in-one package, or create dependency file ${USERVER_ROOT_DIR}/scripts/docs/en/deps/${DEPENDENCIES_FILESTEM}/${ARG_MODULE}.")
+ endif()
+ endif()
+
+ execute_process(
+ COMMAND cat "${USERVER_ROOT_DIR}/scripts/docs/en/deps/${DEPENDENCIES_FILESTEM}/${ARG_MODULE}"
+ COMMAND tr "\n" " "
+ COMMAND sed "s/ \\(.\\)/, \\1/g"
+ OUTPUT_VARIABLE MODULE_DEPENDS
+ )
+ file(APPEND "${CMAKE_BINARY_DIR}/cpack.variables.inc" "
+ set(CPACK_DEBIAN_${MODULE_UPPER}_PACKAGE_NAME libuserver-${ARG_MODULE}-dev)
+ set(CPACK_DEBIAN_${MODULE_UPPER}_PACKAGE_CONFLICTS libuserver-all-dev)
+ set(CPACK_COMPONENT_${MODULE_UPPER}_DEPENDS ${ARG_DEPENDS})
+ set(CPACK_DEBIAN_${MODULE_UPPER}_PACKAGE_DEPENDS \"${MODULE_DEPENDS}\")
+ ")
+
+ file(APPEND "${CMAKE_BINARY_DIR}/cpack.inc" "
+ cpack_add_component_group(${ARG_MODULE} EXPANDED)
+ cpack_add_component(${ARG_MODULE} GROUP ${ARG_MODULE} INSTALL_TYPES Full)
+ ")
+endfunction()
+
+function(_userver_prepare_components)
+ file(REMOVE "${CMAKE_BINARY_DIR}/cpack.inc")
+ file(REMOVE "${CMAKE_BINARY_DIR}/cpack.variables.inc")
+
+ # DEB dependencies:
+ execute_process(COMMAND lsb_release -cs OUTPUT_VARIABLE OS_CODENAME)
+ if(OS_CODENAME MATCHES "^bookworm")
+ set(DEPENDENCIES_FILESTEM "debian-12")
+ elseif(OS_CODENAME MATCHES "^bullseye")
+ set(DEPENDENCIES_FILESTEM "debian-11")
+ elseif(OS_CODENAME MATCHES "^noble")
+ set(DEPENDENCIES_FILESTEM "ubuntu-24.04")
+ elseif(OS_CODENAME MATCHES "^jammy")
+ set(DEPENDENCIES_FILESTEM "ubuntu-22.04")
+ elseif(OS_CODENAME MATCHES "^impish")
+ set(DEPENDENCIES_FILESTEM "ubuntu-21.04")
+ elseif(OS_CODENAME MATCHES "^focal")
+ set(DEPENDENCIES_FILESTEM "ubuntu-20.04")
+ elseif(OS_CODENAME MATCHES "^bionic")
+ set(DEPENDENCIES_FILESTEM "ubuntu-18.04")
+ endif()
+ set(DEPENDENCIES_FILESTEM ${DEPENDENCIES_FILESTEM} CACHE INTERNAL "")
+endfunction()
+
+_userver_prepare_components()
diff --git a/cmake/SetupAbseil.cmake b/cmake/SetupAbseil.cmake
index fb669c056f75..596beddee6dd 100644
--- a/cmake/SetupAbseil.cmake
+++ b/cmake/SetupAbseil.cmake
@@ -31,6 +31,9 @@ cpmaddpackage(
GITHUB_REPOSITORY
abseil/abseil-cpp
SYSTEM
+ PATCHES
+ abseil_pr_1707.patch
+ abseil_pr_1739.patch
OPTIONS
"ABSL_PROPAGATE_CXX_STD ON"
"ABSL_ENABLE_INSTALL ON"
diff --git a/cmake/SetupBoost.cmake b/cmake/SetupBoost.cmake
new file mode 100644
index 000000000000..b5af70ac0447
--- /dev/null
+++ b/cmake/SetupBoost.cmake
@@ -0,0 +1,73 @@
+include_guard(GLOBAL)
+
+option(USERVER_DOWNLOAD_PACKAGE_BOOST "Download and setup Boost if no library of matching version was found"
+ ${USERVER_DOWNLOAD_PACKAGES}
+)
+option(USERVER_FORCE_DOWNLOAD_BOOST "Download Boost even if there is an installed system package"
+ ${USERVER_FORCE_DOWNLOAD_PACKAGES}
+)
+
+set(BOOST_VERSION 1.89.0)
+set(BOOST_INCLUDE_LIBRARIES_FIND_PACKAGE
+ atomic
+ program_options
+ filesystem
+ regex
+ locale
+ iostreams
+ context
+ coroutine
+)
+set(BOOST_INCLUDE_LIBRARIES
+ ${BOOST_INCLUDE_LIBRARIES_FIND_PACKAGE}
+ coroutine2
+ stacktrace
+ uuid
+ lockfree
+ endian
+ assert
+ predef
+)
+string(REGEX REPLACE ";" "\\\\\\\\;" BOOST_INCLUDE_LIBRARIES_LIST "${BOOST_INCLUDE_LIBRARIES}")
+
+if(NOT USERVER_FORCE_DOWNLOAD_BOOST AND NOT BOOST_CPM)
+ if(USERVER_DOWNLOAD_PACKAGE_BOOST)
+ set(MAYBE_REQUIRED)
+ else()
+ set(MAYBE_REQUIRED REQUIRED)
+ endif()
+ find_package(
+ Boost ${MAYBE_REQUIRED} CONFIG
+ COMPONENTS ${BOOST_INCLUDE_LIBRARIES_FIND_PACKAGE} stacktrace_basic
+ OPTIONAL_COMPONENTS stacktrace_backtrace stacktrace_windbg coroutine2 config assert
+ )
+
+ if(Boost_FOUND)
+ return()
+ endif()
+endif()
+
+include(DownloadUsingCPM)
+set(BOOST_CPM TRUE CACHE BOOL "")
+
+cpmaddpackage(
+ NAME Boost
+ VERSION ${BOOST_VERSION}
+ URL https://github.com/boostorg/boost/releases/download/boost-${BOOST_VERSION}/boost-${BOOST_VERSION}-cmake.tar.xz
+ URL_HASH SHA256=67acec02d0d118b5de9eb441f5fb707b3a1cdd884be00ca24b9a73c995511f74
+ OPTIONS
+ "BOOST_ENABLE_CMAKE ON"
+ "BOOST_INCLUDE_LIBRARIES ${BOOST_INCLUDE_LIBRARIES_LIST}"
+ "BOOST_SKIP_INSTALL_RULES ON"
+ "BUILD_SHARED_LIBS OFF"
+ "BOOST_RUNTIME_LINK static"
+ "BUILD_TESTING OFF"
+ "BOOST_LOCKFREE_BUILD_TESTS OFF"
+ EXCLUDE_FROM_ALL
+)
+
+# set version variable as find_package() does
+set(Boost_VERSION_STRING ${BOOST_VERSION} CACHE STRING "")
+
+# We have fresh version of boost, DWCAS should work
+set(USERVER_IMPL_DWCAS_CHECKED TRUE CACHE INTERNAL "TRUE iff checked that DWCAS works")
diff --git a/cmake/SetupCURL.cmake b/cmake/SetupCURL.cmake
index 0eb5f2b57fcf..c640c35c88d4 100644
--- a/cmake/SetupCURL.cmake
+++ b/cmake/SetupCURL.cmake
@@ -61,6 +61,8 @@ cpmaddpackage(
"BUILD_CURL_EXE OFF"
"BUILD_SHARED_LIBS OFF"
"CURL_DISABLE_TESTS ON"
+ "CURL_DISABLE_LDAP ON"
+ "HAVE_DLOPEN TRUE"
${CURL_LTO_OPTION}
)
diff --git a/cmake/SetupGTest.cmake b/cmake/SetupGTest.cmake
index c92275400232..3988026f626a 100644
--- a/cmake/SetupGTest.cmake
+++ b/cmake/SetupGTest.cmake
@@ -5,8 +5,11 @@ endif()
option(USERVER_DOWNLOAD_PACKAGE_GTEST "Download and setup gtest if no gtest of matching version was found"
${USERVER_DOWNLOAD_PACKAGES}
)
+option(USERVER_FORCE_DOWNLOAD_GTEST "Download gtest even if there is an installed system package"
+ ${USERVER_FORCE_DOWNLOAD_PACKAGES}
+)
-if(NOT USERVER_FORCE_DOWNLOAD_PACKAGES)
+if(NOT USERVER_FORCE_DOWNLOAD_GTEST)
find_package(GTest QUIET)
if(NOT GTest_FOUND AND NOT USERVER_DOWNLOAD_PACKAGE_GTEST)
message(
diff --git a/cmake/SetupGrpc.cmake b/cmake/SetupGrpc.cmake
index ec0f0afea6d3..c826a13200f3 100644
--- a/cmake/SetupGrpc.cmake
+++ b/cmake/SetupGrpc.cmake
@@ -73,6 +73,7 @@ cpmaddpackage(
GITHUB_REPOSITORY
grpc/grpc
SYSTEM
+ PATCHES grpc_pr_36805.patch
OPTIONS
"BUILD_SHARED_LIBS OFF"
"CARES_BUILD_TOOLS OFF"
diff --git a/cmake/SetupOpenssl.cmake b/cmake/SetupOpenssl.cmake
new file mode 100644
index 000000000000..08b6b1b6c77f
--- /dev/null
+++ b/cmake/SetupOpenssl.cmake
@@ -0,0 +1,72 @@
+include_guard(GLOBAL)
+
+option(USERVER_DOWNLOAD_PACKAGE_OPENSSL "Download and setup OpenSSL if no library of matching version was found"
+ ${USERVER_DOWNLOAD_PACKAGES}
+)
+option(USERVER_FORCE_DOWNLOAD_OPENSSL "Download OpenSSL even if there is an installed system package"
+ ${USERVER_FORCE_DOWNLOAD_PACKAGES}
+)
+
+if(NOT USERVER_FORCE_DOWNLOAD_OPENSSL AND NOT OpenSSL_CPM)
+ if(USERVER_DOWNLOAD_PACKAGE_OPENSSL)
+ find_package(OpenSSL)
+ else()
+ find_package(OpenSSL REQUIRED)
+ endif()
+
+ if(OpenSSL_FOUND)
+ return()
+ endif()
+endif()
+set(OpenSSL_CPM TRUE CACHE BOOL "")
+
+include(DownloadUsingCPM)
+
+set(OPENSSL_INSTALL_DIR ${CMAKE_BINARY_DIR}/openssl)
+execute_process(COMMAND mkdir -p ${OPENSSL_INSTALL_DIR}/usr/local/include)
+
+# Flags are copied from Ubuntu's debian/rules
+set(CONFIGURE_FLAGS no-idea no-mdc2 no-rc5 no-zlib no-ssl3 enable-unit-test no-ssl3-method enable-rfc3779 enable-cms no-capieng)
+set(OPENSSL_VERSION 3.5.2)
+
+cpmaddpackage(
+ NAME OpenSSL
+ URL https://github.com/openssl/openssl/releases/download/openssl-${OPENSSL_VERSION}/openssl-${OPENSSL_VERSION}.tar.gz
+ URL_HASH SHA512=db2c7a88bea432f96d867a98af15f850f371d4136c657338de93cb88a39a3578c025b5df7310e195a02fc715ad5a2422a319a44f0247c6a7e2ba8b36aad77651
+)
+
+# We use custom target to be able to set build dependency
+# for *external* libcurl from CPM
+add_custom_target(
+ OpenSSL
+ test -e ${OPENSSL_INSTALL_DIR}/.installed || CFLAGS=${CMAKE_C_FLAGS} ./config --libdir=/usr/local/lib ${CONFIGURE_FLAGS}
+ COMMAND
+ test -e ${OPENSSL_INSTALL_DIR}/.installed || make -j8
+ COMMAND
+ test -e ${OPENSSL_INSTALL_DIR}/.installed || make DESTDIR=${OPENSSL_INSTALL_DIR} install_sw
+ COMMAND
+ touch ${OPENSSL_INSTALL_DIR}/.installed
+ WORKING_DIRECTORY ${OpenSSL_SOURCE_DIR}
+ COMMENT "Compiling OpenSSL library"
+)
+
+
+add_library(Crypto STATIC IMPORTED GLOBAL)
+add_dependencies(Crypto OpenSSL)
+set_property(TARGET Crypto PROPERTY IMPORTED_LOCATION ${OPENSSL_INSTALL_DIR}/usr/local/lib/libcrypto.a)
+target_include_directories(Crypto INTERFACE ${OPENSSL_INSTALL_DIR}/usr/local/include)
+
+
+add_library(SSL STATIC IMPORTED GLOBAL)
+add_dependencies(SSL OpenSSL)
+set_property(TARGET SSL PROPERTY IMPORTED_LOCATION ${OPENSSL_INSTALL_DIR}/usr/local/lib/libssl.a)
+target_include_directories(SSL INTERFACE ${OPENSSL_INSTALL_DIR}/usr/local/include)
+
+add_library(OpenSSL::Crypto ALIAS Crypto)
+add_library(OpenSSL::SSL ALIAS SSL)
+
+# Light emulation of find_package(OpenSSL) for libcurl
+set(OpenSSL_FOUND TRUE CACHE BOOL "" FORCE)
+set(OPENSSL_FOUND TRUE CACHE BOOL "" FORCE)
+set(OPENSSL_CRYPTO_LIBRARY OpenSSL::Crypto CACHE STRING "" FORCE)
+set(OPENSSL_INCLUDE_DIR ${OPENSSL_INSTALL_DIR}/usr/local/include CACHE FILEPATH "" FORCE)
diff --git a/cmake/SetupProtobuf.cmake b/cmake/SetupProtobuf.cmake
index 2987e2d31b0a..85346f6d7d52 100644
--- a/cmake/SetupProtobuf.cmake
+++ b/cmake/SetupProtobuf.cmake
@@ -4,7 +4,12 @@ option(USERVER_FORCE_DOWNLOAD_PROTOBUF "Download Protobuf even if there is an in
)
function(_userver_set_protobuf_version_category)
- if(Protobuf_VERSION VERSION_GREATER_EQUAL 5.26.0
+ if(Protobuf_VERSION VERSION_GREATER_EQUAL 6.30.0
+ AND Protobuf_VERSION VERSION_LESS 7.0.0
+ OR Protobuf_VERSION VERSION_GREATER_EQUAL 30.0.0
+ )
+ set_property(GLOBAL PROPERTY userver_protobuf_version_category 6)
+ elseif(Protobuf_VERSION VERSION_GREATER_EQUAL 5.26.0
AND Protobuf_VERSION VERSION_LESS 6.0.0
OR Protobuf_VERSION VERSION_GREATER_EQUAL 26.0.0
)
diff --git a/cmake/UserverGenerateDynamicConfigsDocs.cmake b/cmake/UserverGenerateDynamicConfigsDocs.cmake
index c80a0303c308..3cbd3d3c4522 100644
--- a/cmake/UserverGenerateDynamicConfigsDocs.cmake
+++ b/cmake/UserverGenerateDynamicConfigsDocs.cmake
@@ -4,7 +4,8 @@ function(_userver_add_target_gen_dynamic_configs_docs)
add_custom_target(
userver-gen-dynamic-configs-docs
COMMENT "Generate dynamic_configs .md docs"
- COMMAND
+ COMMAND
+ ${USERVER_PYTHON_PATH}
${CMAKE_CURRENT_SOURCE_DIR}/scripts/docs/dynamic_config_yaml_to_md.py
-o ${CMAKE_CURRENT_BINARY_DIR}/docs-dynamic-configs
${YAML_FILENAMES}
diff --git a/cmake/UserverModule.cmake b/cmake/UserverModule.cmake
index 4b537c95da5e..86067c334f14 100644
--- a/cmake/UserverModule.cmake
+++ b/cmake/UserverModule.cmake
@@ -23,6 +23,7 @@ function(userver_module MODULE)
UBENCH_LINK_LIBRARIES
UBENCH_DATABASES
UBENCH_ENV
+ DEPENDS
)
cmake_parse_arguments(ARG "${OPTIONS}" "${ONE_VALUE_ARGS}" "${MULTI_VALUE_ARGS}" ${ARGN})
if(ARG_UNPARSED_ARGUMENTS)
@@ -89,6 +90,8 @@ function(userver_module MODULE)
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/userver"
)
endif()
+
+ _userver_install_component(MODULE ${MODULE} DEPENDS ${ARG_DEPENDS})
endif()
# 1. userver-${MODULE}-unittest
diff --git a/cmake/UserverPack.cmake b/cmake/UserverPack.cmake
index 2b702528d61d..af281aa9419b 100644
--- a/cmake/UserverPack.cmake
+++ b/cmake/UserverPack.cmake
@@ -6,6 +6,13 @@ set(CPACK_PACKAGE_DESCRIPTION
services and utilities."
)
+option(USERVER_INSTALL_MULTIPACKAGE "Whether create per-component packages" OFF)
+if(USERVER_INSTALL_MULTIPACKAGE)
+ set(CPACK_COMPONENTS_GROUPING ONE_PER_GROUP)
+else()
+ set(CPACK_COMPONENTS_GROUPING ALL_COMPONENTS_IN_ONE)
+endif()
+
set(CPACK_PACKAGE_NAME "libuserver-all-dev")
set(CPACK_DEBIAN_FILE_NAME DEB-DEFAULT)
set(CPACK_PACKAGE_VERSION "${USERVER_VERSION}")
@@ -26,27 +33,9 @@ set(CPACK_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS
WORLD_EXECUTE
)
-# DEB dependencies:
-execute_process(COMMAND lsb_release -cs OUTPUT_VARIABLE OS_CODENAME)
-if(OS_CODENAME MATCHES "^bookworm")
- set(DEPENDENCIES_FILE "debian-12.md")
-elseif(OS_CODENAME MATCHES "^bullseye")
- set(DEPENDENCIES_FILE "debian-11.md")
-elseif(OS_CODENAME MATCHES "^noble")
- set(DEPENDENCIES_FILE "ubuntu-24.04.md")
-elseif(OS_CODENAME MATCHES "^jammy")
- set(DEPENDENCIES_FILE "ubuntu-22.04.md")
-elseif(OS_CODENAME MATCHES "^impish")
- set(DEPENDENCIES_FILE "ubuntu-21.04.md")
-elseif(OS_CODENAME MATCHES "^focal")
- set(DEPENDENCIES_FILE "ubuntu-20.04.md")
-elseif(OS_CODENAME MATCHES "^bionic")
- set(DEPENDENCIES_FILE "ubuntu-18.04.md")
-endif()
-
-if(DEPENDENCIES_FILE)
+if(DEPENDENCIES_FILESTEM)
execute_process(
- COMMAND cat "${USERVER_ROOT_DIR}/scripts/docs/en/deps/${DEPENDENCIES_FILE}"
+ COMMAND cat "${USERVER_ROOT_DIR}/scripts/docs/en/deps/${DEPENDENCIES_FILESTEM}.md"
COMMAND tr "\n" " "
COMMAND sed "s/ \\(.\\)/, \\1/g"
OUTPUT_VARIABLE CPACK_DEBIAN_PACKAGE_DEPENDS
@@ -55,5 +44,16 @@ else()
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6")
endif()
+if(CPACK_COMPONENTS_GROUPING STREQUAL ONE_PER_GROUP)
+ set(CPACK_DEB_COMPONENT_INSTALL ON)
+ set(CPACK_DEBIAN_ENABLE_COMPONENT_DEPENDS ON)
+endif()
+
+# Must go just before CPack
+include("${CMAKE_BINARY_DIR}/cpack.variables.inc")
+
# CPack setup is ready. Including it:
include(CPack)
+
+# Must go after all modules
+include("${CMAKE_BINARY_DIR}/cpack.inc")
diff --git a/cmake/UserverRequireDWCAS.cmake b/cmake/UserverRequireDWCAS.cmake
index f280b0ec8926..12d502a119b4 100644
--- a/cmake/UserverRequireDWCAS.cmake
+++ b/cmake/UserverRequireDWCAS.cmake
@@ -38,6 +38,8 @@ function(userver_target_require_dwcas target visibility)
# boost::atomic::value() since Boost 1.74.0.
if("${Boost_VERSION_STRING}" VERSION_GREATER_EQUAL "${BOOST_DWCAS_MIN_VERSION}")
message(STATUS "DWCAS: Using boost::atomic")
+ list(APPEND TEST_LIBRARIES "Boost::atomic")
+ get_target_property(BOOST_ATOMIC_INCLUDE_DIR Boost::atomic INTERFACE_INCLUDE_DIRECTORIES)
else()
message(WARNING "DWCAS: Using std::atomic")
target_compile_definitions(${target} ${visibility} USERVER_USE_STD_DWCAS=1)
@@ -92,8 +94,9 @@ function(userver_target_require_dwcas target visibility)
try_run(
RUN_RESULT COMPILE_RESULT "${CMAKE_CURRENT_BINARY_DIR}/require_dwcas"
"${USERVER_ROOT_DIR}/cmake/UserverRequireDWCAS.cpp"
- CMAKE_FLAGS "-DINCLUDE_DIRECTORIES=${Boost_INCLUDE_DIRS}"
- COMPILE_DEFINITIONS ${TEST_DEFINITIONS} LINK_LIBRARIES ${TEST_LIBRARIES}
+ CMAKE_FLAGS "-DINCLUDE_DIRECTORIES=${BOOST_ATOMIC_INCLUDE_DIR};${Boost_INCLUDE_DIRS}"
+ COMPILE_DEFINITIONS ${TEST_DEFINITIONS}
+ LINK_LIBRARIES ${TEST_LIBRARIES}
COMPILE_OUTPUT_VARIABLE COMPILE_OUTPUT
)
diff --git a/cmake/UserverSetupEnvironment.cmake b/cmake/UserverSetupEnvironment.cmake
index 7f1d52c4edbe..351a34627001 100644
--- a/cmake/UserverSetupEnvironment.cmake
+++ b/cmake/UserverSetupEnvironment.cmake
@@ -77,6 +77,12 @@ function(_userver_setup_environment_impl)
PARENT_SCOPE
)
+ if (NOT DEFINED CMAKE_CXX_SCAN_FOR_MODULES)
+ # For now, we don't use C++ modules, so we can save ourselves the overhead
+ # of scanning every source file
+ set(CMAKE_CXX_SCAN_FOR_MODULES OFF)
+ endif()
+
add_compile_options("-pipe" "-g" "-fPIC")
add_compile_definitions("PIC=1")
diff --git a/cmake/UserverSql.cmake b/cmake/UserverSql.cmake
index fcf8e0bd13e4..00c5e7d36fdd 100644
--- a/cmake/UserverSql.cmake
+++ b/cmake/UserverSql.cmake
@@ -20,7 +20,7 @@ _userver_prepare_sql()
function(userver_add_sql_library TARGET)
set(OPTIONS)
- set(ONE_VALUE_ARGS OUTPUT_DIR NAMESPACE QUERY_LOG_MODE)
+ set(ONE_VALUE_ARGS SOURCE_DIR OUTPUT_DIR NAMESPACE QUERY_LOG_MODE)
set(MULTI_VALUE_ARGS SQL_FILES)
cmake_parse_arguments(ARG "${OPTIONS}" "${ONE_VALUE_ARGS}" "${MULTI_VALUE_ARGS}" ${ARGN})
if(NOT ARG_NAMESPACE)
@@ -30,12 +30,19 @@ function(userver_add_sql_library TARGET)
set(ARG_QUERY_LOG_MODE "full")
endif()
set(FILENAME "sql_queries")
+ if(NOT ARG_SOURCE_DIR)
+ set(ARG_SOURCE_DIR ".")
+ endif()
+ if(NOT IS_ABSOLUTE "${ARG_SOURCE_DIR}")
+ set(ARG_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/${ARG_SOURCE_DIR}")
+ endif()
set(SQL_FILES)
foreach(WILDCARD ${ARG_SQL_FILES})
- file(GLOB FILES ${WILDCARD})
+ file(GLOB_RECURSE FILES RELATIVE ${ARG_SOURCE_DIR} "${ARG_SOURCE_DIR}/${WILDCARD}")
list(APPEND SQL_FILES ${FILES})
endforeach()
+ list(TRANSFORM SQL_FILES PREPEND "${ARG_SOURCE_DIR}/")
get_property(USERVER_SQL_PYTHON_BINARY GLOBAL PROPERTY userver_sql_python_binary)
get_property(USERVER_SQL_SCRIPTS_PATH GLOBAL PROPERTY userver_scripts_sql)
@@ -50,7 +57,8 @@ function(userver_add_sql_library TARGET)
OUTPUT ${output_files}
COMMAND
${USERVER_SQL_PYTHON_BINARY} ${USERVER_SQL_SCRIPTS_PATH}/generator.py --namespace ${ARG_NAMESPACE}
- --output-dir ${ARG_OUTPUT_DIR} --query-log-mode ${ARG_QUERY_LOG_MODE} --testsuite-output-dir
+ --source-dir ${ARG_SOURCE_DIR} --output-dir ${ARG_OUTPUT_DIR}
+ --query-log-mode ${ARG_QUERY_LOG_MODE} --testsuite-output-dir
${TESTSUITE_OUTPUT_DIR} ${SQL_FILES} ${CODEGEN}
DEPENDS ${SQL_FILES}
)
diff --git a/cmake/abseil_pr_1707.patch b/cmake/abseil_pr_1707.patch
new file mode 100644
index 000000000000..31f44ade0dfd
--- /dev/null
+++ b/cmake/abseil_pr_1707.patch
@@ -0,0 +1,33 @@
+commit 6dee153242d7becebe026a9bed52f4114441719d
+Author: Soo-Hwan Na
+Date: Wed Jul 10 09:20:07 2024 -0700
+
+ PR #1707: Fixup absl_random compile breakage in Apple ARM64 targets
+
+ Imported from GitHub PR https://github.com/abseil/abseil-cpp/pull/1707
+
+ Switched to append a full string of "-Xarch_x86_64 -maes" instead of " -Xarch_x86_64" "-maes", for example. Now cmake correctly appends -Xarch_x86_64 to each x64 specific compile option, removing the error caused in recent clang releases:
+
+ clang++: error: unsupported option '-msse4.1' for target 'arm64-apple-darwin23.5.0'
+
+ Merge 83d17537ee70158d627681a0f0c15f15f30ef838 into f46495ea96f68fc3f6c394f099b2992743f6ff7f
+
+ Merging this change closes #1707
+
+ COPYBARA_INTEGRATE_REVIEW=https://github.com/abseil/abseil-cpp/pull/1707 from Royna2544:patch-1 83d17537ee70158d627681a0f0c15f15f30ef838
+ PiperOrigin-RevId: 651046496
+ Change-Id: Ifdb3848febeead4fb562a2d9f0fdca2e0aea185d
+
+diff --git a/absl/copts/AbseilConfigureCopts.cmake b/absl/copts/AbseilConfigureCopts.cmake
+index 1afb9610..e0007ef8 100644
+--- a/absl/copts/AbseilConfigureCopts.cmake
++++ b/absl/copts/AbseilConfigureCopts.cmake
+@@ -42,7 +42,7 @@ if(APPLE AND CMAKE_CXX_COMPILER_ID MATCHES [[Clang]])
+ string(TOUPPER "${_arch}" _arch_uppercase)
+ string(REPLACE "X86_64" "X64" _arch_uppercase ${_arch_uppercase})
+ foreach(_flag IN LISTS ABSL_RANDOM_HWAES_${_arch_uppercase}_FLAGS)
+- list(APPEND ABSL_RANDOM_RANDEN_COPTS "-Xarch_${_arch}" "${_flag}")
++ list(APPEND ABSL_RANDOM_RANDEN_COPTS "-Xarch_${_arch} ${_flag}")
+ endforeach()
+ endforeach()
+ # If a compiler happens to deal with an argument for a currently unused
diff --git a/cmake/abseil_pr_1739.patch b/cmake/abseil_pr_1739.patch
new file mode 100644
index 000000000000..ca0c47dcfb7b
--- /dev/null
+++ b/cmake/abseil_pr_1739.patch
@@ -0,0 +1,38 @@
+From faf1b03a591f06933da02976119da5743f428e4f Mon Sep 17 00:00:00 2001
+From: Christopher Fore
+Date: Mon, 5 Aug 2024 10:48:19 -0400
+Subject: [PATCH] container/internal: Explicitly include
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+GCC 15 will no longer include by default, resulting in build
+failures in projects that do not explicitly include it.
+
+Error:
+absl/container/internal/container_memory.h:66:27: error: ‘uintptr_t’ does not name a type
+ 66 | assert(reinterpret_cast(p) % Alignment == 0 &&
+ | ^~~~~~~~~
+absl/container/internal/container_memory.h:31:1: note: ‘uintptr_t’ is defined in header ‘’; this is probably fixable by adding ‘#include ’
+ 30 | #include "absl/utility/utility.h"
+ +++ |+#include
+ 31 |
+
+See-also: https://gcc.gnu.org/pipermail/gcc-cvs/2024-August/407124.html
+Signed-off-by: Christopher Fore
+---
+ absl/container/internal/container_memory.h | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/absl/container/internal/container_memory.h b/absl/container/internal/container_memory.h
+index ba8e08a2d22..e7031797018 100644
+--- a/absl/container/internal/container_memory.h
++++ b/absl/container/internal/container_memory.h
+@@ -17,6 +17,7 @@
+
+ #include
+ #include
++#include
+ #include
+ #include
+ #include
diff --git a/cmake/embedded_config.cmake b/cmake/embedded_config.cmake
index e226a44d20d4..727626def2af 100644
--- a/cmake/embedded_config.cmake
+++ b/cmake/embedded_config.cmake
@@ -1,6 +1,6 @@
cmake_policy(SET CMP0053 NEW)
-set(NAMESPACE userver)
+set(NAMESPACE USERVER_NAMESPACE)
set(FILE_IN ${CMAKE_CURRENT_BINARY_DIR}/embedded.h.in)
set(TEMPLATE
"
@@ -35,6 +35,7 @@ APPLE_PREFIX \"@NAME@_end:\\n\"
\".byte 0\\n\"
APPLE_PREFIX \"@NAME@_size:\\n\"
\".int \" APPLE_PREFIX \"@NAME@_end - \" APPLE_PREFIX \"@NAME@_begin\\n\"
+\".previous\\n\"
);
extern \"C\" const char @NAME@_begin[];
@@ -43,7 +44,7 @@ extern \"C\" const int @NAME@_size;
__attribute__((constructor)) void @NAME@_call() {
- utils::RegisterResource(\"@NAME@\", std::string_view{@NAME@_begin, static_cast(@NAME@_size)});
+ ${NAMESPACE}::utils::RegisterResource(\"@NAME@\", std::string_view{@NAME@_begin, static_cast(@NAME@_size)});
}
"
)
diff --git a/cmake/grpc_pr_36805.patch b/cmake/grpc_pr_36805.patch
new file mode 100644
index 000000000000..9a50fabef48e
--- /dev/null
+++ b/cmake/grpc_pr_36805.patch
@@ -0,0 +1,30 @@
+commit e55f69cedd0ef7344e0bcb64b5ec9205e6aa4f04
+Author: Amy Huang
+Date: Wed Jun 5 09:41:28 2024 -0700
+
+ [Fix] new clang -Wmissing-template-arg-list-after-template-kw warning (#36805)
+
+ Clang now requires a template argument list after the use of the template keyword. Edit this
+ instance to remove the template keyword since there are no template arguments.
+
+ See https://github.com/llvm/llvm-project/pull/80801.
+
+ Closes #36805
+
+ COPYBARA_INTEGRATE_REVIEW=https://github.com/grpc/grpc/pull/36805 from amykhuang:master 6f385be2a0f014d1a27fb32e427938c4ced57f83
+ PiperOrigin-RevId: 640554705
+
+diff --git a/src/core/lib/promise/detail/basic_seq.h b/src/core/lib/promise/detail/basic_seq.h
+index 663609d41..5f4199e5d 100644
+--- a/src/core/lib/promise/detail/basic_seq.h
++++ b/src/core/lib/promise/detail/basic_seq.h
+@@ -99,8 +99,7 @@ class BasicSeqIter {
+ }
+ cur_ = next;
+ state_.~State();
+- Construct(&state_,
+- Traits::template CallSeqFactory(f_, *cur_, std::move(arg)));
++ Construct(&state_, Traits::CallSeqFactory(f_, *cur_, std::move(arg)));
+ return PollNonEmpty();
+ });
+ }
diff --git a/cmake/modules/FindPythonDev.cmake b/cmake/modules/FindPythonDev.cmake
index 28c5922e11e8..77babfbf125c 100644
--- a/cmake/modules/FindPythonDev.cmake
+++ b/cmake/modules/FindPythonDev.cmake
@@ -11,19 +11,14 @@ _userver_module_begin(
python
)
+# minimum version is bound by testsuite
_userver_module_find_include(
NAMES
Python.h
PATHS
/usr/include/python3m
- /usr/include/python3.6m
- /usr/include/python3.7m
- /usr/include/python3.8m
/usr/include/python3.9m
/usr/include/python3
- /usr/include/python3.6
- /usr/include/python3.7
- /usr/include/python3.8
/usr/include/python3.9
/usr/include/python3.10
/usr/include/python3.11
diff --git a/cmake/modules/Findc-ares.cmake b/cmake/modules/Findc-ares.cmake
index 22cb379ee89a..d077dccfba44 100644
--- a/cmake/modules/Findc-ares.cmake
+++ b/cmake/modules/Findc-ares.cmake
@@ -13,6 +13,16 @@ _userver_module_begin(
c-ares
PKG_CONFIG_NAMES
libcares
+
+ CPM_NAME c-ares
+ CPM_GITHUB_REPOSITORY c-ares/c-ares
+ CPM_VERSION 1.34.5
+ CPM_OPTIONS
+ "CARES_STATIC ON"
+ "CARES_SHARED OFF"
+ "CARES_INSTALL OFF"
+ "CARES_BUILD_TOOLS OFF"
+ "CARES_STATIC_PIC ON"
)
_userver_module_find_include(NAMES ares.h)
diff --git a/cmake/modules/Findlibev.cmake b/cmake/modules/Findlibev.cmake
index 68022a78f1f0..f030286fcfe9 100644
--- a/cmake/modules/Findlibev.cmake
+++ b/cmake/modules/Findlibev.cmake
@@ -9,6 +9,10 @@ _userver_module_begin(
libev-devel
PACMAN_NAMES
libev
+
+ CPM_NAME libev
+ CPM_URL http://dist.schmorp.de/libev/libev-4.33.tar.gz
+ CPM_DOWNLOAD_ONLY
)
_userver_module_find_include(NAMES ev.h libev/ev.h)
@@ -17,6 +21,52 @@ _userver_module_find_library(NAMES ev)
_userver_module_end()
+function(_userver_execute_process)
+ execute_process(
+ ${ARGV}
+ RESULT_VARIABLE RET
+ )
+ if(NOT ("${RET}" EQUAL 0))
+ message(FATAL_ERROR "Command failed with return code ${RET} (${ARGV})")
+ endif()
+endfunction()
+
if(NOT TARGET libev::libev)
- add_library(libev::libev ALIAS libev)
+ if(TARGET libev)
+ add_library(libev::libev ALIAS libev)
+ elseif(libev_ADDED)
+ # nghttp2 doesn't use find_package(), but calls find_path() and find_library()
+ # so we have to provide libev.a at the _configure_ time, not at build time, =(
+ if(NOT EXISTS ${libev_BINARY_DIR}/.built)
+ _userver_execute_process(
+ COMMAND ${CMAKE_COMMAND} -E copy_directory ${libev_SOURCE_DIR} ${libev_BINARY_DIR}
+ )
+ _userver_execute_process(
+ COMMAND ./configure
+ WORKING_DIRECTORY ${libev_BINARY_DIR}
+ )
+ _userver_execute_process(
+ COMMAND make
+ WORKING_DIRECTORY ${libev_BINARY_DIR}
+ )
+ _userver_execute_process(
+ COMMAND rm -rf ${libev_BINARY_DIR}/.libs/libev.so
+ )
+ _userver_execute_process(
+ COMMAND touch ${libev_BINARY_DIR}/.built
+ )
+ endif()
+
+ add_library(libev STATIC IMPORTED)
+ target_include_directories(libev INTERFACE ${libev_BINARY_DIR})
+ set_target_properties(libev PROPERTIES IMPORTED_LOCATION ${libev_BINARY_DIR}/.libs/libev.a)
+
+ # For nghttp2 installed from CPM
+ list(APPEND CMAKE_INCLUDE_PATH ${libev_BINARY_DIR})
+ list(APPEND CMAKE_LIBRARY_PATH ${libev_BINARY_DIR}/.libs)
+
+ add_library(libev::libev ALIAS libev)
+ else()
+ message(FATAL_ERROR "libev cmake target not found, don't know how to link")
+ endif()
endif()
diff --git a/cmake/modules/Findlibmariadb.cmake b/cmake/modules/Findlibmariadb.cmake
index f513785d9963..840526fe40cc 100644
--- a/cmake/modules/Findlibmariadb.cmake
+++ b/cmake/modules/Findlibmariadb.cmake
@@ -7,6 +7,8 @@ _userver_module_begin(
libmariadb-dev
FORMULA_NAMES
mariadb
+ PACMAN_NAMES
+ mariadb-libs
PKG_CONFIG_NAMES
mariadb
)
diff --git a/cmake/modules/Findlibnghttp2.cmake b/cmake/modules/Findlibnghttp2.cmake
index 968f850663bf..03811d16fb3f 100644
--- a/cmake/modules/Findlibnghttp2.cmake
+++ b/cmake/modules/Findlibnghttp2.cmake
@@ -7,6 +7,16 @@ _userver_module_begin(
nghttp2
PACMAN_NAMES
libnghttp2
+
+ CPM_NAME libnghttp2
+ CPM_GITHUB_REPOSITORY nghttp2/nghttp2
+ CPM_VERSION 1.66.0
+ CPM_GIT_TAG v1.66.0
+ CPM_OPTIONS
+ "BUILD_STATIC_LIBS ON"
+ "BUILD_SHARED_LIBS OFF"
+ "ENABLE_APP OFF"
+ "ENABLE_EXAMPLES OFF"
)
_userver_module_find_include(NAMES nghttp2/nghttp2.h)
@@ -16,5 +26,11 @@ _userver_module_find_library(NAMES nghttp2)
_userver_module_end()
if(NOT TARGET libnghttp2::nghttp2)
- add_library(libnghttp2::nghttp2 ALIAS libnghttp2)
+ if(TARGET libnghttp2)
+ add_library(libnghttp2::nghttp2 ALIAS libnghttp2)
+ elseif(TARGET nghttp2_static)
+ add_library(libnghttp2::nghttp2 ALIAS nghttp2_static)
+ else()
+ message(FATAL_ERROR "libnghttp2{,_static} cmake target not found, don't know how to link")
+ endif()
endif()
diff --git a/cmake/modules/Findyaml-cpp.cmake b/cmake/modules/Findyaml-cpp.cmake
index 042f615f3196..3e10058d8c40 100644
--- a/cmake/modules/Findyaml-cpp.cmake
+++ b/cmake/modules/Findyaml-cpp.cmake
@@ -7,6 +7,10 @@ _userver_module_begin(
yaml-cpp
PACMAN_NAMES
yaml-cpp
+
+ CPM_NAME yaml-cpp
+ CPM_GITHUB_REPOSITORY jbeder/yaml-cpp
+ CPM_GIT_TAG yaml-cpp-0.7.0
)
_userver_module_find_include(NAMES yaml-cpp/yaml.h yaml-cpp/node.h PATH_SUFFIXES include)
diff --git a/cmake/modules/Findzstd.cmake b/cmake/modules/Findzstd.cmake
index 29b5865ab2c2..13ef2650f5e5 100644
--- a/cmake/modules/Findzstd.cmake
+++ b/cmake/modules/Findzstd.cmake
@@ -9,6 +9,8 @@ _userver_module_begin(
libzstd-dev
PACMAN_NAMES
zstd
+
+ # TODO: CPM
)
_userver_module_find_include(NAMES zdict.h zstd.h zstd_errors.h PATH_SUFFIXES include)
@@ -18,7 +20,11 @@ _userver_module_find_library(NAMES zstd PATH_SUFFIXES lib)
_userver_module_end()
if(NOT TARGET zstd::zstd)
- add_library(zstd::zstd ALIAS zstd)
+ if(TARGET libzstd_static)
+ add_library(zstd::zstd ALIAS libzstd_static)
+ else()
+ add_library(zstd::zstd ALIAS zstd)
+ endif()
endif()
if(NOT TARGET ZSTD::ZSTD)
add_library(ZSTD::ZSTD ALIAS zstd)
diff --git a/conanfile.py b/conanfile.py
index 5c084f9db151..d0ba854da730 100644
--- a/conanfile.py
+++ b/conanfile.py
@@ -1,7 +1,6 @@
# pylint: disable=no-member
import os
import platform
-import re
from conan import ConanFile
from conan.errors import ConanInvalidConfiguration
@@ -12,6 +11,7 @@
from conan.tools.files import copy
from conan.tools.files import load
from conan.tools.scm import Git
+from conan.tools.system import package_manager
required_conan_version = '>=2.8.0' # pylint: disable=invalid-name
@@ -104,13 +104,11 @@ def set_version(self):
self,
os.path.join(
os.path.dirname(os.path.realpath(__file__)),
- 'cmake/GetUserverVersion.cmake',
+ 'version.txt',
),
)
- major_version = re.search(r'set\(USERVER_MAJOR_VERSION (.*)\)', content).group(1).strip()
- minor_version = re.search(r'set\(USERVER_MINOR_VERSION (.*)\)', content).group(1).strip()
- self.version = f'{major_version}.{minor_version}' # pylint: disable=attribute-defined-outside-init
+ self.version = content.strip() # pylint: disable=attribute-defined-outside-init
def layout(self):
cmake_layout(self)
@@ -152,7 +150,9 @@ def requirements(self):
)
self.requires('googleapis/cci.20230501')
if self.options.with_postgresql:
- self.requires('libpq/14.5')
+ # `run=True` required to find `pg_config` binary during `psycopg2` python module build
+ # without system package. We use system package.
+ self.requires('libpq/14.9')
if self.options.with_mongodb or self.options.with_kafka:
self.requires('cyrus-sasl/2.1.28')
if self.options.with_mongodb:
@@ -249,6 +249,12 @@ def generate(self):
CMakeDeps(self).generate()
def build(self):
+ # pg_config is required to build psycopg2 from source without system package.
+ # However, this approach fails on later stage, when venv for tests is built.
+ # libpq = self.dependencies["libpq"]
+ # if libpq:
+ # os.environ["PATH"] = os.environ["PATH"] + ":" + libpq.package_folder+ "/bin"
+
cmake = CMake(self)
cmake.configure()
cmake.build()
@@ -261,3 +267,12 @@ def package_info(self):
# https://docs.conan.io/2/examples/tools/cmake/cmake_toolchain/use_package_config_cmake.html
self.cpp_info.set_property('cmake_find_mode', 'none')
self.cpp_info.builddirs.append(os.path.join('lib', 'cmake', 'userver'))
+
+ def system_requirements(self):
+ if self.options.with_postgresql:
+ # pg_config is required to build psycopg2 python module from source at
+ # testsuite venv creation during functional testing of user code.
+ package_manager.Apt(self).install(['libpq-dev'])
+ package_manager.Yum(self).install(['libpq-devel'])
+ package_manager.PacMan(self).install(['libpq-dev'])
+ package_manager.Zypper(self).install(['libpq-devel'])
diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt
index 8cb558eb936d..659b6194f1db 100644
--- a/core/CMakeLists.txt
+++ b/core/CMakeLists.txt
@@ -37,16 +37,13 @@ file(GLOB_RECURSE INTERNAL_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/internal/*.cpp
list(REMOVE_ITEM SOURCES ${INTERNAL_SOURCES})
-find_package(Boost REQUIRED CONFIG COMPONENTS program_options filesystem locale regex iostreams)
find_package_required(ZLIB "zlib1g-dev")
find_package(Iconv REQUIRED)
-_userver_macos_set_default_dir(OPENSSL_ROOT_DIR "brew;--prefix;openssl")
-find_package_required(OpenSSL "libssl-dev")
-find_package(libnghttp2 REQUIRED)
find_package(libev REQUIRED)
+find_package(libnghttp2 REQUIRED)
if(USERVER_CONAN)
find_package(c-ares REQUIRED)
@@ -98,15 +95,26 @@ endif()
target_link_libraries(
${PROJECT_NAME}
- PUBLIC userver-universal Boost::locale CURL::libcurl
- PRIVATE Boost::filesystem
- Boost::program_options
- Boost::iostreams
- Iconv::Iconv
- OpenSSL::Crypto
- OpenSSL::SSL
- ZLIB::ZLIB
+ PUBLIC
+ userver-universal
+ Boost::locale
+ CURL::libcurl
+ PRIVATE
+ Boost::filesystem
+ Boost::program_options
+ Boost::iostreams
+ Iconv::Iconv
+ OpenSSL::SSL
+ OpenSSL::Crypto
+ ZLIB::ZLIB
)
+if(TARGET Boost::endian)
+ # Boost from CPM
+ target_link_libraries(
+ ${PROJECT_NAME}
+ PUBLIC userver-universal Boost::endian Boost::lockfree
+ )
+endif()
add_subdirectory(${USERVER_THIRD_PARTY_DIRS}/llhttp llhttp)
@@ -130,17 +138,24 @@ if(CMAKE_SYSTEM_NAME MATCHES "Darwin" AND CMAKE_SYSTEM_PROCESSOR MATCHES "arm64"
endif()
endif()
-option(USERVER_FEATURE_UBOOST_CORO "Use vendored boost context instead of a system one"
- "${USERVER_UBOOST_CORO_DEFAULT}"
-)
+if("${Boost_VERSION_STRING}" VERSION_LESS "1.88.0")
+ option(USERVER_FEATURE_UBOOST_CORO "Use vendored boost context instead of a system one"
+ "${USERVER_UBOOST_CORO_DEFAULT}"
+ )
+else()
+ message(STATUS "Found a modern Boost version (${Boost_VERSION_STRING}), not using vendored Boost::context and Boost::coroutine2")
+ set(USERVER_FEATURE_UBOOST_CORO OFF)
+endif()
if(USERVER_FEATURE_UBOOST_CORO)
add_subdirectory(${USERVER_THIRD_PARTY_DIRS}/uboost_coro uboost_coro_build)
target_link_libraries(${PROJECT_NAME} PRIVATE userver-uboost-coro)
target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/uboost_coro/include)
else()
- find_package(Boost REQUIRED CONFIG COMPONENTS context coroutine)
target_link_libraries(${PROJECT_NAME} PRIVATE Boost::context)
+ if(NOT USERVER_CONAN AND (TARGET Boost::coroutine2))
+ target_link_libraries(${PROJECT_NAME} PRIVATE Boost::coroutine2)
+ endif()
target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/sys_coro/include)
endif()
@@ -319,3 +334,5 @@ _userver_directory_install(
DIRECTORY "${USERVER_ROOT_DIR}/testsuite/include_tests/"
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/userver/testsuite/include_tests/
)
+
+_userver_install_component(MODULE core DEPENDS chaotic)
diff --git a/core/dynamic_configs/EGRESS_HTTP_PROXY_ENABLED.yaml b/core/dynamic_configs/EGRESS_HTTP_PROXY_ENABLED.yaml
new file mode 100644
index 000000000000..35ff566e5c41
--- /dev/null
+++ b/core/dynamic_configs/EGRESS_HTTP_PROXY_ENABLED.yaml
@@ -0,0 +1,4 @@
+default: false
+description: ''
+schema:
+ type: boolean
diff --git a/core/dynamic_configs/EGRESS_NO_PROXY_TARGETS.yaml b/core/dynamic_configs/EGRESS_NO_PROXY_TARGETS.yaml
new file mode 100644
index 000000000000..24e07b0c0514
--- /dev/null
+++ b/core/dynamic_configs/EGRESS_NO_PROXY_TARGETS.yaml
@@ -0,0 +1,15 @@
+default:
+ targets: []
+description: ''
+schema:
+ type: object
+ additionalProperties: false
+ required:
+ - targets
+ properties:
+ targets:
+ type: array
+ items:
+ type: string
+ pattern: '^([^\/]+)(:\d+)?$'
+ x-taxi-cpp-type: std::unordered_set
diff --git a/core/functional_tests/CMakeLists.txt b/core/functional_tests/CMakeLists.txt
index 7b06961cb8d6..fca7a32ff14d 100644
--- a/core/functional_tests/CMakeLists.txt
+++ b/core/functional_tests/CMakeLists.txt
@@ -29,6 +29,9 @@ add_dependencies(${PROJECT_NAME} ${PROJECT_NAME}-static-service)
add_subdirectory(tracing)
add_dependencies(${PROJECT_NAME} ${PROJECT_NAME}-tracing)
+add_subdirectory(trx_tracker)
+add_dependencies(${PROJECT_NAME} ${PROJECT_NAME}-trx-tracker)
+
add_subdirectory(uctl)
add_dependencies(${PROJECT_NAME} ${PROJECT_NAME}-uctl)
diff --git a/core/functional_tests/metrics/tests/static/metrics_values.txt b/core/functional_tests/metrics/tests/static/metrics_values.txt
index 694908a4c240..2b11b4aa5eb9 100644
--- a/core/functional_tests/metrics/tests/static/metrics_values.txt
+++ b/core/functional_tests/metrics/tests/static/metrics_values.txt
@@ -4,7 +4,7 @@ alerts.config_parse_error: GAUGE 0
alerts.dynamic_debug_invalid_location: GAUGE 0
alerts.log_reopening_error: GAUGE 0
-# Info on cahces
+# Info on caches
cache.any.documents.parse_failures.v2: cache_name=dynamic-config-client-updater RATE 0
cache.any.documents.parse_failures.v2: cache_name=sample-cache RATE 0
cache.any.documents.parse_failures: cache_name=dynamic-config-client-updater GAUGE 0
@@ -114,7 +114,13 @@ engine.coro-pool.stack-usage.is-monitor-active: GAUGE 0
engine.coro-pool.stack-usage.max-usage-percent: GAUGE 0
engine.ev-threads.cpu-load-percent: ev_thread_name=event-worker_0 GAUGE 0
engine.ev-threads.cpu-load-percent: ev_thread_name=event-worker_1 GAUGE 0
+
+# Duration of time the service is running
engine.load-ms: GAUGE 0
+
+# Service startup took this amount of time
+engine.pre-load-ms: GAUGE 0
+
engine.task-processors-load-percent: task_processor=fs-task-processor, thread=0 GAUGE 0
engine.task-processors-load-percent: task_processor=fs-task-processor, thread=1 GAUGE 0
engine.task-processors-load-percent: task_processor=main-task-processor, thread=0 GAUGE 0
@@ -188,6 +194,10 @@ engine.task-processors.worker-threads: task_processor=fs-task-processor GAUGE 0
engine.task-processors.worker-threads: task_processor=main-task-processor GAUGE 0
engine.task-processors.worker-threads: task_processor=monitor-task-processor GAUGE 0
+# How many times heavy operations (like http calls) were called with open DB transaction
+# See userver/utils/trx_tracker.hpp
+engine.heavy-operations-in-transactions: RATE 0
+
# How long the engine is running. It does not account components state (i.e. `components::State::IsAnyComponentInFatalState()`),
# the engine could be fine, the components could be in components::ComponentHealth::kFatal state.
engine.uptime-seconds: GAUGE 0
diff --git a/core/functional_tests/trx_tracker/CMakeLists.txt b/core/functional_tests/trx_tracker/CMakeLists.txt
new file mode 100644
index 000000000000..d01c6df66c24
--- /dev/null
+++ b/core/functional_tests/trx_tracker/CMakeLists.txt
@@ -0,0 +1,8 @@
+project(userver-core-tests-trx-tracker CXX)
+
+file(GLOB_RECURSE SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/*pp")
+add_executable(${PROJECT_NAME} ${SOURCES} "main.cpp")
+target_include_directories(${PROJECT_NAME} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src")
+target_link_libraries(${PROJECT_NAME} userver::core)
+
+userver_chaos_testsuite_add()
diff --git a/core/functional_tests/trx_tracker/main.cpp b/core/functional_tests/trx_tracker/main.cpp
new file mode 100644
index 000000000000..9d946e30ed59
--- /dev/null
+++ b/core/functional_tests/trx_tracker/main.cpp
@@ -0,0 +1,21 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "src/handler.hpp"
+
+int main(int argc, char* argv[]) {
+ const auto component_list = components::MinimalServerComponentList()
+ .Append()
+ .Append()
+ .Append()
+ .Append()
+ .Append()
+ .Append();
+ return utils::DaemonMain(argc, argv, component_list);
+}
diff --git a/core/functional_tests/trx_tracker/src/handler.hpp b/core/functional_tests/trx_tracker/src/handler.hpp
new file mode 100644
index 000000000000..c4f2018f6e19
--- /dev/null
+++ b/core/functional_tests/trx_tracker/src/handler.hpp
@@ -0,0 +1,32 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+
+namespace handlers {
+
+class Handler final : public server::handlers::HttpHandlerBase {
+public:
+ static constexpr std::string_view kName = "handler";
+
+ Handler(const components::ComponentConfig& config, const components::ComponentContext& context)
+ : HttpHandlerBase(config, context) {}
+
+ std::string HandleRequestThrow(const server::http::HttpRequest& /*request*/, server::request::RequestContext&)
+ const override {
+ utils::trx_tracker::TransactionLock lock;
+ lock.Lock();
+ // Tespoint shouldn't actually trigger heavy operation
+ // in transaction check despite being implemented
+ // using http request
+ TESTPOINT("tp", formats::json::Value{});
+ // Other heavy operation in transaction check
+ // should actually trigger
+ utils::trx_tracker::CheckNoTransactions();
+ return "";
+ }
+};
+
+} // namespace handlers
diff --git a/core/functional_tests/trx_tracker/static_config.yaml b/core/functional_tests/trx_tracker/static_config.yaml
new file mode 100644
index 000000000000..ac3d0a481d07
--- /dev/null
+++ b/core/functional_tests/trx_tracker/static_config.yaml
@@ -0,0 +1,53 @@
+components_manager:
+ task_processors:
+ main-task-processor:
+ worker_threads: 4
+
+ fs-task-processor:
+ worker_threads: 2
+
+ monitor-task-processor:
+ thread_name: mon-worker
+ worker_threads: 2
+
+ default_task_processor: main-task-processor
+
+ components:
+ logging:
+ fs-task-processor: fs-task-processor
+ loggers:
+ default:
+ file_path: '@stderr'
+ level: debug
+ overflow_behavior: discard
+
+ server:
+ listener:
+ port: 8080
+ task_processor: main-task-processor
+ listener-monitor:
+ port: 8081
+ task_processor: main-task-processor
+
+ handler:
+ path: /handler
+ method: GET
+ task_processor: main-task-processor
+
+ handler-server-monitor:
+ path: /service/monitor
+ method: GET
+ task_processor: monitor-task-processor
+
+ http-client:
+ fs-task-processor: fs-task-processor
+ user-agent: $server-name
+ user-agent#fallback: 'userver-based-service 1.0'
+ testsuite-support:
+ tests-control:
+ path: /tests/{action}
+ method: POST
+ task_processor: main-task-processor
+ testpoint-timeout: 10s
+ testpoint-url: $mockserver/testpoint
+ throttling_enabled: false
diff --git a/core/functional_tests/trx_tracker/tests/conftest.py b/core/functional_tests/trx_tracker/tests/conftest.py
new file mode 100644
index 000000000000..699ab947e59b
--- /dev/null
+++ b/core/functional_tests/trx_tracker/tests/conftest.py
@@ -0,0 +1 @@
+pytest_plugins = ['pytest_userver.plugins.core']
diff --git a/core/functional_tests/trx_tracker/tests/test_ignore_testpoint.py b/core/functional_tests/trx_tracker/tests/test_ignore_testpoint.py
new file mode 100644
index 000000000000..8ce9dd87d4ee
--- /dev/null
+++ b/core/functional_tests/trx_tracker/tests/test_ignore_testpoint.py
@@ -0,0 +1,18 @@
+HEAVY_OPERAIONS_IN_TRANSACTIONS = 'engine.heavy-operations-in-transactions'
+
+
+async def test_trx_tracker_ignore_testpoint(service_client, monitor_client, testpoint):
+ @testpoint('tp')
+ def tp(data):
+ pass
+
+ response = await service_client.get('/handler')
+ assert response.status == 200
+
+ # Check that testpoint was called
+ assert tp.times_called == 1
+
+ # Check that only manual heavy operation in
+ # transaction check was triggered
+ metrics = await monitor_client.metrics(prefix=HEAVY_OPERAIONS_IN_TRANSACTIONS)
+ assert metrics.value_at(path=HEAVY_OPERAIONS_IN_TRANSACTIONS) == 1
diff --git a/core/functional_tests/websocket/tests/test_websocket.py b/core/functional_tests/websocket/tests/test_websocket.py
index 004a95af7c76..56f0eb2ca449 100644
--- a/core/functional_tests/websocket/tests/test_websocket.py
+++ b/core/functional_tests/websocket/tests/test_websocket.py
@@ -16,6 +16,19 @@ async def test_echo(websocket_client):
assert response == 'hello'
+async def test_echo_with_continuation(websocket_client):
+ async with websocket_client.get('chat') as chat:
+ # Send first fragment (not final, text frame)
+ await chat.write_frame(fin=False, opcode=0x1, data=b'First')
+ # Send intermediate fragment (not final, continuation frame)
+ await chat.write_frame(fin=False, opcode=0x0, data=b' second')
+ # Send last fragment (final, continuation frame)
+ await chat.write_frame(fin=True, opcode=0x0, data=b' third')
+
+ response = await chat.recv()
+ assert response == 'First second third'
+
+
async def test_close_by_server(websocket_client):
async with websocket_client.get('chat') as chat:
await chat.send('close')
@@ -142,3 +155,28 @@ async def test_ping_pong_close(websocket_client):
break
assert connection_closed_by_ping
+
+
+async def test_upgrade_header_with_tab_then_reconnect(service_port):
+ reader, writer = await asyncio.open_connection('localhost', service_port)
+
+ # request with tab in Upgrade header
+ bad_request = (
+ 'GET /chat HTTP/1.1\r\n'
+ 'Sec-WebSocket-Version: 13\r\n'
+ 'Sec-WebSocket-Key: fQU/VaAZ3+lpmSjWKevurQ==\r\n'
+ 'Connection: Upgrade\r\n'
+ 'Upgrade:\twebsocket\r\n'
+ '\r\n'
+ )
+
+ writer.write(bad_request.encode('ascii'))
+ await writer.drain()
+ writer.close()
+ await writer.wait_closed()
+
+ # 2. Check new connection can be established
+ async with websockets.connect(f'ws://localhost:{service_port}/chat') as chat:
+ await chat.send('ping')
+ resp = await chat.recv()
+ assert resp == 'ping'
diff --git a/core/include/userver/cache/expirable_lru_cache.hpp b/core/include/userver/cache/expirable_lru_cache.hpp
index 464466551743..5f23f0ccd7df 100644
--- a/core/include/userver/cache/expirable_lru_cache.hpp
+++ b/core/include/userver/cache/expirable_lru_cache.hpp
@@ -121,6 +121,8 @@ class ExpirableLruCache final {
*/
std::optional GetOptionalNoUpdate(const Key& key);
+ std::optional> GetOptionalNoUpdateWithLastUpdateTime(const Key& key);
+
void Put(const Key& key, const Value& value);
void Put(const Key& key, Value&& value);
@@ -286,21 +288,28 @@ std::optional ExpirableLruCache::GetOptionalUnex
}
template
-std::optional ExpirableLruCache::GetOptionalNoUpdate(const Key& key) {
- auto now = utils::datetime::SteadyNow();
- auto old_value = lru_.Get(key);
-
+std::optional>
+ExpirableLruCache::GetOptionalNoUpdateWithLastUpdateTime(const Key& key) {
+ const auto now = utils::datetime::SteadyNow();
+ const auto old_value = lru_.Get(key);
if (old_value) {
if (!IsExpired(old_value->update_time, now)) {
impl::CacheHit(stats_);
-
- return old_value->value;
+ return old_value;
} else {
impl::CacheStale(stats_);
}
}
impl::CacheMiss(stats_);
+ return std::nullopt;
+}
+template
+std::optional ExpirableLruCache::GetOptionalNoUpdate(const Key& key) {
+ auto value_with_update_time = GetOptionalNoUpdateWithLastUpdateTime(key);
+ if (value_with_update_time.has_value()) {
+ return value_with_update_time->value;
+ }
return std::nullopt;
}
diff --git a/core/include/userver/clients/http/client.hpp b/core/include/userver/clients/http/client.hpp
index eefe720fcdd0..032334c09566 100644
--- a/core/include/userver/clients/http/client.hpp
+++ b/core/include/userver/clients/http/client.hpp
@@ -62,7 +62,11 @@ class DestinationStatistics;
/// @snippet clients/http/client_test.cpp Sample HTTP Client usage
class Client final {
public:
- Client(ClientSettings settings, engine::TaskProcessor& fs_task_processor, impl::PluginPipeline&& plugin_pipeline);
+ Client(
+ ClientSettings settings,
+ engine::TaskProcessor& fs_task_processor,
+ std::vector> plugins
+ );
~Client();
@@ -172,7 +176,7 @@ class Client final {
clients::dns::Resolver* resolver_{nullptr};
utils::NotNull tracing_manager_;
- impl::PluginPipeline plugin_pipeline_;
+ std::vector> plugins_;
};
} // namespace clients::http
diff --git a/core/include/userver/clients/http/plugin.hpp b/core/include/userver/clients/http/plugin.hpp
index 7d0f908af1e8..72369f656cbc 100644
--- a/core/include/userver/clients/http/plugin.hpp
+++ b/core/include/userver/clients/http/plugin.hpp
@@ -91,7 +91,7 @@ namespace impl {
class PluginPipeline final {
public:
- PluginPipeline(const std::vector>& plugins);
+ explicit PluginPipeline(const std::vector>& plugins);
void HookPerformRequest(RequestState& request);
@@ -104,7 +104,7 @@ class PluginPipeline final {
bool HookOnRetry(RequestState& request);
private:
- const std::vector> plugins_;
+ utils::NotNull>*> plugins_;
};
} // namespace impl
diff --git a/core/include/userver/clients/http/request.hpp b/core/include/userver/clients/http/request.hpp
index 4dc922a243e2..687956445035 100644
--- a/core/include/userver/clients/http/request.hpp
+++ b/core/include/userver/clients/http/request.hpp
@@ -89,7 +89,7 @@ class Request final {
RequestStats&& req_stats,
const std::shared_ptr& dest_stats,
clients::dns::Resolver* resolver,
- impl::PluginPipeline& plugin_pipeline,
+ const std::vector>& plugins,
const tracing::TracingManagerBase& tracing_manager
);
/// @endcond
@@ -250,6 +250,9 @@ class Request final {
return *this;
}
+ /// Override list of plugins from @ref components::HttpClient for specific request
+ Request& SetPluginsList(const std::vector>& plugins) &;
+
/// Override log URL. Useful for "there's a secret in the query".
/// @warning The query might be logged by other intermediate HTTP agents
/// (nginx, L7 balancer, etc.).
diff --git a/core/include/userver/clients/http/response_future.hpp b/core/include/userver/clients/http/response_future.hpp
index 49015207f1a4..13d12d4ecf77 100644
--- a/core/include/userver/clients/http/response_future.hpp
+++ b/core/include/userver/clients/http/response_future.hpp
@@ -13,6 +13,7 @@
#include
#include
#include
+#include
USERVER_NAMESPACE_BEGIN
@@ -44,10 +45,10 @@ class ResponseFuture final {
/// @brief Stops the current task execution until the request finishes
/// @throws clients::http::CancelException if the current task is being cancelled
/// @returns std::future_status::ready or std::future_status::timeout
- std::future_status Wait();
+ std::future_status Wait(utils::impl::SourceLocation location = utils::impl::SourceLocation::Current());
/// @brief Wait for the response and return it
- std::shared_ptr Get();
+ std::shared_ptr Get(utils::impl::SourceLocation location = utils::impl::SourceLocation::Current());
void SetCancellationPolicy(CancellationPolicy cp);
diff --git a/core/include/userver/components/manager_controller_component.hpp b/core/include/userver/components/manager_controller_component.hpp
index 5a5458b5fb54..5a3bf391fa0d 100644
--- a/core/include/userver/components/manager_controller_component.hpp
+++ b/core/include/userver/components/manager_controller_component.hpp
@@ -49,6 +49,7 @@ class Manager;
/// preheat_stacktrace_collector | whether to collect a dummy stacktrace at server start up (usable to avoid loading debug info at random point at runtime) | true
/// userver_experiments.*NAME* | whether to enable certain userver experiments; these are gradually enabled by userver team, for internal use only | false
/// graceful_shutdown_interval | at shutdown, first hang for this duration with /ping 5xx to give the balancer a chance to redirect new requests to other hosts | 0s
+/// enable_trx_tracker | Enable checking of heavy operations (like http calls) while having active database transactions. | true
///
/// ## Static task_processor options:
/// Name | Description | Default value
diff --git a/core/include/userver/concurrent/queue.hpp b/core/include/userver/concurrent/queue.hpp
index 2d7b4d820893..6ae1853dda92 100644
--- a/core/include/userver/concurrent/queue.hpp
+++ b/core/include/userver/concurrent/queue.hpp
@@ -278,14 +278,12 @@ class GenericQueue final : public std::enable_shared_from_this
[[nodiscard]] bool Push(Token& token, T&& value, engine::Deadline deadline) {
const std::size_t value_size = QueuePolicy::GetElementSize(value);
- UASSERT(value_size > 0);
return producer_side_.Push(token, std::move(value), deadline, value_size);
}
template
[[nodiscard]] bool PushNoblock(Token& token, T&& value) {
const std::size_t value_size = QueuePolicy::GetElementSize(value);
- UASSERT(value_size > 0);
return producer_side_.PushNoblock(token, std::move(value), value_size);
}
diff --git a/core/include/userver/congestion_control/component.hpp b/core/include/userver/congestion_control/component.hpp
index b1a62f00a4c9..dae0aa80b3b3 100644
--- a/core/include/userver/congestion_control/component.hpp
+++ b/core/include/userver/congestion_control/component.hpp
@@ -14,6 +14,8 @@ USERVER_NAMESPACE_BEGIN
namespace congestion_control {
+class Controller;
+
// clang-format off
/// @ingroup userver_components
@@ -52,6 +54,7 @@ class Component final : public components::ComponentBase {
server::congestion_control::Limiter& GetServerLimiter();
server::congestion_control::Sensor& GetServerSensor();
+ const congestion_control::Controller& GetServerController() const;
private:
void OnConfigUpdate(const dynamic_config::Snapshot& cfg);
diff --git a/core/include/userver/dump/aggregates.hpp b/core/include/userver/dump/aggregates.hpp
index c22c4b86a89e..bfe2652be03f 100644
--- a/core/include/userver/dump/aggregates.hpp
+++ b/core/include/userver/dump/aggregates.hpp
@@ -33,7 +33,7 @@ constexpr bool AreAllDumpable(std::index_sequence) {
template
constexpr bool IsDumpableAggregate() {
- if constexpr (std::is_aggregate_v && !meta::kIsDetected) {
+ if constexpr (std::is_aggregate_v && !meta::IsDetected) {
constexpr auto kSize = boost::pfr::tuple_size_v;
static_assert(
AreAllDumpable(std::make_index_sequence{}),
diff --git a/core/include/userver/dump/meta_containers.hpp b/core/include/userver/dump/meta_containers.hpp
index 0c4b64dc6231..ba0c08f9ebba 100644
--- a/core/include/userver/dump/meta_containers.hpp
+++ b/core/include/userver/dump/meta_containers.hpp
@@ -10,6 +10,7 @@
#include
#include
+#include
USERVER_NAMESPACE_BEGIN
@@ -53,7 +54,7 @@ using InsertResult = decltype(dump::Insert(std::declval(), std::declval
inline constexpr bool kIsContainer = meta::kIsRange && std::is_default_constructible_v && meta::kIsSizable &&
- meta::kIsDetected;
+ meta::IsDetected;
} // namespace dump
diff --git a/core/include/userver/dynamic_config/source.hpp b/core/include/userver/dynamic_config/source.hpp
index 5106bbd23f6b..dde5e393a1a3 100644
--- a/core/include/userver/dynamic_config/source.hpp
+++ b/core/include/userver/dynamic_config/source.hpp
@@ -174,7 +174,7 @@ class Source final {
/// the first time immediately invokes the function with the current config
/// snapshot (this invocation will be executed synchronously).
///
- /// @note Сallbacks occur only if one of the passed config is changed. This is
+ /// @note Callbacks occur only if one of the passed config is changed. This is
/// true under any components::DynamicConfigClientUpdater options.
///
/// @warning To use this function, configs must have the `operator==`.
diff --git a/core/include/userver/engine/io/tls_wrapper.hpp b/core/include/userver/engine/io/tls_wrapper.hpp
index f07d7163e4f8..6757b647f14d 100644
--- a/core/include/userver/engine/io/tls_wrapper.hpp
+++ b/core/include/userver/engine/io/tls_wrapper.hpp
@@ -8,6 +8,7 @@
#include
#include
+#include
#include
#include
#include
@@ -40,13 +41,7 @@ class [[nodiscard]] TlsWrapper final : public RwBase {
);
/// Starts a TLS server on an opened socket
- static TlsWrapper StartTlsServer(
- Socket&& socket,
- const crypto::CertificatesChain& cert_chain,
- const crypto::PrivateKey& key,
- Deadline deadline,
- const std::vector& extra_cert_authorities = {}
- );
+ static TlsWrapper StartTlsServer(Socket&& socket, const crypto::SslCtx& ctx, Deadline deadline);
~TlsWrapper() override;
@@ -72,6 +67,14 @@ class [[nodiscard]] TlsWrapper final : public RwBase {
/// received any more, received bytes count otherwise.
[[nodiscard]] size_t RecvSome(void* buf, size_t len, Deadline deadline);
+ /// @brief Receives up to len bytes from the socket
+ /// @returns
+ /// - nullopt on data absence
+ /// - optional{0} if socket is closed by peer.
+ /// - optional{data_bytes_available} otherwise,
+ /// 1 <= data_bytes_available <= len
+ [[nodiscard]] std::optional RecvNoblock(void* buf, size_t len);
+
/// @brief Receives exactly len bytes from the socket.
/// @note Can return less than len if socket is closed by peer.
[[nodiscard]] size_t RecvAll(void* buf, size_t len, Deadline deadline);
@@ -85,6 +88,14 @@ class [[nodiscard]] TlsWrapper final : public RwBase {
/// socket extraction if interrupted.
[[nodiscard]] Socket StopTls(Deadline deadline);
+ /// @brief Receives up to len bytes from the stream
+ /// @returns
+ /// - nullopt on data absence
+ /// - optional{0} if socket is closed by peer.
+ /// - optional{data_bytes_available} otherwise,
+ /// 1 <= data_bytes_available <= len
+ [[nodiscard]] std::optional ReadNoblock(void* buf, size_t len) override { return RecvNoblock(buf, len); }
+
/// @brief Receives at least one byte from the socket.
/// @returns 0 if connection is closed on one side and no data could be
/// received any more, received bytes count otherwise.
diff --git a/core/include/userver/engine/task/task_with_result.hpp b/core/include/userver/engine/task/task_with_result.hpp
index 7cf2c79d00a8..5eee39c1cf11 100644
--- a/core/include/userver/engine/task/task_with_result.hpp
+++ b/core/include/userver/engine/task/task_with_result.hpp
@@ -54,11 +54,11 @@ class [[nodiscard]] TaskWithResult : public Task {
EnsureValid();
Wait();
+ const utils::FastScopeGuard invalidate([this]() noexcept { Invalidate(); });
if (GetState() == State::kCancelled) {
throw TaskCancelledException(CancellationReason());
}
- const utils::FastScopeGuard invalidate([this]() noexcept { Invalidate(); });
return utils::impl::CastWrappedCall(GetPayload()).Retrieve();
}
diff --git a/core/include/userver/fs/temp_file.hpp b/core/include/userver/fs/temp_file.hpp
index 4b73ca2b14ed..6032bf8ae565 100644
--- a/core/include/userver/fs/temp_file.hpp
+++ b/core/include/userver/fs/temp_file.hpp
@@ -40,7 +40,7 @@ class TempFile final {
TempFile() = delete;
TempFile(TempFile&& other) noexcept = default;
TempFile& operator=(TempFile&& other) noexcept = default;
- ~TempFile();
+ ~TempFile() noexcept;
/// Take ownership of an existing file
static TempFile Adopt(std::string path, engine::TaskProcessor& fs_task_processor);
diff --git a/core/include/userver/logging/component.hpp b/core/include/userver/logging/component.hpp
index 48b63d8e72f3..1296e9c0808f 100644
--- a/core/include/userver/logging/component.hpp
+++ b/core/include/userver/logging/component.hpp
@@ -54,9 +54,9 @@ namespace components {
///
/// ### Logs output
/// You can specify where logs are written in the `file_path` option:
-/// - Use file_path: '@stdout' to write your logs to standard output stream;
-/// - Use file_path: '@stderr' to write your logs to standard error stream;
-/// - Use file_path: '@null' to suppress sending of logs;
+/// - Use file_path: '\@stdout' to write your logs to standard output stream;
+/// - Use file_path: '\@stderr' to write your logs to standard error stream;
+/// - Use file_path: '\@null' to suppress sending of logs;
/// - Use file_path: /absolute/path/to/log/file.log to write your logs to file. Use USR1 signal or @ref server::handlers::OnLogRotate to reopen files after log rotation;
/// - Use file_path: 'unix:/absolute/path/to/logs.sock' to write your logs to unix socket. Socket must be created before the service starts and closed by listener after service is shut down.
///
diff --git a/core/include/userver/logging/logger.hpp b/core/include/userver/logging/logger.hpp
index b46eca0a07d9..36061518e69c 100644
--- a/core/include/userver/logging/logger.hpp
+++ b/core/include/userver/logging/logger.hpp
@@ -44,13 +44,13 @@ LoggerPtr MakeStdoutLogger(const std::string& name, Format format, Level level =
/// @see components::Logging
LoggerPtr MakeFileLogger(const std::string& name, const std::string& path, Format format, Level level = Level::kInfo);
-namespace impl::default_ {
+namespace impl {
bool DoShouldLog(Level) noexcept;
void PrependCommonTags(TagWriter writer, Level logger_level);
-} // namespace impl::default_
+} // namespace impl
} // namespace logging
diff --git a/core/include/userver/server/component.hpp b/core/include/userver/server/component.hpp
index 4abddae83899..4823dbf03e72 100644
--- a/core/include/userver/server/component.hpp
+++ b/core/include/userver/server/component.hpp
@@ -38,7 +38,7 @@ namespace components {
/// ---- | ----------- | -------------
/// logger_access | set to logger name from components::Logging component to write access logs into it; do not set to avoid writing access logs | -
/// logger_access_tskv | set to logger name from components::Logging component to write access logs in TSKV format into it; do not set to avoid writing access logs | -
-/// max_response_size_in_flight | set it to the size of response in bytes and the component will drop bigger responses from handlers that allow throttling | -
+/// max_response_size_in_flight | drop incomming requests if the handler allows throttling and the size of waiting for send responses in bytes is greater than this value | -
/// server-name | value to send in HTTP Server header | value from utils::GetUserverIdentifier()
/// listener | (*required*) *see below* | -
/// listener-monitor | *see below* | -
diff --git a/core/include/userver/server/handlers/auth/digest/auth_checker_base.hpp b/core/include/userver/server/handlers/auth/digest/auth_checker_base.hpp
index b7f326ca0422..63339a88237c 100644
--- a/core/include/userver/server/handlers/auth/digest/auth_checker_base.hpp
+++ b/core/include/userver/server/handlers/auth/digest/auth_checker_base.hpp
@@ -15,6 +15,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -45,9 +46,11 @@ class Hasher final {
/// algorithm.
std::string GetHash(std::string_view data) const;
+ /// @overload
+ std::string GetHash(std::initializer_list data) const;
+
private:
- using HashAlgorithm = std::function;
- HashAlgorithm hash_algorithm_;
+ const HashAlgTypes hash_algorithm_;
const SecdistConfig& secdist_config_;
};
diff --git a/core/include/userver/server/handlers/exceptions.hpp b/core/include/userver/server/handlers/exceptions.hpp
index 0abfde4700ed..d9b9cf1cac97 100644
--- a/core/include/userver/server/handlers/exceptions.hpp
+++ b/core/include/userver/server/handlers/exceptions.hpp
@@ -107,13 +107,13 @@ template
using HasInternalMessage = decltype(std::declval().GetInternalMessage());
template
-inline constexpr bool kHasInternalMessage = meta::kIsDetected;
+inline constexpr bool kHasInternalMessage = meta::IsDetected;
template
using HasExternalBody = decltype(std::declval().GetExternalBody());
template
-inline constexpr bool kHasExternalBody = meta::kIsDetected;
+inline constexpr bool kHasExternalBody = meta::IsDetected;
template
inline constexpr bool kIsMessageBuilder = kHasExternalBody;
@@ -121,7 +121,7 @@ inline constexpr bool kIsMessageBuilder = kHasExternalBody;
template
struct MessageExtractor {
static_assert(
- meta::kIsDetected,
+ meta::IsDetected,
"Please use your message builder to build external body for "
"your error. See server::handlers::CustomHandlerException "
"for more info"
@@ -134,7 +134,7 @@ struct MessageExtractor {
}
std::string GetServiceCode() const {
- if constexpr (meta::kIsDetected