postjob 0.3.8 → 0.4.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
  SHA1:
3
- metadata.gz: 04c8dbb68c7f61be34b8bfe642fdd6ce04933e08
4
- data.tar.gz: 355aca75cf91e2107d850163802ab87aabf94126
3
+ metadata.gz: 5bb4b3e0053cc36fb49b56b5711559dd4758ccab
4
+ data.tar.gz: 5d16630d479c92c4287c03201f3b3a5f28f9126f
5
5
  SHA512:
6
- metadata.gz: 50c02c184aab359a4c67d5b4b853560aac3a54636ed603cbfd7c161b61057c807d9ea0a752bce79a4c57c77f0f4841c4d7b5cbe33535bfafb42d2c0b8a50e46b
7
- data.tar.gz: 56e861011da77177e1e8b147ff2447a26f292b8bdeceeec117dfd2b979cd4ca0449e493c2f00f4cce3cecceb99b2677f2fce177aaca241c61997082d7a6764d5
6
+ metadata.gz: 052a5b8e8db5e7b13b1c48d7e2a1daadf575f013374634d7f3cca32eefb95d0a0c5ad25d17be223d68807481b63fb64305de4f213835f67c07484bf3c4a39fd3
7
+ data.tar.gz: 8616e9115a0b6a1e4f5c0e709864b0878db39d65aa4f04ca195b9fe2c79ae54988e0b5678557a1be40e9eebed398280deb577e24927a58cb5a97bc26dabc5921
@@ -2,6 +2,16 @@ require "postjob/cli"
2
2
  require "postjob/migrations"
3
3
 
4
4
  module Postjob::CLI
5
+ # Prints all settings in the queue
6
+ def db_settings
7
+ connect_to_database!
8
+
9
+ raise "No settings in this database schema" unless ::Postjob::Queue.settings?
10
+
11
+ records = "SELECT name,value FROM postjob.settings ORDER BY name"
12
+ tp Simple::SQL.all(records, into: Hash)
13
+ end
14
+
5
15
  def db_migrate
6
16
  connect_to_database!
7
17
  Postjob::Migrations.migrate!
@@ -0,0 +1,10 @@
1
+ module Postjob::CLI
2
+ # Prints version info
3
+ def version
4
+ connect_to_database!
5
+
6
+ gem_version = Gem.loaded_specs["postjob"].version
7
+ puts "postjob/ruby: #{gem_version}"
8
+ puts "postjob/queue: #{::Postjob::Queue.version}"
9
+ end
10
+ end
@@ -1,12 +1,15 @@
1
1
  # rubocop:disable Security/Eval
2
2
 
3
3
  module Postjob
4
+ VERSION = Gem.loaded_specs["postjob"].version
5
+
4
6
  module Migrations
5
7
  extend self
6
8
 
7
9
  SQL = ::Simple::SQL
8
10
  SCHEMA_NAME = ::Postjob::Queue::SCHEMA_NAME
9
11
  CHANNEL = ::Postjob::Queue::Notifications::CHANNEL
12
+ CLIENT_VERSION = "ruby/#{::Postjob::VERSION}"
10
13
 
11
14
  # Note that the SCHEMA_NAME should not be the default name, since unmigrate!
12
15
  # below drops that schema, and we don't want to drop the default schema.
@@ -0,0 +1,7 @@
1
+ CREATE TABLE IF NOT EXISTS {SCHEMA_NAME}.settings (
2
+ name VARCHAR PRIMARY KEY NOT NULL,
3
+ value VARCHAR
4
+ );
5
+
6
+ INSERT INTO {SCHEMA_NAME}.settings (name, value) VALUES('version', '0.4.0') ON CONFLICT DO NOTHING;
7
+ INSERT INTO {SCHEMA_NAME}.settings (name, value) VALUES('client_version', '{CLIENT_VERSION}') ON CONFLICT DO NOTHING;
@@ -15,6 +15,7 @@ end
15
15
  require_relative "queue/encoder"
16
16
  require_relative "queue/notifications"
17
17
  require_relative "queue/search"
18
+ require_relative "queue/settings"
18
19
 
19
20
  module Postjob::Queue
20
21
  Job = ::Postjob::Job
@@ -1,81 +1,57 @@
1
1
  module Postjob::Queue
2
- end
3
-
4
- #
5
- # The Postjob::Queue::Search module is able to inspect the Postjob Queue.
6
- module Postjob::Queue::Search
7
- extend self
8
-
9
- def one(id, filter: {}, into: Hash)
10
- query = query(page: 0, per: 1, filter: filter, id: id)
11
- Simple::SQL.ask(query, into: into)
12
- end
2
+ DEFAULT_ATTRIBUTES = [
3
+ "id",
4
+ "full_id",
5
+ "workflow || COALESCE('@' || workflow_version, '') || args AS job",
6
+ "workflow_status",
7
+ "status",
8
+ "error",
9
+ "COALESCE((results->0)::varchar, error_message) AS result",
10
+ "next_run_at",
11
+ "error_backtrace",
12
+ "(now() at time zone 'utc') - created_at AS age",
13
+ "updated_at - created_at AS runtime",
14
+ "tags"
15
+ ]
16
+
17
+ # Builds a search scope (see Simple::SQL::Scope) for the passed in filter criteria.
18
+ def search(filter = {})
19
+ expect! filter => Hash
13
20
 
14
- def all(page: 0, per: 100, filter: {}, into: Hash)
15
- query = query(page: page, per: per, filter: filter)
16
- Simple::SQL.all(query, into: into)
21
+ # extract options
22
+ filter = filter.dup
23
+ root_only = filter.delete(:root_only) || false
24
+ attributes = filter.delete(:attributes) || DEFAULT_ATTRIBUTES
25
+ expect! attributes => Array
26
+
27
+ # build Scope
28
+ scope = Simple::SQL::Scope.new("SELECT #{attributes.join(", ")} FROM #{SCHEMA_NAME}.postjobs")
29
+ scope = scope.where("root_id=id") if root_only
30
+ scope = apply_filters(scope, filter) if filter && !filter.empty?
31
+ scope
17
32
  end
18
33
 
19
34
  private
20
35
 
21
- def query(page: nil, per: nil, filter: {}, root_only: true, id: nil)
22
- expect! id => [Integer, nil]
23
- expect! page => [Integer, nil]
24
- expect! per => [Integer, nil]
25
- expect! { page >= 0 && per > 0 }
26
- expect! filter => Hash
27
-
28
- conditions = []
29
- conditions << "id=#{id}" if id
30
- conditions << "root_id=id" if root_only
31
- conditions << tags_condition(filter)
32
-
33
- query = base_query(conditions)
34
- query = paginated_query query, per: per, page: page
35
- query
36
- end
37
-
38
- def paginated_query(query, per:, page:)
39
- expect! per => Integer
40
- expect! page => Integer
36
+ def apply_filters(scope, filter)
37
+ filter.each_key do |key|
38
+ expect! key => [Symbol, String]
39
+ end
41
40
 
42
- <<~SQL
43
- SELECT * FROM (#{query}) sq LIMIT #{per} OFFSET #{per * page}
44
- SQL
45
- end
41
+ column_names = Simple::SQL::Reflection.columns("#{SCHEMA_NAME}.postjobs")
42
+ column_names += column_names.map(&:to_sym)
46
43
 
47
- def base_query(conditions = [])
48
- conditions.compact!
49
- conditions << "TRUE"
50
- condition_fragment = conditions
51
- .compact
52
- .map { |s| "(#{s})" }
53
- .join(" AND ")
44
+ column_filters, tags_filters = filter.partition { |key, _| column_names.include?(key) }
54
45
 
55
- <<~SQL
56
- SELECT
57
- id,
58
- full_id,
59
- workflow || COALESCE('@' || workflow_version, '') || args AS job,
60
- workflow_status,
61
- status,
62
- error,
63
- COALESCE((results->0)::varchar, error_message) AS result,
64
- next_run_at,
65
- error_backtrace,
66
- (now() at time zone 'utc') - created_at AS age,
67
- updated_at - created_at AS runtime,
68
- tags
69
- FROM postjob.postjobs
70
- WHERE #{condition_fragment}
71
- ORDER BY id
72
- SQL
73
- end
46
+ scope = column_filters.inject(scope) do |sc, (key, value)|
47
+ sc.where(key => value)
48
+ end
74
49
 
75
- def tags_condition(keys_and_values)
76
- expect! keys_and_values => Hash
77
- return nil if keys_and_values.empty?
50
+ unless tags_filters.empty?
51
+ tags_filters = Hash[tags_filters]
52
+ scope = scope.where "tags @> '#{Postjob::Queue::Encoder.encode(tags_filters)}'"
53
+ end
78
54
 
79
- "tags @> '#{Postjob::Queue::Encoder.encode(keys_and_values)}'"
55
+ scope
80
56
  end
81
57
  end
@@ -0,0 +1,13 @@
1
+ module Postjob::Queue
2
+ def settings?
3
+ tables = SQL::Reflection.tables(schema: "postjob")
4
+ tables.include?("postjob.settings")
5
+ end
6
+
7
+ # returns the version of the Postjob queue.
8
+ def version
9
+ return "0.3.*" unless settings?
10
+
11
+ SQL.ask("SELECT value FROM postjob.settings WHERE name=$1", "version") || "unknown"
12
+ end
13
+ end
@@ -19,32 +19,22 @@ module SearchWorkflow
19
19
  Postjob.register_workflow self
20
20
  end
21
21
 
22
- describe "Postjob::Queue::Search" do
23
- let!(:id) { Postjob.enqueue! "SearchWorkflow", tags: { "initial" => "yay" } }
24
-
22
+ describe "Postjob::Queue.search" do
25
23
  include TestHelper
26
24
 
27
- Search = Postjob::Queue::Search
28
-
29
25
  before do
30
- Postjob.process_all
31
- end
32
-
33
- def load_child_job
34
- TestHelper.load_job "SELECT * FROM postjobs WHERE parent_id=$1", id
26
+ Simple::SQL.ask("DELETE FROM postjob.postjobs")
35
27
  end
36
28
 
37
- def token
38
- load_token(load_child_job)
39
- end
29
+ let!(:id) { Postjob.enqueue! "SearchWorkflow", tags: { "initial" => "yay" } }
40
30
 
41
- it "creates a token" do
42
- expect! token => /[A-F0-9]{8}-[A-F0-9]{4}-[A-F0-9]{4}-[a-fA-F0-9]{4}-[A-F0-9]{12}/i
43
- end
31
+ Queue = Postjob::Queue
44
32
 
45
- describe "returned data shape" do
33
+ context "attributes" do
46
34
  it "returns a specific set of attributes" do
47
- result = Search.one(id)
35
+ scope = Postjob::Queue.search(id: id)
36
+ result = Simple::SQL.ask scope, into: Hash
37
+
48
38
  expected_keys = [
49
39
  :id,
50
40
  :full_id,
@@ -59,81 +49,59 @@ describe "Postjob::Queue::Search" do
59
49
  :runtime,
60
50
  :tags
61
51
  ]
52
+ expect(result.keys).to include(*expected_keys)
53
+ end
54
+
55
+ it "with attributes: argument it only returns the specified attributes" do
56
+ scope = Postjob::Queue.search(attributes: %w(id))
57
+ result = Simple::SQL.ask scope, into: Hash
58
+
59
+ expected_keys = [
60
+ :id
61
+ ]
62
62
  expect(result.keys).to contain_exactly(*expected_keys)
63
63
  end
64
64
  end
65
65
 
66
- describe "search_one" do
67
- it "can find a job by id" do
68
- result = Search.one(id)
69
- expect(result).to be_a(Hash)
70
- expect(Search.one(id)[:id]).to eq(id)
66
+ context "with multiple jobs" do
67
+ before do
68
+ Postjob.enqueue! "SearchWorkflow", tags: { "secondary" => "yay" }
69
+ expect(Simple::SQL.ask("SELECT COUNT(*) FROM postjob.postjobs")).to eq(2)
70
+ end
71
+
72
+ it "returns all entries" do
73
+ scope = Postjob::Queue.search
74
+ result = Simple::SQL.all(scope, into: OpenStruct)
75
+ expect(result.length).to eq(2)
71
76
  end
72
77
 
73
- it "writes into the into: type" do
74
- result = Search.one(id, into: OpenStruct)
75
- expect(result).to be_a(OpenStruct)
76
- expect(result.id).to eq(id)
78
+ it "sorts by id" do
79
+ scope = Postjob::Queue.search
80
+ result = Simple::SQL.all(scope, into: OpenStruct)
81
+
82
+ # Note that the second job has a id larger than the original job's id.
83
+ expect(result.first.id).to eq(id)
77
84
  end
78
85
  end
79
86
 
80
- describe "search_all" do
87
+ describe "filtering" do
81
88
  it "can find a job by id" do
82
- result = Search.all into: OpenStruct
83
- expect(result.map(&:id)).to eq([id])
89
+ scope = Postjob::Queue.search(id: id)
90
+ result = Simple::SQL.ask scope, into: Hash
91
+ expect(result[:id]).to eq(id)
84
92
  end
85
93
 
86
- context "with multiple jobs" do
87
- before do
88
- Postjob.enqueue! "SearchWorkflow", tags: { "secondary" => "yay" }
89
- end
90
-
91
- it "returns all entries" do
92
- result = Search.all into: OpenStruct
93
- expect(result.length).to eq(2)
94
- end
95
-
96
- it "honors page and per arguments" do
97
- result = Search.all into: OpenStruct, page: 0, per: 1
98
- expect(result.length).to eq(1)
99
- expect(result.first.id).to eq(id)
100
-
101
- result = Search.all into: OpenStruct, page: 1, per: 1
102
- expect(result.length).to eq(1)
103
- expect(result.first.id).not_to eq(id)
104
-
105
- result = Search.all into: OpenStruct, page: 2, per: 1
106
- expect(result.length).to eq(0)
107
-
108
- expect do
109
- Search.all into: OpenStruct, page: -10
110
- end.to raise_error(ArgumentError)
111
-
112
- expect do
113
- Search.all into: OpenStruct, per: -10
114
- end.to raise_error(ArgumentError)
115
- end
116
-
117
- it "sorts by id" do
118
- # Note that the second job has a id larger than the original job's id.
119
- result = Search.all into: OpenStruct
120
- expect(result.first.id).to eq(id)
121
- end
94
+ it "positive filters by tags" do
95
+ scope = Postjob::Queue.search("initial" => "yay")
96
+ result = Simple::SQL.all scope, into: OpenStruct
97
+ expect(result.length).to eq(1)
98
+ expect(result.first.id).to eq(id)
122
99
  end
123
100
 
124
- describe "filtering" do
125
- it "positive filters by tags" do
126
- filter = { "initial" => "yay" }
127
- result = Search.all filter: filter, into: OpenStruct
128
- expect(result.length).to eq(1)
129
- expect(result.first.id).to eq(id)
130
- end
131
-
132
- it "negative filters by tags" do
133
- filter = { "initial" => "nay" }
134
- result = Search.all filter: filter, into: OpenStruct
135
- expect(result.length).to eq(0)
136
- end
101
+ it "negative filters by tags" do
102
+ scope = Postjob::Queue.search("initial" => "nay")
103
+ result = Simple::SQL.all(scope)
104
+ expect(result.length).to eq(0)
137
105
  end
138
106
  end
139
107
  end
@@ -29,6 +29,7 @@ RSpec.configure do |config|
29
29
  config.filter_run focus: (ENV["CI"] != "true")
30
30
  config.expect_with(:rspec) { |c| c.syntax = :expect }
31
31
  config.order = "random"
32
+ config.example_status_persistence_file_path = ".rspec.persistence"
32
33
 
33
34
  config.before(:all) {}
34
35
  config.after {}
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: postjob
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.8
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - radiospiel
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-05-08 00:00:00.000000000 Z
11
+ date: 2018-05-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -123,7 +123,7 @@ dependencies:
123
123
  version: '0.4'
124
124
  - - ">="
125
125
  - !ruby/object:Gem::Version
126
- version: 0.4.3
126
+ version: 0.4.5
127
127
  type: :runtime
128
128
  prerelease: false
129
129
  version_requirements: !ruby/object:Gem::Requirement
@@ -133,7 +133,7 @@ dependencies:
133
133
  version: '0.4'
134
134
  - - ">="
135
135
  - !ruby/object:Gem::Version
136
- version: 0.4.3
136
+ version: 0.4.5
137
137
  - !ruby/object:Gem::Dependency
138
138
  name: simple-cli
139
139
  requirement: !ruby/object:Gem::Requirement
@@ -210,6 +210,7 @@ files:
210
210
  - lib/postjob/cli/job.rb
211
211
  - lib/postjob/cli/ps.rb
212
212
  - lib/postjob/cli/run.rb
213
+ - lib/postjob/cli/version.rb
213
214
  - lib/postjob/error.rb
214
215
  - lib/postjob/job.rb
215
216
  - lib/postjob/migrations.rb
@@ -225,10 +226,12 @@ files:
225
226
  - lib/postjob/migrations/008_checkout_runnable.sql
226
227
  - lib/postjob/migrations/008a_childjobs.sql
227
228
  - lib/postjob/migrations/009_tokens.sql
229
+ - lib/postjob/migrations/010_settings.sql
228
230
  - lib/postjob/queue.rb
229
231
  - lib/postjob/queue/encoder.rb
230
232
  - lib/postjob/queue/notifications.rb
231
233
  - lib/postjob/queue/search.rb
234
+ - lib/postjob/queue/settings.rb
232
235
  - lib/postjob/registry.rb
233
236
  - lib/postjob/runner.rb
234
237
  - lib/postjob/workflow.rb