atsd 1.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.
@@ -0,0 +1,11 @@
1
+ require 'atsd/errors/error'
2
+
3
+ module ATSD
4
+ class APIError < Error
5
+ attr_reader :status
6
+
7
+ def initialize(env)
8
+ @status = env.status
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ module ATSD
2
+ class Error < StandardError; end
3
+ end
@@ -0,0 +1,88 @@
1
+ require 'atsd/errors/api_error'
2
+
3
+ module ATSD
4
+ # Faraday Middleware
5
+ module Middleware
6
+ # Middleware to raise error on request fail and possibly catch error message from API.
7
+ class ErrorsHandlerMiddleware < Faraday::Response::Middleware
8
+ # HTTP status codes with messages generated by
9
+ #
10
+ # ```bash
11
+ # curl -s www.iana.org/assignments/http-status-codes/http-status-codes-1.csv | \
12
+ # ruby -ne 'm = /^(\d{3}),(?!Unassigned|\(Unused\))([^,]+)/.match($_) and \
13
+ # puts "#{m[1]} => \x27#{m[2].strip}\x27,"'
14
+ # ```
15
+ HTTP_STATUS_CODES = {
16
+ 100 => 'Continue',
17
+ 101 => 'Switching Protocols',
18
+ 102 => 'Processing',
19
+ 200 => 'OK',
20
+ 201 => 'Created',
21
+ 202 => 'Accepted',
22
+ 203 => 'Non-Authoritative Information',
23
+ 204 => 'No Content',
24
+ 205 => 'Reset Content',
25
+ 206 => 'Partial Content',
26
+ 207 => 'Multi-Status',
27
+ 208 => 'Already Reported',
28
+ 226 => 'IM Used',
29
+ 300 => 'Multiple Choices',
30
+ 301 => 'Moved Permanently',
31
+ 302 => 'Found',
32
+ 303 => 'See Other',
33
+ 304 => 'Not Modified',
34
+ 305 => 'Use Proxy',
35
+ 307 => 'Temporary Redirect',
36
+ 308 => 'Permanent Redirect',
37
+ 400 => 'Bad Request',
38
+ 401 => 'Unauthorized',
39
+ 402 => 'Payment Required',
40
+ 403 => 'Forbidden',
41
+ 404 => 'Not Found',
42
+ 405 => 'Method Not Allowed',
43
+ 406 => 'Not Acceptable',
44
+ 407 => 'Proxy Authentication Required',
45
+ 408 => 'Request Timeout',
46
+ 409 => 'Conflict',
47
+ 410 => 'Gone',
48
+ 411 => 'Length Required',
49
+ 412 => 'Precondition Failed',
50
+ 413 => 'Payload Too Large',
51
+ 414 => 'URI Too Long',
52
+ 415 => 'Unsupported Media Type',
53
+ 416 => 'Range Not Satisfiable',
54
+ 417 => 'Expectation Failed',
55
+ 421 => 'Misdirected Request',
56
+ 422 => 'Unprocessable Entity',
57
+ 423 => 'Locked',
58
+ 424 => 'Failed Dependency',
59
+ 426 => 'Upgrade Required',
60
+ 428 => 'Precondition Required',
61
+ 429 => 'Too Many Requests',
62
+ 431 => 'Request Header Fields Too Large',
63
+ 500 => 'Internal Server Error',
64
+ 501 => 'Not Implemented',
65
+ 502 => 'Bad Gateway',
66
+ 503 => 'Service Unavailable',
67
+ 504 => 'Gateway Timeout',
68
+ 505 => 'HTTP Version Not Supported',
69
+ 506 => 'Variant Also Negotiates',
70
+ 507 => 'Insufficient Storage',
71
+ 508 => 'Loop Detected',
72
+ 510 => 'Not Extended',
73
+ 511 => 'Network Authentication Required',
74
+ }
75
+
76
+ def call(environment)
77
+ @app.call(environment).on_complete do |env|
78
+ unless env.success?
79
+ message = env.body['error'] || HTTP_STATUS_CODES[env.status]
80
+ fail APIError.new(env), message
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ Faraday::Response.register_middleware :errors_handler => ATSD::Middleware::ErrorsHandlerMiddleware
@@ -0,0 +1,7 @@
1
+ require 'atsd/models/base_model'
2
+
3
+ module ATSD
4
+ class Alert < BaseModel
5
+ end
6
+ end
7
+
@@ -0,0 +1,7 @@
1
+ require 'atsd/models/base_model'
2
+
3
+ module ATSD
4
+ class AlertHistory < BaseModel
5
+ end
6
+ end
7
+
@@ -0,0 +1,23 @@
1
+ module ATSD
2
+ # Base class for all models
3
+ # @abstract
4
+ class BaseModel < ::Hash
5
+ include ::Hashie::Extensions::MethodAccess
6
+ include ::Hashie::Extensions::MergeInitializer
7
+ include Utils::UnderscoreKeys
8
+
9
+ # Converts model to hash usable for API
10
+ #
11
+ # @return [Hash]
12
+ def to_request_hash
13
+ hash = {}
14
+ keys.each do |k|
15
+ new_key = k.to_s.camelize(:lower)
16
+ new_key = new_key.to_sym if k.is_a? Symbol
17
+ hash[new_key] = self[k]
18
+ end
19
+ hash
20
+ end
21
+ end
22
+ end
23
+
@@ -0,0 +1,7 @@
1
+ require 'atsd/models/base_model'
2
+
3
+ module ATSD
4
+ class Entity < BaseModel
5
+ end
6
+ end
7
+
@@ -0,0 +1,8 @@
1
+ require 'atsd/models/base_model'
2
+ require 'atsd/models/entity'
3
+
4
+ module ATSD
5
+ class EntityGroup < BaseModel
6
+ end
7
+ end
8
+
@@ -0,0 +1,7 @@
1
+ require 'atsd/models/base_model'
2
+
3
+ module ATSD
4
+ class Metric < BaseModel
5
+ end
6
+ end
7
+
@@ -0,0 +1,11 @@
1
+ require 'atsd/models/base_model'
2
+
3
+ module ATSD
4
+ # Property model
5
+ #
6
+ # @note Please use `property[:key]` instead of `property.key` to access
7
+ # `key` attribute due to implementation details (`#key` is a Hash method).
8
+ class Property < BaseModel
9
+ end
10
+ end
11
+
@@ -0,0 +1,8 @@
1
+ require 'atsd/models/base_model'
2
+
3
+ module ATSD
4
+ # Time Series model
5
+ class Series < BaseModel
6
+ end
7
+ end
8
+
@@ -0,0 +1,31 @@
1
+ require 'atsd/queries/base_query'
2
+
3
+ module ATSD
4
+ # Class for building and executing Alerts History Query
5
+ # @see https://axibase.com/atsd/api/#alerts:-history-query
6
+ class AlertsHistoryQuery < BaseQuery
7
+ TO_MILLISECONDS_LAMBDA = ->(v) do
8
+ case v
9
+ when Time
10
+ v.to_i * 1_000
11
+ else
12
+ v.to_i
13
+ end
14
+ end
15
+
16
+ coerce_key :end_time, TO_MILLISECONDS_LAMBDA
17
+ coerce_key :start_time, TO_MILLISECONDS_LAMBDA
18
+
19
+ # @return [Array<AlertHistory>]
20
+ def result
21
+ super
22
+ end
23
+
24
+ # @return (see #result)
25
+ def execute
26
+ result = @client.alerts_history_query to_request_hash
27
+ @result = result.map { |json| AlertHistory.new json }
28
+ end
29
+ end
30
+ end
31
+
@@ -0,0 +1,31 @@
1
+ require 'atsd/queries/base_query'
2
+
3
+ module ATSD
4
+ # Class for building and executing Alerts Query
5
+ # @see https://axibase.com/atsd/api/#alerts:-query
6
+ class AlertsQuery < BaseQuery
7
+ # `severity` levels
8
+ module Severity
9
+ UNDEFINED = 0
10
+ UNKNOWN = 1
11
+ NORMAL = 2
12
+ WARNING = 3
13
+ MINOR = 4
14
+ MAJOR = 5
15
+ CRITICAL = 6
16
+ FATAL = 7
17
+ end
18
+
19
+ # @return [Array<Alert>]
20
+ def result
21
+ super
22
+ end
23
+
24
+ # @return (see #result)
25
+ def execute
26
+ result = @client.alerts_query to_request_hash
27
+ @result = result.map { |json| Alert.new json }
28
+ end
29
+ end
30
+ end
31
+
@@ -0,0 +1,40 @@
1
+ module ATSD
2
+ # Base class for API query builders
3
+ # @abstract
4
+ class BaseQuery < ::Hashie::Clash
5
+ include ::Hashie::Extensions::Coercion
6
+
7
+ # @return [Client]
8
+ attr_reader :client
9
+
10
+ # @param [Client] client
11
+ def initialize(client)
12
+ @client = client
13
+ end
14
+
15
+ # Build request parameters hash
16
+ # @return [Hash]
17
+ def to_request_hash
18
+ Utils::CamelizeKeys.camelize_keys(self)
19
+ end
20
+
21
+ # Execute query on client
22
+ # @return (see #result)
23
+ # @raise [APIError]
24
+ def execute
25
+ raise NotImplementedError
26
+ end
27
+
28
+ # Result of query execution.
29
+ #
30
+ # @return [Object]
31
+ # @raise [APIError]
32
+ def result
33
+ @result ||= execute
34
+ end
35
+
36
+ protected
37
+
38
+ attr_writer :result
39
+ end
40
+ end
@@ -0,0 +1,31 @@
1
+ require 'atsd/queries/base_query'
2
+
3
+ module ATSD
4
+ # Class for building and executing Properties Query
5
+ # @see https://axibase.com/atsd/api/#properties:-query
6
+ class PropertiesQuery < BaseQuery
7
+ TO_MILLISECONDS_LAMBDA = ->(v) do
8
+ case v
9
+ when Time
10
+ v.to_i * 1_000
11
+ else
12
+ v.to_i
13
+ end
14
+ end
15
+
16
+ coerce_key :end_time, TO_MILLISECONDS_LAMBDA
17
+ coerce_key :start_time, TO_MILLISECONDS_LAMBDA
18
+
19
+ # @return [Array<Property>]
20
+ def result
21
+ super
22
+ end
23
+
24
+ # @return (see #result)
25
+ def execute
26
+ result = @client.properties_query to_request_hash
27
+ @result = result.map { |json| Property.new json }
28
+ end
29
+ end
30
+ end
31
+
@@ -0,0 +1,150 @@
1
+ require 'atsd/queries/base_query'
2
+
3
+ module ATSD
4
+ # Class for building and executing Series Query
5
+ # @see https://axibase.com/atsd/api/#series:-query
6
+ class SeriesQuery < BaseQuery
7
+
8
+ # @!method type(type)
9
+ # specifies source for underlying data
10
+ # @param [String] type see {Type} for possible values
11
+ # @return [self]
12
+
13
+ # `type` parameter possible values
14
+ module Type
15
+ HISTORY = 'HISTORY' # default
16
+ FORECAST = 'FORECAST'
17
+ FORECAST_DEVIATION = 'FORECAST_DEVIATION'
18
+ end
19
+
20
+ # `join.type` parameter possible values
21
+ module JoinType
22
+ COUNT = 'COUNT'
23
+ MIN = 'MIN'
24
+ MAX = 'MAX'
25
+ AVG = 'AVG'
26
+ SUM = 'SUM'
27
+ PERCENTILE_999 = 'PERCENTILE_999'
28
+ PERCENTILE_995 = 'PERCENTILE_995'
29
+ PERCENTILE_99 = 'PERCENTILE_99'
30
+ PERCENTILE_95 = 'PERCENTILE_95'
31
+ PERCENTILE_90 = 'PERCENTILE_90'
32
+ PERCENTILE_75 = 'PERCENTILE_75'
33
+ PERCENTILE_50 = 'PERCENTILE_50'
34
+ STANDARD_DEVIATION = 'STANDARD_DEVIATION'
35
+ end
36
+
37
+ # `join.interpolate` parameter possible values
38
+ module JoinInterpolate
39
+ STEP = 'STEP' # default
40
+ NONE = 'NONE'
41
+ LINEAR = 'LINEAR'
42
+ end
43
+
44
+ # `aggregate.type` parameter possible values
45
+ module AggregateType
46
+ DETAIL = 'DETAIL'
47
+ COUNT = 'COUNT'
48
+ MIN = 'MIN'
49
+ MAX = 'MAX'
50
+ AVG = 'AVG'
51
+ SUM = 'SUM'
52
+ PERCENTILE_999 = 'PERCENTILE_999'
53
+ PERCENTILE_995 = 'PERCENTILE_995'
54
+ PERCENTILE_99 = 'PERCENTILE_99'
55
+ PERCENTILE_95 = 'PERCENTILE_95'
56
+ PERCENTILE_90 = 'PERCENTILE_90'
57
+ PERCENTILE_75 = 'PERCENTILE_75'
58
+ PERCENTILE_50 = 'PERCENTILE_50'
59
+ STANDARD_DEVIATION = 'STANDARD_DEVIATION'
60
+ FIRST = 'FIRST'
61
+ LAST = 'LAST'
62
+ DELTA = 'DELTA'
63
+ WAVG = 'WAVG'
64
+ WTAVG = 'WTAVG'
65
+ THRESHOLD_COUNT = 'THRESHOLD_COUNT'
66
+ THRESHOLD_DURATION = 'THRESHOLD_DURATION'
67
+ THRESHOLD_PERCENT = 'THRESHOLD_PERCENT'
68
+ end
69
+
70
+ # `aggregate.interpolate` parameter possible values
71
+ module AggregateInterpolate
72
+ STEP = 'STEP'
73
+ NONE = 'NONE'
74
+ LINEAR = 'LINEAR'
75
+ end
76
+
77
+ # interval's unit possible values
78
+ module Interval
79
+ MILLISECOND = 'MILLISECOND'
80
+ SECOND = 'SECOND'
81
+ MINUTE = 'MINUTE'
82
+ HOUR = 'HOUR'
83
+ DAY = 'DAY'
84
+ WEEK = 'WEEK'
85
+ MONTH = 'MONTH'
86
+ QUARTER = 'QUARTER'
87
+ YEAR = 'YEAR'
88
+ end
89
+
90
+ TO_MILLISECONDS_LAMBDA = ->(v) do
91
+ case v
92
+ when Time
93
+ v.to_i * 1_000
94
+ else
95
+ v.to_i
96
+ end
97
+ end
98
+
99
+ coerce_key :end_time, TO_MILLISECONDS_LAMBDA
100
+ coerce_key :start_time, TO_MILLISECONDS_LAMBDA
101
+
102
+ # @return [Array<Series>]
103
+ def result
104
+ super
105
+ end
106
+
107
+ # Add tag to `tags` parameter
108
+ #
109
+ # @param [String] name tag name
110
+ # @param [String, Array<String>] values possible tag value of array of values.
111
+ # `*` and `?` wildcards allowed.
112
+ # @return [self]
113
+ def tag_add(name, values)
114
+ self[:tags][name] = Utils.ensure_array(values)
115
+ self
116
+ end
117
+
118
+ # Executes query and sets `result` attribute.
119
+ #
120
+ # @return (see #result)
121
+ def execute
122
+ result = @client.series_query to_request_hash
123
+ @result = result.map { |series_json| Series.new series_json }
124
+ end
125
+
126
+ # Executes multiple queries at the same time. Adds `request_id` parameter
127
+ # to each query if necessary. All queries will have a corresponding `series`
128
+ # attribute set.
129
+ #
130
+ # @param [SeriesQuery] others list of series query to execute with receiver
131
+ # @return [Array<Series>] all results
132
+ def execute_with(*others)
133
+ others ||= []
134
+ queries = [self] + others
135
+ queries_by_id = {}
136
+ if queries.count > 1
137
+ queries.each_with_index do |query, index|
138
+ query.request_id("query_#{index}") unless query.request_id
139
+ query.result = []
140
+ queries_by_id[query[:request_id]] = query
141
+ end
142
+ end
143
+
144
+ results = @client.series_query queries.map {|q| q.to_request_hash}
145
+ results.map! { |json| Series.new(json) }
146
+ results.each { |r| queries_by_id[r.request_id].result << r }
147
+ end
148
+ end
149
+ end
150
+