2
0

pizzasql.rb 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. require 'net/http'
  2. require 'uri'
  3. require 'json'
  4. module PizzaSQL
  5. class Client
  6. attr_reader :base_url, :db_name, :api_key
  7. # Creates a new PizzaSQL client connection
  8. #
  9. # @param uri [String] Database URI (e.g., 'http://localhost:8080/mydb')
  10. # @param api_key [String, nil] Optional API key for authentication
  11. # @return [Client] A new client instance
  12. def initialize(uri, api_key = nil)
  13. parsed_uri = URI.parse(uri)
  14. # Extract database name from path
  15. path = parsed_uri.path.strip.delete_prefix('/')
  16. raise ArgumentError, 'Database name not found in URI path' if path.empty?
  17. # Split path to get database name (last segment)
  18. path_parts = path.split('/')
  19. @db_name = path_parts.last
  20. # Reconstruct base URL without the database path
  21. @base_url = "#{parsed_uri.scheme}://#{parsed_uri.host}:#{parsed_uri.port}"
  22. @api_key = api_key
  23. end
  24. # Executes a SQL query and returns the results
  25. #
  26. # @param query [String] SQL query string
  27. # @return [Array<Hash>] Array of rows (each row is a hash)
  28. # @raise [RuntimeError] if the query fails
  29. def sql(query)
  30. uri = URI.parse("#{@base_url}/#{@db_name}/query")
  31. request = Net::HTTP::Post.new(uri)
  32. request['Content-Type'] = 'application/json'
  33. request['Authorization'] = "Bearer #{@api_key}" if @api_key
  34. request.body = { query: query }.to_json
  35. response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
  36. http.request(request)
  37. end
  38. unless response.is_a?(Net::HTTPSuccess)
  39. raise "Request failed with status #{response.code}: #{response.body}"
  40. end
  41. result = JSON.parse(response.body)
  42. result['rows'] || []
  43. end
  44. # Exports database or table data
  45. #
  46. # @param table [String, nil] Table name (nil for entire database)
  47. # @param format [String] Export format: 'sql' or 'csv'
  48. # @return [String] Exported data
  49. # @raise [RuntimeError] if the export fails
  50. def export(table: nil, format: 'sql')
  51. params = {}
  52. params['table'] = table if table
  53. params['format'] = format if format
  54. query_string = URI.encode_www_form(params)
  55. uri = URI.parse("#{@base_url}/#{@db_name}/export?#{query_string}")
  56. request = Net::HTTP::Get.new(uri)
  57. request['Authorization'] = "Bearer #{@api_key}" if @api_key
  58. response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
  59. http.request(request)
  60. end
  61. unless response.is_a?(Net::HTTPSuccess)
  62. raise "Export failed with status #{response.code}: #{response.body}"
  63. end
  64. response.body
  65. end
  66. # Imports data into the database
  67. #
  68. # @param data [String] Data to import
  69. # @param format [String] Import format: 'sql' or 'csv'
  70. # @param create_table [Boolean] Create table if it doesn't exist (CSV only)
  71. # @return [void]
  72. # @raise [RuntimeError] if the import fails
  73. def import(data, format: 'sql', create_table: false)
  74. params = {}
  75. params['format'] = format if format
  76. params['create_table'] = 'true' if create_table
  77. query_string = URI.encode_www_form(params)
  78. uri = URI.parse("#{@base_url}/#{@db_name}/import?#{query_string}")
  79. request = Net::HTTP::Post.new(uri)
  80. request['Content-Type'] = 'application/octet-stream'
  81. request['Authorization'] = "Bearer #{@api_key}" if @api_key
  82. request.body = data
  83. response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
  84. http.request(request)
  85. end
  86. unless response.is_a?(Net::HTTPSuccess)
  87. raise "Import failed with status #{response.code}: #{response.body}"
  88. end
  89. nil
  90. end
  91. end
  92. # Module-level convenience method to create a new client connection
  93. #
  94. # @param uri [String] Database URI
  95. # @param api_key [String, nil] Optional API key
  96. # @return [Client] A new client instance
  97. def self.connect(uri, api_key = nil)
  98. Client.new(uri, api_key)
  99. end
  100. end