postjob 0.3.8 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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