logsnag-ruby 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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 290549e28821123d90a3a090d8fbfbddfb4121966467d67e166009956586ad9c
4
+ data.tar.gz: 163c0b7bc7ff995f1aa932cfc46348918e53eb618e1f6be4bbf913442eb38837
5
+ SHA512:
6
+ metadata.gz: 755f3063453f5555b7f5d6c02265d6e28d029055979a9a1f31104abc26cd447ec27415049f90cb6313c52934b29ba6904805847540d5cd443547722e770521e5
7
+ data.tar.gz: 716e831b19d6d79d4810647a2c5e3591b860aba9b7d1e47e2b43817d3055455f65e87adc52de4aea8129663f720b200c226a3b1aca08c34448e8355938637845
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,36 @@
1
+ require:
2
+ - rubocop-rspec
3
+
4
+ AllCops:
5
+ NewCops: enable
6
+ SuggestExtensions: false
7
+ TargetRubyVersion: 2.6
8
+
9
+ GemSpec/RequireMFA:
10
+ Enabled: false
11
+
12
+ Layout/LineLength:
13
+ Enabled: false
14
+
15
+ Lint/NonDeterministicRequireOrder:
16
+ Enabled: false
17
+
18
+ Metrics/MethodLength:
19
+ Enabled: false
20
+
21
+ RSpec/ExampleLength:
22
+ Enabled: false
23
+
24
+ RSpec/MultipleExpectations:
25
+ Enabled: false
26
+
27
+ RSpec/NestedGroups:
28
+ Enabled: false
29
+
30
+ Style/StringLiterals:
31
+ Enabled: true
32
+ EnforcedStyle: double_quotes
33
+
34
+ Style/StringLiteralsInInterpolation:
35
+ Enabled: true
36
+ EnforcedStyle: double_quotes
data/.tool-versions ADDED
@@ -0,0 +1 @@
1
+ ruby 3.1.4
data/CHANGELOG.md ADDED
@@ -0,0 +1,9 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 (2023-11-11)
4
+
5
+ ### Features
6
+
7
+ * initial release ([77a98a4](https://github.com/damonbauer/logsnag-ruby/commit/77a98a4ade4725e5d4562be962aa80da00cdd853))
8
+
9
+ ## [Unreleased]
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2023 Damon Bauer
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
13
+ all 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
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,178 @@
1
+ # logsnag-ruby
2
+
3
+ Interact with the [LogSnag](https://logsnag.com) API to send logs, identify users, and send insights.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'logsnag-ruby'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ ```bash
16
+ bundle install
17
+ ```
18
+
19
+ Or install it yourself as:
20
+
21
+ ```bash
22
+ gem install logsnag-ruby
23
+ ```
24
+
25
+ ## Configuration
26
+
27
+ Before you can send logs to LogSnag, you need to configure the gem with your API token and project name.
28
+ This is typically done in an initializer in your application.
29
+ For example:
30
+
31
+ ```ruby
32
+ # config/initializers/logsnag.rb
33
+
34
+ LogSnag.configure do |config|
35
+ config.api_token = "your_api_token"
36
+ config.project = "your_project_name"
37
+ end
38
+ ```
39
+
40
+ ## Usage
41
+
42
+ The `logsnag-ruby` gem provides several methods to interact with the LogSnag API.
43
+
44
+ Each method will automatically add the `project` and `api_token` parameters to the request.
45
+
46
+ ### Logging Events
47
+
48
+ To send an event log:
49
+
50
+ ```ruby
51
+ LogSnag.log({
52
+ channel: "server",
53
+ event: "server_start",
54
+ # ... other optional parameters ...
55
+ })
56
+ ```
57
+
58
+ **Arguments:**
59
+
60
+ - `data`: A hash containing the event data.
61
+ - **Required keys:**
62
+ - `channel` [String]: The channel within the project to which the log belongs.
63
+ - `event` [String]: The name of the event.
64
+ - Optional keys:
65
+ - `user_id` [String]: The user ID of the user related to the event.
66
+ - `description` [String]: The description of the event.
67
+ - `icon` [String]: The icon to be displayed with the event.
68
+ - `notify` [Boolean]: Whether to send a push notification for the event.
69
+ - `tags` [Hash]: The tags associated with the event. See [the LogSnag docs](https://docs.logsnag.com/api-reference/log#tags) for more information regarding the format of the `tags` hash.
70
+ - `parser` [String]: The parser to be used for the event. One of "text" or "markdown".
71
+ - `timestamp` [Numeric]: The timestamp of the event (in Unix seconds).
72
+
73
+ **Returns:**
74
+
75
+ - `LogSnag::Result`: A result object with the following methods:
76
+ - `success?`: Returns `true` if the request was successful.
77
+ - `error?`: Returns `true` if the request failed.
78
+ - `data`: The parsed response data from the server.
79
+ - `error_message`: The error message if the request failed.
80
+ - `status_code`: The HTTP status code of the response.
81
+
82
+ ### Identifying Users
83
+
84
+ To add or update properties to a user profile:
85
+
86
+ ```ruby
87
+ LogSnag.identify({
88
+ user_id: "user_123",
89
+ properties: {
90
+ email: "user@example.com",
91
+ plan: "premium"
92
+ }
93
+ })
94
+ ```
95
+
96
+ **Arguments:**
97
+
98
+ - `data`: A hash containing the identification data.
99
+ - **Required keys:**
100
+ - `user_id` [String]: The user ID of the user to be identified.
101
+ - `properties` [Hash]: The properties of the user to be identified. See [the LogSnag docs](https://docs.logsnag.com/api-reference/identify#properties-schema) for more information regarding the format of the `properties` hash.
102
+
103
+ **Returns:**
104
+
105
+ - `LogSnag::Result`: A result object with the following methods:
106
+ - `success?`: Returns `true` if the request was successful.
107
+ - `error?`: Returns `true` if the request failed.
108
+ - `data`: The parsed response data from the server.
109
+ - `error_message`: The error message if the request failed.
110
+ - `status_code`: The HTTP status code of the response.
111
+
112
+ ### Sending Insights
113
+
114
+ To send an insight log:
115
+
116
+ ```ruby
117
+ LogSnag.insight({
118
+ title: "New Signups",
119
+ value: 42,
120
+ # ... other optional parameters ...
121
+ })
122
+ ```
123
+
124
+ **Arguments:**
125
+
126
+ - `data`: A hash containing the insight data.
127
+ - **Required keys:**
128
+ - `title` [String]: The title of the insight.
129
+ - `value` [String, Numeric]: The numerical value of the insight.
130
+ - Optional keys:
131
+ - `icon` [String]: The icon to be displayed with the insight.
132
+
133
+ **Returns:**
134
+
135
+ - `LogSnag::Result`: A result object with the following methods:
136
+ - `success?`: Returns `true` if the request was successful.
137
+ - `error?`: Returns `true` if the request failed.
138
+ - `data`: The parsed response data from the server.
139
+ - `error_message`: The error message if the request failed.
140
+ - `status_code`: The HTTP status code of the response.
141
+
142
+ ### Mutating Insights
143
+
144
+ To mutate (increment) an existing numerical insight:
145
+
146
+ ```ruby
147
+ LogSnag.mutate_insight({
148
+ title: "Total Users",
149
+ value: 5
150
+ })
151
+ ```
152
+
153
+ **Arguments:**
154
+
155
+ - `data`: A hash containing the insight mutation data.
156
+ - **Required keys:**
157
+ - `title` [String]: The title of the insight.
158
+ - `value` [Numeric]: The amount to increment the insight by.
159
+ - Optional keys:
160
+ - `icon` [String]: The icon to be displayed with the insight.
161
+
162
+ **Returns:**
163
+
164
+ - `LogSnag::Result`: A result object with the following methods:
165
+ - `success?`: Returns `true` if the request was successful.
166
+ - `error?`: Returns `true` if the request failed.
167
+ - `data`: The parsed response data from the server.
168
+ - `error_message`: The error message if the request failed.
169
+ - `status_code`: The HTTP status code of the response.
170
+
171
+ ## Contributing
172
+
173
+ Bug reports and pull requests are welcome on GitHub
174
+ at [https://github.com/damonbauer/logsnag-ruby](https://github.com/damonbauer/logsnag-ruby).
175
+
176
+ ## License
177
+
178
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec rubocop]
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LogSnag
4
+ # Configuration class for storing API token, project, and logger
5
+ class Configuration
6
+ attr_accessor :logger
7
+ attr_reader :api_token, :project
8
+
9
+ def api_token=(token)
10
+ raise ArgumentError, "API token cannot be nil" if token.nil? || token.empty?
11
+
12
+ @api_token = token
13
+ end
14
+
15
+ def project=(project_name)
16
+ raise ArgumentError, "Project cannot be nil" if project_name.nil? || project_name.empty?
17
+
18
+ @project = project_name
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LogSnag
4
+ # Provides a base class for LogSnag events.
5
+ # Ensures that the data hash is valid and that the configuration object is present.
6
+ # @abstract Subclass and override {#data} and {#config} to implement.
7
+ # @attr_reader data [Hash] The data to be sent to LogSnag.
8
+ # @attr_reader config [LogSnag::Configuration] The configuration object.
9
+ # @raise [ArgumentError] If the hash is missing required keys or contains invalid keys.
10
+ # @return [LogSnag::EventBase] The new EventBase object.
11
+ class EventBase
12
+ attr_reader :data, :config
13
+
14
+ def initialize(data, config)
15
+ @data = data
16
+ @config = config
17
+
18
+ raise ArgumentError, "`data` must be a Hash" unless data.is_a?(Hash)
19
+
20
+ return if config.is_a?(LogSnag::Configuration)
21
+
22
+ raise ArgumentError,
23
+ "LogSnag::Configuration not found. Did you call LogSnag.configure?"
24
+ end
25
+
26
+ def to_json(*_args)
27
+ data.to_json
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "event_base"
4
+ require_relative "validator"
5
+
6
+ module LogSnag
7
+ # Constructs and validates an "identify" object
8
+ class Identify < EventBase
9
+ REQUIRED_KEYS = %i[project user_id properties].freeze
10
+
11
+ attr_reader :data, :config
12
+
13
+ # Creates a new Identify object.
14
+ # @param data [Hash] The data to be sent to LogSnag.
15
+ # The data hash must include the following keys:
16
+ # - :user_id [String] The user ID of the user to be identified.
17
+ # - :properties [Hash] The properties of the user to be identified.
18
+ # @param config [LogSnag::Configuration] The configuration object.
19
+ # @see https://docs.logsnag.com/api-reference/identify
20
+ # @raise [ArgumentError] If the hash is missing required keys, has invalid keys, or has values of incorrect types.
21
+ # @return [LogSnag::Insight] The new Identify object.
22
+ def initialize(data, config)
23
+ super(data, config)
24
+
25
+ append_required_fields!
26
+ compact_properties!
27
+ validate_data
28
+ end
29
+
30
+ private
31
+
32
+ def append_required_fields!
33
+ data[:project] = config.project
34
+ data
35
+ end
36
+
37
+ def compact_properties!
38
+ Validator.compact_hash!(data, :properties)
39
+ end
40
+
41
+ def validate_data
42
+ Validator.validate_required_keys(REQUIRED_KEYS, data)
43
+ Validator.validate_hash(data[:properties])
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "event_base"
4
+ require_relative "validator"
5
+
6
+ module LogSnag
7
+ # Constructs and validates an "insight" object
8
+ class Insight < EventBase
9
+ REQUIRED_KEYS = %i[project title value].freeze
10
+
11
+ attr_reader :data, :config
12
+
13
+ # Creates a new Insight object.
14
+ # @param data [Hash] The data to be sent to LogSnag.
15
+ # The data hash MUST include the following keys:
16
+ # - :title [String] The title of the insight.
17
+ # - :value [String,Numeric] The numerical value of the insight.
18
+ # The data hash MAY include the following keys:
19
+ # - :icon [String] The icon to be displayed with the insight.
20
+ # @param config [LogSnag::Configuration] The configuration object.
21
+ # @param mutate [Boolean] Whether or not to mutate the data hash.
22
+ # @see https://docs.logsnag.com/api-reference/insight
23
+ # @raise [ArgumentError] If the hash is missing required keys, has invalid keys, or has values of incorrect types.
24
+ # @return [LogSnag::Insight] The new Insight object.
25
+ def initialize(data, config, mutate: false)
26
+ super(data, config)
27
+
28
+ append_required_fields!
29
+
30
+ if mutate
31
+ validate_mutation_data
32
+ else
33
+ validate_data
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def append_required_fields!
40
+ data[:project] = config.project
41
+ data
42
+ end
43
+
44
+ def validate_mutation_data
45
+ Validator.validate_required_keys(REQUIRED_KEYS, data)
46
+ validate_mutation_values
47
+ end
48
+
49
+ def validate_data
50
+ Validator.validate_required_keys(REQUIRED_KEYS, data)
51
+ validate_values
52
+ end
53
+
54
+ def validate_mutation_values
55
+ return if [Numeric].any? { |type| data[:value].is_a?(type) }
56
+
57
+ raise ArgumentError, "Invalid value for Insight mutation: '#{data[:value]}'. Value must be a number."
58
+ end
59
+
60
+ def validate_values
61
+ return if [String, Numeric].any? { |type| data[:value].is_a?(type) }
62
+
63
+ raise ArgumentError, "Invalid value for Insight: '#{data[:value]}'. Values must be a string or number."
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "event_base"
4
+ require_relative "validator"
5
+
6
+ module LogSnag
7
+ # Constructs and validates an event log
8
+ class Log < EventBase
9
+ REQUIRED_KEYS = %i[project channel event].freeze
10
+ ALLOWED_KEYS = REQUIRED_KEYS + %i[
11
+ user_id description icon notify tags parser timestamp
12
+ ].freeze
13
+
14
+ # attr_reader :data, :config
15
+
16
+ # Creates a new Log object.
17
+ # @param data [Hash] The data to be sent to LogSnag.
18
+ # The data hash must include the following keys:
19
+ # - :channel [String] The channel within the project to which the log belongs.
20
+ # - :event [String] The name of the event.
21
+ # The data hash may include the following keys:
22
+ # - :user_id [String] The user ID of the user to be identified.
23
+ # - :description [String] The description of the event.
24
+ # - :icon [String] The icon to be displayed with the event.
25
+ # - :notify [Boolean] Whether or not to send a push notification for the event.
26
+ # - :tags [Hash] The tags to be associated with the event.
27
+ # - :parser [String] The parser to be used for the event. One of "text" or "markdown".
28
+ # - :timestamp [Numeric] The timestamp of the event (in Unix seconds).
29
+ # @param config [LogSnag::Configuration] The configuration object.
30
+ # @see https://docs.logsnag.com/api-reference/log
31
+ # @raise [ArgumentError] If the hash is missing required keys, has invalid keys, or has values of incorrect types.
32
+ # @return [LogSnag::Insight] The new Identify object.
33
+ def initialize(data, config)
34
+ super(data, config)
35
+
36
+ append_required_fields!
37
+ compact_tags!
38
+ validate_data
39
+ end
40
+
41
+ private
42
+
43
+ def append_required_fields!
44
+ data[:project] = config.project
45
+ data
46
+ end
47
+
48
+ def compact_tags!
49
+ Validator.compact_hash!(data, :tags)
50
+ end
51
+
52
+ def validate_data
53
+ Validator.validate_required_keys(REQUIRED_KEYS, data)
54
+ Validator.validate_allowed_keys(ALLOWED_KEYS, data)
55
+ Validator.validate_hash(data[:tags])
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LogSnag
4
+ Result = Struct.new(:success, :data, :error_message, :status_code, keyword_init: true) do
5
+ def success?
6
+ success
7
+ end
8
+
9
+ def error?
10
+ !success
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LogSnag
4
+ # Provides helpers for validating data.
5
+ class Validator
6
+ # Ensure that the `required_keys` are present in the `data` hash.
7
+ # @param required_keys [Array<Symbol>] The keys that must be present in the hash.
8
+ # @param data [Hash] The hash to be validated.
9
+ # @raise [ArgumentError] If the hash is missing required keys.
10
+ # @return [void]
11
+ def self.validate_required_keys(required_keys, data)
12
+ missing_keys = required_keys - data.keys
13
+ raise ArgumentError, "Missing required keys: #{missing_keys}" unless missing_keys.empty?
14
+ end
15
+
16
+ # Ensure that the `data` hash only contains keys in the `allowed_keys` array.
17
+ # @param allowed_keys [Array<Symbol>] The keys that are allowed in the hash.
18
+ # @param data [Hash] The hash to be validated.
19
+ # @raise [ArgumentError] If the hash contains invalid keys.
20
+ # @return [void]
21
+ def self.validate_allowed_keys(allowed_keys, data)
22
+ invalid_keys = data.keys - allowed_keys
23
+ raise ArgumentError, "Found invalid keys: #{invalid_keys}" unless invalid_keys.empty?
24
+ end
25
+
26
+ # Removes any nil values from the hash at the specified path.
27
+ # @param hash [Hash] The hash from which to remove nil values.
28
+ # @param path [Symbol] The path to the hash to be compacted.
29
+ # @return [Hash] The hash with nil values removed.
30
+ def self.compact_hash!(hash, path)
31
+ hash[path] = hash[path].compact if hash[path]
32
+ hash
33
+ end
34
+
35
+ # Ensures that the keys and values in the hash conform to the specified rules.
36
+ # @param hash [Hash] The hash to be validated.
37
+ # @raise [ArgumentError] If the hash contains invalid keys or values.
38
+ # @return [void]
39
+ def self.validate_hash(hash)
40
+ return unless hash
41
+
42
+ hash.each do |key, value|
43
+ raise ArgumentError, "Invalid key: '#{key}'. Keys must be lowercase and may include dashes." unless key.to_s.match?(/\A[a-z]+(-[a-z]+)*\z/)
44
+
45
+ raise ArgumentError, "Invalid value for '#{key}': '#{value}'. Values must be a string, boolean, or number." unless [String, TrueClass, FalseClass, Numeric].any? { |type| value.is_a?(type) }
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LogSnag
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,5 @@
1
+ # rubocop:disable Naming/FileName
2
+ # frozen_string_literal: true
3
+
4
+ require "logsnag"
5
+ # rubocop:enable Naming/FileName
data/lib/logsnag.rb ADDED
@@ -0,0 +1,173 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "httparty"
4
+ require "json"
5
+
6
+ require_relative "logsnag/version"
7
+ require_relative "logsnag/configuration"
8
+ require_relative "logsnag/log"
9
+ require_relative "logsnag/identify"
10
+ require_relative "logsnag/insight"
11
+ require_relative "logsnag/result"
12
+
13
+ # The main module for the LogSnag gem, containing methods for sending logs to LogSnag.
14
+ module LogSnag
15
+ include HTTParty
16
+
17
+ base_uri "https://api.logsnag.com/v1"
18
+ PATHS = {
19
+ LOG: "/log",
20
+ IDENTIFY: "/identify",
21
+ INSIGHT: "/insight"
22
+ }.freeze
23
+
24
+ class << self
25
+ attr_accessor :config
26
+
27
+ # Configures LogSnag with the provided configuration options.
28
+ # @yield [LogSnag::Configuration] The configuration object.
29
+ def configure
30
+ self.config ||= LogSnag::Configuration.new
31
+ yield(config)
32
+ end
33
+
34
+ # Sends an event log to LogSnag.
35
+ #
36
+ # Logs are the core of LogSnag. They are used to track events in your application. These events could be
37
+ # anything from user actions to server events, such as a database running out of space or a server crash.
38
+ #
39
+ # @param data [Hash] The data to be sent to LogSnag.
40
+ # The data hash MUST include the following keys:
41
+ # - :channel [String] The channel within the project to which the log belongs.
42
+ # - :event [String] The name of the event.
43
+ # The data hash MAY include the following keys:
44
+ # - :user_id [String] The user ID of the user to be identified.
45
+ # - :description [String] The description of the event.
46
+ # - :icon [String] The icon to be displayed with the event.
47
+ # - :notify [Boolean] Whether or not to send a push notification for the event.
48
+ # - :tags [Hash] The tags to be associated with the event.
49
+ # - :parser [String] The parser to be used for the event. One of "text" or "markdown".
50
+ # - :timestamp [Numeric] The timestamp of the event (in Unix seconds).
51
+ # @see https://docs.logsnag.com/api-reference/log
52
+ # @raise [ArgumentError] If the hash is missing required keys or contains invalid keys.
53
+ # @return [LogSnag::Result] The formatted result from the request.
54
+ def log(data = {})
55
+ event = LogSnag::Log.new(data, config)
56
+ execute_post(PATHS[:LOG], event)
57
+ end
58
+
59
+ # Sends an identify log to LogSnag.
60
+ #
61
+ # The identify endpoint lets you add key-value properties to a user profile.
62
+ # This endpoint is optional and useful for getting a complete picture of a user just by looking at their profile,
63
+ # and additionally, these properties can be used for filtering and searching.
64
+ #
65
+ # For example, you may add a user's email address, their plan, last payment date, etc., to their profile and
66
+ # then use these properties to filter and search for users, such as searching for all users on a specific plan.
67
+ #
68
+ # @param data [Hash] The data to be sent to LogSnag.
69
+ # The data hash MUST include the following keys:
70
+ # - :user_id [String] The user ID of the user to be identified.
71
+ # - :properties [Hash] The properties of the user to be identified.
72
+ # @see https://docs.logsnag.com/api-reference/identify
73
+ # @raise [ArgumentError] If the hash is missing required keys or contains invalid keys.
74
+ # @return [LogSnag::Result] The formatted result from the request.
75
+ def identify(data = {})
76
+ event = LogSnag::Identify.new(data, config)
77
+ execute_post(PATHS[:IDENTIFY], event)
78
+ end
79
+
80
+ # Sends an insight log to LogSnag.
81
+ #
82
+ # Insights are real-time widgets that you can add to each of your projects.
83
+ # They are use-case agnostic and can be used to display any information that you want in real-time.
84
+ #
85
+ # @param data [Hash] The data to be sent to LogSnag.
86
+ # The data hash MUST include the following keys:
87
+ # - :title [String] The title of the insight.
88
+ # - :value [String,Numeric] The numerical value of the insight.
89
+ # @see https://docs.logsnag.com/api-reference/insight
90
+ # @raise [ArgumentError] If the hash is missing required keys, has invalid keys, or has values of incorrect types.
91
+ # @return [LogSnag::Result] The formatted result from the request.
92
+ def insight(data = {})
93
+ event = LogSnag::Insight.new(data, config)
94
+ execute_post(PATHS[:INSIGHT], event)
95
+ end
96
+
97
+ # Mutate an insight to LogSnag.
98
+ # This endpoint allows you to change (mutate) existing numerical insights.
99
+ # @param data [Hash] The data to be sent to LogSnag.
100
+ # The data hash MUST include the following keys:
101
+ # - :title [String] The title of the insight.
102
+ # - :value [Numeric] The numerical value of the insight.
103
+ # The data hash MAY include the following keys:
104
+ # - :icon [String] The icon to be displayed with the event.
105
+ # @see https://docs.logsnag.com/api-reference/insight-mutate
106
+ # @raise [ArgumentError] If the hash is missing required keys, has invalid keys, or has values of incorrect types.
107
+ # @return [LogSnag::Result] The formatted result from the request.
108
+ def mutate_insight(data = {})
109
+ event = LogSnag::Insight.new(data, config, mutate: true)
110
+ execute_patch(PATHS[:INSIGHT], event)
111
+ end
112
+
113
+ private
114
+
115
+ # Executes a POST request to the specified path with the provided body.
116
+ # @param path [String] The path to which the request should be made.
117
+ # @param body [LogSnag::Log,LogSnag::Identify,LogSnag::Insight] The body of the request.
118
+ # @return [LogSnag::Result] The formatted result from the request.
119
+ def execute_post(path, body)
120
+ response = post(
121
+ path,
122
+ body: body.to_json,
123
+ headers: headers
124
+ )
125
+
126
+ handle_response(response)
127
+ rescue HTTParty::Error => e
128
+ Result.new(success: false, error_message: e.message, status_code: nil)
129
+ end
130
+
131
+ # Executes a PATCH request to the specified path with the provided body.
132
+ # @param path [String] The path to which the request should be made.
133
+ # @param body [LogSnag::Insight] The body of the request.
134
+ # @return [LogSnag::Result] The formatted result from the request.
135
+ def execute_patch(path, body)
136
+ response = patch(
137
+ path,
138
+ body: {
139
+ **body.data,
140
+ value: {
141
+ "$inc": body.data[:value]
142
+ }
143
+ }.to_json,
144
+ headers: headers
145
+ )
146
+
147
+ handle_response(response)
148
+ rescue HTTParty::Error => e
149
+ Result.new(success: false, error_message: e.message, status_code: nil)
150
+ end
151
+
152
+ # Handles the response from the HTTP request.
153
+ # @param response [HTTParty::Response] The response from the HTTP request.
154
+ # @return [LogSnag::Result] The formatted result from the request.
155
+ def handle_response(response)
156
+ if response.success?
157
+ Result.new(success: true, data: response.parsed_response, status_code: response.code)
158
+ else
159
+ error_message = response.parsed_response["message"] || response.body
160
+ Result.new(success: false, error_message: error_message, status_code: response.code)
161
+ end
162
+ end
163
+
164
+ # Returns the headers to be used in the HTTP request.
165
+ # @return [Hash] The headers to be used in the HTTP request.
166
+ def headers
167
+ {
168
+ "Content-Type" => "application/json",
169
+ "Authorization" => "Bearer #{config.api_token}"
170
+ }
171
+ end
172
+ end
173
+ end
data/logsnag.gemspec ADDED
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/logsnag/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "logsnag-ruby"
7
+ spec.version = LogSnag::VERSION
8
+ spec.authors = ["Damon Bauer"]
9
+ spec.email = ["damonbauer@protonmail.com"]
10
+
11
+ spec.summary = "Unofficial LogSnag API client, written in Ruby."
12
+ spec.homepage = "https://github.com/damonbauer/logsnag-ruby"
13
+ spec.license = "MIT"
14
+ spec.required_ruby_version = ">= 2.6.0"
15
+
16
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
17
+ spec.metadata["rubygems_mfa_required"] = "false"
18
+
19
+ spec.metadata["homepage_uri"] = spec.homepage
20
+ spec.metadata["source_code_uri"] = spec.homepage
21
+ spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/master/CHANGELOG.md"
22
+
23
+ spec.add_dependency "httparty", "~> 0.21.0"
24
+
25
+ # Specify which files should be added to the gem when it is released.
26
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
27
+ spec.files = Dir.chdir(__dir__) do
28
+ `git ls-files -z`.split("\x0").reject do |f|
29
+ (File.expand_path(f) == __FILE__) ||
30
+ f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor Gemfile])
31
+ end
32
+ end
33
+ spec.bindir = "exe"
34
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
35
+ spec.require_paths = ["lib"]
36
+ end
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: logsnag-ruby
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Damon Bauer
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2023-11-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: httparty
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.21.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.21.0
27
+ description:
28
+ email:
29
+ - damonbauer@protonmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - ".rspec"
35
+ - ".rubocop.yml"
36
+ - ".tool-versions"
37
+ - CHANGELOG.md
38
+ - LICENSE.txt
39
+ - README.md
40
+ - Rakefile
41
+ - lib/logsnag-ruby.rb
42
+ - lib/logsnag.rb
43
+ - lib/logsnag/configuration.rb
44
+ - lib/logsnag/event_base.rb
45
+ - lib/logsnag/identify.rb
46
+ - lib/logsnag/insight.rb
47
+ - lib/logsnag/log.rb
48
+ - lib/logsnag/result.rb
49
+ - lib/logsnag/validator.rb
50
+ - lib/logsnag/version.rb
51
+ - logsnag.gemspec
52
+ homepage: https://github.com/damonbauer/logsnag-ruby
53
+ licenses:
54
+ - MIT
55
+ metadata:
56
+ allowed_push_host: https://rubygems.org
57
+ rubygems_mfa_required: 'false'
58
+ homepage_uri: https://github.com/damonbauer/logsnag-ruby
59
+ source_code_uri: https://github.com/damonbauer/logsnag-ruby
60
+ changelog_uri: https://github.com/damonbauer/logsnag-ruby/blob/master/CHANGELOG.md
61
+ post_install_message:
62
+ rdoc_options: []
63
+ require_paths:
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: 2.6.0
70
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ requirements: []
76
+ rubygems_version: 3.3.26
77
+ signing_key:
78
+ specification_version: 4
79
+ summary: Unofficial LogSnag API client, written in Ruby.
80
+ test_files: []