acidic_job 1.0.0.rc5 → 1.0.0.rc7
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 +4 -4
- data/README.md +1 -1
- data/app/assets/stylesheets/acidic_job/application.css +15 -0
- data/app/controllers/acidic_job/application_controller.rb +4 -0
- data/app/jobs/acidic_job/application_job.rb +4 -0
- data/app/mailers/acidic_job/application_mailer.rb +6 -0
- data/app/models/acidic_job/{record.rb → application_record.rb} +2 -2
- data/app/models/acidic_job/entry.rb +4 -4
- data/app/models/acidic_job/execution.rb +4 -4
- data/app/models/acidic_job/value.rb +1 -1
- data/app/views/layouts/acidic_job/application.html.erb +17 -0
- data/lib/acidic_job/builder.rb +2 -2
- data/lib/acidic_job/context.rb +3 -3
- data/lib/acidic_job/engine.rb +14 -8
- data/lib/acidic_job/log_subscriber.rb +1 -1
- data/lib/acidic_job/serializers/exception_serializer.rb +4 -0
- data/lib/acidic_job/serializers/job_serializer.rb +4 -0
- data/lib/acidic_job/serializers/new_record_serializer.rb +4 -0
- data/lib/acidic_job/serializers/range_serializer.rb +5 -5
- data/lib/acidic_job/testing.rb +3 -3
- data/lib/acidic_job/version.rb +1 -1
- data/lib/acidic_job/workflow.rb +4 -4
- data/lib/acidic_job.rb +1 -1
- metadata +24 -31
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6583544dc3b3545721d81facda79a461cb8a3a7a8078d2dc309dbc887c0dc1a7
|
|
4
|
+
data.tar.gz: 89f94171898b64372b85b6dcca3c4f6134e608f39c4aadeb8a776ce7c1a6e294
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 25fb20afe5782e1e73f23c2c020948ba732f25e9abb9cf32dcc5803eb74fa7f3c3bb4e674aaf6bd80263d235251fc043b5ee23f2176cdb0b2e99397c1e74ef7a
|
|
7
|
+
data.tar.gz: a09e720a54df383804b54d63b1acf8bd6b2bc912e05dea373f2912b87bbaa5f4e7e60413491ae13e4c780ba99e5cc842e73774837a08dbc87d8e223502534a70
|
data/README.md
CHANGED
|
@@ -183,7 +183,7 @@ class Job < ActiveJob::Base
|
|
|
183
183
|
```
|
|
184
184
|
|
|
185
185
|
> [!TIP]
|
|
186
|
-
> You should think carefully about what constitutes a unique execution of a workflow. Imagine you had a workflow job for balance
|
|
186
|
+
> You should think carefully about what constitutes a unique execution of a workflow. Imagine you had a workflow job for balance transfers. Jill transfers $10 to John. Your system **must** be able to differentiate between retries of this transfer and new independent transfers. If you were only to use the `sender`, `recipient`, and `amount` as your `unique_by` values, then if Jill tries to transfer another $10 to John at some point in the future, that work will be considered a retry of the first transfer and not a new transfer.
|
|
187
187
|
|
|
188
188
|
|
|
189
189
|
### Orchestrating steps
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
|
3
|
+
* listed below.
|
|
4
|
+
*
|
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
|
6
|
+
* or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
|
|
7
|
+
*
|
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
|
9
|
+
* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
|
|
10
|
+
* files in this directory. Styles in this file should be added after the last require_* statement.
|
|
11
|
+
* It is generally better to create a new file per style scope.
|
|
12
|
+
*
|
|
13
|
+
*= require_tree .
|
|
14
|
+
*= require_self
|
|
15
|
+
*/
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module AcidicJob
|
|
4
|
-
class
|
|
4
|
+
class ApplicationRecord < ActiveRecord::Base
|
|
5
5
|
self.abstract_class = true
|
|
6
6
|
|
|
7
7
|
connects_to(**AcidicJob.connects_to) if AcidicJob.connects_to
|
|
8
8
|
end
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
ActiveSupport.run_load_hooks :acidic_job_record, AcidicJob::
|
|
11
|
+
ActiveSupport.run_load_hooks :acidic_job_record, AcidicJob::ApplicationRecord
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module AcidicJob
|
|
4
|
-
class Entry <
|
|
4
|
+
class Entry < ApplicationRecord
|
|
5
5
|
belongs_to :execution, class_name: "AcidicJob::Execution"
|
|
6
6
|
|
|
7
7
|
serialize :data, coder: AcidicJob::Serializer
|
|
8
8
|
|
|
9
|
-
scope :for_step, ->
|
|
10
|
-
scope :for_action, ->
|
|
11
|
-
scope :ordered, -> { order(timestamp: :asc) }
|
|
9
|
+
scope :for_step, ->(step) { where(step: step) }
|
|
10
|
+
scope :for_action, ->(action) { where(action: action) }
|
|
11
|
+
scope :ordered, -> { order(timestamp: :asc, created_at: :asc) }
|
|
12
12
|
|
|
13
13
|
def self.most_recent
|
|
14
14
|
order(created_at: :desc).first
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module AcidicJob
|
|
4
|
-
class Execution <
|
|
4
|
+
class Execution < ApplicationRecord
|
|
5
5
|
has_many :entries, class_name: "AcidicJob::Entry", dependent: :destroy
|
|
6
6
|
has_many :values, class_name: "AcidicJob::Value", dependent: :destroy
|
|
7
7
|
|
|
@@ -14,9 +14,9 @@ module AcidicJob
|
|
|
14
14
|
where(recover_to: FINISHED_RECOVERY_POINT)
|
|
15
15
|
}
|
|
16
16
|
scope :outstanding, -> {
|
|
17
|
-
where.not(recover_to: FINISHED_RECOVERY_POINT).or(where(recover_to: [nil, ""]))
|
|
17
|
+
where.not(recover_to: FINISHED_RECOVERY_POINT).or(where(recover_to: [ nil, "" ]))
|
|
18
18
|
}
|
|
19
|
-
scope :clearable, ->
|
|
19
|
+
scope :clearable, ->(finished_before: AcidicJob.clear_finished_executions_after.ago) {
|
|
20
20
|
finished.where(last_run_at: ...finished_before)
|
|
21
21
|
}
|
|
22
22
|
|
|
@@ -34,7 +34,7 @@ module AcidicJob
|
|
|
34
34
|
step: step,
|
|
35
35
|
action: action,
|
|
36
36
|
timestamp: timestamp,
|
|
37
|
-
data: kwargs.except(:ignored)
|
|
37
|
+
data: kwargs.except(:ignored)
|
|
38
38
|
})
|
|
39
39
|
end
|
|
40
40
|
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>Acidic job</title>
|
|
5
|
+
<%= csrf_meta_tags %>
|
|
6
|
+
<%= csp_meta_tag %>
|
|
7
|
+
|
|
8
|
+
<%= yield :head %>
|
|
9
|
+
|
|
10
|
+
<%= stylesheet_link_tag "acidic_job/application", media: "all" %>
|
|
11
|
+
</head>
|
|
12
|
+
<body>
|
|
13
|
+
|
|
14
|
+
<%= yield %>
|
|
15
|
+
|
|
16
|
+
</body>
|
|
17
|
+
</html>
|
data/lib/acidic_job/builder.rb
CHANGED
data/lib/acidic_job/context.rb
CHANGED
|
@@ -12,19 +12,19 @@ module AcidicJob
|
|
|
12
12
|
{
|
|
13
13
|
execution_id: @execution.id,
|
|
14
14
|
key: key,
|
|
15
|
-
value: value
|
|
15
|
+
value: value
|
|
16
16
|
}
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
case AcidicJob::Value.connection.adapter_name.downcase.to_sym
|
|
20
20
|
when :postgresql, :sqlite
|
|
21
|
-
AcidicJob::Value.upsert_all(records, unique_by: [:execution_id, :key])
|
|
21
|
+
AcidicJob::Value.upsert_all(records, unique_by: [ :execution_id, :key ])
|
|
22
22
|
when :mysql2, :mysql, :trilogy
|
|
23
23
|
AcidicJob::Value.upsert_all(records)
|
|
24
24
|
else
|
|
25
25
|
# Fallback for other adapters - try with unique_by first, fall back without
|
|
26
26
|
begin
|
|
27
|
-
AcidicJob::Value.upsert_all(records, unique_by: [:execution_id, :key])
|
|
27
|
+
AcidicJob::Value.upsert_all(records, unique_by: [ :execution_id, :key ])
|
|
28
28
|
rescue ArgumentError => e
|
|
29
29
|
if e.message.include?("does not support :unique_by")
|
|
30
30
|
AcidicJob::Value.upsert_all(records)
|
data/lib/acidic_job/engine.rb
CHANGED
|
@@ -26,14 +26,20 @@ module AcidicJob
|
|
|
26
26
|
require_relative "serializers/exception_serializer"
|
|
27
27
|
require_relative "serializers/new_record_serializer"
|
|
28
28
|
require_relative "serializers/job_serializer"
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
Serializers::
|
|
33
|
-
Serializers::
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
29
|
+
|
|
30
|
+
serializers = [
|
|
31
|
+
Serializers::ExceptionSerializer.instance,
|
|
32
|
+
Serializers::NewRecordSerializer.instance,
|
|
33
|
+
Serializers::JobSerializer.instance
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
# Rails 7.1+ includes a RangeSerializer, so only add ours for older versions
|
|
37
|
+
unless defined?(ActiveJob::Serializers::RangeSerializer)
|
|
38
|
+
require_relative "serializers/range_serializer"
|
|
39
|
+
serializers << Serializers::RangeSerializer.instance
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
ActiveJob::Serializers.add_serializers(*serializers)
|
|
37
43
|
end
|
|
38
44
|
end
|
|
39
45
|
|
|
@@ -2,25 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
require "active_job/serializers/object_serializer"
|
|
4
4
|
|
|
5
|
-
# :nocov:
|
|
6
5
|
module AcidicJob
|
|
7
6
|
module Serializers
|
|
7
|
+
# This serializer is only used for Rails versions prior to 7.1,
|
|
8
|
+
# which introduced ActiveJob::Serializers::RangeSerializer.
|
|
8
9
|
class RangeSerializer < ::ActiveJob::Serializers::ObjectSerializer
|
|
9
10
|
KEYS = %w[begin end exclude_end].freeze
|
|
10
11
|
|
|
11
12
|
def serialize(range)
|
|
12
|
-
args = Arguments.serialize([range.begin, range.end, range.exclude_end?])
|
|
13
|
+
args = ::ActiveJob::Arguments.serialize([range.begin, range.end, range.exclude_end?])
|
|
13
14
|
super(KEYS.zip(args).to_h)
|
|
14
15
|
end
|
|
15
16
|
|
|
16
17
|
def deserialize(hash)
|
|
17
|
-
klass.new(
|
|
18
|
+
klass.new(*::ActiveJob::Arguments.deserialize(hash.values_at(*KEYS)))
|
|
18
19
|
end
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
def klass
|
|
21
22
|
::Range
|
|
22
23
|
end
|
|
23
24
|
end
|
|
24
25
|
end
|
|
25
26
|
end
|
|
26
|
-
# :nocov:
|
data/lib/acidic_job/testing.rb
CHANGED
|
@@ -26,7 +26,7 @@ module AcidicJob
|
|
|
26
26
|
# except that any `transaction` strategies for any ORMs are replaced with a `deletion` strategy.
|
|
27
27
|
private def transaction_free_cleaners_for(original_cleaners)
|
|
28
28
|
non_transaction_cleaners = original_cleaners.dup.to_h do |(orm, opts), cleaner|
|
|
29
|
-
[[orm, opts], ensure_no_transaction_strategies_for(cleaner)]
|
|
29
|
+
[ [ orm, opts ], ensure_no_transaction_strategies_for(cleaner) ]
|
|
30
30
|
end
|
|
31
31
|
::DatabaseCleaner::Cleaners.new(non_transaction_cleaners)
|
|
32
32
|
end
|
|
@@ -55,12 +55,12 @@ module AcidicJob
|
|
|
55
55
|
.name # "DatabaseCleaner::ActiveRecord::Truncation"
|
|
56
56
|
.rpartition("::") # ["DatabaseCleaner::ActiveRecord", "::", "Truncation"]
|
|
57
57
|
.first # "DatabaseCleaner::ActiveRecord"
|
|
58
|
-
deletion_strategy_class_name = [strategy_namespace, "::", "Deletion"].join
|
|
58
|
+
deletion_strategy_class_name = [ strategy_namespace, "::", "Deletion" ].join
|
|
59
59
|
deletion_strategy_class = deletion_strategy_class_name.constantize
|
|
60
60
|
instance_variable_hash = strategy.instance_variables.to_h do |var|
|
|
61
61
|
[
|
|
62
62
|
var.to_s.remove("@"),
|
|
63
|
-
strategy.instance_variable_get(var)
|
|
63
|
+
strategy.instance_variable_get(var)
|
|
64
64
|
]
|
|
65
65
|
end
|
|
66
66
|
options = instance_variable_hash.except("db", "connection_class")
|
data/lib/acidic_job/version.rb
CHANGED
data/lib/acidic_job/workflow.rb
CHANGED
|
@@ -31,12 +31,12 @@ module AcidicJob
|
|
|
31
31
|
AcidicJob.instrument(:initialize_workflow, definition: workflow_definition) do
|
|
32
32
|
transaction_args = case ::ActiveRecord::Base.connection.adapter_name.downcase.to_sym
|
|
33
33
|
# SQLite doesn't support `serializable` transactions
|
|
34
|
-
|
|
34
|
+
when :sqlite
|
|
35
35
|
{}
|
|
36
|
-
|
|
36
|
+
else
|
|
37
37
|
{ isolation: :serializable }
|
|
38
38
|
end
|
|
39
|
-
idempotency_key = Digest::SHA256.hexdigest(JSON.generate([self.class.name, unique_by], strict: true))
|
|
39
|
+
idempotency_key = Digest::SHA256.hexdigest(JSON.generate([ self.class.name, unique_by ], strict: true))
|
|
40
40
|
|
|
41
41
|
@__acidic_job_execution__ = ::ActiveRecord::Base.transaction(**transaction_args) do
|
|
42
42
|
record = Execution.find_by(idempotency_key: idempotency_key)
|
|
@@ -162,7 +162,7 @@ module AcidicJob
|
|
|
162
162
|
step: curr_step,
|
|
163
163
|
action: :succeeded,
|
|
164
164
|
ignored: {
|
|
165
|
-
result: result
|
|
165
|
+
result: result
|
|
166
166
|
}
|
|
167
167
|
)
|
|
168
168
|
next_step
|
data/lib/acidic_job.rb
CHANGED
|
@@ -22,7 +22,7 @@ module AcidicJob
|
|
|
22
22
|
|
|
23
23
|
mattr_accessor :logger, default: DEFAULT_LOGGER
|
|
24
24
|
mattr_accessor :connects_to
|
|
25
|
-
mattr_accessor :plugins, default: [Plugins::TransactionalStep]
|
|
25
|
+
mattr_accessor :plugins, default: [ Plugins::TransactionalStep ]
|
|
26
26
|
mattr_accessor :clear_finished_executions_after, default: 1.week
|
|
27
27
|
|
|
28
28
|
def instrument(channel, **options, &block)
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: acidic_job
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.0.
|
|
4
|
+
version: 1.0.0.rc7
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
|
-
-
|
|
8
|
-
bindir:
|
|
7
|
+
- Stephen Margheim
|
|
8
|
+
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: json
|
|
@@ -80,35 +80,21 @@ dependencies:
|
|
|
80
80
|
- !ruby/object:Gem::Version
|
|
81
81
|
version: '7.1'
|
|
82
82
|
- !ruby/object:Gem::Dependency
|
|
83
|
-
name:
|
|
83
|
+
name: chaotic_job
|
|
84
84
|
requirement: !ruby/object:Gem::Requirement
|
|
85
85
|
requirements:
|
|
86
86
|
- - ">="
|
|
87
87
|
- !ruby/object:Gem::Version
|
|
88
|
-
version:
|
|
88
|
+
version: 0.11.2
|
|
89
89
|
type: :development
|
|
90
90
|
prerelease: false
|
|
91
91
|
version_requirements: !ruby/object:Gem::Requirement
|
|
92
92
|
requirements:
|
|
93
93
|
- - ">="
|
|
94
94
|
- !ruby/object:Gem::Version
|
|
95
|
-
version:
|
|
95
|
+
version: 0.11.2
|
|
96
96
|
- !ruby/object:Gem::Dependency
|
|
97
|
-
name:
|
|
98
|
-
requirement: !ruby/object:Gem::Requirement
|
|
99
|
-
requirements:
|
|
100
|
-
- - '='
|
|
101
|
-
- !ruby/object:Gem::Version
|
|
102
|
-
version: 0.3.0
|
|
103
|
-
type: :development
|
|
104
|
-
prerelease: false
|
|
105
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
106
|
-
requirements:
|
|
107
|
-
- - '='
|
|
108
|
-
- !ruby/object:Gem::Version
|
|
109
|
-
version: 0.3.0
|
|
110
|
-
- !ruby/object:Gem::Dependency
|
|
111
|
-
name: combustion
|
|
97
|
+
name: simplecov
|
|
112
98
|
requirement: !ruby/object:Gem::Requirement
|
|
113
99
|
requirements:
|
|
114
100
|
- - ">="
|
|
@@ -136,7 +122,7 @@ dependencies:
|
|
|
136
122
|
- !ruby/object:Gem::Version
|
|
137
123
|
version: '0'
|
|
138
124
|
- !ruby/object:Gem::Dependency
|
|
139
|
-
name:
|
|
125
|
+
name: mysql2
|
|
140
126
|
requirement: !ruby/object:Gem::Requirement
|
|
141
127
|
requirements:
|
|
142
128
|
- - ">="
|
|
@@ -150,7 +136,7 @@ dependencies:
|
|
|
150
136
|
- !ruby/object:Gem::Version
|
|
151
137
|
version: '0'
|
|
152
138
|
- !ruby/object:Gem::Dependency
|
|
153
|
-
name:
|
|
139
|
+
name: pg
|
|
154
140
|
requirement: !ruby/object:Gem::Requirement
|
|
155
141
|
requirements:
|
|
156
142
|
- - ">="
|
|
@@ -164,7 +150,7 @@ dependencies:
|
|
|
164
150
|
- !ruby/object:Gem::Version
|
|
165
151
|
version: '0'
|
|
166
152
|
- !ruby/object:Gem::Dependency
|
|
167
|
-
name:
|
|
153
|
+
name: sqlite3
|
|
168
154
|
requirement: !ruby/object:Gem::Requirement
|
|
169
155
|
requirements:
|
|
170
156
|
- - ">="
|
|
@@ -178,7 +164,7 @@ dependencies:
|
|
|
178
164
|
- !ruby/object:Gem::Version
|
|
179
165
|
version: '0'
|
|
180
166
|
- !ruby/object:Gem::Dependency
|
|
181
|
-
name:
|
|
167
|
+
name: actionmailer
|
|
182
168
|
requirement: !ruby/object:Gem::Requirement
|
|
183
169
|
requirements:
|
|
184
170
|
- - ">="
|
|
@@ -191,7 +177,9 @@ dependencies:
|
|
|
191
177
|
- - ">="
|
|
192
178
|
- !ruby/object:Gem::Version
|
|
193
179
|
version: '0'
|
|
194
|
-
description:
|
|
180
|
+
description: "Write reliable and repeatable multi-step distributed operations that
|
|
181
|
+
are Atomic ⚛️, Consistent \U0001F916, Isolated \U0001F574\U0001F3FC, and Durable
|
|
182
|
+
⛰️."
|
|
195
183
|
email:
|
|
196
184
|
- stephen.margheim@gmail.com
|
|
197
185
|
executables: []
|
|
@@ -200,10 +188,15 @@ extra_rdoc_files: []
|
|
|
200
188
|
files:
|
|
201
189
|
- LICENSE
|
|
202
190
|
- README.md
|
|
191
|
+
- app/assets/stylesheets/acidic_job/application.css
|
|
192
|
+
- app/controllers/acidic_job/application_controller.rb
|
|
193
|
+
- app/jobs/acidic_job/application_job.rb
|
|
194
|
+
- app/mailers/acidic_job/application_mailer.rb
|
|
195
|
+
- app/models/acidic_job/application_record.rb
|
|
203
196
|
- app/models/acidic_job/entry.rb
|
|
204
197
|
- app/models/acidic_job/execution.rb
|
|
205
|
-
- app/models/acidic_job/record.rb
|
|
206
198
|
- app/models/acidic_job/value.rb
|
|
199
|
+
- app/views/layouts/acidic_job/application.html.erb
|
|
207
200
|
- lib/acidic_job.rb
|
|
208
201
|
- lib/acidic_job/arguments.rb
|
|
209
202
|
- lib/acidic_job/builder.rb
|
|
@@ -238,14 +231,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
238
231
|
requirements:
|
|
239
232
|
- - ">="
|
|
240
233
|
- !ruby/object:Gem::Version
|
|
241
|
-
version:
|
|
234
|
+
version: 3.0.0
|
|
242
235
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
243
236
|
requirements:
|
|
244
237
|
- - ">="
|
|
245
238
|
- !ruby/object:Gem::Version
|
|
246
239
|
version: '0'
|
|
247
240
|
requirements: []
|
|
248
|
-
rubygems_version: 3.6.
|
|
241
|
+
rubygems_version: 3.6.7
|
|
249
242
|
specification_version: 4
|
|
250
|
-
summary:
|
|
243
|
+
summary: "\U0001F9EA Durable execution workflows for Active Job"
|
|
251
244
|
test_files: []
|