legate 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +345 -0
- data/bin/legate +13 -0
- data/examples/00_quickstart.rb +51 -0
- data/examples/01_simple_agent.rb +105 -0
- data/examples/02_multi_tool_agent.rb +140 -0
- data/examples/03_custom_tool.rb +93 -0
- data/examples/04_agent_instructions.rb +84 -0
- data/examples/05_state_and_sessions.rb +91 -0
- data/examples/06_callbacks.rb +186 -0
- data/examples/07_async_jobs.rb +112 -0
- data/examples/08_loop_agent.rb +197 -0
- data/examples/09_sequential_workflow.rb +40 -0
- data/examples/10_parallel_workflow.rb +34 -0
- data/examples/11_agent_delegation.rb +24 -0
- data/examples/12_http_client_tool.rb +156 -0
- data/examples/13_authentication.rb +220 -0
- data/examples/14_mcp_client.rb +154 -0
- data/examples/15_mcp_server.rb +79 -0
- data/examples/16_webhooks.rb +91 -0
- data/examples/README_sequential_agents.md +164 -0
- data/examples/advanced/auth/cookie_auth_tool.rb +146 -0
- data/examples/advanced/auth/custom_auth_flows_example.rb +626 -0
- data/examples/advanced/auth/excon_middleware.rb +317 -0
- data/examples/advanced/auth/excon_middleware_auth.rb +399 -0
- data/examples/advanced/auth/fiber_auth_example.rb +281 -0
- data/examples/advanced/auth/fiber_oidc_example.rb +403 -0
- data/examples/advanced/auth/httpbin_bearer_tool.rb +159 -0
- data/examples/advanced/auth/oauth2_auth.rb +419 -0
- data/examples/advanced/auth/oidc_auth.rb +514 -0
- data/examples/advanced/auth/openweather_api.rb +251 -0
- data/examples/advanced/auth/openweather_tool.rb +153 -0
- data/examples/advanced/auth/query_param_middleware_test.rb +138 -0
- data/examples/advanced/auth/service_account.rb +135 -0
- data/examples/advanced/auth/test_with_httpbin.rb +202 -0
- data/examples/advanced/auth/token_lifecycle_example.rb +428 -0
- data/examples/advanced/callback_monitoring.rb +679 -0
- data/examples/advanced/mas/fixed_delegation_example.rb +191 -0
- data/examples/advanced/mas/loop_workflow.rb +28 -0
- data/examples/advanced/mas/mock_planner.rb +77 -0
- data/examples/advanced/mas/proper_delegation_example.rb +276 -0
- data/examples/advanced/mcp/legate_mcp_server_resource_example.rb +182 -0
- data/examples/advanced/mcp/mcp_resource_server_example.rb +309 -0
- data/examples/advanced/mcp/mcp_server_async.rb +76 -0
- data/examples/advanced/mcp/mcp_server_async_tools.rb +122 -0
- data/examples/advanced/mcp/mcp_server_legate_agent.rb +95 -0
- data/examples/advanced/mcp/mcp_server_rack.rb +89 -0
- data/examples/advanced/random_calculator.rb +104 -0
- data/examples/advanced/sleep_agent.rb +153 -0
- data/examples/advanced/webhooks/webhook_e2e_runner.rb +110 -0
- data/examples/advanced/webhooks/webhook_receiver_agent.rb +58 -0
- data/examples/advanced/workflows/task_refinement_loop_agent.rb +278 -0
- data/examples/advanced/workflows/travel_planner_auto_sequential.rb +444 -0
- data/examples/advanced/workflows/travel_planner_parallel.rb +656 -0
- data/examples/advanced/workflows/travel_planner_sequential.rb +512 -0
- data/examples/tools/oauth2_example.rb +136 -0
- data/examples/tools/sleepy_tool.rb +42 -0
- data/lib/legate/activity_log.rb +71 -0
- data/lib/legate/agent.rb +959 -0
- data/lib/legate/agent_code_generator.rb +185 -0
- data/lib/legate/agent_definition.rb +812 -0
- data/lib/legate/agentic/decision.rb +49 -0
- data/lib/legate/agentic/loop.rb +134 -0
- data/lib/legate/agentic.rb +5 -0
- data/lib/legate/agents/loop_agent.rb +248 -0
- data/lib/legate/agents/parallel_agent.rb +163 -0
- data/lib/legate/agents/sequential_agent.rb +190 -0
- data/lib/legate/agents.rb +14 -0
- data/lib/legate/auth/config.rb +148 -0
- data/lib/legate/auth/coordinator.rb +218 -0
- data/lib/legate/auth/coordinators/oauth2_coordinator.rb +99 -0
- data/lib/legate/auth/coordinators/oidc_coordinator.rb +68 -0
- data/lib/legate/auth/coordinators/service_account_coordinator.rb +122 -0
- data/lib/legate/auth/credential.rb +157 -0
- data/lib/legate/auth/encryption.rb +108 -0
- data/lib/legate/auth/error.rb +94 -0
- data/lib/legate/auth/exchanged_credential.rb +180 -0
- data/lib/legate/auth/excon_middleware.rb +285 -0
- data/lib/legate/auth/http_client_utils.rb +364 -0
- data/lib/legate/auth/manager.rb +531 -0
- data/lib/legate/auth/manager_store.rb +394 -0
- data/lib/legate/auth/middleware_factory.rb +290 -0
- data/lib/legate/auth/runner.rb +279 -0
- data/lib/legate/auth/scheme.rb +125 -0
- data/lib/legate/auth/schemes/api_key.rb +212 -0
- data/lib/legate/auth/schemes/google_service_account.rb +108 -0
- data/lib/legate/auth/schemes/http_bearer.rb +98 -0
- data/lib/legate/auth/schemes/oauth2.rb +396 -0
- data/lib/legate/auth/schemes/openid_connect.rb +346 -0
- data/lib/legate/auth/schemes/service_account.rb +388 -0
- data/lib/legate/auth/schemes.rb +40 -0
- data/lib/legate/auth/token_manager.rb +362 -0
- data/lib/legate/auth/token_store.rb +86 -0
- data/lib/legate/auth/tool_context_extension.rb +97 -0
- data/lib/legate/auth/tool_integration.rb +188 -0
- data/lib/legate/auth/url_guard.rb +81 -0
- data/lib/legate/auth.rb +453 -0
- data/lib/legate/callbacks/callback_context.rb +71 -0
- data/lib/legate/cli/agent_commands.rb +950 -0
- data/lib/legate/cli/auth_commands.rb +520 -0
- data/lib/legate/cli/base_command.rb +24 -0
- data/lib/legate/cli/deployment_commands.rb +934 -0
- data/lib/legate/cli/output_helper.rb +108 -0
- data/lib/legate/cli/session_commands.rb +138 -0
- data/lib/legate/cli/skaffold_commands.rb +223 -0
- data/lib/legate/cli/tool_commands.rb +261 -0
- data/lib/legate/cli/web_commands.rb +182 -0
- data/lib/legate/cli.rb +40 -0
- data/lib/legate/configuration/webhooks.rb +113 -0
- data/lib/legate/configuration.rb +39 -0
- data/lib/legate/definition_store.rb +23 -0
- data/lib/legate/errors.rb +118 -0
- data/lib/legate/event.rb +161 -0
- data/lib/legate/gemini_ai_beta_patch.rb +39 -0
- data/lib/legate/generators/agent_generator.rb +412 -0
- data/lib/legate/generators/code_validator.rb +48 -0
- data/lib/legate/generators/legate/install_generator.rb +35 -0
- data/lib/legate/generators/legate/templates/create_legate_tables.rb.tt +36 -0
- data/lib/legate/generators/legate/templates/initializer.rb +18 -0
- data/lib/legate/generators/runtime_tool_loader.rb +76 -0
- data/lib/legate/generators/tool_generator.rb +408 -0
- data/lib/legate/generators.rb +11 -0
- data/lib/legate/global_definition_registry.rb +506 -0
- data/lib/legate/global_tool_manager.rb +135 -0
- data/lib/legate/llm/adapter.rb +69 -0
- data/lib/legate/llm/gemini.rb +172 -0
- data/lib/legate/llm/ollama.rb +80 -0
- data/lib/legate/llm.rb +34 -0
- data/lib/legate/mcp/client.rb +320 -0
- data/lib/legate/mcp/connection/sse.rb +292 -0
- data/lib/legate/mcp/connection/stdio.rb +273 -0
- data/lib/legate/mcp/connection_manager.rb +103 -0
- data/lib/legate/mcp/server/legate_agent_adapter.rb +170 -0
- data/lib/legate/mcp/server/legate_direct_agent_adapter.rb +140 -0
- data/lib/legate/mcp/server/legate_tool_adapter.rb +119 -0
- data/lib/legate/mcp/tool_wrapper.rb +138 -0
- data/lib/legate/mcp/util/schema_converter.rb +134 -0
- data/lib/legate/mcp.rb +23 -0
- data/lib/legate/plan_executor.rb +375 -0
- data/lib/legate/planner.rb +839 -0
- data/lib/legate/rails/railtie.rb +43 -0
- data/lib/legate/rails.rb +9 -0
- data/lib/legate/redaction.rb +32 -0
- data/lib/legate/session.rb +299 -0
- data/lib/legate/session_service/active_record.rb +300 -0
- data/lib/legate/session_service/base.rb +68 -0
- data/lib/legate/session_service/event_broadcast.rb +74 -0
- data/lib/legate/session_service/in_memory.rb +188 -0
- data/lib/legate/tool/metadata_dsl.rb +122 -0
- data/lib/legate/tool.rb +276 -0
- data/lib/legate/tool_code_generator.rb +103 -0
- data/lib/legate/tool_context.rb +350 -0
- data/lib/legate/tool_loader.rb +39 -0
- data/lib/legate/tool_registry.rb +73 -0
- data/lib/legate/tool_result.rb +61 -0
- data/lib/legate/tools/agent_tool.rb +187 -0
- data/lib/legate/tools/base/http_client.rb +319 -0
- data/lib/legate/tools/base/safe_url.rb +56 -0
- data/lib/legate/tools/base_async_job_tool.rb +91 -0
- data/lib/legate/tools/calculator.rb +89 -0
- data/lib/legate/tools/cat_facts.rb +81 -0
- data/lib/legate/tools/check_job_status_tool.rb +48 -0
- data/lib/legate/tools/current_time_tool.rb +64 -0
- data/lib/legate/tools/echo.rb +43 -0
- data/lib/legate/tools/http_request_tool.rb +105 -0
- data/lib/legate/tools/random_number_tool.rb +64 -0
- data/lib/legate/tools/read_webpage_tool.rb +92 -0
- data/lib/legate/tools/sleepy_tool.rb +74 -0
- data/lib/legate/tools/webhook_tool.rb +146 -0
- data/lib/legate/version.rb +5 -0
- data/lib/legate/web/app.rb +984 -0
- data/lib/legate/web/public/css/main.css +4980 -0
- data/lib/legate/web/public/images/favicon-256.png +0 -0
- data/lib/legate/web/public/images/favicon-32.png +0 -0
- data/lib/legate/web/public/images/legate-logo-dark.png +0 -0
- data/lib/legate/web/public/images/legate-logo-light.png +0 -0
- data/lib/legate/web/public/js/legate.js +616 -0
- data/lib/legate/web/public/styles/main.scss +4402 -0
- data/lib/legate/web/routes/agent_authentication_routes.rb +530 -0
- data/lib/legate/web/routes/agent_definition_routes.rb +803 -0
- data/lib/legate/web/routes/agent_generator_routes.rb +80 -0
- data/lib/legate/web/routes/agent_interaction_routes.rb +734 -0
- data/lib/legate/web/routes/agent_runtime_routes.rb +323 -0
- data/lib/legate/web/routes/api_routes.rb +56 -0
- data/lib/legate/web/routes/authentication_routes.rb +1541 -0
- data/lib/legate/web/routes/core_routes.rb +111 -0
- data/lib/legate/web/routes/documentation_routes.rb +220 -0
- data/lib/legate/web/routes/tool_generator_routes.rb +81 -0
- data/lib/legate/web/routes/tools_ui_routes.rb +207 -0
- data/lib/legate/web/sass_compiler.rb +73 -0
- data/lib/legate/web/views/_active_session_info.slim +25 -0
- data/lib/legate/web/views/_activity_list.slim +55 -0
- data/lib/legate/web/views/_agent_card.slim +56 -0
- data/lib/legate/web/views/_agent_generator_modal.slim +382 -0
- data/lib/legate/web/views/_agent_status_controls.slim +71 -0
- data/lib/legate/web/views/_agent_tool_table.slim +74 -0
- data/lib/legate/web/views/_chat_message.slim +95 -0
- data/lib/legate/web/views/_display_agent_configuration.slim +26 -0
- data/lib/legate/web/views/_display_agent_description.slim +11 -0
- data/lib/legate/web/views/_display_agent_fallback.slim +15 -0
- data/lib/legate/web/views/_display_agent_hierarchy.slim +93 -0
- data/lib/legate/web/views/_display_agent_instruction.slim +17 -0
- data/lib/legate/web/views/_display_agent_mcp.slim +13 -0
- data/lib/legate/web/views/_display_agent_model.slim +17 -0
- data/lib/legate/web/views/_display_agent_name.slim +42 -0
- data/lib/legate/web/views/_display_agent_output_key.slim +26 -0
- data/lib/legate/web/views/_display_agent_type.slim +65 -0
- data/lib/legate/web/views/_edit_agent_configuration.slim +74 -0
- data/lib/legate/web/views/_edit_agent_description.slim +16 -0
- data/lib/legate/web/views/_edit_agent_fallback.slim +25 -0
- data/lib/legate/web/views/_edit_agent_hierarchy.slim +98 -0
- data/lib/legate/web/views/_edit_agent_instruction.slim +49 -0
- data/lib/legate/web/views/_edit_agent_mcp.slim +33 -0
- data/lib/legate/web/views/_edit_agent_model.slim +23 -0
- data/lib/legate/web/views/_edit_agent_output_key.slim +36 -0
- data/lib/legate/web/views/_edit_agent_tools.slim +40 -0
- data/lib/legate/web/views/_edit_agent_type.slim +67 -0
- data/lib/legate/web/views/_session_error.slim +4 -0
- data/lib/legate/web/views/_skeleton.slim +69 -0
- data/lib/legate/web/views/_tool_card.slim +9 -0
- data/lib/legate/web/views/_tool_generator_modal.slim +311 -0
- data/lib/legate/web/views/agent.slim +436 -0
- data/lib/legate/web/views/agent_auth.slim +562 -0
- data/lib/legate/web/views/agents.slim +369 -0
- data/lib/legate/web/views/auth.slim +112 -0
- data/lib/legate/web/views/auth_credential_detail.slim +327 -0
- data/lib/legate/web/views/auth_credentials.slim +261 -0
- data/lib/legate/web/views/auth_debug.slim +94 -0
- data/lib/legate/web/views/auth_mapping_detail.slim +151 -0
- data/lib/legate/web/views/auth_mapping_new.slim +123 -0
- data/lib/legate/web/views/auth_mappings.slim +120 -0
- data/lib/legate/web/views/auth_scheme_detail.slim +274 -0
- data/lib/legate/web/views/auth_schemes.slim +259 -0
- data/lib/legate/web/views/auth_test.slim +418 -0
- data/lib/legate/web/views/chat.slim +192 -0
- data/lib/legate/web/views/docs_index.slim +105 -0
- data/lib/legate/web/views/docs_show.slim +105 -0
- data/lib/legate/web/views/error_404.slim +5 -0
- data/lib/legate/web/views/index.slim +148 -0
- data/lib/legate/web/views/layout.slim +144 -0
- data/lib/legate/web/views/tool_detail.slim +87 -0
- data/lib/legate/web/views/tools.slim +50 -0
- data/lib/legate/web/webhook_listener.rb +367 -0
- data/lib/legate/web.rb +9 -0
- data/lib/legate.rb +220 -0
- data/public/docs/advanced/callbacks.md +828 -0
- data/public/docs/advanced/mcp_schema_conversion.md +59 -0
- data/public/docs/authentication/api_reference/config.md +210 -0
- data/public/docs/authentication/api_reference/credential.md +246 -0
- data/public/docs/authentication/api_reference/encryption.md +218 -0
- data/public/docs/authentication/api_reference/exchanged_credential.md +271 -0
- data/public/docs/authentication/api_reference/excon_middleware.md +175 -0
- data/public/docs/authentication/api_reference/index.md +30 -0
- data/public/docs/authentication/api_reference/scheme.md +250 -0
- data/public/docs/authentication/api_reference/schemes/api_key.md +175 -0
- data/public/docs/authentication/api_reference/schemes/google_service_account.md +221 -0
- data/public/docs/authentication/api_reference/schemes/http_bearer.md +169 -0
- data/public/docs/authentication/api_reference/schemes/oauth2.md +343 -0
- data/public/docs/authentication/api_reference/schemes/oidc.md +73 -0
- data/public/docs/authentication/api_reference/schemes/openid_connect.md +311 -0
- data/public/docs/authentication/api_reference/schemes/service_account.md +287 -0
- data/public/docs/authentication/api_reference/token_manager.md +221 -0
- data/public/docs/authentication/api_reference/token_store.md +146 -0
- data/public/docs/authentication/api_reference/tool_context_extension.md +166 -0
- data/public/docs/authentication/guides/api_key.md +190 -0
- data/public/docs/authentication/guides/bearer.md +172 -0
- data/public/docs/authentication/guides/configuration.md +255 -0
- data/public/docs/authentication/guides/custom_flow.md +523 -0
- data/public/docs/authentication/guides/index.md +24 -0
- data/public/docs/authentication/guides/migration.md +435 -0
- data/public/docs/authentication/guides/oauth2.md +252 -0
- data/public/docs/authentication/guides/oidc.md +241 -0
- data/public/docs/authentication/guides/overview.md +155 -0
- data/public/docs/authentication/guides/secure_storage.md +301 -0
- data/public/docs/authentication/guides/service_account.md +228 -0
- data/public/docs/authentication/guides/token_lifecycle.md +295 -0
- data/public/docs/authentication/guides/web_ui_integration.md +504 -0
- data/public/docs/authentication/index.md +58 -0
- data/public/docs/authentication/troubleshooting/credential_storage.md +550 -0
- data/public/docs/authentication/troubleshooting/environment_variables.md +540 -0
- data/public/docs/authentication/troubleshooting/index.md +11 -0
- data/public/docs/authentication/troubleshooting/oauth2_issues.md +220 -0
- data/public/docs/authentication/troubleshooting/oidc_issues.md +412 -0
- data/public/docs/authentication/troubleshooting/token_refresh.md +338 -0
- data/public/docs/cli/legate_cli_usage.md +363 -0
- data/public/docs/core_concepts/legate_agent_lifecycle.md +124 -0
- data/public/docs/core_concepts/legate_architecture_overview.md +110 -0
- data/public/docs/core_concepts/legate_configuration.md +116 -0
- data/public/docs/core_concepts/legate_definition_store.md +102 -0
- data/public/docs/core_concepts/legate_planner.md +94 -0
- data/public/docs/core_concepts/legate_session_service.md +104 -0
- data/public/docs/error_handling/legate_error_handling.md +122 -0
- data/public/docs/examples.md +199 -0
- data/public/docs/getting_started.md +111 -0
- data/public/docs/guides/agentic_agents.md +137 -0
- data/public/docs/guides/ai_code_generators.md +437 -0
- data/public/docs/guides/auto_loading.md +326 -0
- data/public/docs/guides/configuring_agent_webhooks.md +219 -0
- data/public/docs/guides/http_client_usage.md +264 -0
- data/public/docs/guides/llm_providers.md +137 -0
- data/public/docs/guides/mcp_client_integration.md +232 -0
- data/public/docs/guides/mcp_server_exposure.md +206 -0
- data/public/docs/guides/rails_integration.md +128 -0
- data/public/docs/guides/sending_outbound_webhooks.md +227 -0
- data/public/docs/guides/streaming.md +112 -0
- data/public/docs/guides/webhooks.md +288 -0
- data/public/docs/introduction.md +51 -0
- data/public/docs/multi_agent_systems/advanced_features.md +57 -0
- data/public/docs/multi_agent_systems/agent_delegation.md +190 -0
- data/public/docs/multi_agent_systems/agent_hierarchy.md +49 -0
- data/public/docs/multi_agent_systems/state_management.md +47 -0
- data/public/docs/multi_agent_systems/workflow_agents.md +72 -0
- data/public/docs/tools/legate_built_in_tools.md +332 -0
- data/public/docs/tools/legate_tools_and_registry.md +263 -0
- data/public/docs/web_ui/legate_web_ui.md +137 -0
- metadata +823 -0
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Example of using Legate::Auth with OpenWeatherMap API
|
|
5
|
+
#
|
|
6
|
+
# This example demonstrates different ways to authenticate with OpenWeatherMap API
|
|
7
|
+
# using both direct Excon calls and Legate::Auth middleware.
|
|
8
|
+
#
|
|
9
|
+
# Usage:
|
|
10
|
+
# ruby examples/advanced/auth/openweather_api.rb
|
|
11
|
+
|
|
12
|
+
require 'bundler/setup'
|
|
13
|
+
require 'legate'
|
|
14
|
+
require 'legate/auth' # Explicitly require the auth module
|
|
15
|
+
require 'json'
|
|
16
|
+
require 'excon'
|
|
17
|
+
require 'legate/session_service/in_memory'
|
|
18
|
+
require 'uri'
|
|
19
|
+
|
|
20
|
+
# Enable debug mode
|
|
21
|
+
ENV['DEBUG'] = 'true'
|
|
22
|
+
|
|
23
|
+
# API Key for OpenWeatherMap
|
|
24
|
+
OPENWEATHER_API_KEY = ENV['API_KEY']
|
|
25
|
+
|
|
26
|
+
puts 'OpenWeatherMap API Authentication Example'
|
|
27
|
+
puts '----------------------------------------'
|
|
28
|
+
puts "API Key: #{OPENWEATHER_API_KEY[0..5]}...#{OPENWEATHER_API_KEY[-4..-1]}"
|
|
29
|
+
|
|
30
|
+
# Create session service for token storage
|
|
31
|
+
session_service = Legate::SessionService::InMemory.new
|
|
32
|
+
|
|
33
|
+
# Create a basic token store for caching
|
|
34
|
+
token_store = Legate::Auth::TokenStore.new(session_service)
|
|
35
|
+
|
|
36
|
+
# Create the ApiKey scheme for our OpenWeatherMap API
|
|
37
|
+
api_key_scheme = Legate::Auth::Schemes::ApiKey.new
|
|
38
|
+
|
|
39
|
+
# Create the credential to use with the scheme
|
|
40
|
+
api_key_credential = Legate::Auth::Credential.new(
|
|
41
|
+
auth_type: :api_key,
|
|
42
|
+
api_key: OPENWEATHER_API_KEY,
|
|
43
|
+
location: 'query',
|
|
44
|
+
name: 'appid'
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
puts "\nDEMO 1: Direct API call with Excon (no Legate middleware)"
|
|
48
|
+
puts '------------------------------------------------------'
|
|
49
|
+
|
|
50
|
+
begin
|
|
51
|
+
# Make a direct Excon request to the OpenWeatherMap API
|
|
52
|
+
url = 'https://api.openweathermap.org/data/2.5/weather?q=London,uk&units=metric'
|
|
53
|
+
|
|
54
|
+
# Manually add the API key to the URL
|
|
55
|
+
url_with_key = "#{url}&appid=#{OPENWEATHER_API_KEY}"
|
|
56
|
+
|
|
57
|
+
puts "Making request to: #{url_with_key.gsub(OPENWEATHER_API_KEY, 'API_KEY_REDACTED')}"
|
|
58
|
+
|
|
59
|
+
response = Excon.get(url_with_key,
|
|
60
|
+
connect_timeout: 30,
|
|
61
|
+
read_timeout: 30,
|
|
62
|
+
write_timeout: 30)
|
|
63
|
+
|
|
64
|
+
puts "Response Status: #{response.status}"
|
|
65
|
+
|
|
66
|
+
if response.status == 200
|
|
67
|
+
weather_data = JSON.parse(response.body)
|
|
68
|
+
puts "Location: #{weather_data['name']}, #{weather_data['sys']['country']}"
|
|
69
|
+
puts "Weather: #{weather_data['weather'][0]['main']} - #{weather_data['weather'][0]['description']}"
|
|
70
|
+
puts "Temperature: #{weather_data['main']['temp']}°C (Feels like: #{weather_data['main']['feels_like']}°C)"
|
|
71
|
+
puts "Humidity: #{weather_data['main']['humidity']}%"
|
|
72
|
+
puts "Wind: #{weather_data['wind']['speed']} m/s"
|
|
73
|
+
else
|
|
74
|
+
puts "Error: #{response.status} - #{response.body}"
|
|
75
|
+
end
|
|
76
|
+
rescue StandardError => e
|
|
77
|
+
puts "Request Error: #{e.message}"
|
|
78
|
+
puts e.backtrace.join("\n") if ENV['DEBUG']
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
puts "\nDEMO 2: Using Legate::Auth to apply authentication manually"
|
|
82
|
+
puts '------------------------------------------------------'
|
|
83
|
+
|
|
84
|
+
begin
|
|
85
|
+
# Create a request hash (without authentication)
|
|
86
|
+
request = {
|
|
87
|
+
method: :get,
|
|
88
|
+
path: '/data/2.5/weather',
|
|
89
|
+
query: {
|
|
90
|
+
q: 'Paris,fr',
|
|
91
|
+
units: 'metric'
|
|
92
|
+
},
|
|
93
|
+
headers: {}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
# Use Legate::Auth::ToolIntegration to apply authentication to the request
|
|
97
|
+
puts "Applying authentication with params: #{request.inspect}"
|
|
98
|
+
request_with_auth = Legate::Auth::ToolIntegration.apply_authentication(
|
|
99
|
+
request,
|
|
100
|
+
api_key_scheme,
|
|
101
|
+
api_key_credential,
|
|
102
|
+
token_store
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
puts "Authentication applied: #{request_with_auth.inspect}"
|
|
106
|
+
|
|
107
|
+
# Extract the query parameters - we need to handle this manually
|
|
108
|
+
query_params = request_with_auth[:query]
|
|
109
|
+
|
|
110
|
+
# Add appid parameter manually if it's not already present
|
|
111
|
+
query_hash = if query_params.is_a?(Hash)
|
|
112
|
+
# If it's already a hash, we can use it directly
|
|
113
|
+
query_params
|
|
114
|
+
elsif query_params.is_a?(String)
|
|
115
|
+
# Parse the query string into a hash
|
|
116
|
+
params = {}
|
|
117
|
+
URI.decode_www_form(query_params).each do |key, value|
|
|
118
|
+
params[key] = value
|
|
119
|
+
end
|
|
120
|
+
params
|
|
121
|
+
else
|
|
122
|
+
# Default to an empty hash if we can't parse it
|
|
123
|
+
{ q: 'Paris,fr', units: 'metric' }
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Make sure the API key is in the query parameters
|
|
127
|
+
query_hash['appid'] ||= OPENWEATHER_API_KEY
|
|
128
|
+
|
|
129
|
+
# Construct the full URL from the request with proper query params
|
|
130
|
+
base_url = 'https://api.openweathermap.org'
|
|
131
|
+
path = request_with_auth[:path]
|
|
132
|
+
|
|
133
|
+
# Convert the hash to a query string
|
|
134
|
+
query_string = URI.encode_www_form(query_hash)
|
|
135
|
+
|
|
136
|
+
# Full URL
|
|
137
|
+
full_url = "#{base_url}#{path}?#{query_string}"
|
|
138
|
+
puts "Request URL with auth applied: #{full_url.gsub(OPENWEATHER_API_KEY, 'API_KEY_REDACTED')}"
|
|
139
|
+
|
|
140
|
+
# Make the request
|
|
141
|
+
response = Excon.get(full_url,
|
|
142
|
+
connect_timeout: 30,
|
|
143
|
+
read_timeout: 30,
|
|
144
|
+
write_timeout: 30)
|
|
145
|
+
|
|
146
|
+
puts "Response Status: #{response.status}"
|
|
147
|
+
|
|
148
|
+
if response.status == 200
|
|
149
|
+
weather_data = JSON.parse(response.body)
|
|
150
|
+
puts "Location: #{weather_data['name']}, #{weather_data['sys']['country']}"
|
|
151
|
+
puts "Weather: #{weather_data['weather'][0]['main']} - #{weather_data['weather'][0]['description']}"
|
|
152
|
+
puts "Temperature: #{weather_data['main']['temp']}°C (Feels like: #{weather_data['main']['feels_like']}°C)"
|
|
153
|
+
puts "Humidity: #{weather_data['main']['humidity']}%"
|
|
154
|
+
puts "Wind: #{weather_data['wind']['speed']} m/s"
|
|
155
|
+
else
|
|
156
|
+
puts "Error: #{response.status} - #{response.body}"
|
|
157
|
+
end
|
|
158
|
+
rescue StandardError => e
|
|
159
|
+
puts "Request Error: #{e.message}"
|
|
160
|
+
puts e.backtrace.join("\n") if ENV['DEBUG']
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
puts "\nDEMO 3: Using API Key directly with Excon"
|
|
164
|
+
puts '-------------------------------------'
|
|
165
|
+
|
|
166
|
+
begin
|
|
167
|
+
# Create a request with direct query params approach
|
|
168
|
+
puts 'Making direct query parameter request...'
|
|
169
|
+
|
|
170
|
+
# Use a direct approach without middleware
|
|
171
|
+
query_params = {
|
|
172
|
+
q: 'Tokyo,jp',
|
|
173
|
+
units: 'metric',
|
|
174
|
+
appid: OPENWEATHER_API_KEY
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
# Create the connection
|
|
178
|
+
connection = Excon.new('https://api.openweathermap.org',
|
|
179
|
+
connect_timeout: 10,
|
|
180
|
+
read_timeout: 10,
|
|
181
|
+
write_timeout: 10)
|
|
182
|
+
|
|
183
|
+
# Make the request directly
|
|
184
|
+
response = connection.request(
|
|
185
|
+
method: :get,
|
|
186
|
+
path: '/data/2.5/weather',
|
|
187
|
+
query: query_params
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
puts "Response Status: #{response.status}"
|
|
191
|
+
|
|
192
|
+
if response.status == 200
|
|
193
|
+
weather_data = JSON.parse(response.body)
|
|
194
|
+
puts "Location: #{weather_data['name']}, #{weather_data['sys']['country']}"
|
|
195
|
+
puts "Weather: #{weather_data['weather'][0]['main']} - #{weather_data['weather'][0]['description']}"
|
|
196
|
+
puts "Temperature: #{weather_data['main']['temp']}°C (Feels like: #{weather_data['main']['feels_like']}°C)"
|
|
197
|
+
puts "Humidity: #{weather_data['main']['humidity']}%"
|
|
198
|
+
puts "Wind: #{weather_data['wind']['speed']} m/s"
|
|
199
|
+
else
|
|
200
|
+
puts "Error: #{response.status} - #{response.body}"
|
|
201
|
+
end
|
|
202
|
+
rescue StandardError => e
|
|
203
|
+
puts "Request Error: #{e.message}"
|
|
204
|
+
puts e.backtrace.join("\n") if ENV['DEBUG']
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
puts "\nDEMO 4: Using Legate::Auth middleware (with fixed implementation)"
|
|
208
|
+
puts '---------------------------------------------------------'
|
|
209
|
+
|
|
210
|
+
begin
|
|
211
|
+
puts 'Creating connection with Legate::Auth middleware...'
|
|
212
|
+
|
|
213
|
+
# Use the fixed middleware via Legate::Auth.create_api_key_connection
|
|
214
|
+
connection = Legate::Auth.create_api_key_connection(
|
|
215
|
+
'https://api.openweathermap.org',
|
|
216
|
+
api_key: OPENWEATHER_API_KEY,
|
|
217
|
+
location: 'query',
|
|
218
|
+
name: 'appid',
|
|
219
|
+
token_store: token_store
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
puts 'Connection created. Making request to get weather for New York...'
|
|
223
|
+
|
|
224
|
+
# Make the request through the middleware
|
|
225
|
+
response = connection.request(
|
|
226
|
+
method: :get,
|
|
227
|
+
path: '/data/2.5/weather',
|
|
228
|
+
query: {
|
|
229
|
+
q: 'New York,us',
|
|
230
|
+
units: 'metric'
|
|
231
|
+
}
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
puts "Response Status: #{response.status}"
|
|
235
|
+
|
|
236
|
+
if response.status == 200
|
|
237
|
+
weather_data = JSON.parse(response.body)
|
|
238
|
+
puts "Location: #{weather_data['name']}, #{weather_data['sys']['country']}"
|
|
239
|
+
puts "Weather: #{weather_data['weather'][0]['main']} - #{weather_data['weather'][0]['description']}"
|
|
240
|
+
puts "Temperature: #{weather_data['main']['temp']}°C (Feels like: #{weather_data['main']['feels_like']}°C)"
|
|
241
|
+
puts "Humidity: #{weather_data['main']['humidity']}%"
|
|
242
|
+
puts "Wind: #{weather_data['wind']['speed']} m/s"
|
|
243
|
+
else
|
|
244
|
+
puts "Error: #{response.status} - #{response.body}"
|
|
245
|
+
end
|
|
246
|
+
rescue StandardError => e
|
|
247
|
+
puts "Request Error: #{e.message}"
|
|
248
|
+
puts e.backtrace.join("\n") if ENV['DEBUG']
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
puts "\nExample complete."
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Example of using Legate::Auth with OpenWeatherMap API using the tool-based approach
|
|
5
|
+
# and proper authentication handling through HttpClient.
|
|
6
|
+
#
|
|
7
|
+
# Usage:
|
|
8
|
+
# ruby examples/advanced/auth/openweather_tool.rb
|
|
9
|
+
|
|
10
|
+
require 'bundler/setup'
|
|
11
|
+
require 'legate'
|
|
12
|
+
require 'legate/auth'
|
|
13
|
+
require 'json'
|
|
14
|
+
# API Key for OpenWeatherMap
|
|
15
|
+
OPENWEATHER_API_KEY = ENV['API_KEY']
|
|
16
|
+
|
|
17
|
+
puts 'OpenWeatherMap API Tool-Based Authentication Example'
|
|
18
|
+
puts '------------------------------------------------'
|
|
19
|
+
puts "API Key: #{OPENWEATHER_API_KEY[0..5]}...#{OPENWEATHER_API_KEY[-4..-1]}"
|
|
20
|
+
|
|
21
|
+
# First, let's create a tool class for OpenWeather API
|
|
22
|
+
module Legate
|
|
23
|
+
module Tools
|
|
24
|
+
class OpenWeather < Legate::Tool
|
|
25
|
+
include Legate::Tools::Base::HttpClient
|
|
26
|
+
|
|
27
|
+
# Tool metadata
|
|
28
|
+
tool_description 'Fetches weather data from OpenWeatherMap API'
|
|
29
|
+
|
|
30
|
+
parameter :city,
|
|
31
|
+
type: :string,
|
|
32
|
+
description: 'The city to get weather for',
|
|
33
|
+
required: true
|
|
34
|
+
|
|
35
|
+
parameter :country_code,
|
|
36
|
+
type: :string,
|
|
37
|
+
description: 'The country code (e.g., uk, us, jp)',
|
|
38
|
+
required: true
|
|
39
|
+
|
|
40
|
+
parameter :units,
|
|
41
|
+
type: :string,
|
|
42
|
+
description: 'The units to use (metric or imperial)',
|
|
43
|
+
required: false
|
|
44
|
+
|
|
45
|
+
def initialize(options = {})
|
|
46
|
+
super()
|
|
47
|
+
@auth_scheme = options[:auth_scheme]
|
|
48
|
+
@auth_credential = options[:auth_credential]
|
|
49
|
+
setup_http_client(
|
|
50
|
+
base_url: 'https://api.openweathermap.org',
|
|
51
|
+
options: {
|
|
52
|
+
connect_timeout: 3,
|
|
53
|
+
read_timeout: 3,
|
|
54
|
+
write_timeout: 3
|
|
55
|
+
}
|
|
56
|
+
)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
def perform_execution(params, _context)
|
|
62
|
+
# Extract parameters
|
|
63
|
+
city = params[:city]
|
|
64
|
+
country_code = params[:country_code]
|
|
65
|
+
units = params.fetch(:units, 'metric')
|
|
66
|
+
|
|
67
|
+
# Prepare query parameters
|
|
68
|
+
query = {
|
|
69
|
+
q: "#{city},#{country_code}",
|
|
70
|
+
units: units
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
# If we have auth credentials, let them be applied through the auth scheme
|
|
74
|
+
if @auth_scheme && @auth_credential
|
|
75
|
+
request = { query: query }
|
|
76
|
+
modified_request = @auth_scheme.apply_to_request(request, @auth_credential)
|
|
77
|
+
query = modified_request[:query]
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Make the request using HttpClient's helper
|
|
81
|
+
response = http_get('/data/2.5/weather', query: query)
|
|
82
|
+
|
|
83
|
+
# Parse and return the response
|
|
84
|
+
begin
|
|
85
|
+
data = JSON.parse(response.body)
|
|
86
|
+
{ status: :success, result: data }
|
|
87
|
+
rescue JSON::ParserError => e
|
|
88
|
+
raise Legate::ToolError, "Failed to parse API response: #{e.message}"
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
puts "\nDEMO 1: Using OpenWeather Tool with API Key Authentication"
|
|
96
|
+
puts '-----------------------------------------------------'
|
|
97
|
+
|
|
98
|
+
begin
|
|
99
|
+
# Create an API Key scheme
|
|
100
|
+
scheme = Legate::Auth::Schemes::ApiKey.new
|
|
101
|
+
|
|
102
|
+
# Create a credential with the API key
|
|
103
|
+
credential = Legate::Auth::Credential.new(
|
|
104
|
+
auth_type: :api_key,
|
|
105
|
+
api_key: OPENWEATHER_API_KEY,
|
|
106
|
+
location: 'query',
|
|
107
|
+
name: 'appid'
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
# Create our OpenWeather tool instance with authentication
|
|
111
|
+
weather_tool = Legate::Tools::OpenWeather.new(
|
|
112
|
+
auth_scheme: scheme,
|
|
113
|
+
auth_credential: credential
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
# Example cities to check weather for
|
|
117
|
+
cities = [
|
|
118
|
+
{ city: 'London', country: 'uk' },
|
|
119
|
+
{ city: 'Tokyo', country: 'jp' },
|
|
120
|
+
{ city: 'New York', country: 'us' }
|
|
121
|
+
]
|
|
122
|
+
|
|
123
|
+
# Get weather for each city
|
|
124
|
+
cities.each do |location|
|
|
125
|
+
puts "\nChecking weather for #{location[:city]}, #{location[:country].upcase}..."
|
|
126
|
+
|
|
127
|
+
begin
|
|
128
|
+
result = weather_tool.execute(
|
|
129
|
+
city: location[:city],
|
|
130
|
+
country_code: location[:country]
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
if result[:status] == :success
|
|
134
|
+
weather_data = result[:result]
|
|
135
|
+
puts "Location: #{weather_data['name']}, #{weather_data['sys']['country']}"
|
|
136
|
+
puts "Weather: #{weather_data['weather'][0]['main']} - #{weather_data['weather'][0]['description']}"
|
|
137
|
+
puts "Temperature: #{weather_data['main']['temp']}°C (Feels like: #{weather_data['main']['feels_like']}°C)"
|
|
138
|
+
puts "Humidity: #{weather_data['main']['humidity']}%"
|
|
139
|
+
puts "Wind: #{weather_data['wind']['speed']} m/s"
|
|
140
|
+
else
|
|
141
|
+
puts "Error: #{result[:error_message]}"
|
|
142
|
+
end
|
|
143
|
+
rescue StandardError => e
|
|
144
|
+
puts "Error getting weather for #{location[:city]}: #{e.message}"
|
|
145
|
+
puts e.backtrace.join("\n") if ENV['DEBUG']
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
rescue StandardError => e
|
|
149
|
+
puts "Error: #{e.message}"
|
|
150
|
+
puts e.backtrace.join("\n") if ENV['DEBUG']
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
puts "\nExample complete."
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Test focusing on query parameter handling in middleware
|
|
5
|
+
#
|
|
6
|
+
# Usage:
|
|
7
|
+
# ruby examples/advanced/auth/query_param_middleware_test.rb
|
|
8
|
+
|
|
9
|
+
require 'bundler/setup'
|
|
10
|
+
require 'json'
|
|
11
|
+
require 'excon'
|
|
12
|
+
require 'uri'
|
|
13
|
+
|
|
14
|
+
# Middleware that correctly adds a query parameter
|
|
15
|
+
class QueryParamMiddleware < Excon::Middleware::Base
|
|
16
|
+
def initialize(stack)
|
|
17
|
+
puts 'QueryParamMiddleware initialized'
|
|
18
|
+
@stack = stack
|
|
19
|
+
@api_key = 'test-api-key-123'
|
|
20
|
+
@api_key_name = 'apikey'
|
|
21
|
+
super(stack)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def request_call(datum)
|
|
25
|
+
puts 'QueryParamMiddleware request_call invoked'
|
|
26
|
+
puts " Method: #{datum[:method]}, Path: #{datum[:path]}"
|
|
27
|
+
puts " Original query: #{datum[:query].inspect}"
|
|
28
|
+
puts " Connect timeout: #{datum[:connect_timeout]}"
|
|
29
|
+
puts " Read timeout: #{datum[:read_timeout]}"
|
|
30
|
+
puts " Write timeout: #{datum[:write_timeout]}"
|
|
31
|
+
|
|
32
|
+
# Add API key to query parameters
|
|
33
|
+
query = datum[:query] || {}
|
|
34
|
+
|
|
35
|
+
# Convert query to Hash if it's a string
|
|
36
|
+
if query.is_a?(String)
|
|
37
|
+
params = {}
|
|
38
|
+
URI.decode_www_form(query).each do |key, value|
|
|
39
|
+
params[key] = value
|
|
40
|
+
end
|
|
41
|
+
query = params
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Add API key parameter
|
|
45
|
+
query[@api_key_name] = @api_key
|
|
46
|
+
|
|
47
|
+
# Update the datum with the modified query
|
|
48
|
+
datum[:query] = query
|
|
49
|
+
|
|
50
|
+
# Debug the updated query
|
|
51
|
+
puts " Modified query: #{datum[:query].inspect}"
|
|
52
|
+
|
|
53
|
+
# Continue with the middleware stack - pass through the modified datum
|
|
54
|
+
@stack.request_call(datum)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def response_call(datum)
|
|
58
|
+
puts 'QueryParamMiddleware response_call invoked'
|
|
59
|
+
# Pass the datum through unmodified
|
|
60
|
+
@stack.response_call(datum)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
puts 'Query Parameter Middleware Test'
|
|
65
|
+
puts '----------------------------'
|
|
66
|
+
|
|
67
|
+
puts "\nTest 1: Direct API call with Excon"
|
|
68
|
+
puts '-------------------------------'
|
|
69
|
+
|
|
70
|
+
begin
|
|
71
|
+
# Make a direct Excon request
|
|
72
|
+
url = 'https://httpbin.org/get?test=value'
|
|
73
|
+
|
|
74
|
+
puts "Making direct request to: #{url}"
|
|
75
|
+
|
|
76
|
+
# Create the connection with normal timeouts
|
|
77
|
+
response = Excon.get(url,
|
|
78
|
+
connect_timeout: 10,
|
|
79
|
+
read_timeout: 10,
|
|
80
|
+
write_timeout: 10)
|
|
81
|
+
|
|
82
|
+
puts "Response Status: #{response.status}"
|
|
83
|
+
|
|
84
|
+
if response.status == 200
|
|
85
|
+
data = JSON.parse(response.body)
|
|
86
|
+
puts "Data received: #{data['args'].inspect}"
|
|
87
|
+
else
|
|
88
|
+
puts "Error: #{response.status} - #{response.body}"
|
|
89
|
+
end
|
|
90
|
+
rescue StandardError => e
|
|
91
|
+
puts "Request Error: #{e.message}"
|
|
92
|
+
puts e.backtrace.join("\n")
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
puts "\nTest 2: Query Parameter Middleware"
|
|
96
|
+
puts '-------------------------------'
|
|
97
|
+
|
|
98
|
+
begin
|
|
99
|
+
# Create a connection with our custom middleware
|
|
100
|
+
connection = Excon.new('https://httpbin.org',
|
|
101
|
+
middlewares: [
|
|
102
|
+
Excon::Middleware::ResponseParser,
|
|
103
|
+
Excon::Middleware::Expects,
|
|
104
|
+
Excon::Middleware::Idempotent,
|
|
105
|
+
Excon::Middleware::Instrumentor,
|
|
106
|
+
Excon::Middleware::Mock,
|
|
107
|
+
QueryParamMiddleware # Our query parameter middleware
|
|
108
|
+
],
|
|
109
|
+
connect_timeout: 10,
|
|
110
|
+
read_timeout: 10,
|
|
111
|
+
write_timeout: 10)
|
|
112
|
+
|
|
113
|
+
puts 'Connection created with query parameter middleware'
|
|
114
|
+
puts 'Making request...'
|
|
115
|
+
|
|
116
|
+
# Make the request
|
|
117
|
+
response = connection.request(
|
|
118
|
+
method: :get,
|
|
119
|
+
path: '/get',
|
|
120
|
+
query: {
|
|
121
|
+
test: 'value'
|
|
122
|
+
}
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
puts "Response Status: #{response.status}"
|
|
126
|
+
|
|
127
|
+
if response.status == 200
|
|
128
|
+
data = JSON.parse(response.body)
|
|
129
|
+
puts "Data received: #{data['args'].inspect}"
|
|
130
|
+
else
|
|
131
|
+
puts "Error: #{response.status} - #{response.body}"
|
|
132
|
+
end
|
|
133
|
+
rescue StandardError => e
|
|
134
|
+
puts "Request Error: #{e.message}"
|
|
135
|
+
puts e.backtrace.join("\n")
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
puts "\nTest complete."
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Example of using the ServiceAccount authentication with automatic token exchange and refresh
|
|
5
|
+
#
|
|
6
|
+
# This example demonstrates how to authenticate using a service account JSON key file
|
|
7
|
+
# and make requests that are automatically authenticated.
|
|
8
|
+
#
|
|
9
|
+
# Usage:
|
|
10
|
+
# ruby examples/advanced/auth/service_account.rb [path/to/service_account_key.json]
|
|
11
|
+
|
|
12
|
+
require 'bundler/setup'
|
|
13
|
+
require 'legate'
|
|
14
|
+
require 'json'
|
|
15
|
+
require 'fileutils'
|
|
16
|
+
|
|
17
|
+
# Check if a service account key file was provided
|
|
18
|
+
key_file_path = ARGV[0] || ENV['SERVICE_ACCOUNT_KEY_FILE']
|
|
19
|
+
unless key_file_path
|
|
20
|
+
puts 'Error: Please provide a service account key JSON file as an argument'
|
|
21
|
+
puts 'Usage: ruby examples/advanced/auth/service_account.rb [path/to/service_account_key.json]'
|
|
22
|
+
puts 'Alternatively, set the SERVICE_ACCOUNT_KEY_FILE environment variable'
|
|
23
|
+
exit 1
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Ensure the key file exists
|
|
27
|
+
unless File.exist?(key_file_path)
|
|
28
|
+
puts "Error: Service account key file not found: #{key_file_path}"
|
|
29
|
+
exit 1
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Load the service account key file
|
|
33
|
+
begin
|
|
34
|
+
service_account_key = JSON.parse(File.read(key_file_path))
|
|
35
|
+
puts "Loaded service account key for: #{service_account_key['client_email']}"
|
|
36
|
+
rescue JSON::ParserError => e
|
|
37
|
+
puts "Error parsing service account key file: #{e.message}"
|
|
38
|
+
exit 1
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Create the Auth::Credential for the service account
|
|
42
|
+
# You can use either the raw JSON key or the individual components
|
|
43
|
+
credential = if ENV['USE_RAW_JSON'] == 'true'
|
|
44
|
+
Legate::Auth::Credential.new(
|
|
45
|
+
auth_type: :service_account,
|
|
46
|
+
service_account_key: File.read(key_file_path)
|
|
47
|
+
)
|
|
48
|
+
else
|
|
49
|
+
Legate::Auth::Credential.new(
|
|
50
|
+
auth_type: :service_account,
|
|
51
|
+
client_email: service_account_key['client_email'],
|
|
52
|
+
private_key: service_account_key['private_key'],
|
|
53
|
+
token_uri: service_account_key['token_uri']
|
|
54
|
+
)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Create a Google Service Account scheme with the appropriate scopes
|
|
58
|
+
# You can use other service account implementations as well
|
|
59
|
+
scheme = Legate::Auth::Schemes::GoogleServiceAccount.new(
|
|
60
|
+
scopes: ['https://www.googleapis.com/auth/cloud-platform']
|
|
61
|
+
# Optional custom audience if needed:
|
|
62
|
+
# audience: 'https://your-api.example.com'
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
# Create a session service to manage authentication state
|
|
66
|
+
session_service = Legate::SessionService::InMemory.new
|
|
67
|
+
|
|
68
|
+
# Create a token store (optional but recommended for caching tokens)
|
|
69
|
+
token_store = Legate::Auth::TokenStore.new(session_service: session_service)
|
|
70
|
+
|
|
71
|
+
# Create the service account coordinator
|
|
72
|
+
coordinator = Legate::Auth::Coordinators::ServiceAccountCoordinator.new(
|
|
73
|
+
scheme: scheme,
|
|
74
|
+
credential: credential,
|
|
75
|
+
session_service: session_service,
|
|
76
|
+
token_store: token_store
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# Authenticate and get the token
|
|
80
|
+
begin
|
|
81
|
+
token = Legate::Auth.authenticate_with_coordinator(coordinator)
|
|
82
|
+
puts 'Successfully authenticated with service account'
|
|
83
|
+
puts "Access token: #{token.access_token[0..10]}... (expires in #{token[:expires_in]} seconds)"
|
|
84
|
+
rescue Legate::Auth::Error => e
|
|
85
|
+
puts "Authentication failed: #{e.message}"
|
|
86
|
+
exit 1
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Example of making an authenticated request with the token
|
|
90
|
+
require 'faraday'
|
|
91
|
+
|
|
92
|
+
puts "\nMaking an authenticated request..."
|
|
93
|
+
conn = Faraday.new(url: 'https://www.googleapis.com/storage/v1') do |builder|
|
|
94
|
+
builder.request :authorization, 'Bearer', token.access_token
|
|
95
|
+
builder.request :json
|
|
96
|
+
builder.response :json
|
|
97
|
+
builder.adapter Faraday.default_adapter
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Example: List Google Cloud Storage buckets
|
|
101
|
+
begin
|
|
102
|
+
response = conn.get('b', { project: service_account_key['project_id'] })
|
|
103
|
+
|
|
104
|
+
if response.success?
|
|
105
|
+
puts "Request succeeded with status: #{response.status}"
|
|
106
|
+
puts "Found #{response.body['items']&.length || 0} buckets"
|
|
107
|
+
|
|
108
|
+
if response.body['items']&.any?
|
|
109
|
+
puts "\nBucket names:"
|
|
110
|
+
response.body['items'].each do |bucket|
|
|
111
|
+
puts "- #{bucket['name']}"
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
else
|
|
115
|
+
puts "Request failed with status: #{response.status}"
|
|
116
|
+
puts "Error: #{response.body['error']&.dig('message') || response.body.inspect}"
|
|
117
|
+
end
|
|
118
|
+
rescue StandardError => e
|
|
119
|
+
puts "Request failed: #{e.message}"
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
puts "\nToken refresh demonstration:"
|
|
123
|
+
puts "Current token will expire in #{token[:expires_in]} seconds"
|
|
124
|
+
puts 'Explicitly refreshing token...'
|
|
125
|
+
|
|
126
|
+
# Example of refreshing a token
|
|
127
|
+
begin
|
|
128
|
+
refreshed_token = coordinator.refresh(token)
|
|
129
|
+
puts 'Token refreshed successfully'
|
|
130
|
+
puts "New access token: #{refreshed_token.access_token[0..10]}... (expires in #{refreshed_token[:expires_in]} seconds)"
|
|
131
|
+
rescue Legate::Auth::Error => e
|
|
132
|
+
puts "Token refresh failed: #{e.message}"
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
puts "\nDemonstration completed successfully!"
|