cron_to_go_sync 0.0.1
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 +7 -0
- data/.editorconfig +9 -0
- data/.gitattributes +2 -0
- data/.github/dependabot.yml +29 -0
- data/.github/workflows/ci.yml +53 -0
- data/.github/workflows/release.yml +25 -0
- data/.gitignore +3 -0
- data/.pre-commit-config.yaml +18 -0
- data/.rubocop.yml +9 -0
- data/.standard.yml +3 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +16 -0
- data/Gemfile.lock +171 -0
- data/LICENSE.txt +5 -0
- data/README.md +6 -0
- data/cron_to_go_sync.gemspec +32 -0
- data/lib/cron_to_go_sync/parser.rb +104 -0
- data/lib/cron_to_go_sync/sync_interactor.rb +127 -0
- data/lib/cron_to_go_sync/version.rb +3 -0
- data/lib/cron_to_go_sync.rb +6 -0
- data/spec/cassettes/CronToGoSync_SyncInteractor/_call/works.json +1 -0
- data/spec/data/staging.toml +42 -0
- data/spec/parser_spec.rb +34 -0
- data/spec/spec_helper.rb +57 -0
- data/spec/sync_interactor_spec.rb +18 -0
- metadata +203 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 6d51ed36d2923293ce7c72f8dff71b16f2703adda73bd9769e1734ce474b4ec8
|
4
|
+
data.tar.gz: 80cb621f80dc461a634c94a3df036d765e45d5bd97de7eb0e380fc8d51d98a0a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d9b43e4dd3dc46960ad077c9e47507e21c1019cf05071656101653efee5f4f896cb65f89cb4887ef51a145038975ea8c7715c32b098cdc52086496c8a6a4c554
|
7
|
+
data.tar.gz: c014625e1f23df03a36164cfb4995f4b70a2f3bce2eadc40b7350b410b2891029a3a7ed3ee22a13f90afefb95f66fa340d016f1a110b52d84a4e08def4a08a9a
|
data/.editorconfig
ADDED
data/.gitattributes
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# To get started with Dependabot version updates, you'll need to specify which
|
2
|
+
# package ecosystems to update and where the package manifests are located.
|
3
|
+
# Please see the documentation for all configuration options:
|
4
|
+
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
5
|
+
|
6
|
+
version: 2
|
7
|
+
updates:
|
8
|
+
- package-ecosystem: "github-actions"
|
9
|
+
directory: "/"
|
10
|
+
schedule:
|
11
|
+
interval: "weekly"
|
12
|
+
day: "wednesday"
|
13
|
+
labels:
|
14
|
+
- ":robot: dependabot"
|
15
|
+
- ":octocat: github-actions"
|
16
|
+
- ":heavy_plus_sign: dependencies"
|
17
|
+
reviewers:
|
18
|
+
- instrumentl/code-reviewers
|
19
|
+
- package-ecosystem: "bundler"
|
20
|
+
directory: "/"
|
21
|
+
schedule:
|
22
|
+
interval: "weekly"
|
23
|
+
day: "wednesday"
|
24
|
+
labels:
|
25
|
+
- ":robot: dependabot"
|
26
|
+
- ":gem: ruby"
|
27
|
+
- ":heavy_plus_sign: dependencies"
|
28
|
+
reviewers:
|
29
|
+
- instrumentl/code-reviewers
|
@@ -0,0 +1,53 @@
|
|
1
|
+
name: "CI"
|
2
|
+
on:
|
3
|
+
pull_request:
|
4
|
+
push:
|
5
|
+
branches: [ main ]
|
6
|
+
|
7
|
+
jobs:
|
8
|
+
test:
|
9
|
+
runs-on: ubuntu-latest
|
10
|
+
timeout-minutes: 30
|
11
|
+
permissions:
|
12
|
+
contents: read
|
13
|
+
checks: write
|
14
|
+
env:
|
15
|
+
RAILS_ENV: test
|
16
|
+
CRONTOGO_ORGANIZATION_ID: "fake-crontogo-organization-id"
|
17
|
+
CRONTOGO_API_KEY: "fake-crontogo-api-key"
|
18
|
+
strategy:
|
19
|
+
fail-fast: true
|
20
|
+
matrix:
|
21
|
+
ruby: ["3.0", "3.1", "3.2"]
|
22
|
+
steps:
|
23
|
+
- name: Checkout code
|
24
|
+
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
|
25
|
+
- name: Install Ruby and gems
|
26
|
+
uses: ruby/setup-ruby@8a45918450651f5e4784b6031db26f4b9f76b251
|
27
|
+
with:
|
28
|
+
bundler-cache: true
|
29
|
+
ruby-version: ${{ matrix.ruby }}
|
30
|
+
- name: Run RSpec Tests
|
31
|
+
timeout-minutes: 20
|
32
|
+
run: bundle exec rspec -f doc
|
33
|
+
lint:
|
34
|
+
runs-on: ubuntu-latest
|
35
|
+
timeout-minutes: 20
|
36
|
+
permissions:
|
37
|
+
contents: read
|
38
|
+
steps:
|
39
|
+
- name: Checkout code
|
40
|
+
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
|
41
|
+
- name: Install Ruby and gems
|
42
|
+
uses: ruby/setup-ruby@8a45918450651f5e4784b6031db26f4b9f76b251
|
43
|
+
with:
|
44
|
+
bundler-cache: true
|
45
|
+
ruby-version: "3.2"
|
46
|
+
- name: Bundle Audit Check
|
47
|
+
run: bundle exec bundle-audit update && bundle exec bundle-audit check
|
48
|
+
- name: Setup Python
|
49
|
+
uses: actions/setup-python@bd6b4b6205c4dbad673328db7b31b7fab9e241c0
|
50
|
+
with:
|
51
|
+
python-version: "3.10"
|
52
|
+
- name: Run pre-commit
|
53
|
+
uses: pre-commit/action@5f528da5c95691c4cf42ff76a4d10854b62cbb82
|
@@ -0,0 +1,25 @@
|
|
1
|
+
name: Release to RubyGems
|
2
|
+
on:
|
3
|
+
release:
|
4
|
+
types: [published]
|
5
|
+
jobs:
|
6
|
+
release:
|
7
|
+
runs-on: ubuntu-latest
|
8
|
+
steps:
|
9
|
+
- name: Checkout code
|
10
|
+
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
|
11
|
+
- name: Install Ruby and gems
|
12
|
+
uses: ruby/setup-ruby@8a45918450651f5e4784b6031db26f4b9f76b251
|
13
|
+
with:
|
14
|
+
bundler-cache: true
|
15
|
+
ruby-version: "3.2"
|
16
|
+
- name: Publish gem
|
17
|
+
run: |
|
18
|
+
umask 077
|
19
|
+
mkdir -p "$HOME/.gem"
|
20
|
+
printf -- "---\n:rubygems_api_key: ${RUBYGEMS_API_KEY}\n" > $HOME/.gem/credentials
|
21
|
+
gem build *.gemspec
|
22
|
+
gem push *.gem
|
23
|
+
rm -f "$HOME/.gem/credentials"
|
24
|
+
env:
|
25
|
+
RUBYGEMS_API_KEY: "${{secrets.RUBYGEMS_PUSH_API_KEY}}"
|
data/.gitignore
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
repos:
|
2
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
3
|
+
rev: v3.2.0
|
4
|
+
hooks:
|
5
|
+
- id: trailing-whitespace
|
6
|
+
- id: end-of-file-fixer
|
7
|
+
exclude: '^(config/secrets.yml.enc$)|(spec/data/)|(db/.*\.sql$)'
|
8
|
+
- id: check-yaml
|
9
|
+
exclude: '.rubocop.yml'
|
10
|
+
- id: check-added-large-files
|
11
|
+
- id: check-byte-order-marker
|
12
|
+
exclude: '^spec/data/'
|
13
|
+
- id: check-merge-conflict
|
14
|
+
- repo: https://github.com/rubocop/rubocop.git
|
15
|
+
rev: '675114c1b5a1999a112f8adaad8c99f8de6f7bcc'
|
16
|
+
hooks:
|
17
|
+
- id: rubocop
|
18
|
+
additional_dependencies: ["standard:1.25.3"]
|
data/.rubocop.yml
ADDED
data/.standard.yml
ADDED
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
|
5
|
+
|
6
|
+
gemspec
|
7
|
+
|
8
|
+
group :development, :test do
|
9
|
+
gem "rspec", "~> 3.0"
|
10
|
+
gem "rspec-its", "~>1.3"
|
11
|
+
gem "standard", "~> 1.28.3"
|
12
|
+
gem "pry", "~> 0.14"
|
13
|
+
gem "webmock", "~> 3"
|
14
|
+
gem "vcr", "~> 6"
|
15
|
+
gem "bundle-audit", "~> 0.1.0"
|
16
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,171 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
cron_to_go_sync (0.0.1)
|
5
|
+
activesupport
|
6
|
+
dry-types
|
7
|
+
dry-validation
|
8
|
+
faraday (>= 1.0, < 3.0)
|
9
|
+
faraday-follow_redirects
|
10
|
+
faraday-retry
|
11
|
+
interactor
|
12
|
+
interactor-contracts
|
13
|
+
toml-rb
|
14
|
+
|
15
|
+
GEM
|
16
|
+
remote: https://rubygems.org/
|
17
|
+
specs:
|
18
|
+
activesupport (7.0.5)
|
19
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
20
|
+
i18n (>= 1.6, < 2)
|
21
|
+
minitest (>= 5.1)
|
22
|
+
tzinfo (~> 2.0)
|
23
|
+
addressable (2.8.4)
|
24
|
+
public_suffix (>= 2.0.2, < 6.0)
|
25
|
+
ast (2.4.2)
|
26
|
+
bundle-audit (0.1.0)
|
27
|
+
bundler-audit
|
28
|
+
bundler-audit (0.9.1)
|
29
|
+
bundler (>= 1.2.0, < 3)
|
30
|
+
thor (~> 1.0)
|
31
|
+
citrus (3.0.2)
|
32
|
+
coderay (1.1.3)
|
33
|
+
concurrent-ruby (1.2.2)
|
34
|
+
crack (0.4.5)
|
35
|
+
rexml
|
36
|
+
diff-lcs (1.5.0)
|
37
|
+
dry-configurable (1.0.1)
|
38
|
+
dry-core (~> 1.0, < 2)
|
39
|
+
zeitwerk (~> 2.6)
|
40
|
+
dry-core (1.0.0)
|
41
|
+
concurrent-ruby (~> 1.0)
|
42
|
+
zeitwerk (~> 2.6)
|
43
|
+
dry-inflector (1.0.0)
|
44
|
+
dry-initializer (3.1.1)
|
45
|
+
dry-logic (1.5.0)
|
46
|
+
concurrent-ruby (~> 1.0)
|
47
|
+
dry-core (~> 1.0, < 2)
|
48
|
+
zeitwerk (~> 2.6)
|
49
|
+
dry-schema (1.13.1)
|
50
|
+
concurrent-ruby (~> 1.0)
|
51
|
+
dry-configurable (~> 1.0, >= 1.0.1)
|
52
|
+
dry-core (~> 1.0, < 2)
|
53
|
+
dry-initializer (~> 3.0)
|
54
|
+
dry-logic (>= 1.4, < 2)
|
55
|
+
dry-types (>= 1.7, < 2)
|
56
|
+
zeitwerk (~> 2.6)
|
57
|
+
dry-types (1.7.1)
|
58
|
+
concurrent-ruby (~> 1.0)
|
59
|
+
dry-core (~> 1.0)
|
60
|
+
dry-inflector (~> 1.0)
|
61
|
+
dry-logic (~> 1.4)
|
62
|
+
zeitwerk (~> 2.6)
|
63
|
+
dry-validation (1.10.0)
|
64
|
+
concurrent-ruby (~> 1.0)
|
65
|
+
dry-core (~> 1.0, < 2)
|
66
|
+
dry-initializer (~> 3.0)
|
67
|
+
dry-schema (>= 1.12, < 2)
|
68
|
+
zeitwerk (~> 2.6)
|
69
|
+
faraday (2.7.5)
|
70
|
+
faraday-net_http (>= 2.0, < 3.1)
|
71
|
+
ruby2_keywords (>= 0.0.4)
|
72
|
+
faraday-follow_redirects (0.3.0)
|
73
|
+
faraday (>= 1, < 3)
|
74
|
+
faraday-net_http (3.0.2)
|
75
|
+
faraday-retry (2.1.0)
|
76
|
+
faraday (~> 2.0)
|
77
|
+
hashdiff (1.0.1)
|
78
|
+
i18n (1.13.0)
|
79
|
+
concurrent-ruby (~> 1.0)
|
80
|
+
interactor (3.1.2)
|
81
|
+
interactor-contracts (0.3.0)
|
82
|
+
dry-validation (~> 1.0)
|
83
|
+
interactor (~> 3)
|
84
|
+
json (2.6.3)
|
85
|
+
language_server-protocol (3.17.0.3)
|
86
|
+
lint_roller (1.0.0)
|
87
|
+
method_source (1.0.0)
|
88
|
+
minitest (5.18.0)
|
89
|
+
parallel (1.23.0)
|
90
|
+
parser (3.2.2.1)
|
91
|
+
ast (~> 2.4.1)
|
92
|
+
pry (0.14.2)
|
93
|
+
coderay (~> 1.1)
|
94
|
+
method_source (~> 1.0)
|
95
|
+
public_suffix (5.0.1)
|
96
|
+
rainbow (3.1.1)
|
97
|
+
regexp_parser (2.8.0)
|
98
|
+
rexml (3.2.5)
|
99
|
+
rspec (3.12.0)
|
100
|
+
rspec-core (~> 3.12.0)
|
101
|
+
rspec-expectations (~> 3.12.0)
|
102
|
+
rspec-mocks (~> 3.12.0)
|
103
|
+
rspec-core (3.12.2)
|
104
|
+
rspec-support (~> 3.12.0)
|
105
|
+
rspec-expectations (3.12.3)
|
106
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
107
|
+
rspec-support (~> 3.12.0)
|
108
|
+
rspec-its (1.3.0)
|
109
|
+
rspec-core (>= 3.0.0)
|
110
|
+
rspec-expectations (>= 3.0.0)
|
111
|
+
rspec-mocks (3.12.5)
|
112
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
113
|
+
rspec-support (~> 3.12.0)
|
114
|
+
rspec-support (3.12.0)
|
115
|
+
rubocop (1.50.2)
|
116
|
+
json (~> 2.3)
|
117
|
+
parallel (~> 1.10)
|
118
|
+
parser (>= 3.2.0.0)
|
119
|
+
rainbow (>= 2.2.2, < 4.0)
|
120
|
+
regexp_parser (>= 1.8, < 3.0)
|
121
|
+
rexml (>= 3.2.5, < 4.0)
|
122
|
+
rubocop-ast (>= 1.28.0, < 2.0)
|
123
|
+
ruby-progressbar (~> 1.7)
|
124
|
+
unicode-display_width (>= 2.4.0, < 3.0)
|
125
|
+
rubocop-ast (1.28.1)
|
126
|
+
parser (>= 3.2.1.0)
|
127
|
+
rubocop-performance (1.16.0)
|
128
|
+
rubocop (>= 1.7.0, < 2.0)
|
129
|
+
rubocop-ast (>= 0.4.0)
|
130
|
+
ruby-progressbar (1.13.0)
|
131
|
+
ruby2_keywords (0.0.5)
|
132
|
+
standard (1.28.3)
|
133
|
+
language_server-protocol (~> 3.17.0.2)
|
134
|
+
lint_roller (~> 1.0)
|
135
|
+
rubocop (~> 1.50.2)
|
136
|
+
standard-custom (~> 1.0.0)
|
137
|
+
standard-performance (~> 1.0.1)
|
138
|
+
standard-custom (1.0.0)
|
139
|
+
lint_roller (~> 1.0)
|
140
|
+
standard-performance (1.0.1)
|
141
|
+
lint_roller (~> 1.0)
|
142
|
+
rubocop-performance (~> 1.16.0)
|
143
|
+
thor (1.2.2)
|
144
|
+
toml-rb (2.2.0)
|
145
|
+
citrus (~> 3.0, > 3.0)
|
146
|
+
tzinfo (2.0.6)
|
147
|
+
concurrent-ruby (~> 1.0)
|
148
|
+
unicode-display_width (2.4.2)
|
149
|
+
vcr (6.1.0)
|
150
|
+
webmock (3.18.1)
|
151
|
+
addressable (>= 2.8.0)
|
152
|
+
crack (>= 0.3.2)
|
153
|
+
hashdiff (>= 0.4.0, < 2.0.0)
|
154
|
+
zeitwerk (2.6.8)
|
155
|
+
|
156
|
+
PLATFORMS
|
157
|
+
arm64-darwin-22
|
158
|
+
x86_64-linux
|
159
|
+
|
160
|
+
DEPENDENCIES
|
161
|
+
bundle-audit (~> 0.1.0)
|
162
|
+
cron_to_go_sync!
|
163
|
+
pry (~> 0.14)
|
164
|
+
rspec (~> 3.0)
|
165
|
+
rspec-its (~> 1.3)
|
166
|
+
standard (~> 1.28.3)
|
167
|
+
vcr (~> 6)
|
168
|
+
webmock (~> 3)
|
169
|
+
|
170
|
+
BUNDLED WITH
|
171
|
+
2.4.13
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,5 @@
|
|
1
|
+
Copyright 2023 Instrumentl, Inc.
|
2
|
+
|
3
|
+
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
|
4
|
+
|
5
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,6 @@
|
|
1
|
+
This repository contains a Ruby library for synchronizing a checked-in YAML file with [CronToGo](https://crontogo.com/).
|
2
|
+
|
3
|
+
CronToGo is a trademark of Crazy Ant Labs; this library is an independent work and is not endorsed by or maintained
|
4
|
+
by Crazy Ant Labs.
|
5
|
+
|
6
|
+
This work is licensed under the ISC license, a copy of which can be found at [LICENSE.txt](LICENSE.txt)
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/cron_to_go_sync/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |gem|
|
6
|
+
gem.name = "cron_to_go_sync"
|
7
|
+
gem.version = CronToGoSync::VERSION
|
8
|
+
gem.authors = ["Instrumentl, Inc."]
|
9
|
+
gem.email = ["oss@instrumentl.com"]
|
10
|
+
gem.description = <<-EOF
|
11
|
+
Library to synchronize a checked-in YAML file with the SaaS scheduled task service CronToGo
|
12
|
+
EOF
|
13
|
+
gem.summary = "CronToGo configuration syncing library"
|
14
|
+
gem.homepage = "https://github.com/instrumentl/cron_to_go_sync-rb"
|
15
|
+
gem.license = "ISC"
|
16
|
+
gem.files = `git ls-files`.split($/)
|
17
|
+
gem.require_paths = ["lib"]
|
18
|
+
gem.metadata["homepage_uri"] = gem.homepage
|
19
|
+
gem.metadata["source_code_uri"] = gem.homepage
|
20
|
+
gem.metadata["changelog_uri"] = "https://github.com/instrumentl/cron_to_go_sync-rb/blob/main/CHANGELOG.md"
|
21
|
+
gem.required_ruby_version = ">= 3.0.0"
|
22
|
+
|
23
|
+
gem.add_dependency "activesupport"
|
24
|
+
gem.add_dependency "dry-types"
|
25
|
+
gem.add_dependency "dry-validation"
|
26
|
+
gem.add_dependency "faraday", ">= 1.0", "< 3.0"
|
27
|
+
gem.add_dependency "faraday-follow_redirects"
|
28
|
+
gem.add_dependency "faraday-retry"
|
29
|
+
gem.add_dependency "interactor"
|
30
|
+
gem.add_dependency "interactor-contracts"
|
31
|
+
gem.add_dependency "toml-rb"
|
32
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require "dry-types"
|
2
|
+
require "dry-validation"
|
3
|
+
require "toml-rb"
|
4
|
+
|
5
|
+
module CronToGoSync
|
6
|
+
module Parser
|
7
|
+
module Types
|
8
|
+
include Dry::Types()
|
9
|
+
|
10
|
+
State = Types::String.enum("enabled", "paused")
|
11
|
+
DynoSize = Types::String.enum("Eco", "Basic", "Standard-1X", "Standard-2X", "Performance-M", "Performance-L")
|
12
|
+
end
|
13
|
+
|
14
|
+
class JobContract < Dry::Validation::Contract
|
15
|
+
params do
|
16
|
+
required(:alias).filled(:string)
|
17
|
+
required(:enabled).filled(:bool)
|
18
|
+
required(:schedule).filled(:string)
|
19
|
+
required(:ttl).value(:integer)
|
20
|
+
required(:dyno_size).value(Types::DynoSize)
|
21
|
+
required(:command).filled(:string)
|
22
|
+
end
|
23
|
+
|
24
|
+
rule(:schedule) do
|
25
|
+
fields = value.split(" ")
|
26
|
+
unless fields.size == 5
|
27
|
+
key.failure("must contain exactly five fields")
|
28
|
+
next
|
29
|
+
end
|
30
|
+
unless (error = valid_cron_field(fields[0], 0, 59)).nil?
|
31
|
+
key.failure("in minute: #{error}")
|
32
|
+
end
|
33
|
+
unless (error = valid_cron_field(fields[1], 0, 23)).nil?
|
34
|
+
key.failure("in hour: #{error}")
|
35
|
+
end
|
36
|
+
unless (error = valid_cron_field(fields[2], 1, 31)).nil?
|
37
|
+
key.failure("in day of month: #{error}")
|
38
|
+
end
|
39
|
+
unless (error = valid_cron_field(fields[3], 1, 12)).nil?
|
40
|
+
key.failure("in month: #{error}")
|
41
|
+
end
|
42
|
+
unless (error = valid_cron_field(fields[4], 0, 7)).nil?
|
43
|
+
key.failure("in day of week: #{error}")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def valid_cron_field(value, lower_limit, upper_limit)
|
50
|
+
parts = value.split("/")
|
51
|
+
if parts.size == 1
|
52
|
+
numerator = parts[0]
|
53
|
+
elsif parts.size == 2
|
54
|
+
numerator, denominator = parts
|
55
|
+
return "invalid denominator" unless denominator.match?(/\A[0-9]+\z/)
|
56
|
+
denominator = denominator.to_i
|
57
|
+
return "invalid denominator value" if denominator < lower_limit || denominator > upper_limit
|
58
|
+
else
|
59
|
+
return "too many characters"
|
60
|
+
end
|
61
|
+
|
62
|
+
return if numerator == "*"
|
63
|
+
|
64
|
+
# the numerator is a comma-separated list of ranges
|
65
|
+
numerator.split(",").each do |range|
|
66
|
+
if range.count("-") == 1
|
67
|
+
lower, upper = range.split("-")
|
68
|
+
lower = lower.to_i
|
69
|
+
upper = upper.to_i
|
70
|
+
return "invalid range lower #{lower}" if lower < lower_limit || lower > upper_limit
|
71
|
+
return "invalid range upper #{upper}" if upper < upper_limit || upper > upper_limit
|
72
|
+
elsif !range.match?(/\A[0-9]+\z/)
|
73
|
+
return "invalid range value #{range}"
|
74
|
+
else
|
75
|
+
value = range.to_i
|
76
|
+
return "invalid range value #{value}" if value < lower_limit || value > upper_limit
|
77
|
+
end
|
78
|
+
end
|
79
|
+
nil
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Parse a hash of {alias => properties}
|
84
|
+
def self.parse_hash(hash)
|
85
|
+
contract = JobContract.new
|
86
|
+
hash.map do |key, value|
|
87
|
+
res = contract.call(
|
88
|
+
alias: key, schedule: value[:schedule], dyno_size: value[:dyno_size],
|
89
|
+
command: value[:command], ttl: value[:ttl], enabled: value.fetch(:enabled, true)
|
90
|
+
)
|
91
|
+
if res.errors.present?
|
92
|
+
raise "Invalid schedule for #{key}: #{res.errors.to_h}"
|
93
|
+
end
|
94
|
+
[key, res.to_h]
|
95
|
+
end.to_h
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.parse_toml_file(path)
|
99
|
+
hash = TomlRB.load_file(path).with_indifferent_access
|
100
|
+
defaults = hash.delete("DEFAULTS")
|
101
|
+
parse_hash(hash.transform_values { defaults.merge(_1) })
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
require "faraday"
|
2
|
+
require "faraday/follow_redirects"
|
3
|
+
require "faraday/retry"
|
4
|
+
require "interactor"
|
5
|
+
require "interactor-contracts"
|
6
|
+
|
7
|
+
module CronToGoSync
|
8
|
+
class SyncInteractor
|
9
|
+
include ::Interactor
|
10
|
+
include ::Interactor::Contracts
|
11
|
+
|
12
|
+
BASE_URL = "https://api.crontogo.com".freeze
|
13
|
+
|
14
|
+
expects do
|
15
|
+
required(:api_key).value(:string)
|
16
|
+
required(:organization_id).value(:string)
|
17
|
+
required(:input_filename).value(:string)
|
18
|
+
optional(:timeout).value(:float)
|
19
|
+
optional(:retries).value(:integer)
|
20
|
+
end
|
21
|
+
|
22
|
+
before :set_variables
|
23
|
+
|
24
|
+
def call
|
25
|
+
unless File.exist? context.input_filename
|
26
|
+
context.fail!(error: "No such file found", filename: context.input_filename)
|
27
|
+
end
|
28
|
+
target_jobs = Parser.parse_toml_file(context.input_filename)
|
29
|
+
existing_jobs = get_jobs_from_crontogo
|
30
|
+
context.deleted = (existing_jobs.keys.to_set - target_jobs.keys.to_set).map do |alias_to_remove|
|
31
|
+
delete_job existing_jobs[alias_to_remove][:id]
|
32
|
+
end.size
|
33
|
+
context.created = (target_jobs.keys.to_set - existing_jobs.keys.to_set).map do |alias_to_add|
|
34
|
+
create_job target_jobs[alias_to_add]
|
35
|
+
end.size
|
36
|
+
context.updated = (target_jobs.keys.to_set & existing_jobs.keys.to_set).sum do |alias_to_update|
|
37
|
+
existing = existing_jobs[alias_to_update]
|
38
|
+
target = target_jobs[alias_to_update]
|
39
|
+
if target.merge({id: existing[:id]}) != existing
|
40
|
+
update_job(target, existing[:id])
|
41
|
+
1
|
42
|
+
else
|
43
|
+
0
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def get_jobs_from_crontogo
|
51
|
+
client.get(organization_url("/jobs")).body.map do |job|
|
52
|
+
[job["Alias"], {
|
53
|
+
alias: job["Alias"],
|
54
|
+
schedule: job["ScheduleExpression"],
|
55
|
+
dyno_size: job["Target"]["Size"],
|
56
|
+
command: job["Target"]["Command"],
|
57
|
+
ttl: job["Target"]["TimeToLive"],
|
58
|
+
enabled: job["State"] == "enabled",
|
59
|
+
id: job["Id"]
|
60
|
+
}]
|
61
|
+
end.to_h
|
62
|
+
end
|
63
|
+
|
64
|
+
def delete_job(id)
|
65
|
+
client.delete(organization_url("/jobs/#{id}"))
|
66
|
+
end
|
67
|
+
|
68
|
+
def request_for(struct)
|
69
|
+
{
|
70
|
+
"Alias" => struct[:alias],
|
71
|
+
"ScheduleExpression" => struct[:schedule],
|
72
|
+
"ScheduleType" => "cron",
|
73
|
+
"Target" => {
|
74
|
+
"Type" => "dyno",
|
75
|
+
"Size" => struct[:dyno_size],
|
76
|
+
"Command" => struct[:command],
|
77
|
+
"TimeToLive" => struct[:ttl]
|
78
|
+
},
|
79
|
+
"State" => struct[:enabled] ? "enabled" : "paused"
|
80
|
+
}
|
81
|
+
end
|
82
|
+
|
83
|
+
def create_job(struct)
|
84
|
+
client.post(organization_url("/jobs"), request_for(struct))
|
85
|
+
end
|
86
|
+
|
87
|
+
def update_job(struct, id)
|
88
|
+
client.patch(organization_url("/jobs/#{id}"), request_for(struct))
|
89
|
+
end
|
90
|
+
|
91
|
+
def set_variables
|
92
|
+
context.timeout ||= 10.0
|
93
|
+
context.retries ||= 3
|
94
|
+
end
|
95
|
+
|
96
|
+
def api_key
|
97
|
+
context.api_key
|
98
|
+
end
|
99
|
+
|
100
|
+
def organization_id
|
101
|
+
context.organization_id
|
102
|
+
end
|
103
|
+
|
104
|
+
def url(path)
|
105
|
+
"#{BASE_URL}#{path}"
|
106
|
+
end
|
107
|
+
|
108
|
+
def organization_url(path)
|
109
|
+
url "/organizations/#{organization_id}#{path}"
|
110
|
+
end
|
111
|
+
|
112
|
+
def client
|
113
|
+
@client ||= Faraday.new do |builder|
|
114
|
+
builder.headers["Authorization"] = "Bearer #{api_key}"
|
115
|
+
builder.options.timeout = context.timeout
|
116
|
+
builder.options.open_timeout = context.timeout
|
117
|
+
builder.request :json
|
118
|
+
builder.response :json
|
119
|
+
builder.response :follow_redirects
|
120
|
+
builder.response :raise_error
|
121
|
+
if context.retries > 0
|
122
|
+
builder.request :retry, {max: context.retries}
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
{"http_interactions":[{"request":{"method":"get","uri":"https://api.crontogo.com/organizations/<crontogo_organization_id>/jobs","body":{"encoding":"US-ASCII","string":""},"headers":{"Authorization":["<basic auth header>"],"User-Agent":["Faraday v1.10.2"],"Accept-Encoding":["gzip;q=1.0,deflate;q=0.6,identity;q=0.3"],"Accept":["*/*"]}},"response":{"status":{"code":200,"message":"OK"},"headers":{"Date":["Tue, 21 Mar 2023 19:40:09 GMT"],"Content-Type":["application/json"],"Content-Length":["4126"],"Connection":["keep-alive"],"X-Amzn-Requestid":["b4bd6613-07c5-4a9c-a006-e0710adf419d"],"Access-Control-Allow-Origin":["*"],"X-Amz-Apigw-Id":["CJYz-F-6oAMF6hA="],"Access-Control-Expose-Headers":["Link, X-Total-Count"],"X-Amzn-Trace-Id":["Root=1-641a0819-40683a372ddb6288582bbcaf;Sampled=0"]},"body":{"encoding":"ASCII-8BIT","string":"[{\"UpdatedAt\":1679427593355,\"ScheduleType\":\"cron\",\"Timezone\":\"UTC\",\"ScheduleExpression\":\"45 2 * * *\",\"State\":\"enabled\",\"OrganizationId\":\"<crontogo_organization_id>\",\"Alias\":\"bmf:parse_latest\",\"Retries\":2,\"JobExecutionsCount\":0,\"Target\":{\"Type\":\"dyno\",\"TimeToLive\":86400,\"AppId\":\"38277779-272e-46f2-afab-7bd91bf8535e\",\"Command\":\"env STATEMENT_TIMEOUT=120s rake bmf:parse_latest\",\"Size\":\"Standard-1X\"},\"Id\":\"eaa0254e-0b99-4918-89ff-bca99e845448\",\"LastAttempt\":null,\"CreatedAt\":1679427593355},{\"UpdatedAt\":1679424153176,\"ScheduleType\":\"cron\",\"Timezone\":\"UTC\",\"ScheduleExpression\":\"0 4 * * 0\",\"State\":\"enabled\",\"OrganizationId\":\"<crontogo_organization_id>\",\"Alias\":\"canonical_taxpayer:maybe_run_non_canonicalized\",\"Retries\":2,\"JobExecutionsCount\":0,\"Target\":{\"Type\":\"dyno\",\"TimeToLive\":86400,\"AppId\":\"38277779-272e-46f2-afab-7bd91bf8535e\",\"Command\":\"env STATEMENT_TIMEOUT=1000s rake canonical_taxpayer:maybe_run_non_canonicalized\",\"Size\":\"Standard-1X\"},\"Id\":\"a6584e57-cd00-4df4-a52d-e4374d17c1de\",\"LastAttempt\":null,\"CreatedAt\":1679424153176},{\"UpdatedAt\":1679424263971,\"ScheduleType\":\"cron\",\"Timezone\":\"UTC\",\"ScheduleExpression\":\"10 7 * * 5\",\"State\":\"enabled\",\"OrganizationId\":\"<crontogo_organization_id>\",\"Alias\":\"awards:populate_problematic_awards\",\"Retries\":2,\"JobExecutionsCount\":0,\"Target\":{\"Type\":\"dyno\",\"TimeToLive\":86400,\"AppId\":\"38277779-272e-46f2-afab-7bd91bf8535e\",\"Command\":\"env STATEMENT_TIMEOUT=1000s rake awards:populate_problematic_awards\",\"Size\":\"Standard-1X\"},\"Id\":\"3068c3e4-23e8-4588-9a3e-9b1c0f5c07b9\",\"LastAttempt\":null,\"CreatedAt\":1679416937522},{\"UpdatedAt\":1679424156282,\"ScheduleType\":\"cron\",\"Timezone\":\"UTC\",\"ScheduleExpression\":\"30 1 * * *\",\"State\":\"enabled\",\"OrganizationId\":\"<crontogo_organization_id>\",\"Alias\":\"irs:process_epostcards\",\"Retries\":2,\"JobExecutionsCount\":0,\"Target\":{\"Type\":\"dyno\",\"TimeToLive\":86400,\"AppId\":\"38277779-272e-46f2-afab-7bd91bf8535e\",\"Command\":\"rake irs:process_epostcards\",\"Size\":\"Standard-1X\"},\"Id\":\"a2ba7e29-176d-4d7d-b315-55400e9959e4\",\"LastAttempt\":null,\"CreatedAt\":1679424156282},{\"UpdatedAt\":1679424151827,\"ScheduleType\":\"cron\",\"Timezone\":\"UTC\",\"ScheduleExpression\":\"0 9 * * *\",\"State\":\"enabled\",\"OrganizationId\":\"<crontogo_organization_id>\",\"Alias\":\"materialized_views:refresh_all\",\"Retries\":2,\"JobExecutionsCount\":0,\"Target\":{\"Type\":\"dyno\",\"TimeToLive\":7200,\"AppId\":\"38277779-272e-46f2-afab-7bd91bf8535e\",\"Command\":\"env STATEMENT_TIMEOUT=1000s rake materialized_views:refresh_all\",\"Size\":\"Standard-1X\"},\"Id\":\"10965d9a-5e9a-4f4d-b661-3efa880aa799\",\"LastAttempt\":null,\"CreatedAt\":1679424151827},{\"UpdatedAt\":1679427595382,\"ScheduleType\":\"cron\",\"Timezone\":\"UTC\",\"ScheduleExpression\":\"10 3 * * 5\",\"State\":\"enabled\",\"OrganizationId\":\"<crontogo_organization_id>\",\"Alias\":\"awards:populate_non_canonicalized_awards\",\"Retries\":2,\"JobExecutionsCount\":0,\"Target\":{\"Type\":\"dyno\",\"TimeToLive\":14400,\"AppId\":\"38277779-272e-46f2-afab-7bd91bf8535e\",\"Command\":\"env STATEMENT_TIMEOUT=1000s rake awards:populate_non_canonicalized_awards\",\"Size\":\"Standard-1X\"},\"Id\":\"fe5fd04b-00b4-46c1-89e4-289b6df4f247\",\"LastAttempt\":null,\"CreatedAt\":1679416994777},{\"UpdatedAt\":1679424155097,\"ScheduleType\":\"cron\",\"Timezone\":\"UTC\",\"ScheduleExpression\":\"10 13 * * *\",\"State\":\"enabled\",\"OrganizationId\":\"<crontogo_organization_id>\",\"Alias\":\"irs:process_revocation_list\",\"Retries\":2,\"JobExecutionsCount\":0,\"Target\":{\"Type\":\"dyno\",\"TimeToLive\":86400,\"AppId\":\"38277779-272e-46f2-afab-7bd91bf8535e\",\"Command\":\"rake irs:process_revocation_list\",\"Size\":\"Standard-1X\"},\"Id\":\"24e1fd36-15f4-4b59-bd5d-05c6f8fd7694\",\"LastAttempt\":null,\"CreatedAt\":1679424155097},{\"UpdatedAt\":1679424150306,\"ScheduleType\":\"cron\",\"Timezone\":\"UTC\",\"ScheduleExpression\":\"0 0 * * *\",\"State\":\"enabled\",\"OrganizationId\":\"<crontogo_organization_id>\",\"Alias\":\"content_stat:perform\",\"Retries\":2,\"JobExecutionsCount\":0,\"Target\":{\"Type\":\"dyno\",\"TimeToLive\":3600,\"AppId\":\"38277779-272e-46f2-afab-7bd91bf8535e\",\"Command\":\"rake content_stat:perform\",\"Size\":\"Standard-1X\"},\"Id\":\"d5d2e441-ca23-4814-ad9d-b677fd3ec3e0\",\"LastAttempt\":null,\"CreatedAt\":1679424150306}]"}},"recorded_at":"Tue, 21 Mar 2023 19:40:09 GMT"},{"request":{"method":"delete","uri":"https://api.crontogo.com/organizations/<crontogo_organization_id>/jobs/eaa0254e-0b99-4918-89ff-bca99e845448","body":{"encoding":"US-ASCII","string":""},"headers":{"Authorization":["<basic auth header>"],"User-Agent":["Faraday v1.10.2"],"Accept-Encoding":["gzip;q=1.0,deflate;q=0.6,identity;q=0.3"],"Accept":["*/*"]}},"response":{"status":{"code":200,"message":"OK"},"headers":{"Date":["Tue, 21 Mar 2023 19:40:09 GMT"],"Content-Type":["application/json"],"Content-Length":["475"],"Connection":["keep-alive"],"X-Amzn-Requestid":["f57dcdb6-c06a-4400-a3b6-5650b7c5f500"],"Access-Control-Allow-Origin":["*"],"X-Amz-Apigw-Id":["CJY0DGcAoAMF_pg="],"Access-Control-Expose-Headers":["Link, X-Total-Count"],"X-Amzn-Trace-Id":["Root=1-641a0819-5012ee9514806d175b5afe1b;Sampled=0"]},"body":{"encoding":"UTF-8","string":"{\"Id\":\"eaa0254e-0b99-4918-89ff-bca99e845448\",\"OrganizationId\":\"<crontogo_organization_id>\",\"State\":\"enabled\",\"Alias\":\"bmf:parse_latest\",\"ScheduleType\":\"cron\",\"ScheduleExpression\":\"45 2 * * *\",\"Timezone\":\"UTC\",\"Retries\":2,\"Target\":{\"Type\":\"dyno\",\"TimeToLive\":86400,\"AppId\":\"38277779-272e-46f2-afab-7bd91bf8535e\",\"Command\":\"env STATEMENT_TIMEOUT=120s rake bmf:parse_latest\",\"Size\":\"Standard-1X\"},\"LastAttempt\":null,\"CreatedAt\":1679427593355,\"UpdatedAt\":1679427593355}"}},"recorded_at":"Tue, 21 Mar 2023 19:40:09 GMT"},{"request":{"method":"post","uri":"https://api.crontogo.com/organizations/<crontogo_organization_id>/jobs","body":{"encoding":"UTF-8","string":"{\"Alias\":\"new\",\"ScheduleExpression\":\"* * * * *\",\"ScheduleType\":\"cron\",\"Target\":{\"Type\":\"dyno\",\"Size\":\"Standard-1X\",\"Command\":\"echo 'coolio'\",\"TimeToLive\":86400},\"State\":\"enabled\"}"},"headers":{"Authorization":["<basic auth header>"],"User-Agent":["Faraday v1.10.2"],"Content-Type":["application/json"],"Accept-Encoding":["gzip;q=1.0,deflate;q=0.6,identity;q=0.3"],"Accept":["*/*"]}},"response":{"status":{"code":201,"message":"Created"},"headers":{"Date":["Tue, 21 Mar 2023 19:40:10 GMT"],"Content-Type":["application/json"],"Content-Length":["426"],"Connection":["keep-alive"],"X-Amzn-Requestid":["9d2d625c-2961-4b27-b967-5c42433c15c3"],"Access-Control-Allow-Origin":["*"],"X-Amz-Apigw-Id":["CJY0KHcDIAMFg6g="],"Access-Control-Expose-Headers":["Link, X-Total-Count"],"X-Amzn-Trace-Id":["Root=1-641a081a-0d58daea2f8fcc096a142d92;Sampled=0"]},"body":{"encoding":"UTF-8","string":"{\"Id\":\"514fdcb1-ae38-4deb-b3cd-501da311e0e9\",\"OrganizationId\":\"<crontogo_organization_id>\",\"State\":\"enabled\",\"Alias\":\"new\",\"ScheduleType\":\"cron\",\"ScheduleExpression\":\"* * * * *\",\"Timezone\":\"UTC\",\"Retries\":2,\"Target\":{\"Type\":\"dyno\",\"Size\":\"Standard-1X\",\"Command\":\"echo 'coolio'\",\"TimeToLive\":86400,\"AppId\":\"38277779-272e-46f2-afab-7bd91bf8535e\"},\"LastAttempt\":null,\"CreatedAt\":1679427610495,\"UpdatedAt\":1679427610495}"}},"recorded_at":"Tue, 21 Mar 2023 19:40:11 GMT"},{"request":{"method":"patch","uri":"https://api.crontogo.com/organizations/<crontogo_organization_id>/jobs/fe5fd04b-00b4-46c1-89e4-289b6df4f247","body":{"encoding":"UTF-8","string":"{\"Alias\":\"awards:populate_non_canonicalized_awards\",\"ScheduleExpression\":\"10 3 * * 6\",\"ScheduleType\":\"cron\",\"Target\":{\"Type\":\"dyno\",\"Size\":\"Standard-1X\",\"Command\":\"env STATEMENT_TIMEOUT=1000s rake awards:populate_non_canonicalized_awards\",\"TimeToLive\":14400},\"State\":\"enabled\"}"},"headers":{"Authorization":["<basic auth header>"],"User-Agent":["Faraday v1.10.2"],"Content-Type":["application/json"],"Accept-Encoding":["gzip;q=1.0,deflate;q=0.6,identity;q=0.3"],"Accept":["*/*"]}},"response":{"status":{"code":200,"message":"OK"},"headers":{"Date":["Tue, 21 Mar 2023 19:40:11 GMT"],"Content-Type":["application/json"],"Content-Length":["524"],"Connection":["keep-alive"],"X-Amzn-Requestid":["8c730638-f16d-4395-82d4-97d449b5eb75"],"Access-Control-Allow-Origin":["*"],"X-Amz-Apigw-Id":["CJY0WGAcIAMFtEA="],"Access-Control-Expose-Headers":["Link, X-Total-Count"],"X-Amzn-Trace-Id":["Root=1-641a081b-5f0571cc116018d2332bb83a;Sampled=0"]},"body":{"encoding":"UTF-8","string":"{\"Id\":\"fe5fd04b-00b4-46c1-89e4-289b6df4f247\",\"OrganizationId\":\"<crontogo_organization_id>\",\"State\":\"enabled\",\"Alias\":\"awards:populate_non_canonicalized_awards\",\"ScheduleType\":\"cron\",\"ScheduleExpression\":\"10 3 * * 6\",\"Timezone\":\"UTC\",\"Retries\":2,\"Target\":{\"Type\":\"dyno\",\"TimeToLive\":14400,\"AppId\":\"38277779-272e-46f2-afab-7bd91bf8535e\",\"Command\":\"env STATEMENT_TIMEOUT=1000s rake awards:populate_non_canonicalized_awards\",\"Size\":\"Standard-1X\"},\"LastAttempt\":null,\"CreatedAt\":1679416994777,\"UpdatedAt\":1679427611930}"}},"recorded_at":"Tue, 21 Mar 2023 19:40:12 GMT"}],"recorded_with":"VCR 6.1.0"}
|
@@ -0,0 +1,42 @@
|
|
1
|
+
[DEFAULTS]
|
2
|
+
dyno_size="Standard-1X"
|
3
|
+
ttl=86400
|
4
|
+
|
5
|
+
["awards:populate_non_canonicalized_awards"]
|
6
|
+
schedule="10 3 * * 6"
|
7
|
+
dyno_size="Standard-1X"
|
8
|
+
ttl=14400
|
9
|
+
command="env STATEMENT_TIMEOUT=1000s rake awards:populate_non_canonicalized_awards"
|
10
|
+
|
11
|
+
["awards:populate_problematic_awards"]
|
12
|
+
schedule="10 7 * * 5"
|
13
|
+
dyno_size="Standard-1X"
|
14
|
+
command="env STATEMENT_TIMEOUT=1000s rake awards:populate_problematic_awards"
|
15
|
+
|
16
|
+
["content_stat:perform"]
|
17
|
+
schedule="0 0 * * *"
|
18
|
+
dyno_size="Standard-1X"
|
19
|
+
ttl=3600
|
20
|
+
command="rake content_stat:perform"
|
21
|
+
|
22
|
+
["materialized_views:refresh_all"]
|
23
|
+
schedule="0 9 * * *"
|
24
|
+
dyno_size="Standard-1X"
|
25
|
+
ttl=7200
|
26
|
+
command="env STATEMENT_TIMEOUT=1000s rake materialized_views:refresh_all"
|
27
|
+
|
28
|
+
["canonical_taxpayer:maybe_run_non_canonicalized"]
|
29
|
+
schedule="0 4 * * 0"
|
30
|
+
command="env STATEMENT_TIMEOUT=1000s rake canonical_taxpayer:maybe_run_non_canonicalized"
|
31
|
+
|
32
|
+
["irs:process_revocation_list"]
|
33
|
+
schedule="10 13 * * *"
|
34
|
+
command="rake irs:process_revocation_list"
|
35
|
+
|
36
|
+
["irs:process_epostcards"]
|
37
|
+
schedule="30 1 * * *"
|
38
|
+
command="rake irs:process_epostcards"
|
39
|
+
|
40
|
+
[new]
|
41
|
+
schedule="* * * * *"
|
42
|
+
command="echo 'coolio'"
|
data/spec/parser_spec.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
RSpec.describe CronToGoSync::Parser do
|
4
|
+
describe "#parse_hash" do
|
5
|
+
it "accepts a valid schedule" do
|
6
|
+
input_hash = {"job_name" => {schedule: "* * * * *", enabled: true, ttl: 86400, dyno_size: "Basic", command: "/bin/true"}}
|
7
|
+
expect(described_class.parse_hash(input_hash)).to eq({
|
8
|
+
"job_name" => {
|
9
|
+
alias: "job_name",
|
10
|
+
schedule: "* * * * *",
|
11
|
+
enabled: true,
|
12
|
+
ttl: 86400,
|
13
|
+
dyno_size: "Basic",
|
14
|
+
command: "/bin/true"
|
15
|
+
}
|
16
|
+
})
|
17
|
+
end
|
18
|
+
|
19
|
+
it "validates number of fields" do
|
20
|
+
input_hash = {"job_name" => {schedule: "* * * *", enabled: true, ttl: 86400, dyno_size: "Basic", command: "/bin/true"}}
|
21
|
+
expect { described_class.parse_hash(input_hash) }.to raise_error(RuntimeError, /must contain exactly five fields/)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "validates schedule numerator" do
|
25
|
+
input_hash = {"job_name" => {schedule: "* 25 * * *", enabled: true, ttl: 86400, dyno_size: "Basic", command: "/bin/true"}}
|
26
|
+
expect { described_class.parse_hash(input_hash) }.to raise_error(RuntimeError, /invalid range value/)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "validates schedule denominator" do
|
30
|
+
input_hash = {"job_name" => {schedule: "*/99 * * * *", enabled: true, ttl: 86400, dyno_size: "Basic", command: "/bin/true"}}
|
31
|
+
expect { described_class.parse_hash(input_hash) }.to raise_error(RuntimeError, /invalid denominator value/)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
require "rspec"
|
2
|
+
require "rspec/its"
|
3
|
+
require "webmock/rspec"
|
4
|
+
require "vcr"
|
5
|
+
|
6
|
+
require "cron_to_go_sync"
|
7
|
+
|
8
|
+
RSpec.configure do |config|
|
9
|
+
config.expect_with :rspec do |expectations|
|
10
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
11
|
+
end
|
12
|
+
|
13
|
+
config.mock_with :rspec do |mocks|
|
14
|
+
mocks.verify_partial_doubles = true
|
15
|
+
end
|
16
|
+
|
17
|
+
config.shared_context_metadata_behavior = :apply_to_host_groups
|
18
|
+
|
19
|
+
WebMock.disable_net_connect!(
|
20
|
+
allow_localhost: true
|
21
|
+
)
|
22
|
+
end
|
23
|
+
|
24
|
+
class JsonNlSerializer
|
25
|
+
class << self
|
26
|
+
def file_extension
|
27
|
+
"json"
|
28
|
+
end
|
29
|
+
|
30
|
+
def serialize(hash)
|
31
|
+
JSON.dump(hash) + "\n"
|
32
|
+
end
|
33
|
+
|
34
|
+
def deserialize(s)
|
35
|
+
JSON.parse(s)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
VCR.configure do |config|
|
41
|
+
config.cassette_library_dir = "spec/cassettes"
|
42
|
+
config.hook_into :webmock
|
43
|
+
config.ignore_localhost = true
|
44
|
+
config.ignore_hosts "localhost", "127.0.0.1"
|
45
|
+
config.cassette_serializers[:jsonnl] = JsonNlSerializer
|
46
|
+
config.default_cassette_options = {
|
47
|
+
serialize_with: :jsonnl,
|
48
|
+
record: (ENV["CI"] == "true") ? :none : :once
|
49
|
+
}
|
50
|
+
config.configure_rspec_metadata!
|
51
|
+
["CRONTOGO_API_KEY", "CRONTOGO_ORGANIZATION_ID"].each do |sensitive_env_var|
|
52
|
+
config.filter_sensitive_data("<#{sensitive_env_var.downcase}>") { ENV[sensitive_env_var] }
|
53
|
+
end
|
54
|
+
config.filter_sensitive_data("<basic auth header>") do |interaction|
|
55
|
+
interaction.request.headers["Authorization"]&.first&.sub("Basic ", "")
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
RSpec.describe CronToGoSync::SyncInteractor, :vcr, type: :interactor do
|
4
|
+
describe "#call" do
|
5
|
+
let(:api_key) { ENV["CRONTOGO_API_KEY"] }
|
6
|
+
let(:organization_id) { ENV["CRONTOGO_ORGANIZATION_ID"] }
|
7
|
+
let(:data_file) { "spec/data/staging.toml".to_s }
|
8
|
+
|
9
|
+
subject(:context) { described_class.call(input_filename: data_file, api_key: api_key, organization_id: organization_id) }
|
10
|
+
|
11
|
+
it "works" do
|
12
|
+
expect(context).to be_a_success
|
13
|
+
expect(context.created).to eq 1
|
14
|
+
expect(context.updated).to eq 1
|
15
|
+
expect(context.deleted).to eq 1
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
metadata
ADDED
@@ -0,0 +1,203 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cron_to_go_sync
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Instrumentl, Inc.
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-05-26 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: dry-types
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: dry-validation
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: faraday
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.0'
|
62
|
+
- - "<"
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: '3.0'
|
65
|
+
type: :runtime
|
66
|
+
prerelease: false
|
67
|
+
version_requirements: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: '1.0'
|
72
|
+
- - "<"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '3.0'
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: faraday-follow_redirects
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
type: :runtime
|
83
|
+
prerelease: false
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
- !ruby/object:Gem::Dependency
|
90
|
+
name: faraday-retry
|
91
|
+
requirement: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
type: :runtime
|
97
|
+
prerelease: false
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0'
|
103
|
+
- !ruby/object:Gem::Dependency
|
104
|
+
name: interactor
|
105
|
+
requirement: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
type: :runtime
|
111
|
+
prerelease: false
|
112
|
+
version_requirements: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0'
|
117
|
+
- !ruby/object:Gem::Dependency
|
118
|
+
name: interactor-contracts
|
119
|
+
requirement: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- - ">="
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: '0'
|
124
|
+
type: :runtime
|
125
|
+
prerelease: false
|
126
|
+
version_requirements: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - ">="
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: '0'
|
131
|
+
- !ruby/object:Gem::Dependency
|
132
|
+
name: toml-rb
|
133
|
+
requirement: !ruby/object:Gem::Requirement
|
134
|
+
requirements:
|
135
|
+
- - ">="
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
version: '0'
|
138
|
+
type: :runtime
|
139
|
+
prerelease: false
|
140
|
+
version_requirements: !ruby/object:Gem::Requirement
|
141
|
+
requirements:
|
142
|
+
- - ">="
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: '0'
|
145
|
+
description: " Library to synchronize a checked-in YAML file with the SaaS scheduled
|
146
|
+
task service CronToGo\n"
|
147
|
+
email:
|
148
|
+
- oss@instrumentl.com
|
149
|
+
executables: []
|
150
|
+
extensions: []
|
151
|
+
extra_rdoc_files: []
|
152
|
+
files:
|
153
|
+
- ".editorconfig"
|
154
|
+
- ".gitattributes"
|
155
|
+
- ".github/dependabot.yml"
|
156
|
+
- ".github/workflows/ci.yml"
|
157
|
+
- ".github/workflows/release.yml"
|
158
|
+
- ".gitignore"
|
159
|
+
- ".pre-commit-config.yaml"
|
160
|
+
- ".rubocop.yml"
|
161
|
+
- ".standard.yml"
|
162
|
+
- CHANGELOG.md
|
163
|
+
- Gemfile
|
164
|
+
- Gemfile.lock
|
165
|
+
- LICENSE.txt
|
166
|
+
- README.md
|
167
|
+
- cron_to_go_sync.gemspec
|
168
|
+
- lib/cron_to_go_sync.rb
|
169
|
+
- lib/cron_to_go_sync/parser.rb
|
170
|
+
- lib/cron_to_go_sync/sync_interactor.rb
|
171
|
+
- lib/cron_to_go_sync/version.rb
|
172
|
+
- spec/cassettes/CronToGoSync_SyncInteractor/_call/works.json
|
173
|
+
- spec/data/staging.toml
|
174
|
+
- spec/parser_spec.rb
|
175
|
+
- spec/spec_helper.rb
|
176
|
+
- spec/sync_interactor_spec.rb
|
177
|
+
homepage: https://github.com/instrumentl/cron_to_go_sync-rb
|
178
|
+
licenses:
|
179
|
+
- ISC
|
180
|
+
metadata:
|
181
|
+
homepage_uri: https://github.com/instrumentl/cron_to_go_sync-rb
|
182
|
+
source_code_uri: https://github.com/instrumentl/cron_to_go_sync-rb
|
183
|
+
changelog_uri: https://github.com/instrumentl/cron_to_go_sync-rb/blob/main/CHANGELOG.md
|
184
|
+
post_install_message:
|
185
|
+
rdoc_options: []
|
186
|
+
require_paths:
|
187
|
+
- lib
|
188
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
189
|
+
requirements:
|
190
|
+
- - ">="
|
191
|
+
- !ruby/object:Gem::Version
|
192
|
+
version: 3.0.0
|
193
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
194
|
+
requirements:
|
195
|
+
- - ">="
|
196
|
+
- !ruby/object:Gem::Version
|
197
|
+
version: '0'
|
198
|
+
requirements: []
|
199
|
+
rubygems_version: 3.4.10
|
200
|
+
signing_key:
|
201
|
+
specification_version: 4
|
202
|
+
summary: CronToGo configuration syncing library
|
203
|
+
test_files: []
|