pgchief 0.1.0 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 72ba9fb5a28f3128a49fa7944b30154be2de8b785a24295bae9c3b4544c29c3d
4
- data.tar.gz: b049ed99e5365e1fd78d727947950aa99e3e5426d9aa76bb4fd77ffdea091d73
3
+ metadata.gz: 756ccf173c3d0cbe56bb969e2f7d20e9032e1a7b1c600155e212548fa81f93bb
4
+ data.tar.gz: c00f369d5e6013bedb955daba82da8c54ad2a77c978b2ead411cae3ef0ca5f0f
5
5
  SHA512:
6
- metadata.gz: b8cfe8f92033c986892e551b8cdc6ff1f7847ed91010ec38ffdd73d2283000b02ae70b9669a79eb4509d5c69c2449fd8f8891d2a5d7a75ccf2372ab141f83087
7
- data.tar.gz: d8ee3dadb057da4338ab8467040f286c4de2f4383a62a162204de8a686da42734aab4ccfe7e768dc53ef94962b432c3e4be8269129f2ce10bde727bd31129318
6
+ metadata.gz: 5ec3b0a81250bd7c8b479780ec658adf5a51f7f19db08c0447f1470ba4477cee24c7a00affec84ba5db0cf136a18e02de636e1d2f6701f0b6aa13d3ffee88afa
7
+ data.tar.gz: 828bf9aebb8235af34a0462f2d2890e5d51245e98841c97644a1d4411ba9147d5f9a1b594c2367f0dd34a96dff47f45201d0d312e8b862e90f27f83b5aad4514
data/.rubocop.yml CHANGED
@@ -18,5 +18,17 @@ Metrics/BlockLength:
18
18
  - 'spec/**/*'
19
19
  - '*.gemspec'
20
20
 
21
+ Lint/MixedRegexpCaptureTypes:
22
+ Enabled: false
23
+
21
24
  RSpec/MultipleExpectations:
22
25
  Max: 5
26
+
27
+ RSpec/ExampleLength:
28
+ Max: 20
29
+
30
+ Lint/MissingSuper:
31
+ Enabled: false
32
+
33
+ Metrics/ParameterLists:
34
+ Enabled: false
data/CHANGELOG.md CHANGED
@@ -1,6 +1,59 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project will try its best to adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
1
8
  ## [Unreleased]
2
9
 
3
- ## [0.1.0] - 2024-07-20
10
+ ### Additions
11
+
12
+ ### Changes
13
+
14
+ ### Fixes
15
+
16
+ ## [0.3.0]
17
+
18
+ ### Additions
19
+
20
+ - Refactor `exe/pgchief` to utilize `TTY::Option` for kicking off config initialization
21
+ - `pgchief --init` now creates a toml config file in your `$HOME`
22
+ - Added ability to store credentials if your config sets `credentials_file`
23
+ when db's and users are created
24
+ - Added `ConnectionString` class that abstracts the base db connection,
25
+ allowing for additions of users and db's
26
+ - Load everything in the config file to the Config attributes
27
+
28
+ ### Changes
29
+
30
+ - Default location of config changed from `~/.pgchief.toml` to `~/.config/pgchief/config.toml`
31
+ - Automatically require 'pry' in the test suite
32
+
33
+ ### Fixes
34
+
35
+ - When dropping user, ignore whenever a database has no privileges for the
36
+ selected user
37
+ - Retroactive addition of tests to cover any regressions
38
+
39
+ ## [0.2.0] - 2024-08-30
40
+
41
+ ### Additions
42
+
43
+ - Add `j` and `k` keys as substitutes for `↑` and `↓`.
44
+ - Allow exiting the program with the `esc` key.
45
+ - Add ability to grant access privileges for newly created users.
46
+ - Or grant privileges for existing users to database(s).
47
+
48
+ ### Fixes
49
+
50
+ - GitHub now running CI successfully.
51
+ - Newly created databases are no longer open for connection by default.
52
+ `CONNECT` is revoked by default for them.
53
+ - When dropping users, loop through all the databases they have access to and
54
+ revoke access before dropping them.
55
+
56
+ ## [0.1.0] - 2024-08-30
4
57
 
5
58
  - Initial release
6
59
  - Create database ✅
@@ -8,3 +61,8 @@
8
61
  - Drop database ✅
9
62
  - Drop user ✅
10
63
  - List databases ✅
64
+
65
+ [Unreleased]: https://github.com/jayroh/pgchief/compare/v0.3.0...HEAD
66
+ [0.3.0]: https://github.com/jayroh/pgchief/releases/tag/v0.3.0
67
+ [0.2.0]: https://github.com/jayroh/pgchief/releases/tag/v0.2.0
68
+ [0.1.0]: https://github.com/jayroh/pgchief/releases/tag/v0.1.0
data/README.md CHANGED
@@ -21,30 +21,58 @@ below for the feature check-list and current progress.
21
21
 
22
22
  ***
23
23
 
24
- ## Usage:
24
+ ## Usage
25
25
 
26
- ```
26
+ ```sh
27
27
  gem install pgchief
28
28
 
29
+ # To initialize the config file at `~/.pgchief.toml`:
30
+
31
+ pgchief --init
32
+
29
33
  # make sure the DATABASE_URL is set to the connection string for a pg server's superuser
30
34
  export DATABASE_URL=postgresql://postgres:password@postgres.local:5432
31
35
 
32
36
  pgchief
33
37
  ```
34
38
 
39
+ ## Config
40
+
41
+ Format of `~/.pgchief.toml`
42
+
43
+ ```toml
44
+ # Connection string to superuser account at your PG instance
45
+ pgurl = "postgresql://username:password@host:5432"
46
+
47
+ # Directory where db backups will be placed
48
+ backup_dir = "~/.pgchief/backups"
49
+
50
+ # ** OPTIONAL **
51
+
52
+ # Location of encrypted database connection strings
53
+ # credentials_file = "~/.pgchief/credentials"
54
+ ```
55
+
56
+ Note:
57
+
58
+ 1. Prompts accept both `↑` and `↓` arrows, as well as `j` and `k`.
59
+ 2. Pressing the `esc` key at any point amidst a prompt will exit out of the program.
60
+
35
61
  ## Development of the gem
36
62
 
37
63
  1. Clone this repo.
38
64
  2. `bundle install`
39
65
  3. `cp .env.sample .env`
40
66
  4. Edit `.env` and change:
41
- * `DATABASE_URL` to point to your main pg instance's superuser account with a connection string.
42
- * `TEST_DATABASE_URL` to point to your local pg instance where tests can be run against.
67
+ * `DATABASE_URL` to point to your main pg instance's superuser account with a
68
+ connection string.
69
+ * `TEST_DATABASE_URL` to point to your local pg instance where tests can be
70
+ run against.
43
71
  5. `bundle exec rake` to run test suite & rubocop.
44
72
 
45
- ## The ideal, aspirational, DX:
73
+ ## The ideal, aspirational, DX
46
74
 
47
- ```
75
+ ```sh
48
76
  $ pgchief --init # create the TOML file in your home dir (w/600 permissions)
49
77
  $ pgchief
50
78
 
@@ -87,26 +115,17 @@ Give "rando-username" access to database(s):
87
115
  # ... etc.
88
116
  ```
89
117
 
90
- Format of `~/.pgchief.toml`
91
-
92
- ```toml
93
- pgurl = "postgres://username:password@host:5432"
94
- backup_dir = "~/.pg_backups"
95
-
96
- # [optional] encryption key (to display hashed passwords)
97
- # encryption_key = "my-password"
98
- ```
99
-
100
118
  ***
101
119
 
102
120
  ## Feature Roadmap
103
121
 
104
- - [x] Create database
105
- - [x] Create user
106
- - [x] Drop database
107
- - [x] Drop user
108
- - [x] List databases
109
- - [ ] Give user permissions to use database
110
- - [ ] Back up database
111
- - [ ] Restore database
112
- - [ ] Initialize toml file
122
+ * [x] Create database
123
+ * [x] Create user
124
+ * [x] Drop database
125
+ * [x] Drop user
126
+ * [x] List databases
127
+ * [x] Give user permissions to use database
128
+ * [x] Initialize toml file
129
+ * [x] Display connection information
130
+ * [ ] Back up database
131
+ * [ ] Restore database
@@ -0,0 +1,10 @@
1
+ # Connection string to superuser account at your PG instance
2
+ pgurl = "postgresql://localhost:5432"
3
+
4
+ # Directory where db backups will be placed
5
+ backup_dir = "~/.config/pgchief/backups"
6
+
7
+ # ** OPTIONAL **
8
+
9
+ # Location of saved database connection strings
10
+ # credentials_file = "~/.config/pgchief/credentials"
data/exe/pgchief CHANGED
@@ -7,4 +7,6 @@ $LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
7
7
 
8
8
  require "pgchief"
9
9
 
10
- Pgchief::Prompt::Start.call
10
+ cli = Pgchief::Cli.new
11
+ cli.parse(ARGV)
12
+ cli.run
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pgchief
4
+ # Command line interface and option parsing
5
+ class Cli
6
+ include TTY::Option
7
+
8
+ option :init do
9
+ short "-i"
10
+ long "--init"
11
+ desc "Initialize the TOML configuration file"
12
+ end
13
+
14
+ def run
15
+ if params[:init]
16
+ Pgchief::Command::ConfigCreate.call
17
+ else
18
+ Pgchief::Prompt::Start.call
19
+ end
20
+ end
21
+ end
22
+ end
@@ -12,7 +12,7 @@ module Pgchief
12
12
 
13
13
  def initialize(*params)
14
14
  @params = params
15
- @conn = PG.connect(ENV.fetch("DATABASE_URL"))
15
+ @conn = PG.connect(Pgchief::DATABASE_URL)
16
16
  end
17
17
 
18
18
  def call
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+
5
+ module Pgchief
6
+ module Command
7
+ # Create a configuration file at $HOME
8
+ class ConfigCreate < Base
9
+ def call(dir: "#{Dir.home}/.config/pgchief")
10
+ return if File.exist?("#{dir}/config.toml")
11
+
12
+ template = File.join(__dir__, "..", "..", "..", "config", "pgchief.toml")
13
+ FileUtils.mkdir_p(dir)
14
+ FileUtils.cp(template, "#{dir}/config.toml")
15
+
16
+ puts "Configuration file created at #{dir}/config.toml"
17
+ end
18
+ end
19
+ end
20
+ end
@@ -11,6 +11,7 @@ module Pgchief
11
11
  raise Pgchief::Errors::DatabaseExistsError if db_exists?
12
12
 
13
13
  conn.exec("CREATE DATABASE #{database}")
14
+ conn.exec("REVOKE CONNECT ON DATABASE #{database} FROM PUBLIC")
14
15
 
15
16
  "Database '#{database}' created successfully!"
16
17
  rescue PG::Error => e
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pgchief
4
+ module Command
5
+ # Class to grant database privileges
6
+ class DatabasePrivilegesGrant < Base
7
+ attr_reader :username, :password, :database
8
+
9
+ def initialize(*params)
10
+ @username = params[0]
11
+ @password = params[1]
12
+ @databases = params[2]
13
+ end
14
+
15
+ def call
16
+ @databases.each do |database|
17
+ @database = database
18
+ grant_privs!
19
+ store_credentials!
20
+ end
21
+
22
+ "Privileges granted to #{username} on #{@databases.join(", ")}"
23
+ end
24
+
25
+ private
26
+
27
+ def grant_privs! # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
28
+ conn = PG.connect("#{Pgchief::DATABASE_URL}/#{database}")
29
+ conn.exec("GRANT CONNECT ON DATABASE #{database} TO #{username};")
30
+ conn.exec("GRANT CREATE ON SCHEMA public TO #{username};")
31
+ conn.exec("GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO #{username};")
32
+ conn.exec("GRANT USAGE ON SCHEMA public TO #{username};")
33
+ conn.exec(
34
+ <<~SQL
35
+ ALTER DEFAULT PRIVILEGES IN SCHEMA public
36
+ GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES
37
+ TO #{username};
38
+ SQL
39
+ )
40
+ conn.close
41
+ rescue PG::Error => e
42
+ "Error: #{e.message}"
43
+ ensure
44
+ conn.finished? || conn.close
45
+ end
46
+
47
+ def store_credentials!
48
+ Pgchief::Command::StoreConnectionString.call(connection_string)
49
+ end
50
+
51
+ def connection_string
52
+ ConnectionString.new(
53
+ Pgchief::DATABASE_URL,
54
+ username: username,
55
+ password: password,
56
+ database: database
57
+ ).to_s
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pgchief
4
+ module Command
5
+ # Class to view database connection string
6
+ class RetrieveConnectionString < Base
7
+ attr_reader :username, :database
8
+
9
+ def initialize(username, database = nil)
10
+ @username = username
11
+ @database = database
12
+ @connection_string = nil
13
+ end
14
+
15
+ def call
16
+ File.foreach(Config.credentials_file) do |line|
17
+ @connection_string = line if regex.match?(line)
18
+ end
19
+
20
+ @connection_string.nil? ? "No connection string found" : @connection_string
21
+ end
22
+
23
+ private
24
+
25
+ def regex
26
+ if database
27
+ /#{username}.*#{database}$/
28
+ else
29
+ /#{username}.*\d$/
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pgchief
4
+ module Command
5
+ # Class to store connection string
6
+ class StoreConnectionString
7
+ def self.call(connection_string)
8
+ new(connection_string).call
9
+ end
10
+
11
+ attr_reader :connection_string
12
+
13
+ def initialize(connection_string)
14
+ @connection_string = connection_string
15
+ end
16
+
17
+ def call
18
+ File.open(Config.credentials_file, "a") do |file|
19
+ file.puts @connection_string
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -18,7 +18,8 @@ module Pgchief
18
18
  @username, @password = params
19
19
  raise Pgchief::Errors::UserExistsError if user_exists?
20
20
 
21
- conn.exec("CREATE USER #{username} WITH #{user_options} PASSWORD '#{password}'")
21
+ create_user!
22
+ save_credentials!
22
23
 
23
24
  "User '#{username}' created successfully!"
24
25
  rescue PG::Error => e
@@ -34,9 +35,25 @@ module Pgchief
34
35
  conn.exec(query).any?
35
36
  end
36
37
 
38
+ def create_user!
39
+ conn.exec("CREATE USER #{username} WITH #{user_options} PASSWORD '#{password}'")
40
+ end
41
+
42
+ def save_credentials!
43
+ Pgchief::Command::StoreConnectionString.call(connection_string)
44
+ end
45
+
37
46
  def user_options
38
47
  USER_OPTIONS.join(" ")
39
48
  end
49
+
50
+ def connection_string
51
+ ConnectionString.new(
52
+ Pgchief::DATABASE_URL,
53
+ username: username,
54
+ password: password
55
+ ).to_s
56
+ end
40
57
  end
41
58
  end
42
59
  end
@@ -10,7 +10,8 @@ module Pgchief
10
10
  @username = params.first
11
11
  raise Pgchief::Errors::UserExistsError unless user_exists?
12
12
 
13
- conn.exec("DROP USER #{username}")
13
+ revoke_all_privileges
14
+ drop_user
14
15
 
15
16
  "User '#{username}' dropped successfully!"
16
17
  rescue PG::Error => e
@@ -19,10 +20,37 @@ module Pgchief
19
20
  conn.close
20
21
  end
21
22
 
23
+ private
24
+
22
25
  def user_exists?
23
26
  query = "SELECT 1 FROM pg_user WHERE usename = '#{username}'"
24
27
  conn.exec(query).any?
25
28
  end
29
+
30
+ def revoke_all_privileges
31
+ databases_with_access.each do |database|
32
+ conn.exec("REVOKE ALL PRIVILEGES ON DATABASE #{database} FROM #{username};")
33
+ rescue PG::Error
34
+ next
35
+ end
36
+ end
37
+
38
+ def drop_user
39
+ conn.exec("DROP USER #{username}")
40
+ end
41
+
42
+ def databases_with_access
43
+ @databases_with_access ||= begin
44
+ results = conn.exec <<~SQL
45
+ SELECT datname
46
+ FROM pg_database
47
+ WHERE has_database_privilege('#{username}', datname, 'CONNECT')
48
+ AND datname NOT IN ('postgres', 'template1', 'template0')
49
+ SQL
50
+
51
+ results.map { |row| row["datname"] }
52
+ end
53
+ end
26
54
  end
27
55
  end
28
56
  end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "toml-rb"
4
+
5
+ module Pgchief
6
+ # Class to store configuration settings
7
+ class Config
8
+ class << self
9
+ attr_accessor \
10
+ :backup_dir,
11
+ :credentials_file,
12
+ :pgurl
13
+
14
+ def load_config!(toml_file = "#{Dir.home}/.config/pgchief/config.toml")
15
+ config = TomlRB.load_file(toml_file, symbolize_keys: true)
16
+
17
+ @backup_dir = config[:backup_dir].gsub("~", Dir.home)
18
+ @credentials_file = config[:credentials_file]&.gsub("~", Dir.home)
19
+ @pgurl = config[:pgurl]
20
+ end
21
+
22
+ def set_up_file_structure!
23
+ FileUtils.mkdir_p(backup_dir)
24
+
25
+ return unless credentials_file && !File.exist?(credentials_file)
26
+
27
+ FileUtils.touch(credentials_file)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pgchief
4
+ # Class to parse and manipulate connection strings
5
+ class ConnectionString
6
+ URL_REGEX = %r{(?x)\A
7
+ postgres(ql)?://
8
+ (?<username>[^:@]*)?
9
+ :?(?<password>[^@]*)?
10
+ @?(?<host>[^:]*)?
11
+ :?(?<port>\d+)?
12
+ /?(?<database>[^\?]*)?
13
+ \z}
14
+
15
+ attr_reader :database_url
16
+
17
+ def initialize(
18
+ database_url,
19
+ username: nil,
20
+ password: nil,
21
+ host: nil,
22
+ port: nil,
23
+ database: nil
24
+ )
25
+ @database_url = database_url
26
+ @host = host
27
+ @username = username
28
+ @password = password
29
+ @port = port
30
+ @database = database
31
+ end
32
+
33
+ def to_s
34
+ "postgresql://#{username}:#{password}@#{host}:#{port}/#{database}"
35
+ end
36
+
37
+ def host
38
+ @host || (matched[:username] if matched[:host].empty?) || matched[:host]
39
+ end
40
+
41
+ def username
42
+ @username || ("" if matched[:host].empty? && !matched[:username].empty?) || matched[:username] || ""
43
+ end
44
+
45
+ def password
46
+ @password || matched[:password] || ""
47
+ end
48
+
49
+ def port
50
+ @port || matched[:port] || "5432"
51
+ end
52
+
53
+ def database
54
+ @database || matched[:database] || ""
55
+ end
56
+
57
+ private
58
+
59
+ def matched
60
+ @matched ||= database_url.match(URL_REGEX) || {}
61
+ end
62
+ end
63
+ end
@@ -6,7 +6,7 @@ module Pgchief
6
6
  # Database information and operations
7
7
  class Database
8
8
  def self.all
9
- conn = PG.connect(ENV.fetch("DATABASE_URL"))
9
+ conn = PG.connect(Pgchief::DATABASE_URL)
10
10
  result = conn.exec("SELECT datname FROM pg_database WHERE datistemplate = false")
11
11
  result
12
12
  .map { |row| row["datname"] }
@@ -4,16 +4,45 @@ module Pgchief
4
4
  module Prompt
5
5
  # Base class for prompt classes
6
6
  class Base
7
- def self.class
8
- raise "Method not defined"
7
+ def self.call(*params)
8
+ new(*params).call
9
9
  end
10
10
 
11
- def self.klassify(scope, words)
11
+ attr_reader :params
12
+
13
+ def initialize(*params)
14
+ @params = params
15
+ end
16
+
17
+ def call
18
+ raise NotImplementedError
19
+ end
20
+
21
+ def klassify(scope, words)
12
22
  Object.const_get([
13
23
  "Pgchief", "::", scope.capitalize, "::",
14
24
  words.split.map(&:capitalize)
15
25
  ].flatten.join)
16
26
  end
27
+
28
+ def yes_or_no(question, yes: nil, no: nil) # rubocop:disable Naming/MethodParameterName
29
+ response = prompt.yes?(question)
30
+ response ? yes&.call : no&.call
31
+ end
32
+
33
+ def prompt
34
+ @prompt ||= TTY::Prompt.new.tap do |p|
35
+ p.on(:keypress) do |event|
36
+ p.trigger(:keydown) if event.value == "j"
37
+ p.trigger(:keyup) if event.value == "k"
38
+ end
39
+
40
+ p.on(:keyescape) do
41
+ p.say "\n\nExiting...bye-bye 👋\n\n"
42
+ exit
43
+ end
44
+ end
45
+ end
17
46
  end
18
47
  end
19
48
  end
@@ -4,8 +4,7 @@ module Pgchief
4
4
  module Prompt
5
5
  # Class to ask for database name, in order to create it
6
6
  class CreateDatabase < Base
7
- def self.call
8
- prompt = TTY::Prompt.new
7
+ def call
9
8
  database = prompt.ask("Database name:")
10
9
  result = Pgchief::Command::DatabaseCreate.call(database)
11
10
 
@@ -4,13 +4,17 @@ module Pgchief
4
4
  module Prompt
5
5
  # Class to prompt for user creation details
6
6
  class CreateUser < Base
7
- def self.call
8
- prompt = TTY::Prompt.new
7
+ def call
9
8
  username = prompt.ask("Username:")
10
9
  password = prompt.mask("Password:")
11
10
  result = Pgchief::Command::UserCreate.call(username, password)
12
11
 
13
12
  prompt.say result
13
+
14
+ yes_or_no(
15
+ "Give \"#{username}\" access to database(s)?",
16
+ yes: -> { Pgchief::Prompt::GrantDatabasePrivileges.call(username, password) }
17
+ )
14
18
  end
15
19
  end
16
20
  end
@@ -4,7 +4,7 @@ module Pgchief
4
4
  module Prompt
5
5
  # Class to manage database operations
6
6
  class DatabaseManagement < Base
7
- def self.call
7
+ def call
8
8
  prompt = TTY::Prompt.new
9
9
  result = prompt.select("Database management", ["Create database", "Drop database", "Database List"])
10
10
  scope = result == "Database List" ? "command" : "prompt"
@@ -4,8 +4,7 @@ module Pgchief
4
4
  module Prompt
5
5
  # Class to prompt for which database to drop
6
6
  class DropDatabase < Base
7
- def self.call
8
- prompt = TTY::Prompt.new
7
+ def call
9
8
  database = prompt.select("Which database needs to be dropped?", Pgchief::Database.all)
10
9
  result = Pgchief::Command::DatabaseDrop.call(database)
11
10
 
@@ -4,8 +4,7 @@ module Pgchief
4
4
  module Prompt
5
5
  # Class to prompt for which user to drop
6
6
  class DropUser < Base
7
- def self.call
8
- prompt = TTY::Prompt.new
7
+ def call
9
8
  user = prompt.select("Which user needs to be deleted?", Pgchief::User.all)
10
9
  result = Pgchief::Command::UserDrop.call(user)
11
10
 
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pgchief
4
+ module Prompt
5
+ # Class to ask for database names, in order to create it
6
+ class GrantDatabasePrivileges < Base
7
+ def call
8
+ username = params[0] || select_user
9
+ password = params[1] || ask_for_password
10
+ databases = params[2] || prompt.multi_select("Give \"#{username}\" access to database(s):",
11
+ Pgchief::Database.all)
12
+
13
+ result = Pgchief::Command::DatabasePrivilegesGrant.call(username, password, databases)
14
+ prompt.say result
15
+ end
16
+
17
+ def select_user
18
+ prompt.select("Select user to update:", Pgchief::User.all)
19
+ end
20
+
21
+ def ask_for_password
22
+ prompt.mask("Password:")
23
+ end
24
+ end
25
+ end
26
+ end
@@ -4,8 +4,9 @@ module Pgchief
4
4
  module Prompt
5
5
  # Kicks off the CLI with an initial prompt
6
6
  class Start < Base
7
- def self.call
8
- prompt = TTY::Prompt.new
7
+ def call
8
+ manage_config!
9
+
9
10
  result = prompt.select(
10
11
  "Welcome! How can I help?",
11
12
  [
@@ -16,6 +17,13 @@ module Pgchief
16
17
 
17
18
  klassify("prompt", result).call
18
19
  end
20
+
21
+ private
22
+
23
+ def manage_config!
24
+ Pgchief::Config.load_config!
25
+ Pgchief::Config.set_up_file_structure!
26
+ end
19
27
  end
20
28
  end
21
29
  end
@@ -4,9 +4,14 @@ module Pgchief
4
4
  module Prompt
5
5
  # Class to manage users
6
6
  class UserManagement < Base
7
- def self.call
8
- prompt = TTY::Prompt.new
9
- result = prompt.select("User management", ["Create user", "Drop user", "User list"])
7
+ def call
8
+ result = prompt.select("User management", [
9
+ "Create user",
10
+ "Drop user",
11
+ "User list",
12
+ "Grant database privileges",
13
+ "View database connection string"
14
+ ])
10
15
 
11
16
  scope = result == "User list" ? "command" : "prompt"
12
17
  klassify(scope, result).call
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pgchief
4
+ module Prompt
5
+ # Class to ask for database names, in order to create it
6
+ class ViewDatabaseConnectionString < Base
7
+ def call
8
+ username = params.first || select_user
9
+ database = prompt.select("Database you're connecting to:", Pgchief::Database.all + ["None"])
10
+ database = nil if database == "None"
11
+ result = Pgchief::Command::RetrieveConnectionString.call(username, database)
12
+
13
+ prompt.say result
14
+ end
15
+
16
+ def select_user
17
+ prompt.select("Select user to update:", Pgchief::User.all)
18
+ end
19
+ end
20
+ end
21
+ end
data/lib/pgchief/user.rb CHANGED
@@ -6,7 +6,7 @@ module Pgchief
6
6
  # Database information and operations
7
7
  class User
8
8
  def self.all
9
- conn = PG.connect(ENV.fetch("DATABASE_URL"))
9
+ conn = PG.connect(Pgchief::DATABASE_URL)
10
10
  result = conn.exec("SELECT usename FROM pg_user")
11
11
 
12
12
  result.map { |row| row["usename"] }.reject { |name| name == "postgres" }
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Pgchief
4
- VERSION = "0.1.0"
4
+ VERSION = "0.3.0"
5
5
  end
data/lib/pgchief.rb CHANGED
@@ -2,10 +2,15 @@
2
2
 
3
3
  require "pg"
4
4
  require "tty-prompt"
5
- require "pgchief/version"
5
+ require "tty-option"
6
6
 
7
+ require "pgchief/cli"
8
+ require "pgchief/config"
9
+ require "pgchief/connection_string"
10
+ require "pgchief/version"
7
11
  require "pgchief/database"
8
12
  require "pgchief/user"
13
+
9
14
  require "pgchief/prompt/base"
10
15
  require "pgchief/prompt/start"
11
16
  require "pgchief/prompt/create_database"
@@ -14,16 +19,25 @@ require "pgchief/prompt/database_management"
14
19
  require "pgchief/prompt/drop_database"
15
20
  require "pgchief/prompt/drop_user"
16
21
  require "pgchief/prompt/user_management"
22
+ require "pgchief/prompt/grant_database_privileges"
23
+ require "pgchief/prompt/view_database_connection_string"
17
24
 
25
+ require "pgchief/command"
18
26
  require "pgchief/command/base"
27
+ require "pgchief/command/config_create"
19
28
  require "pgchief/command/database_create"
20
29
  require "pgchief/command/database_drop"
21
30
  require "pgchief/command/database_list"
31
+ require "pgchief/command/database_privileges_grant"
32
+ require "pgchief/command/retrieve_connection_string"
33
+ require "pgchief/command/store_connection_string"
22
34
  require "pgchief/command/user_create"
23
35
  require "pgchief/command/user_drop"
24
36
  require "pgchief/command/user_list"
25
37
 
26
38
  module Pgchief
39
+ DATABASE_URL = ENV.fetch("DATABASE_URL")
40
+
27
41
  class Error < StandardError; end
28
42
 
29
43
  module Errors
data/tmp/.gitkeep ADDED
File without changes
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pgchief
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joel Oliveira
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-08-30 00:00:00.000000000 Z
11
+ date: 2024-11-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pg
@@ -83,16 +83,24 @@ files:
83
83
  - LICENSE.txt
84
84
  - README.md
85
85
  - Rakefile
86
+ - config/pgchief.toml
86
87
  - exe/pgchief
87
88
  - lib/pgchief.rb
89
+ - lib/pgchief/cli.rb
88
90
  - lib/pgchief/command.rb
89
91
  - lib/pgchief/command/base.rb
92
+ - lib/pgchief/command/config_create.rb
90
93
  - lib/pgchief/command/database_create.rb
91
94
  - lib/pgchief/command/database_drop.rb
92
95
  - lib/pgchief/command/database_list.rb
96
+ - lib/pgchief/command/database_privileges_grant.rb
97
+ - lib/pgchief/command/retrieve_connection_string.rb
98
+ - lib/pgchief/command/store_connection_string.rb
93
99
  - lib/pgchief/command/user_create.rb
94
100
  - lib/pgchief/command/user_drop.rb
95
101
  - lib/pgchief/command/user_list.rb
102
+ - lib/pgchief/config.rb
103
+ - lib/pgchief/connection_string.rb
96
104
  - lib/pgchief/database.rb
97
105
  - lib/pgchief/prompt.rb
98
106
  - lib/pgchief/prompt/base.rb
@@ -101,12 +109,14 @@ files:
101
109
  - lib/pgchief/prompt/database_management.rb
102
110
  - lib/pgchief/prompt/drop_database.rb
103
111
  - lib/pgchief/prompt/drop_user.rb
104
- - lib/pgchief/prompt/grant_database_priveleges.rb
112
+ - lib/pgchief/prompt/grant_database_privileges.rb
105
113
  - lib/pgchief/prompt/start.rb
106
114
  - lib/pgchief/prompt/user_management.rb
115
+ - lib/pgchief/prompt/view_database_connection_string.rb
107
116
  - lib/pgchief/user.rb
108
117
  - lib/pgchief/version.rb
109
118
  - sig/pgchief.rbs
119
+ - tmp/.gitkeep
110
120
  homepage: https://github.com/jayroh/pgchief
111
121
  licenses:
112
122
  - MIT
@@ -130,7 +140,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
130
140
  - !ruby/object:Gem::Version
131
141
  version: '0'
132
142
  requirements: []
133
- rubygems_version: 3.5.11
143
+ rubygems_version: 3.5.21
134
144
  signing_key:
135
145
  specification_version: 4
136
146
  summary: A simple ruby script to manage postgresql databases and users
@@ -1,15 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Pgchief
4
- module Prompt
5
- # Class to ask for database names, in order to create it
6
- class GrantDatabasePrivileges
7
- def self.call(username)
8
- databases = Pgchief::Database.all
9
- prompt = TTY::Prompt.new
10
- databases = prompt.select("Select database:", databases, multiselect: true)
11
- Pgchief::Command::GrantDatabasePrivileges.call(username, databases)
12
- end
13
- end
14
- end
15
- end