skipper-cli 0.1.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 +7 -0
- data/README.md +31 -0
- data/Rakefile +4 -0
- data/bin/clippy +10 -0
- data/config.json +4 -0
- data/lib/skipper_client/configs.rb +22 -0
- data/lib/skipper_client/consumer.rb +98 -0
- data/lib/skipper_client/models/models.rb +176 -0
- data/lib/skipper_client/models/requests.rb +39 -0
- data/lib/skipper_client/version.rb +5 -0
- data/lib/skipper_client.rb +213 -0
- data/sig/skipper_client.rbs +4 -0
- metadata +58 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 197b36f07814e98e9490c32bab5c531f3dd0f226ced2b44a5780b17ceb5485da
|
|
4
|
+
data.tar.gz: c2b578ad4e728a6c5ac829832311600144e5b3c69545340bb02f0446bd6be063
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 7238cbce4df4ecd9a1477fd7289cb6a1e1a737ac601fa2ac9cee6fca6456397c6cd8843355e042c9c11412d309a47b5d279bb67ef1286bde2df970ec1daf7556
|
|
7
|
+
data.tar.gz: 1eaf7a2f794d44571b2c0a6ad14130c0ba8d39dac533b72f6a5603b9cc732ba8460b74e985978f5a549667419cf72b8e0b1d8d3a993e60c1a920185c546063aa
|
data/README.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# SkipperClient
|
|
2
|
+
|
|
3
|
+
TODO: Delete this and the text below, and describe your gem
|
|
4
|
+
|
|
5
|
+
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/skipper_client`. To experiment with that code, run `bin/console` for an interactive prompt.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
|
|
10
|
+
|
|
11
|
+
Install the gem and add to the application's Gemfile by executing:
|
|
12
|
+
|
|
13
|
+
$ bundle add UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
|
|
14
|
+
|
|
15
|
+
If bundler is not being used to manage dependencies, install the gem by executing:
|
|
16
|
+
|
|
17
|
+
$ gem install UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
TODO: Write usage instructions here
|
|
22
|
+
|
|
23
|
+
## Development
|
|
24
|
+
|
|
25
|
+
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
26
|
+
|
|
27
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
28
|
+
|
|
29
|
+
## Contributing
|
|
30
|
+
|
|
31
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/skipper_client.
|
data/Rakefile
ADDED
data/bin/clippy
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'skipper_client'
|
|
5
|
+
|
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
|
8
|
+
|
|
9
|
+
c = SkipperClient::Clippy.new
|
|
10
|
+
c.run
|
data/config.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
|
|
5
|
+
class Config
|
|
6
|
+
@@configs_path = './config.json'
|
|
7
|
+
|
|
8
|
+
attr_reader :uri, :token
|
|
9
|
+
|
|
10
|
+
def initialize
|
|
11
|
+
data = read_configs
|
|
12
|
+
@uri = data['uri']
|
|
13
|
+
@token = data['token']
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def read_configs
|
|
19
|
+
file = File.read @@configs_path
|
|
20
|
+
JSON.parse file
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'uri'
|
|
4
|
+
require 'json'
|
|
5
|
+
require 'http'
|
|
6
|
+
require 'skipper_client/models/models'
|
|
7
|
+
require 'skipper_client/models/requests'
|
|
8
|
+
|
|
9
|
+
# cosumer
|
|
10
|
+
class Consumer
|
|
11
|
+
attr_reader :cfg
|
|
12
|
+
|
|
13
|
+
def initialize(cfg)
|
|
14
|
+
@cfg = cfg
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def content_type
|
|
18
|
+
{ 'Content-Type' => 'application/json' }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def x_api_key
|
|
22
|
+
{ "x-api-key": token }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def merge_headers(*headers)
|
|
26
|
+
HTTP.headers(headers.reduce { |output, header| output.merge(header) })
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def token_valid?
|
|
30
|
+
valid_uri = URI("#{uri}/auth/validate/#{token}".strip)
|
|
31
|
+
begin
|
|
32
|
+
(Models::SkipperResponse.from_response HTTP.get(valid_uri), Models::ValidApiKeyResponse).success
|
|
33
|
+
true
|
|
34
|
+
rescue StandardError
|
|
35
|
+
false
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def create_api_key
|
|
40
|
+
new_token_api = URI "#{uri}/internal/management/api_key"
|
|
41
|
+
req = HTTP::Post.new new_token_api.path, content_type
|
|
42
|
+
req.body = { origin: '*', name: 'new_api_key', purpose: 'general' }
|
|
43
|
+
SkipperResponse.from_response HTTP.request(req), Models::ApiKeyResponse
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def products
|
|
47
|
+
products_uri = URI "#{uri}/api/v1/products"
|
|
48
|
+
Models::SkipperResponse.from_response HTTP.headers("x-api-key": token).get(products_uri), Models::Price, :list
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def coupon(coupon)
|
|
52
|
+
validation_uri = URI "#{uri}/api/v1/products/coupons/#{coupon}".strip
|
|
53
|
+
Models::SkipperResponse.from_response HTTP.headers("x-api-key": token).get(validation_uri), Models::Coupon,
|
|
54
|
+
:unit
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def customer_by_company_and_project(company, project)
|
|
58
|
+
customers_uri = URI "#{uri}/api/v1/customers?project_name=#{project}&company_name=#{company}".strip
|
|
59
|
+
Models::SkipperResponse.from_response HTTP.headers("x-api-key": token).get(customers_uri), Models::Customer,
|
|
60
|
+
:unit
|
|
61
|
+
rescue Models::SkipperApiError
|
|
62
|
+
nil
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def valid_coupon?(input)
|
|
66
|
+
(coupon input).success
|
|
67
|
+
rescue Models::SkipperApiError
|
|
68
|
+
false
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def checkout(checkout_req)
|
|
72
|
+
raise 'Error checkout_req is not a JsonRequest' unless checkout_req.is_a? Models::JsonRequest
|
|
73
|
+
|
|
74
|
+
checkout_uri = URI "#{uri}/api/v1/checkout"
|
|
75
|
+
p checkout_req.as_json
|
|
76
|
+
|
|
77
|
+
res = merge_headers(x_api_key, content_type).post(checkout_uri, json: checkout_req.as_json)
|
|
78
|
+
Models::SkipperResponse.from_response res, Models::Checkout, :unit
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def product(id)
|
|
82
|
+
products.data.find { |d| d.external_id == id }
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def uri
|
|
86
|
+
@cfg.uri
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def token
|
|
90
|
+
@cfg.token
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
private
|
|
94
|
+
|
|
95
|
+
def body_to_json(body)
|
|
96
|
+
JSON.parse! body
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
|
|
5
|
+
module Models
|
|
6
|
+
# Skipper api Exception
|
|
7
|
+
class SkipperApiError < StandardError; end
|
|
8
|
+
|
|
9
|
+
class MissingFromJSON < StandardError; end
|
|
10
|
+
|
|
11
|
+
# json model
|
|
12
|
+
class JsonModel
|
|
13
|
+
def self.from_json(_json_dict)
|
|
14
|
+
raise 'not implemented'
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Basic skipper response
|
|
19
|
+
class SkipperResponse
|
|
20
|
+
attr_reader :success, :message, :data
|
|
21
|
+
|
|
22
|
+
def initialize(json_dict, data)
|
|
23
|
+
@success = json_dict['success']
|
|
24
|
+
@message = json_dict['message']
|
|
25
|
+
@data = data
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.from_response(response, data_class, type)
|
|
29
|
+
raise SkipperApiError, response.to_s unless response.status.success?
|
|
30
|
+
raise MissingFromJSON, 'missing from_json method' unless data_class < JsonModel
|
|
31
|
+
|
|
32
|
+
_parse_from_response parse_json(response.body), data_class, type
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.parse_json(body)
|
|
36
|
+
JSON.parse! body
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def self._parse_from_response(json_dict, data_class, type)
|
|
40
|
+
case type
|
|
41
|
+
when :unit
|
|
42
|
+
_from_unit_response json_dict, data_class
|
|
43
|
+
when :list
|
|
44
|
+
_from_list_response json_dict, data_class
|
|
45
|
+
else
|
|
46
|
+
raise SkipperApiError, 'invalid type passed'
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def self._from_unit_response(json_dict, data_class)
|
|
51
|
+
json_data = json_dict['data']
|
|
52
|
+
raise SkipperApiError, 'data was expected to not be a list' if json_data.is_a? Array
|
|
53
|
+
|
|
54
|
+
new json_dict, data_class.from_json(json_dict['data'])
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def self._from_list_response(json_dict, data_class)
|
|
58
|
+
json_data = json_dict['data']
|
|
59
|
+
raise SkipperApiError, 'data was expected to be a list' unless json_data.is_a? Array
|
|
60
|
+
|
|
61
|
+
new(json_dict, json_data.map { |item| data_class.from_json(item) })
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# api create api key response
|
|
66
|
+
class ApiKeyResponse < JsonModel
|
|
67
|
+
attr_accessor :key
|
|
68
|
+
|
|
69
|
+
def initialize(key)
|
|
70
|
+
@key = key
|
|
71
|
+
super()
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def self.from_json(json_dict)
|
|
75
|
+
new json_dict['key']
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# api key models
|
|
80
|
+
class ValidApiKeyResponse < JsonModel
|
|
81
|
+
attr_reader :value, :name, :origin, :purpose
|
|
82
|
+
|
|
83
|
+
def initialize(value, name, origin, purpose)
|
|
84
|
+
@value = value
|
|
85
|
+
@name = name
|
|
86
|
+
@origin = origin
|
|
87
|
+
@purpose = purpose
|
|
88
|
+
super()
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def self.from_json(json_dict)
|
|
92
|
+
new json_dict['value'], json_dict['name'], json_dict['origin'], json_dict['purpose']
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# ExternalID string `json:"external_id"`
|
|
97
|
+
# Product Product `json:"product"`
|
|
98
|
+
# Currency string `json:"currency"`
|
|
99
|
+
# Amount int64 `json:"amount"`
|
|
100
|
+
# StripeData *stripe.Price `json:"stripe_data"`
|
|
101
|
+
class Price < JsonModel
|
|
102
|
+
attr_reader :external_id, :product, :currency, :amount, :stripe_data, :billing_schema
|
|
103
|
+
|
|
104
|
+
def initialize(external_id, product, currency, amount, stripe_data)
|
|
105
|
+
@external_id = external_id
|
|
106
|
+
@product = product
|
|
107
|
+
@currency = currency
|
|
108
|
+
@amount = amount
|
|
109
|
+
@stripe_data = stripe_data
|
|
110
|
+
@billing_schema = amount > 300_000 ? 'monthly' : 'weekly'
|
|
111
|
+
super()
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def self.from_json(json_dict)
|
|
115
|
+
new json_dict['external_id'], json_dict['product'], json_dict['currency'], json_dict['amount'],
|
|
116
|
+
json_dict['stripe_data']
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def to_dollar
|
|
120
|
+
(@amount / 100).to_s
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# checkout session created res
|
|
125
|
+
class Checkout < JsonModel
|
|
126
|
+
attr_reader :payment_url
|
|
127
|
+
|
|
128
|
+
def initialize(payment_url)
|
|
129
|
+
@payment_url = payment_url
|
|
130
|
+
super()
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def self.from_json(json_dict)
|
|
134
|
+
new json_dict['payment_url']
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# ExternalID string `json:"id"`
|
|
139
|
+
# PercentOff float64 `json:"percent_off"`
|
|
140
|
+
# Duration string `json:"duration"`
|
|
141
|
+
# DurationInMonths int64 `json:"duration_in_months"`
|
|
142
|
+
class Coupon < JsonModel
|
|
143
|
+
attr_reader :id, :name, :percent_off, :duration, :duration_in_months
|
|
144
|
+
|
|
145
|
+
def initialize(id, name, _percent_off, duration, duration_in_months)
|
|
146
|
+
@id = id
|
|
147
|
+
@name = name
|
|
148
|
+
@duration = duration
|
|
149
|
+
@duration_in_months = duration_in_months
|
|
150
|
+
@percent_off = percent_off
|
|
151
|
+
super()
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def self.from_json(json_dict)
|
|
155
|
+
new json_dict['id'], json_dict['name'], json_dict['percent_off'], json_dict['duration'],
|
|
156
|
+
json_dict['duration_in_months']
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# ID int `db:"id"`
|
|
161
|
+
# CompanyName string `db:"company_name" json:"company_name"`
|
|
162
|
+
# ProjectName string `db:"project_name" json:"project_name"`
|
|
163
|
+
class Customer < JsonModel
|
|
164
|
+
attr_reader :id, :company_name, :project_name
|
|
165
|
+
|
|
166
|
+
def initialize(id, company_name, project_name)
|
|
167
|
+
@id = id
|
|
168
|
+
@company_name = company_name
|
|
169
|
+
@project_name = project_name
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def self.from_json(json_dict)
|
|
173
|
+
new json_dict['id'], json_dict['company_name'], json_dict['project_name']
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Models
|
|
4
|
+
# base request
|
|
5
|
+
class JsonRequest
|
|
6
|
+
def as_json
|
|
7
|
+
raise "not implemented"
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# PriceID string `json:"price_id"`
|
|
12
|
+
# Qty int `json:"quantity"`
|
|
13
|
+
# CompanyName string `json:"company_name"`
|
|
14
|
+
# ProjectName string `json:"project_name"`
|
|
15
|
+
# CouponCode *string `json:"coupon_code"`
|
|
16
|
+
# ReferredCode *string `json:"referred_code"`
|
|
17
|
+
class CheckouReq < JsonRequest
|
|
18
|
+
attr_reader :price_id, :quantity, :company_name, :project_name, :coupon_code
|
|
19
|
+
|
|
20
|
+
def initialize(price, quantity, company_name, project_name, coupon_code)
|
|
21
|
+
@price = price
|
|
22
|
+
@quantity = quantity
|
|
23
|
+
@company_name = company_name
|
|
24
|
+
@project_name = project_name
|
|
25
|
+
@coupon_code = coupon_code
|
|
26
|
+
super()
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def as_json
|
|
30
|
+
{
|
|
31
|
+
price_id: @price.external_id,
|
|
32
|
+
quantity: @quantity,
|
|
33
|
+
company_name: @company_name,
|
|
34
|
+
project_name: @project_name,
|
|
35
|
+
coupon_code: @coupon_code
|
|
36
|
+
}
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'skipper_client/version'
|
|
4
|
+
require_relative 'skipper_client/configs'
|
|
5
|
+
require_relative 'skipper_client/consumer'
|
|
6
|
+
require 'tty-prompt'
|
|
7
|
+
require 'cli/ui'
|
|
8
|
+
|
|
9
|
+
module SkipperClient
|
|
10
|
+
class Error < StandardError; end
|
|
11
|
+
|
|
12
|
+
class Clippy
|
|
13
|
+
def initialize
|
|
14
|
+
@cfg = Config.new
|
|
15
|
+
@consumer = Consumer.new @cfg
|
|
16
|
+
@cached_products = []
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def run
|
|
20
|
+
CLI::UI::StdoutRouter.enable
|
|
21
|
+
CLI::UI::Frame.open('SKIPPER DIGITAL STUDIO') do
|
|
22
|
+
puts CLI::UI.fmt title
|
|
23
|
+
fetch_products
|
|
24
|
+
product, amount, company_name, project_name, discount = subscription_form
|
|
25
|
+
subscription_details product, amount, company_name, project_name, discount
|
|
26
|
+
unless CLI::UI::Prompt.confirm 'Continue to checkout?', default: true
|
|
27
|
+
puts CLI::UI.fmt '{{error:Checkout canceled }}'
|
|
28
|
+
puts CLI::UI.fmt 'Thank you for using Clippy the Skipper Command Line Application'
|
|
29
|
+
return
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
session = checkout product, amount, company_name, project_name, discount
|
|
33
|
+
success_output session
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def title
|
|
38
|
+
"
|
|
39
|
+
__|__ |___| |\\
|
|
40
|
+
|o__| |___| | \\
|
|
41
|
+
|___| |___| |o \\
|
|
42
|
+
_|___| |___| |__o\\
|
|
43
|
+
/...\\_____|___|____\\_/
|
|
44
|
+
\\ o * o * * o o /
|
|
45
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
46
|
+
|
|
47
|
+
"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def products
|
|
51
|
+
return @cached_products unless @cached_products.empty?
|
|
52
|
+
|
|
53
|
+
@cached_products = @consumer.products.data
|
|
54
|
+
@cached_products
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def product_from_prompt(str_product)
|
|
58
|
+
str_product.include?('WEEKLY') ? weekly_product : monthly_product
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def success_output(session)
|
|
62
|
+
puts CLI::UI.fmt " {{success: checkout session created: \n #{session.data.payment_url} \n Open the ssession on any broweser you want}}"
|
|
63
|
+
puts CLI::UI.fmt "\n {{success: Thank you for Using Clippy - The Command line tool to work with Skipper Digital Studio }}"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def subscription_form
|
|
67
|
+
product = nil
|
|
68
|
+
CLI::UI::Frame.open('SUBSCRIPTION FORM') do
|
|
69
|
+
CLI::UI::Prompt.ask('Choose the subscription model you want') do |handler|
|
|
70
|
+
prompt_products.each do |prompt|
|
|
71
|
+
handler.option(prompt[:prompt]) do |_|
|
|
72
|
+
product = prompt[:value]
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
[product, cli_amount, cli_company_name, cli_project_name, cli_discount]
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def subscription_details(product, amount, company_name, project_name, discount)
|
|
81
|
+
CLI::UI::Frame.open('Subscription details') do
|
|
82
|
+
puts CLI::UI.fmt "{{info: Company Name -> #{company_name} }}"
|
|
83
|
+
puts CLI::UI.fmt "{{info: Project Name -> #{project_name} }}"
|
|
84
|
+
puts CLI::UI.fmt "{{info: Number of workflows -> #{amount} }}"
|
|
85
|
+
unless discount.nil?
|
|
86
|
+
puts CLI::UI.fmt "\n{{info: Discount coupon applied '#{discount}' = 5% for the first 3 months}}"
|
|
87
|
+
end
|
|
88
|
+
net_amount = product.amount * amount
|
|
89
|
+
customer_res = (@consumer.customer_by_company_and_project company_name, project_name)
|
|
90
|
+
puts CLI::UI.fmt "\n{{info: New customer discount applied 45% for the first 3 months}}" if customer_res.nil?
|
|
91
|
+
new_customer_disc = customer_res.nil? ? 0.45 : 0.0
|
|
92
|
+
coupon_discount = discount.nil? ? 0.0 : 0.05
|
|
93
|
+
total = net_amount * (1 - coupon_discount - new_customer_disc)
|
|
94
|
+
puts CLI::UI.fmt "\n{{success: Total = #{product.currency} #{(total / 100).round} (First 3 months) - Then #{product.currency} #{net_amount / 100} }}"
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def checkout(product, amount, company_name, project_name, discount)
|
|
99
|
+
session = nil
|
|
100
|
+
CLI::UI::Frame.open('CHECKOUT') do
|
|
101
|
+
CLI::UI::SpinGroup.new do |spin_group|
|
|
102
|
+
spin_group.add('Generating checkout session') do |_|
|
|
103
|
+
session = @consumer.checkout Models::CheckouReq.new(product, amount, company_name, project_name,
|
|
104
|
+
discount)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
session
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def fetch_products
|
|
112
|
+
CLI::UI::SpinGroup.new do |spin_group|
|
|
113
|
+
spin_group.add('Fetching all the products') do |_|
|
|
114
|
+
products
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
private
|
|
120
|
+
|
|
121
|
+
def validate_string(str)
|
|
122
|
+
special = "?<>',?[]}{=-)(*&^%$#`,.~{}"
|
|
123
|
+
regex = /[#{special.gsub(/./) { |char| "\\#{char}" }}]/
|
|
124
|
+
!(str =~ regex)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def string_prompt(prompt_text, is_retry)
|
|
128
|
+
puts CLI::UI.fmt 'Invalid input. The text cannot contain any special character or . (dot) | , (comma)' if is_retry
|
|
129
|
+
CLI::UI::Prompt.ask prompt_text, allow_empty: false
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def cli_amount
|
|
133
|
+
CLI::UI::Prompt.ask('How many workstreams you want?', default: '1').to_i
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def cli_company_name
|
|
137
|
+
output = string_prompt "What's your company name?", false
|
|
138
|
+
loop do
|
|
139
|
+
break if validate_string output
|
|
140
|
+
|
|
141
|
+
output = string_prompt "What's your company name?", true
|
|
142
|
+
end
|
|
143
|
+
output
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def cli_project_name
|
|
147
|
+
output = string_prompt "What's your project name?", false
|
|
148
|
+
loop do
|
|
149
|
+
break if validate_string output
|
|
150
|
+
|
|
151
|
+
output = string_prompt "What's your project name?", true
|
|
152
|
+
end
|
|
153
|
+
output
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def cli_discount
|
|
157
|
+
output = nil
|
|
158
|
+
CLI::UI::Frame.open('DISCOUNT FORM') do
|
|
159
|
+
discount_coupon = cli_discount_loop
|
|
160
|
+
output = discount_coupon != '' ? discount_coupon : nil
|
|
161
|
+
end
|
|
162
|
+
output
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def cli_discount_loop
|
|
166
|
+
loop do
|
|
167
|
+
discount_coupon = CLI::UI::Prompt.ask('Input a discount coupon if you have one / (Not required)')
|
|
168
|
+
return '' unless discount_coupon != ''
|
|
169
|
+
|
|
170
|
+
valid = valid_coupon? discount_coupon
|
|
171
|
+
return discount_coupon if valid
|
|
172
|
+
|
|
173
|
+
next if cli_should_try_again_discount
|
|
174
|
+
|
|
175
|
+
puts CLI::UI.fmt '{{info: Continuing without a discount coupon}}'
|
|
176
|
+
break
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def cli_should_try_again_discount
|
|
181
|
+
CLI::UI::Prompt.ask 'The coupon was not a valid one. What you want to do?' do |handler|
|
|
182
|
+
handler.option('Use a different coupon?') { |_| true }
|
|
183
|
+
handler.option('Continue without a coupon?') { |_| false }
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def valid_coupon?(discount_coupon)
|
|
188
|
+
final_glyph = ->(success) { success ? CLI::UI::Glyph::CHEVRON.to_s : CLI::UI::Glyph::X.to_s }
|
|
189
|
+
valid = false
|
|
190
|
+
CLI::UI::SpinGroup.new do |spin_group|
|
|
191
|
+
spin_group.add 'Validating coupon', final_glyph: final_glyph do |spinner|
|
|
192
|
+
valid = @consumer.valid_coupon?(discount_coupon)
|
|
193
|
+
spinner.update_title CLI::UI.fmt "{{error: Discount coupon #{discount_coupon} is Invalid }}" unless valid
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
valid
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def prompt_products
|
|
200
|
+
products.map do |product|
|
|
201
|
+
{ prompt: "#{product.billing_schema.upcase} -> #{product.currency} #{product.amount / 100}", value: product }
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def weekly_product
|
|
206
|
+
products.find { |p| p.billing_schema == 'weekly' }
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def monthly_product
|
|
210
|
+
products.find { |p| p.billing_schema == 'monthly' }
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: skipper-cli
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- wmb1207
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2024-06-10 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: a command line client to interact with skipper
|
|
14
|
+
email:
|
|
15
|
+
- lao@skipper.studio
|
|
16
|
+
executables:
|
|
17
|
+
- clippy
|
|
18
|
+
extensions: []
|
|
19
|
+
extra_rdoc_files: []
|
|
20
|
+
files:
|
|
21
|
+
- README.md
|
|
22
|
+
- Rakefile
|
|
23
|
+
- bin/clippy
|
|
24
|
+
- config.json
|
|
25
|
+
- lib/skipper_client.rb
|
|
26
|
+
- lib/skipper_client/configs.rb
|
|
27
|
+
- lib/skipper_client/consumer.rb
|
|
28
|
+
- lib/skipper_client/models/models.rb
|
|
29
|
+
- lib/skipper_client/models/requests.rb
|
|
30
|
+
- lib/skipper_client/version.rb
|
|
31
|
+
- sig/skipper_client.rbs
|
|
32
|
+
homepage: https://skipper.studio
|
|
33
|
+
licenses: []
|
|
34
|
+
metadata:
|
|
35
|
+
allowed_push_host: https://rubygems.org
|
|
36
|
+
homepage_uri: https://skipper.studio
|
|
37
|
+
source_code_uri: https://gitlab.com/skipperstudio/clippy
|
|
38
|
+
changelog_uri: https://gitlab.com/skipperstudio/clippy/changelog.md
|
|
39
|
+
post_install_message:
|
|
40
|
+
rdoc_options: []
|
|
41
|
+
require_paths:
|
|
42
|
+
- lib
|
|
43
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: 3.0.0
|
|
48
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
49
|
+
requirements:
|
|
50
|
+
- - ">="
|
|
51
|
+
- !ruby/object:Gem::Version
|
|
52
|
+
version: '0'
|
|
53
|
+
requirements: []
|
|
54
|
+
rubygems_version: 3.3.21
|
|
55
|
+
signing_key:
|
|
56
|
+
specification_version: 4
|
|
57
|
+
summary: A command line client to interact with skipper
|
|
58
|
+
test_files: []
|