knockapi 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/.gitignore +49 -0
- data/.ruby-version +1 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +47 -0
- data/LICENSE +21 -0
- data/README.md +151 -0
- data/bin/console +7 -0
- data/knockapi.gemspec +30 -0
- data/lib/knock.rb +51 -0
- data/lib/knock/base.rb +11 -0
- data/lib/knock/client.rb +116 -0
- data/lib/knock/errors.rb +42 -0
- data/lib/knock/preferences.rb +141 -0
- data/lib/knock/users.rb +56 -0
- data/lib/knock/version.rb +5 -0
- data/lib/knock/workflows.rb +63 -0
- metadata +103 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: df2a97ad8702bbb6843e12eeca370179e4ce14e1c591312d8bc6462c9114fd07
|
4
|
+
data.tar.gz: bdaae5b1b47ef4e022ff4fa81f59250705880801ef6075af337b41ee657ef79a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 799e436d7ca5514f82b76f789f7fcf031fb67ed1d5f2705b34c9313c3a9369b04b450bd72e4e18c1d29d4119cf65a353729a5102fabfb1be3ef3ade48a9bd047
|
7
|
+
data.tar.gz: aec1077db54be921685100263417cc360af0ede27c382282a0f117bf59bce26d41d1af7db4775dadfb7b19b27e3b53ceae1fab1c8b4c73b43f56dbd045ebc3a8
|
data/.gitignore
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
*.DS_Store
|
4
|
+
/.config
|
5
|
+
/coverage/
|
6
|
+
/InstalledFiles
|
7
|
+
/pkg/
|
8
|
+
/spec/reports/
|
9
|
+
/spec/examples.txt
|
10
|
+
/test/tmp/
|
11
|
+
/test/version_tmp/
|
12
|
+
/tmp/
|
13
|
+
|
14
|
+
# Used by dotenv library to load environment variables.
|
15
|
+
# .env
|
16
|
+
|
17
|
+
# Ignore Byebug command history file.
|
18
|
+
.byebug_history
|
19
|
+
|
20
|
+
## Specific to RubyMotion (use of CocoaPods):
|
21
|
+
#
|
22
|
+
# We recommend against adding the Pods directory to your .gitignore. However
|
23
|
+
# you should judge for yourself, the pros and cons are mentioned at:
|
24
|
+
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
|
25
|
+
#
|
26
|
+
# vendor/Pods/
|
27
|
+
|
28
|
+
## Documentation cache and generated files:
|
29
|
+
/.yardoc/
|
30
|
+
/_yardoc/
|
31
|
+
/doc/
|
32
|
+
/rdoc/
|
33
|
+
|
34
|
+
## Environment normalization:
|
35
|
+
/.bundle/
|
36
|
+
/vendor/bundle
|
37
|
+
/lib/bundler/man/
|
38
|
+
|
39
|
+
# for a library or gem, you might want to ignore these files since the code is
|
40
|
+
# intended to run in multiple environments; otherwise, check them in:
|
41
|
+
# Gemfile.lock
|
42
|
+
# .ruby-version
|
43
|
+
# .ruby-gemset
|
44
|
+
|
45
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
46
|
+
.rvmrc
|
47
|
+
|
48
|
+
# Used by RuboCop. Remote config files pulled in from inherit_from directive.
|
49
|
+
# .rubocop-https?--*
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
3.0.2
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
knockapi (0.1.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
ast (2.4.2)
|
10
|
+
parallel (1.20.1)
|
11
|
+
parser (3.0.2.0)
|
12
|
+
ast (~> 2.4.1)
|
13
|
+
rainbow (3.0.0)
|
14
|
+
rake (13.0.6)
|
15
|
+
regexp_parser (2.1.1)
|
16
|
+
rexml (3.2.5)
|
17
|
+
rubocop (1.18.4)
|
18
|
+
parallel (~> 1.10)
|
19
|
+
parser (>= 3.0.0.0)
|
20
|
+
rainbow (>= 2.2.2, < 4.0)
|
21
|
+
regexp_parser (>= 1.8, < 3.0)
|
22
|
+
rexml
|
23
|
+
rubocop-ast (>= 1.8.0, < 2.0)
|
24
|
+
ruby-progressbar (~> 1.7)
|
25
|
+
unicode-display_width (>= 1.4.0, < 3.0)
|
26
|
+
rubocop-ast (1.10.0)
|
27
|
+
parser (>= 3.0.1.1)
|
28
|
+
rubocop-performance (1.11.4)
|
29
|
+
rubocop (>= 1.7.0, < 2.0)
|
30
|
+
rubocop-ast (>= 0.4.0)
|
31
|
+
ruby-progressbar (1.11.0)
|
32
|
+
standard (1.1.7)
|
33
|
+
rubocop (= 1.18.4)
|
34
|
+
rubocop-performance (= 1.11.4)
|
35
|
+
unicode-display_width (2.0.0)
|
36
|
+
|
37
|
+
PLATFORMS
|
38
|
+
x86_64-darwin-19
|
39
|
+
|
40
|
+
DEPENDENCIES
|
41
|
+
bundler (>= 2.0.1)
|
42
|
+
knockapi!
|
43
|
+
rake
|
44
|
+
standard
|
45
|
+
|
46
|
+
BUNDLED WITH
|
47
|
+
2.2.22
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2021 Knock Labs, Inc.
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
# Knock Ruby library
|
2
|
+
|
3
|
+
Knock API access for applications written in Ruby.
|
4
|
+
|
5
|
+
## Documentation
|
6
|
+
|
7
|
+
See the documentation for Ruby usage examples.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
```bash
|
12
|
+
gem install knockapi
|
13
|
+
```
|
14
|
+
|
15
|
+
## Configuration
|
16
|
+
|
17
|
+
To use the library you must provide a secret API key, provided in the Knock dashboard.
|
18
|
+
|
19
|
+
You can set it as an environment variable:
|
20
|
+
|
21
|
+
```bash
|
22
|
+
KNOCK_API_KEY="sk_12345"
|
23
|
+
```
|
24
|
+
|
25
|
+
Or, you may set the key yourself in an initializer:
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
# /config/initializers/knock.rb
|
29
|
+
Knock.key = 'sk_12345'
|
30
|
+
```
|
31
|
+
|
32
|
+
## Usage
|
33
|
+
|
34
|
+
### Identifying users
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
require "knockapi"
|
38
|
+
|
39
|
+
Knock.key = "sk_12345"
|
40
|
+
|
41
|
+
Knock::Users.identify(
|
42
|
+
id: "jhammond",
|
43
|
+
data: {
|
44
|
+
name: "John Hammond",
|
45
|
+
email: "jhammond@ingen.net",
|
46
|
+
}
|
47
|
+
)
|
48
|
+
```
|
49
|
+
|
50
|
+
### Sending notifies (triggering workflows)
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
require "knockapi"
|
54
|
+
|
55
|
+
Knock.key = "sk_12345"
|
56
|
+
|
57
|
+
# The key of the workflow (from Knock dashboard)
|
58
|
+
Knock::Workflows.trigger(
|
59
|
+
key: "dinosaurs-loose",
|
60
|
+
# user id of who performed the action
|
61
|
+
actor: "dnedry",
|
62
|
+
# list of user ids for who should receive the notif
|
63
|
+
recipients: ["jhammond", "agrant", "imalcolm", "esattler"],
|
64
|
+
# data payload to send through
|
65
|
+
data: {
|
66
|
+
type: "trex",
|
67
|
+
priority: 1,
|
68
|
+
},
|
69
|
+
# an optional key to provide to cancel a notify
|
70
|
+
cancellation_key: trigger_alert.id,
|
71
|
+
)
|
72
|
+
```
|
73
|
+
|
74
|
+
### Retrieving users
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
require "knockapi"
|
78
|
+
|
79
|
+
Knock.key = "sk_12345"
|
80
|
+
|
81
|
+
Knock::Users.get(id: "jhammond")
|
82
|
+
```
|
83
|
+
|
84
|
+
### Deleting users
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
require "knockapi"
|
88
|
+
|
89
|
+
Knock.key = "sk_12345"
|
90
|
+
|
91
|
+
Knock::Users.delete(id: "jhammond")
|
92
|
+
```
|
93
|
+
|
94
|
+
### Preferences
|
95
|
+
|
96
|
+
```ruby
|
97
|
+
require "knockapi"
|
98
|
+
Knock.key = "sk_12345"
|
99
|
+
|
100
|
+
# Set an entire preference set
|
101
|
+
Knock::Preferences.set(
|
102
|
+
user_id: "jhammond",
|
103
|
+
channel_types: { email: true, sms: false },
|
104
|
+
workflows: {
|
105
|
+
'dinosaurs-loose': {
|
106
|
+
channel_types: { email: false, in_app_feed: false }
|
107
|
+
}
|
108
|
+
}
|
109
|
+
)
|
110
|
+
|
111
|
+
# Get an entire preference set
|
112
|
+
Knock::Preferences.get(user_id: "jhammond")
|
113
|
+
```
|
114
|
+
|
115
|
+
### Cancelling workflows
|
116
|
+
|
117
|
+
```ruby
|
118
|
+
require "knockapi"
|
119
|
+
Knock.key = "sk_12345"
|
120
|
+
|
121
|
+
Knock::Workflows.cancel(
|
122
|
+
key: "dinosaurs-loose",
|
123
|
+
cancellation_key: trigger_alert.id,
|
124
|
+
# Optionally, you can provide recipients to cancel for
|
125
|
+
recipients: ["jhammond"]
|
126
|
+
)
|
127
|
+
```
|
128
|
+
|
129
|
+
### Signing JWTs
|
130
|
+
|
131
|
+
You can use the `jwt` gem to sign JWTs easily. You will need to generate an environment specific signing key, which you can find in the Knock dashboard.
|
132
|
+
|
133
|
+
If you're using a signing token you will need to pass this to your client to perform authentication. You can read more about [clientside authentication here](https://docs.knock.app/client-integration/authenticating-users).
|
134
|
+
|
135
|
+
```ruby
|
136
|
+
require 'jwt'
|
137
|
+
|
138
|
+
secret = ENV['KNOCK_SIGNING_KEY']
|
139
|
+
now = Time.now.to_i
|
140
|
+
|
141
|
+
payload = {
|
142
|
+
# The subject of the token
|
143
|
+
sub: 'jhammond',
|
144
|
+
# When the token was issued
|
145
|
+
iat: now,
|
146
|
+
# Expire the token in 1 week
|
147
|
+
exp: now + (24 * 7 * 3600)
|
148
|
+
}
|
149
|
+
|
150
|
+
JWT.encode(payload, secret, 'RS256')
|
151
|
+
```
|
data/bin/console
ADDED
data/knockapi.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path("lib", __dir__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require "knock/version"
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = "knockapi"
|
9
|
+
spec.version = Knock::VERSION
|
10
|
+
spec.authors = ["Knock Labs, Inc."]
|
11
|
+
spec.email = ["support@knock.app"]
|
12
|
+
spec.description = "API client for Knock"
|
13
|
+
spec.summary = "API client for Knock"
|
14
|
+
spec.homepage = "https://github.com/knocklabs/knock-ruby"
|
15
|
+
spec.license = "MIT"
|
16
|
+
spec.metadata = {
|
17
|
+
"documentation_uri" => "https://docs.knock.app"
|
18
|
+
}
|
19
|
+
|
20
|
+
spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
21
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
22
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
23
|
+
spec.require_paths = ["lib"]
|
24
|
+
|
25
|
+
spec.add_development_dependency "bundler", ">= 2.0.1"
|
26
|
+
spec.add_development_dependency "rake"
|
27
|
+
spec.add_development_dependency "standard"
|
28
|
+
|
29
|
+
spec.required_ruby_version = ">= 2.5"
|
30
|
+
end
|
data/lib/knock.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "knock/version"
|
4
|
+
require "json"
|
5
|
+
|
6
|
+
module Knock
|
7
|
+
API_HOSTNAME = ENV["KNOCK_API_HOSTNAME"] || "api.knock.app"
|
8
|
+
|
9
|
+
def self.key=(value)
|
10
|
+
Base.key = value
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.key
|
14
|
+
Base.key
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.key!
|
18
|
+
key || raise("Knock.key not set")
|
19
|
+
end
|
20
|
+
|
21
|
+
autoload :Base, "knock/base"
|
22
|
+
autoload :Client, "knock/client"
|
23
|
+
|
24
|
+
# Resources
|
25
|
+
autoload :Preferences, "knock/preferences"
|
26
|
+
autoload :Users, "knock/users"
|
27
|
+
autoload :Workflows, "knock/workflows"
|
28
|
+
|
29
|
+
# Errors
|
30
|
+
autoload :APIError, "knock/errors"
|
31
|
+
autoload :AuthenticationError, "knock/errors"
|
32
|
+
autoload :InvalidRequestError, "knock/errors"
|
33
|
+
|
34
|
+
key = ENV["KNOCK_API_KEY"]
|
35
|
+
Knock.key = key unless key.nil?
|
36
|
+
|
37
|
+
# Triggers the workflow with the given key
|
38
|
+
#
|
39
|
+
# @param [String] key The workflow key
|
40
|
+
# @param [String] actor The actor ID
|
41
|
+
# @param [Array<String>] recipients The recipient IDs
|
42
|
+
# @param [Hash] data The data to pass to the workflow
|
43
|
+
# @param [String] cancellation_key An optional key to identify this workflow
|
44
|
+
# invocation for cancelling
|
45
|
+
# @param [String] tenant An optional tenant identifier
|
46
|
+
#
|
47
|
+
# @return [Hash] A workflow trigger result
|
48
|
+
def self.notify(**args)
|
49
|
+
Knock::Workflows.trigger(**args)
|
50
|
+
end
|
51
|
+
end
|
data/lib/knock/base.rb
ADDED
data/lib/knock/client.rb
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
module Knock
|
2
|
+
# A Net::HTTP based API client for interacting with the Knock API
|
3
|
+
module Client
|
4
|
+
include Kernel
|
5
|
+
|
6
|
+
def client
|
7
|
+
return @client if defined?(@client)
|
8
|
+
|
9
|
+
@client = Net::HTTP.new(Knock::API_HOSTNAME, 443)
|
10
|
+
@client.use_ssl = true
|
11
|
+
|
12
|
+
@client
|
13
|
+
end
|
14
|
+
|
15
|
+
def execute_request(request:)
|
16
|
+
response = client.request(request)
|
17
|
+
|
18
|
+
http_status = response.code.to_i
|
19
|
+
handle_error_response(response: response) if http_status >= 400
|
20
|
+
|
21
|
+
JSON.parse(response.body)
|
22
|
+
end
|
23
|
+
|
24
|
+
def get_request(path:, auth: false, params: {}, access_token: nil)
|
25
|
+
uri = URI(path)
|
26
|
+
uri.query = URI.encode_www_form(params) if params
|
27
|
+
|
28
|
+
request = Net::HTTP::Get.new(
|
29
|
+
uri.to_s,
|
30
|
+
"Content-Type" => "application/json"
|
31
|
+
)
|
32
|
+
|
33
|
+
request["Authorization"] = "Bearer #{access_token || Knock.key!}" if auth
|
34
|
+
request["User-Agent"] = user_agent
|
35
|
+
request
|
36
|
+
end
|
37
|
+
|
38
|
+
def post_request(path:, auth: false, idempotency_key: nil, body: nil)
|
39
|
+
request = Net::HTTP::Post.new(path, "Content-Type" => "application/json")
|
40
|
+
request.body = body.to_json if body
|
41
|
+
request["Authorization"] = "Bearer #{Knock.key!}" if auth
|
42
|
+
request["User-Agent"] = user_agent
|
43
|
+
request
|
44
|
+
end
|
45
|
+
|
46
|
+
def delete_request(path:, auth: false, params: {})
|
47
|
+
uri = URI(path)
|
48
|
+
uri.query = URI.encode_www_form(params) if params
|
49
|
+
|
50
|
+
request = Net::HTTP::Delete.new(
|
51
|
+
uri.to_s,
|
52
|
+
"Content-Type" => "application/json"
|
53
|
+
)
|
54
|
+
|
55
|
+
request["Authorization"] = "Bearer #{Knock.key!}" if auth
|
56
|
+
request["User-Agent"] = user_agent
|
57
|
+
request
|
58
|
+
end
|
59
|
+
|
60
|
+
def put_request(path:, auth: false, idempotency_key: nil, body: nil)
|
61
|
+
request = Net::HTTP::Put.new(path, "Content-Type" => "application/json")
|
62
|
+
request.body = body.to_json if body
|
63
|
+
request["Authorization"] = "Bearer #{Knock.key!}" if auth
|
64
|
+
request["User-Agent"] = user_agent
|
65
|
+
request
|
66
|
+
end
|
67
|
+
|
68
|
+
def user_agent
|
69
|
+
"Knock Ruby - v#{Knock::VERSION}"
|
70
|
+
end
|
71
|
+
|
72
|
+
def handle_error_response(response:)
|
73
|
+
http_status = response.code.to_i
|
74
|
+
json = JSON.parse(response.body)
|
75
|
+
|
76
|
+
case http_status
|
77
|
+
when 400
|
78
|
+
raise InvalidRequestError.new(
|
79
|
+
message: json["message"],
|
80
|
+
http_status: http_status,
|
81
|
+
request_id: response["x-request-id"]
|
82
|
+
)
|
83
|
+
when 401
|
84
|
+
raise AuthenticationError.new(
|
85
|
+
message: json["message"],
|
86
|
+
http_status: http_status,
|
87
|
+
request_id: response["x-request-id"]
|
88
|
+
)
|
89
|
+
when 404
|
90
|
+
raise APIError.new(
|
91
|
+
message: json["message"],
|
92
|
+
http_status: http_status,
|
93
|
+
request_id: response["x-request-id"]
|
94
|
+
)
|
95
|
+
when 422
|
96
|
+
message = json["message"]
|
97
|
+
errors = extract_error(json["errors"]) if json["errors"]
|
98
|
+
message += " (#{errors})" if errors
|
99
|
+
|
100
|
+
raise InvalidRequestError.new(
|
101
|
+
message: message,
|
102
|
+
http_status: http_status,
|
103
|
+
request_id: response["x-request-id"]
|
104
|
+
)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
private
|
109
|
+
|
110
|
+
def extract_error(errors)
|
111
|
+
errors.map do |error|
|
112
|
+
"#{error["field"]}: #{error["message"]} (#{error["type"]})"
|
113
|
+
end.join("; ")
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
data/lib/knock/errors.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
module Knock
|
2
|
+
class KnockError < StandardError
|
3
|
+
attr_reader :http_status
|
4
|
+
attr_reader :request_id
|
5
|
+
|
6
|
+
def initialize(message: nil, error: nil, error_description: nil, http_status: nil, request_id: nil)
|
7
|
+
@message = message
|
8
|
+
@error = error
|
9
|
+
@error_description = error_description
|
10
|
+
@http_status = http_status
|
11
|
+
@request_id = request_id
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
status_string = @http_status.nil? ? "" : "Status #{@http_status}, "
|
16
|
+
id_string = @request_id.nil? ? "" : " - request ID: #{@request_id}"
|
17
|
+
|
18
|
+
if @error && @error_description
|
19
|
+
error_string = "error: #{@error}, error_description: #{@error_description}"
|
20
|
+
"#{status_string}#{error_string}#{id_string}"
|
21
|
+
elsif @error
|
22
|
+
"#{status_string}#{@error}#{id_string}"
|
23
|
+
else
|
24
|
+
"#{status_string}#{@message}#{id_string}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# APIError is a generic error that may be raised in cases where none of the
|
30
|
+
# other named errors cover the problem. It could also be raised in the case
|
31
|
+
# that a new error has been introduced in the API, but this version of the
|
32
|
+
# Ruby SDK doesn't know how to handle it.
|
33
|
+
class APIError < KnockError; end
|
34
|
+
|
35
|
+
# AuthenticationError is raised when invalid credentials are used to connect
|
36
|
+
# to Knock's servers.
|
37
|
+
class AuthenticationError < KnockError; end
|
38
|
+
|
39
|
+
# InvalidRequestError is raised when a request is initiated with invalid
|
40
|
+
# parameters.
|
41
|
+
class InvalidRequestError < KnockError; end
|
42
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require "net/http"
|
2
|
+
require "uri"
|
3
|
+
|
4
|
+
module Knock
|
5
|
+
# Provides convienience methods for working with preferences
|
6
|
+
module Preferences
|
7
|
+
class << self
|
8
|
+
include Base
|
9
|
+
include Client
|
10
|
+
|
11
|
+
DEFAULT_SET_ID = "default"
|
12
|
+
|
13
|
+
# Returns all preference sets for the user
|
14
|
+
#
|
15
|
+
# @param [String] user_id The ID of the user to retrieve preferences for
|
16
|
+
#
|
17
|
+
# @return [Hash] The preference sets
|
18
|
+
def get_all(user_id:)
|
19
|
+
endpoint = "/v1/users/#{user_id}/preferences"
|
20
|
+
|
21
|
+
request = get_request(
|
22
|
+
auth: true,
|
23
|
+
path: endpoint
|
24
|
+
)
|
25
|
+
|
26
|
+
execute_request(request: request)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Gets a single preference set, defaults to the 'default' set
|
30
|
+
# for the user given.
|
31
|
+
#
|
32
|
+
# @param [String] user_id The ID of the user to retrieve preferences for
|
33
|
+
# @param [String] preference_set The preference set ID (defaults to `default`)
|
34
|
+
#
|
35
|
+
# @return [Hash] The preference set (if it exists)
|
36
|
+
def get(user_id:, preference_set: DEFAULT_SET_ID)
|
37
|
+
endpoint = "/v1/users/#{user_id}/preferences/#{preference_set}"
|
38
|
+
|
39
|
+
request = get_request(
|
40
|
+
auth: true,
|
41
|
+
path: endpoint
|
42
|
+
)
|
43
|
+
|
44
|
+
execute_request(request: request)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Sets multiple preferences at once for the preference set.
|
48
|
+
#
|
49
|
+
# @param [String] user_id The ID of the user to set preferences for
|
50
|
+
# @param [String] preference_set The preference set ID (defaults to `default`)
|
51
|
+
# @param [Hash] preferences The preferences hash to set
|
52
|
+
#
|
53
|
+
# @return [Hash] The preference set
|
54
|
+
def update(
|
55
|
+
user_id:,
|
56
|
+
channel_types: nil,
|
57
|
+
workflows: nil,
|
58
|
+
categories: nil,
|
59
|
+
preference_set: DEFAULT_SET_ID
|
60
|
+
)
|
61
|
+
endpoint = "/v1/users/#{user_id}/preferences/#{preference_set}"
|
62
|
+
|
63
|
+
request = put_request(
|
64
|
+
auth: true,
|
65
|
+
path: endpoint,
|
66
|
+
body: {
|
67
|
+
channel_types: channel_types,
|
68
|
+
workflows: workflows,
|
69
|
+
categories: categories
|
70
|
+
}
|
71
|
+
)
|
72
|
+
|
73
|
+
execute_request(request: request)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Sets preferences for the given channel type
|
77
|
+
#
|
78
|
+
# @param [String] user_id The ID of the user to set preferences for
|
79
|
+
# @param [String] preference_set The preference set ID (defaults to `default`)
|
80
|
+
# @param [String] channel_type The channel type to set
|
81
|
+
# @param [Bool] setting Whether the channel type is enabled or not
|
82
|
+
#
|
83
|
+
# @return [Hash] The preference set
|
84
|
+
def set_channel_type(user_id:, channel_type:, setting:, preference_set: DEFAULT_SET_ID)
|
85
|
+
endpoint = "/v1/users/#{user_id}/preferences/#{preference_set}/channel_types/#{channel_type}"
|
86
|
+
|
87
|
+
request = put_request(
|
88
|
+
auth: true,
|
89
|
+
path: endpoint,
|
90
|
+
body: {subscribed: setting}
|
91
|
+
)
|
92
|
+
|
93
|
+
execute_request(request: request)
|
94
|
+
end
|
95
|
+
|
96
|
+
# Sets preferences for the given workflow
|
97
|
+
#
|
98
|
+
# @param [String] user_id The ID of the user to set preferences for
|
99
|
+
# @param [String] preference_set The preference set ID (defaults to `default`)
|
100
|
+
# @param [String] workflow The workflow to set preferences for
|
101
|
+
# @param [Bool | Hash] setting Either a boolean to indicate if the type is enabled
|
102
|
+
# or a hash containing channel types and settings
|
103
|
+
#
|
104
|
+
# @return [Hash] The preference set
|
105
|
+
def set_workflow(user_id:, workflow:, setting:, preference_set: DEFAULT_SET_ID)
|
106
|
+
params = setting.is_a?(Hash) ? setting : {subscribed: setting}
|
107
|
+
endpoint = "/v1/users/#{user_id}/preferences/#{preference_set}/workflows/#{workflow}"
|
108
|
+
|
109
|
+
request = put_request(
|
110
|
+
auth: true,
|
111
|
+
path: endpoint,
|
112
|
+
body: params
|
113
|
+
)
|
114
|
+
|
115
|
+
execute_request(request: request)
|
116
|
+
end
|
117
|
+
|
118
|
+
# Sets preferences for the given category
|
119
|
+
#
|
120
|
+
# @param [String] user_id The ID of the user to set preferences for
|
121
|
+
# @param [String] preference_set The preference set ID (defaults to `default`)
|
122
|
+
# @param [String] category The category to set preferences for
|
123
|
+
# @param [Bool | Hash] setting Either a boolean to indicate if the type is enabled
|
124
|
+
# or a hash containing channel types and settings
|
125
|
+
#
|
126
|
+
# @return [Hash] The preference set
|
127
|
+
def set_category(user_id:, category:, setting:, preference_set: DEFAULT_SET_ID)
|
128
|
+
params = setting.is_a?(Hash) ? setting : {subscribed: setting}
|
129
|
+
endpoint = "/v1/users/#{user_id}/preferences/#{preference_set}/categories/#{category}"
|
130
|
+
|
131
|
+
request = put_request(
|
132
|
+
auth: true,
|
133
|
+
path: endpoint,
|
134
|
+
body: params
|
135
|
+
)
|
136
|
+
|
137
|
+
execute_request(request: request)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
data/lib/knock/users.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require "net/http"
|
2
|
+
require "uri"
|
3
|
+
|
4
|
+
module Knock
|
5
|
+
# Provides convienience methods for working with users
|
6
|
+
module Users
|
7
|
+
class << self
|
8
|
+
include Base
|
9
|
+
include Client
|
10
|
+
|
11
|
+
# Identifies the user
|
12
|
+
#
|
13
|
+
# @param [String] id The user ID
|
14
|
+
# @param [Hash] data The traits to attach to the user
|
15
|
+
#
|
16
|
+
# @return [Hash] The user
|
17
|
+
def identify(id:, data: {})
|
18
|
+
request = put_request(
|
19
|
+
auth: true,
|
20
|
+
path: "/v1/users/#{id}",
|
21
|
+
body: data
|
22
|
+
)
|
23
|
+
|
24
|
+
execute_request(request: request)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Retrieves the given user
|
28
|
+
#
|
29
|
+
# @param [String] id The user ID
|
30
|
+
#
|
31
|
+
# @return [Hash] The user
|
32
|
+
def get(id:)
|
33
|
+
request = get_request(
|
34
|
+
auth: true,
|
35
|
+
path: "/v1/users/#{id}"
|
36
|
+
)
|
37
|
+
|
38
|
+
execute_request(request: request)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Deletes the user
|
42
|
+
#
|
43
|
+
# @param [String] id the user ID
|
44
|
+
#
|
45
|
+
# @return [Hash] the user
|
46
|
+
def delete(id:)
|
47
|
+
request = delete_request(
|
48
|
+
auth: true,
|
49
|
+
path: "/v1/users/#{id}"
|
50
|
+
)
|
51
|
+
|
52
|
+
execute_request(request: request)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require "net/http"
|
2
|
+
require "uri"
|
3
|
+
|
4
|
+
module Knock
|
5
|
+
# Methods for interacting with workflows in Knock
|
6
|
+
module Workflows
|
7
|
+
class << self
|
8
|
+
include Base
|
9
|
+
include Client
|
10
|
+
|
11
|
+
# Triggers the workflow with the given key
|
12
|
+
#
|
13
|
+
# @param [String] key The workflow key
|
14
|
+
# @param [String] actor The actor ID
|
15
|
+
# @param [Array<String>] recipients The recipient IDs
|
16
|
+
# @param [Hash] data The data to pass to the workflow
|
17
|
+
# @param [String] cancellation_key An optional key to identify this workflow
|
18
|
+
# invocation for cancelling
|
19
|
+
# @param [String] tenant An optional tenant identifier
|
20
|
+
#
|
21
|
+
# @return [Hash] A workflow trigger result
|
22
|
+
def trigger(key:, actor:, recipients:, data: {}, cancellation_key: nil, tenant: nil)
|
23
|
+
attrs = {
|
24
|
+
actor: actor,
|
25
|
+
recipients: recipients,
|
26
|
+
data: data,
|
27
|
+
cancellation_key: cancellation_key,
|
28
|
+
tenant: tenant
|
29
|
+
}
|
30
|
+
|
31
|
+
request = post_request(
|
32
|
+
auth: true,
|
33
|
+
path: "/v1/workflows/#{key}/trigger",
|
34
|
+
body: attrs
|
35
|
+
)
|
36
|
+
|
37
|
+
execute_request(request: request)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Cancels the workflow with the given key and cancellation key
|
41
|
+
#
|
42
|
+
# @param [String] key The workflow key
|
43
|
+
# @param [String] cancellation_key The cancellation key
|
44
|
+
# @param [Array<String>] recipients The recipient IDs to cancel for
|
45
|
+
#
|
46
|
+
# @return [Hash] - Cancellation result
|
47
|
+
def cancel(key:, cancellation_key:, recipients: nil)
|
48
|
+
attrs = {
|
49
|
+
cancellation_key: cancellation_key,
|
50
|
+
recipients: recipients
|
51
|
+
}
|
52
|
+
|
53
|
+
request = post_request(
|
54
|
+
auth: true,
|
55
|
+
path: "/v1/workflows/#{key}/cancel",
|
56
|
+
body: attrs
|
57
|
+
)
|
58
|
+
|
59
|
+
execute_request(request: request)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
metadata
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: knockapi
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Knock Labs, Inc.
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-08-18 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 2.0.1
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 2.0.1
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
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: standard
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description: API client for Knock
|
56
|
+
email:
|
57
|
+
- support@knock.app
|
58
|
+
executables:
|
59
|
+
- console
|
60
|
+
extensions: []
|
61
|
+
extra_rdoc_files: []
|
62
|
+
files:
|
63
|
+
- ".gitignore"
|
64
|
+
- ".ruby-version"
|
65
|
+
- Gemfile
|
66
|
+
- Gemfile.lock
|
67
|
+
- LICENSE
|
68
|
+
- README.md
|
69
|
+
- bin/console
|
70
|
+
- knockapi.gemspec
|
71
|
+
- lib/knock.rb
|
72
|
+
- lib/knock/base.rb
|
73
|
+
- lib/knock/client.rb
|
74
|
+
- lib/knock/errors.rb
|
75
|
+
- lib/knock/preferences.rb
|
76
|
+
- lib/knock/users.rb
|
77
|
+
- lib/knock/version.rb
|
78
|
+
- lib/knock/workflows.rb
|
79
|
+
homepage: https://github.com/knocklabs/knock-ruby
|
80
|
+
licenses:
|
81
|
+
- MIT
|
82
|
+
metadata:
|
83
|
+
documentation_uri: https://docs.knock.app
|
84
|
+
post_install_message:
|
85
|
+
rdoc_options: []
|
86
|
+
require_paths:
|
87
|
+
- lib
|
88
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
89
|
+
requirements:
|
90
|
+
- - ">="
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: '2.5'
|
93
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '0'
|
98
|
+
requirements: []
|
99
|
+
rubygems_version: 3.2.22
|
100
|
+
signing_key:
|
101
|
+
specification_version: 4
|
102
|
+
summary: API client for Knock
|
103
|
+
test_files: []
|