Class: Webhookdb::Snowflake

Inherits:
Object
  • Object
show all
Includes:
Appydays::Configurable, Appydays::Loggable
Defined in:
lib/webhookdb/snowflake.rb

Class Method Summary collapse

Class Method Details

._parse_json(stdout) ⇒ Object

We can’t parse newline delimited json easily, so split it ourselves and parse each document.



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/webhookdb/snowflake.rb', line 73

def self._parse_json(stdout)
  docs = stdout.split("]\n[")
  result = docs.each_with_index.map do |j, i|
    if docs.size > 1
      if i.zero?
        j += "]"
      else
        j = "[" + j
      end
    end
    Oj.load(j.strip)
  end
  return result
rescue StandardError => e
  msg = "error: #{e}, stdout: #{stdout}"
  raise Webhookdb::InvalidPostcondition, "Error parsing snowsql output: #{msg}"
end

.parse_url_to_cli_args(url, format: "json") ⇒ Object

Given a Snowflake URL, return the command line args. Args for the commandline can be traditional URL pieces (host -> account, user/password, etc), or passed as query params. Rules are:

  • Any query param exactly matching accountname/username/dbname/schemaname/rolename/warehouse is used.

  • Any query param matching account/user/db/schema/role is used.

  • URI hostname is used as accountname, basic auth user as username, and uri path as dbname.

  • Password is pulled from query param ‘password’ or uri basic auth password.

Raises:

  • (ArgumentError)


25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/webhookdb/snowflake.rb', line 25

def self.parse_url_to_cli_args(url, format: "json")
  uri = URI(url)
  params = Rack::Utils.parse_query(uri.query)
  password = params["password"] || uri.password
  raise ArgumentError, "must provide password in uri basic auth or query params" if password.blank?
  cli = [
    self.snowsql,
    "-o", "friendly=false",
    "-o", "output_format=#{format}",
    "-o", "timing=false",
    "--accountname", params["accountname"] || params["account"] || uri.hostname || "",
    "--username", params["username"] || params["user"] || uri.user || "",
    "--dbname", params["dbname"] || params["db"] || uri.path&.delete_prefix("/") || "",
  ]
  raise ArgumentError, "url requires account (host), user, and db (or uri path): #{url}" if cli.include?("")

  if (schemaname = params["schemaname"] || params["schema"]).present?
    cli.push("--schemaname", schemaname)
  end
  if (rolename = params["rolename"] || params["role"]).present?
    cli.push("--rolename", rolename)
  end
  cli.push("--warehouse", params["warehouse"]) if params["warehouse"].present?
  return cli, {"SNOWSQL_PWD" => password}
end

.run_cli(url, query, parse: false, format: "json") ⇒ Object



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/webhookdb/snowflake.rb', line 51

def self.run_cli(url, query, parse: false, format: "json")
  args, env = self.parse_url_to_cli_args(url, format:)
  args.push("-q", query)
  stdout, stderr, status = Open3.capture3(env, *args)

  if stderr.blank? && status.success?
    result = if parse.respond_to?(:call)
               parse.call(stdout)
    elsif parse && format == "json"
      self._parse_json(stdout)
    else
      stdout
    end
    return result
  end

  self.logger.error("snowflake_error", stdout:, stderr:, status:, cli_args: args)
  msg = "status: #{status}, stderr: #{stderr}, stdout: #{stdout}, query: #{query}"
  raise Webhookdb::InvalidPostcondition, "snowflake failed: #{msg}"
end