sumo-search 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +6 -0
- data/Gemfile +3 -0
- data/README.md +44 -0
- data/Rakefile +11 -0
- data/bin/sumo +68 -0
- data/lib/sumo.rb +28 -0
- data/lib/sumo/config.rb +55 -0
- data/lib/sumo/error.rb +18 -0
- data/lib/sumo/formatter.rb +21 -0
- data/lib/sumo/query_builder.rb +61 -0
- data/lib/sumo/version.rb +4 -0
- data/spec/fixtures/sumo_creds +1 -0
- data/spec/lib/sumo/config_spec.rb +130 -0
- data/spec/lib/sumo/formatter_spec.rb +51 -0
- data/spec/lib/sumo/query_builder_spec.rb +128 -0
- data/spec/lib/sumo_spec.rb +35 -0
- data/spec/spec_helper.rb +17 -0
- data/spec/support/vcr.rb +8 -0
- data/spec/vcr/Sumo/_search/when_the_credentials_can_be_found/and_the_query_is_valid/parses_the_response.yml +49 -0
- data/spec/vcr/Sumo/_search/when_the_credentials_can_be_found/but_the_query_is_invalid/raises_an_error.yml +43 -0
- data/spec/vcr/Sumo_QueryBuilder/integration/when_the_request_is_invalid/raises_an_error.yml +43 -0
- data/spec/vcr/Sumo_QueryBuilder/integration/when_the_request_is_valid/compiles_and_sends_the_query_to_the_server.yml +49 -0
- data/sumo-search.gemspec +26 -0
- metadata +192 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a8e3759e39a03e8a3fceef78e0bf775275c35901
|
4
|
+
data.tar.gz: 193be08aa43cc6aebe1281606d6e24034a10900b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f513e2bdb1b53414aba48a4838de8320713d7425a209dde25535e9803b6a97b0ca3ca6ed8e90136da45707e24c696d42debc9f66b8bf3e6801e656a6ee48c397
|
7
|
+
data.tar.gz: f883c5490d9810001a7159fb3d9467dd7e7da03bf4ee9ef67e2184e1f86a42b02a496aad2efbd9765d8d355972373ae1e1092571072df30ab24ad269595cb109
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# sumo-search
|
2
|
+
|
3
|
+
This library can be used to query Sumo Logic's search API.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
From the command line:
|
8
|
+
|
9
|
+
```shell
|
10
|
+
$ sudo gem install sumo-search
|
11
|
+
```
|
12
|
+
|
13
|
+
Installing this gem will install a script called `sumo`.
|
14
|
+
Note that this gem requires a Ruby version of at least 2.0.0.
|
15
|
+
|
16
|
+
## Configuration
|
17
|
+
|
18
|
+
To use this script, you must supply your Sumo Logic credentials.
|
19
|
+
The script can read credentials from one of two places; either an environment variable or a configuration file.
|
20
|
+
The environment variable is `SUMO_CREDS`, and the configuration file is `~/.sumo_creds`.
|
21
|
+
Whichever you choose, the credentials must be in the following format:
|
22
|
+
|
23
|
+
```
|
24
|
+
username@website:password
|
25
|
+
```
|
26
|
+
|
27
|
+
## Usage
|
28
|
+
|
29
|
+
```shell
|
30
|
+
$ sumo [*options/flags*]
|
31
|
+
```
|
32
|
+
|
33
|
+
This script wraps the [Sumo Search API](https://github.com/SumoLogic/sumo-api-doc/wiki/search-api).
|
34
|
+
The API only has one endpoint, `/api/v1/logs/search` which searches your logs.
|
35
|
+
|
36
|
+
| Command Line Option | Corresponding Query Parameter | Description |
|
37
|
+
|------------------------|-------------------------------|--------------------------------------|
|
38
|
+
| -q, --query QUERY | q | The query to search (required) |
|
39
|
+
| -f, --from START | from | Begin searching at START (ISO8601) |
|
40
|
+
| -t, --to END | to | Finish searching at END (ISO8601) |
|
41
|
+
| -z, --time-zone ZONE | tz | The timezone of the search |
|
42
|
+
| -c, --config-file FILE | - | Use the specified config file |
|
43
|
+
| -e, --extract-key KEY | - | Extract the given key from your logs |
|
44
|
+
| -h, --help | - | Display the help message |
|
data/Rakefile
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'lib'))
|
2
|
+
|
3
|
+
require 'rake'
|
4
|
+
require 'sumo'
|
5
|
+
require 'rspec/core/rake_task'
|
6
|
+
require 'cane/rake_task'
|
7
|
+
|
8
|
+
task :default => [:spec, :quality]
|
9
|
+
|
10
|
+
RSpec::Core::RakeTask.new(:spec) { |t| t.pattern = 'spec/**/*_spec.rb' }
|
11
|
+
Cane::RakeTask.new(:quality) { |t| t.canefile = '.cane' }
|
data/bin/sumo
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
require 'sumo'
|
4
|
+
require 'time'
|
5
|
+
require 'optparse'
|
6
|
+
|
7
|
+
include Sumo
|
8
|
+
|
9
|
+
options = {
|
10
|
+
:query_options => {},
|
11
|
+
:config_file => DEFAULT_CONFIG_FILE,
|
12
|
+
:key => nil
|
13
|
+
}
|
14
|
+
|
15
|
+
OptionParser.new do |opts|
|
16
|
+
opts.banner = 'Usage: sumo [flags/options]'
|
17
|
+
|
18
|
+
opts.separator ''
|
19
|
+
opts.separator 'Specific options:'
|
20
|
+
|
21
|
+
opts.on('-q', '--query QUERY', 'Search for QUERY (required)') do |q|
|
22
|
+
options[:query] = q
|
23
|
+
end
|
24
|
+
|
25
|
+
opts.on('-f', '--from START', 'Begin at the given ISO8601 time') do |f|
|
26
|
+
options[:query_options]['from'] = f
|
27
|
+
end
|
28
|
+
|
29
|
+
opts.on('-t', '--to END', 'Finish at the given ISO8601 time') do |t|
|
30
|
+
options[:query_options]['to'] = t
|
31
|
+
end
|
32
|
+
|
33
|
+
opts.on('-z', '--time-zone ZONE', 'Use the given time zone') do |tz|
|
34
|
+
options[:query_options]['tz'] = tz
|
35
|
+
end
|
36
|
+
|
37
|
+
opts.on('-c', '--config-file FILE',
|
38
|
+
"Use the given config file instead of #{DEFAULT_CONFIG_FILE}") do |f|
|
39
|
+
options[:config_file] = config_file
|
40
|
+
end
|
41
|
+
|
42
|
+
opts.on('-e', '--extract-key KEY', 'Extract the specified key') do |k|
|
43
|
+
options[:key] = k
|
44
|
+
end
|
45
|
+
|
46
|
+
opts.on('-h', '--help', 'Print this message') do
|
47
|
+
puts opts
|
48
|
+
exit 0
|
49
|
+
end
|
50
|
+
end.parse!
|
51
|
+
|
52
|
+
if __FILE__ == $0
|
53
|
+
if options[:query].nil?
|
54
|
+
puts 'Please specify a query'
|
55
|
+
exit 1
|
56
|
+
else
|
57
|
+
begin
|
58
|
+
puts search(options[:query],
|
59
|
+
options: options[:query_options],
|
60
|
+
key: options[:key],
|
61
|
+
config_file: options[:config_file])
|
62
|
+
exit 0
|
63
|
+
rescue StandardError => ex
|
64
|
+
puts ex.message
|
65
|
+
exit 1
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/lib/sumo.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'excon'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
# This module acts as a namespace, and contains a `.search` method which ties
|
6
|
+
# together functionality of its child modules.
|
7
|
+
module Sumo
|
8
|
+
autoload :Config, 'sumo/config'
|
9
|
+
autoload :Error, 'sumo/error'
|
10
|
+
autoload :Formatter, 'sumo/formatter'
|
11
|
+
autoload :QueryBuilder, 'sumo/query_builder'
|
12
|
+
|
13
|
+
# Unless otherwise specified in the initializer, the configuration file
|
14
|
+
# defaults to '~/.sumo_creds'.
|
15
|
+
DEFAULT_CONFIG_FILE = File.expand_path('~/.sumo_creds').freeze
|
16
|
+
|
17
|
+
# Search the given query. If any options are supplied, those are sent to the
|
18
|
+
# QueryBuilder. If the key is supplied, that key will be attempted to be
|
19
|
+
# extracted from the JSON response. If a config_file is supplied, that will
|
20
|
+
# be read instead of the default.
|
21
|
+
def search(query, options: {}, key: nil, config_file: DEFAULT_CONFIG_FILE)
|
22
|
+
creds = Config.new(config_file).load_config!
|
23
|
+
response = QueryBuilder.new(creds, options.merge('q' => query)).execute
|
24
|
+
formatted = Formatter.format_json(response)
|
25
|
+
key.nil? ? formatted : Formatter.extract_key(key, formatted)
|
26
|
+
end
|
27
|
+
module_function :search
|
28
|
+
end
|
data/lib/sumo/config.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
# This class contains the logic to find the user's credentials in either an
|
2
|
+
# environment variable or a configuration file. If both exist and a
|
3
|
+
# configuration file has not been specified, the environment variable is
|
4
|
+
# preferred. If both exist and a config file has been specified, the config
|
5
|
+
# file is preferred.
|
6
|
+
#
|
7
|
+
# The environment varibale is called 'SUMO_CREDS'; the default configuration
|
8
|
+
# file is '~/.sumo_creds'.
|
9
|
+
class Sumo::Config
|
10
|
+
include Sumo::Error
|
11
|
+
|
12
|
+
attr_reader :config_file
|
13
|
+
|
14
|
+
# Set and freeze the @config_file instance variable.
|
15
|
+
def initialize(config_file = Sumo::DEFAULT_CONFIG_FILE)
|
16
|
+
if config_file.is_a?(String)
|
17
|
+
@config_file = File.expand_path(config_file).freeze
|
18
|
+
else
|
19
|
+
raise TypeError, "Expected a String, got: #{config_file}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Test if an alternate file has been specified.
|
24
|
+
def file_specified?
|
25
|
+
config_file != Sumo::DEFAULT_CONFIG_FILE
|
26
|
+
end
|
27
|
+
|
28
|
+
# Memoize the credentials from the environment.
|
29
|
+
def env_creds
|
30
|
+
@env_creds ||= ENV['SUMO_CREDS']
|
31
|
+
end
|
32
|
+
|
33
|
+
# Memoize the credentials from the configuration file.
|
34
|
+
def file_creds
|
35
|
+
@file_creds ||= File.read(config_file).chomp if File.exists?(config_file)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Load the credentials.
|
39
|
+
def load_config
|
40
|
+
if file_specified?
|
41
|
+
file_creds || env_creds
|
42
|
+
else
|
43
|
+
env_creds || file_creds
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Load the credentials, raising an error if none are specified.
|
48
|
+
def load_config!
|
49
|
+
if (creds = load_config).nil?
|
50
|
+
raise NoCredsFound, "No credentials were found, set ENV['SUMO_CREDS']."
|
51
|
+
else
|
52
|
+
creds
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/sumo/error.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# This module acts as a namespace for all errors thrown in the gem.
|
2
|
+
module Sumo::Error
|
3
|
+
# This error is never raised, but instead used to catch all other errors that
|
4
|
+
# may be raised.
|
5
|
+
class BaseError < StandardError; end
|
6
|
+
|
7
|
+
# Raised when an unexpected type is passed to a method.
|
8
|
+
class TypeError < BaseError; end
|
9
|
+
|
10
|
+
# This error is raised when no credentials can be found.
|
11
|
+
class NoCredsFound < BaseError; end
|
12
|
+
|
13
|
+
# This error is raised when there is an error on a request.
|
14
|
+
class RequestError < BaseError; end
|
15
|
+
|
16
|
+
# This error is raised when a parsing error occurs.
|
17
|
+
class ParseError < BaseError; end
|
18
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# This module contains formatting functions to help make text more readable.
|
2
|
+
module Sumo::Formatter
|
3
|
+
include Sumo::Error
|
4
|
+
|
5
|
+
# Given a sumo response (string)
|
6
|
+
def format_json(response)
|
7
|
+
JSON.parse(response)
|
8
|
+
.sort_by { |hash| hash['_messagetime'] }
|
9
|
+
.map { |hash| hash['_raw'] }
|
10
|
+
rescue
|
11
|
+
raise ParseError, 'Could not parse the response.'
|
12
|
+
end
|
13
|
+
|
14
|
+
def extract_key(key, logs)
|
15
|
+
logs.map { |log| JSON.parse(log)[key] }
|
16
|
+
rescue
|
17
|
+
raise ParseError, "Error extracting the #{key} from the hash."
|
18
|
+
end
|
19
|
+
|
20
|
+
module_function :format_json, :extract_key
|
21
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# This class can be used to build and execute queries. For example, the
|
2
|
+
# following code builds a query for 'error' in the last 15 minutes.
|
3
|
+
#
|
4
|
+
# q = Sumo::QueryBuilder.new('user@example.com:pass')
|
5
|
+
# .query('error')
|
6
|
+
# .from('-15m')
|
7
|
+
# .to('now')
|
8
|
+
#
|
9
|
+
# Note that this class is immutable.
|
10
|
+
class Sumo::QueryBuilder
|
11
|
+
include Sumo::Error
|
12
|
+
|
13
|
+
attr_reader :creds, :opts
|
14
|
+
|
15
|
+
# Create a new QueryBuilder with the given credentials and query.
|
16
|
+
def initialize(creds, opts = {})
|
17
|
+
if creds.is_a?(String) && opts.is_a?(Hash)
|
18
|
+
@creds = creds.freeze
|
19
|
+
@opts = opts.freeze
|
20
|
+
else
|
21
|
+
raise TypeError, "Invalid initialization parameters to QueryBuilder."
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Metaprogram the #query, #to, #from, #time_zone, and #format methods. Each
|
26
|
+
# of these methods accepts one argument, and returns a new 'QueryBuilder' that
|
27
|
+
# has the specified query parameter key set.
|
28
|
+
def self.builder(name, key)
|
29
|
+
klass = self
|
30
|
+
define_method(name) { |arg| klass.new(creds, opts.merge(key => arg)) }
|
31
|
+
end
|
32
|
+
private_class_method :builder
|
33
|
+
|
34
|
+
builder :to, 'to'
|
35
|
+
builder :from, 'from'
|
36
|
+
builder :query, 'q'
|
37
|
+
builder :format, 'format'
|
38
|
+
builder :time_zone, 'tz'
|
39
|
+
|
40
|
+
# Send the API request to Sumo Logic.
|
41
|
+
def execute
|
42
|
+
encoded = Base64.encode64(creds)
|
43
|
+
resp = connection.get(:path => '/api/v1/logs/search',
|
44
|
+
:query => opts,
|
45
|
+
:headers => { 'Authorization' => "Basic #{encoded}" })
|
46
|
+
if resp.status >= 400
|
47
|
+
if resp.body.nil? || resp.body.empty?
|
48
|
+
raise RequestError, "An error occurred sending your request."
|
49
|
+
else
|
50
|
+
raise RequestError, JSON.parse(resp.body)['message']
|
51
|
+
end
|
52
|
+
end
|
53
|
+
resp.body
|
54
|
+
end
|
55
|
+
|
56
|
+
# A memoized HTTP connection.
|
57
|
+
def connection
|
58
|
+
@connection ||= Excon.new('https://api.sumologic.com')
|
59
|
+
end
|
60
|
+
private :connection
|
61
|
+
end
|
data/lib/sumo/version.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
fake_user@fake_site:fake_pass
|
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Sumo::Config do
|
4
|
+
describe '.new' do
|
5
|
+
subject { Sumo::Config }
|
6
|
+
|
7
|
+
context 'when no argument is given' do
|
8
|
+
it 'sets @config_file to its default value' do
|
9
|
+
subject.new.config_file.should == Sumo::DEFAULT_CONFIG_FILE
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'when an argument is given' do
|
14
|
+
context 'when it is not a String' do
|
15
|
+
it 'raises an error' do
|
16
|
+
expect { subject.new(nil) }.to raise_error(Sumo::Error::TypeError)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'when it is a String' do
|
21
|
+
let(:file) { '/dev/null' }
|
22
|
+
|
23
|
+
it 'sets @config_file to the argument' do
|
24
|
+
subject.new(file).config_file.should == file
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '#file_specified?' do
|
31
|
+
context 'when @config_file is the default' do
|
32
|
+
it 'returns false' do
|
33
|
+
subject.file_specified?.should be_false
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'when @config_file is not the default' do
|
38
|
+
subject { Sumo::Config.new('my-config-file') }
|
39
|
+
|
40
|
+
it 'returns true' do
|
41
|
+
subject.file_specified?.should be_true
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe '#env_creds' do
|
47
|
+
before { ENV['SUMO_CREDS'] = creds }
|
48
|
+
|
49
|
+
context 'when the environment variable is not set' do
|
50
|
+
let(:creds) { nil }
|
51
|
+
|
52
|
+
it 'returns nil' do
|
53
|
+
subject.env_creds.should be_nil
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context 'when the environment variable is set' do
|
58
|
+
let(:creds) { 'alpha' }
|
59
|
+
|
60
|
+
it 'returns that variable' do
|
61
|
+
subject.env_creds.should == creds
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe '#file_creds' do
|
67
|
+
subject { Sumo::Config.new(file) }
|
68
|
+
|
69
|
+
context 'when the @config_file does not exist' do
|
70
|
+
let(:file) { 'not a file' }
|
71
|
+
|
72
|
+
it 'returns nil' do
|
73
|
+
subject.file_creds.should be_nil
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context 'when @config_file does exist' do
|
78
|
+
let(:file) { 'spec/fixtures/sumo_creds' }
|
79
|
+
|
80
|
+
it 'returns its contents' do
|
81
|
+
subject.file_creds.should == 'fake_user@fake_site:fake_pass'
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe '#load_config' do
|
87
|
+
before do
|
88
|
+
subject.stub(:file_specified?).and_return(specified)
|
89
|
+
subject.stub(:file_creds).and_return('alpha')
|
90
|
+
subject.stub(:env_creds).and_return('beta')
|
91
|
+
end
|
92
|
+
|
93
|
+
context 'when an alternate file is specifed' do
|
94
|
+
let(:specified) { true }
|
95
|
+
|
96
|
+
it 'attempts to read the file first' do
|
97
|
+
subject.load_config.should == 'alpha'
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
context 'when an alternate file is not specified' do
|
102
|
+
let(:specified) { false }
|
103
|
+
|
104
|
+
it 'attempts to read the environment variable first' do
|
105
|
+
subject.load_config.should == 'beta'
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
describe 'load_config!' do
|
111
|
+
before { subject.stub(:load_config).and_return(config) }
|
112
|
+
|
113
|
+
context 'when #load_config returns nil' do
|
114
|
+
let(:config) { nil }
|
115
|
+
|
116
|
+
it 'raises an error' do
|
117
|
+
expect { subject.load_config! }
|
118
|
+
.to raise_error(Sumo::Error::NoCredsFound)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
context 'when #load_config returns a value' do
|
123
|
+
let(:config) { 'configuration' }
|
124
|
+
|
125
|
+
it 'returns that value' do
|
126
|
+
subject.load_config!.should == config
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Sumo::Formatter do
|
4
|
+
subject { Sumo::Formatter }
|
5
|
+
|
6
|
+
describe '.format_json' do
|
7
|
+
context 'when the input cannot be parsed' do
|
8
|
+
it 'raises an error' do
|
9
|
+
expect { subject.format_json('cannot parse this') }
|
10
|
+
.to raise_error(Sumo::Error::ParseError)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
context 'when the input can be parsed' do
|
15
|
+
let(:input) {
|
16
|
+
[
|
17
|
+
{ '_messagetime' => 2, '_raw' => 'world' },
|
18
|
+
{ '_messagetime' => 1, '_raw' => 'hello' }
|
19
|
+
].to_json
|
20
|
+
}
|
21
|
+
|
22
|
+
it 'sorts the input by the _messagetime, returning a String' do
|
23
|
+
subject.format_json(input).should == %w(hello world)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '.extract_key' do
|
29
|
+
context 'when at least one element of the Array cannot be parsed' do
|
30
|
+
let(:input) { [ { :a => 1 }.to_json, 'qwerty'] }
|
31
|
+
|
32
|
+
it 'raises an error' do
|
33
|
+
expect { subject.extract_key('anything', input) }
|
34
|
+
.to raise_error(Sumo::Error::ParseError)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context 'when the input is an Array of parseable values' do
|
39
|
+
let(:input) {
|
40
|
+
[
|
41
|
+
{ 'message' => 'rats', 'time' => 2 }.to_json,
|
42
|
+
{ 'message' => 'cats', 'time' => 1 }.to_json
|
43
|
+
]
|
44
|
+
}
|
45
|
+
|
46
|
+
it 'returns an Array with the specified key extracted' do
|
47
|
+
subject.extract_key('message', input).should == %w(rats cats)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Sumo::QueryBuilder do
|
4
|
+
subject { Sumo::QueryBuilder.new('fake-creds') }
|
5
|
+
|
6
|
+
describe '.new' do
|
7
|
+
context 'when the correct types of arguments are supplied' do
|
8
|
+
subject { Sumo::QueryBuilder.new('auth', 'q' => 'error') }
|
9
|
+
|
10
|
+
it 'creates a new QueryBuilder' do
|
11
|
+
subject.creds.should == 'auth'
|
12
|
+
subject.opts.should == { 'q' => 'error' }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'when invalid arguments are supplied' do
|
17
|
+
it 'raises an error' do
|
18
|
+
expect { Sumo::QueryBuilder.new(:symbol, 1) }
|
19
|
+
.to raise_error(Sumo::Error::TypeError)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#query' do
|
25
|
+
it 'adds a query to the request' do
|
26
|
+
subject.query('alpha').opts.should == { 'q' => 'alpha' }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '#to' do
|
31
|
+
it 'adds an end time to the request' do
|
32
|
+
subject.to('now').opts.should == { 'to' => 'now' }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '#from' do
|
37
|
+
it 'adds an end time to the request' do
|
38
|
+
subject.from('-15m').opts.should == { 'from' => '-15m' }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '#time_zone' do
|
43
|
+
it 'adds a time zone to the request' do
|
44
|
+
subject.time_zone('UTC').opts.should == { 'tz' => 'UTC' }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe '#format' do
|
49
|
+
it 'adds a format to the request' do
|
50
|
+
subject.format('JSON').opts.should == { 'format' => 'JSON' }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe '#execute' do
|
55
|
+
let(:creds) { 'user@example.com:pass' }
|
56
|
+
let(:encoded) { Base64.encode64(creds) }
|
57
|
+
let(:conn) { double(:connection) }
|
58
|
+
let(:query) {
|
59
|
+
{
|
60
|
+
'q' => 'production',
|
61
|
+
'from' => '-30m',
|
62
|
+
'to' => '-15m',
|
63
|
+
'tz' => 'UTC',
|
64
|
+
'format' => 'json'
|
65
|
+
}
|
66
|
+
}
|
67
|
+
subject {
|
68
|
+
Sumo::QueryBuilder.new(creds).query('production')
|
69
|
+
.from('-30m')
|
70
|
+
.to('-15m')
|
71
|
+
.time_zone('UTC')
|
72
|
+
.format('json')
|
73
|
+
}
|
74
|
+
|
75
|
+
before do
|
76
|
+
subject.stub(:connection).and_return(conn)
|
77
|
+
conn.stub(:get)
|
78
|
+
.with(:path => '/api/v1/logs/search',
|
79
|
+
:query => query,
|
80
|
+
:headers => { 'Authorization' => "Basic #{encoded}" })
|
81
|
+
.and_return(resp)
|
82
|
+
end
|
83
|
+
|
84
|
+
context 'when the server responds' do
|
85
|
+
let(:resp) { double(:response, :body => '{}', :status => 200) }
|
86
|
+
|
87
|
+
it 'parses the response' do
|
88
|
+
subject.execute.should == '{}'
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
context 'when the server does not respond' do
|
93
|
+
let(:resp) { double(:response, :body => nil, :status => 504) }
|
94
|
+
|
95
|
+
it 'raises an error' do
|
96
|
+
expect { subject.execute }.to raise_error(Sumo::Error::RequestError)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
describe 'integration', :vcr do
|
102
|
+
# WARNING: If you are going to change this VCR, modify the credentials so
|
103
|
+
# that they are valid, record the VCR, remove the credentials from it, and
|
104
|
+
# change the below variable back to its original form.
|
105
|
+
let(:creds) { 'aladdin@swipely.com:open sesame' }
|
106
|
+
|
107
|
+
context 'when the request is valid' do
|
108
|
+
let(:result) { JSON.parse(subject.execute) }
|
109
|
+
subject { Sumo::QueryBuilder.new(creds).query('nginx') }
|
110
|
+
|
111
|
+
it 'compiles and sends the query to the server' do
|
112
|
+
result.length.should_not be_zero
|
113
|
+
result.should be_all { |hash|
|
114
|
+
%w(_sourcecategory _raw _sourcehost _receipttime _sourcename
|
115
|
+
_messagetime).all? { |key| hash.has_key?(key) }
|
116
|
+
}
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
context 'when the request is invalid' do
|
121
|
+
subject { Sumo::QueryBuilder.new(creds).query('nginx').to('nonsense') }
|
122
|
+
|
123
|
+
it 'raises an error' do
|
124
|
+
expect { subject.execute }.to raise_error(Sumo::Error::RequestError)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Sumo do
|
4
|
+
subject { Sumo }
|
5
|
+
|
6
|
+
it { should be_a Module }
|
7
|
+
|
8
|
+
describe '.search' do
|
9
|
+
context 'when the credentials cannot be found' do
|
10
|
+
before { ENV['SUMO_CREDS'] = nil }
|
11
|
+
|
12
|
+
it 'raises an error' do
|
13
|
+
expect { subject.search('anything') }
|
14
|
+
.to raise_error(Sumo::Error::NoCredsFound)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'when the credentials can be found' do
|
19
|
+
before { ENV['SUMO_CREDS'] = 'aladdin@swipely.com:open sesame' }
|
20
|
+
|
21
|
+
context 'but the query is invalid' do
|
22
|
+
it 'raises an error', :vcr do
|
23
|
+
expect { subject.search('Rails', options: { 'from' => 'never' }) }
|
24
|
+
.to raise_error(Sumo::Error::RequestError)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'and the query is valid' do
|
29
|
+
it 'parses the response', :vcr do
|
30
|
+
subject.search('rails').each { |str| str.should_not match(/_raw/) }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
|
3
|
+
require 'rspec'
|
4
|
+
require 'simplecov'
|
5
|
+
require 'sumo'
|
6
|
+
|
7
|
+
SimpleCov.start
|
8
|
+
|
9
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
|
10
|
+
|
11
|
+
RSpec.configure do |config|
|
12
|
+
config.mock_with :rspec
|
13
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
14
|
+
config.color_enabled = true
|
15
|
+
config.formatter = :documentation
|
16
|
+
config.tty = true
|
17
|
+
end
|
data/spec/support/vcr.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: https://api.sumologic.com/api/v1/logs/search?q=rails
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
User-Agent:
|
11
|
+
- excon/0.31.0
|
12
|
+
Authorization:
|
13
|
+
- |
|
14
|
+
Basic YWxhZGRpbkBzd2lwZWx5LmNvbTpvcGVuIHNlc2FtZQ==
|
15
|
+
response:
|
16
|
+
status:
|
17
|
+
code: 200
|
18
|
+
message:
|
19
|
+
headers:
|
20
|
+
Cache-control:
|
21
|
+
- no-cache="set-cookie"
|
22
|
+
Content-Type:
|
23
|
+
- application/json; charset=ISO-8859-1
|
24
|
+
Date:
|
25
|
+
- Tue, 07 Jan 2014 18:32:15 GMT
|
26
|
+
Expires:
|
27
|
+
- Thu, 01-Jan-1970 00:00:00 GMT
|
28
|
+
Set-Cookie:
|
29
|
+
- JSESSIONID=p8dhtx823tvbbxs578ipt3mn;Path=/api, AWSELB=D5C1176F0665104977B708B0B48E6FFEC09E311CD1473A91FD800B5F0A2443FAC23088ACED0D807762BB3B769CEAADA14ED5C9BC9C86BA1749DC556A5C1B04944D462E0D92;PATH=/
|
30
|
+
Content-Length:
|
31
|
+
- '1052'
|
32
|
+
Connection:
|
33
|
+
- Close
|
34
|
+
body:
|
35
|
+
encoding: UTF-8
|
36
|
+
string: |-
|
37
|
+
[
|
38
|
+
{
|
39
|
+
"_sourcecategory" : "all",
|
40
|
+
"_raw" : "{\"@source\":\"syslog://\",\"@tags\":[\"railslog\"],\"@fields\":{\"facility\":\"local0\",\"severity\":[\"notice\",\"D\"],\"program\":\"swipely-ledger-delayed-1\",\"processid\":\"-\",\"sslsubject\":[\"/C=US/ST=Rhode Island/O=Swipely/OU=Engineering/CN=s-ledger\",\"/C=US/ST=Rhode Island/O=Swipely/OU=Engineering/CN=s-ledger\"],\"host\":[\"s-ledger\"],\"timestamp\":[\"140107-18:26:50.605\"],\"thread_id\":[\"8_15364200\"],\"msg\":[\"** [Honeybadger] Environment Info: [Ruby: 1.9.3] [Rails: 3.2.11] [Env: staging]\"]},\"@timestamp\":\"2014-01-07T18:26:54.159449+00:00\",\"@source_host\":\"ip-10-244-158-144\",\"@message\":\"Jan 7 18:26:54 swipely-ledger-delayed-1:D 140107-18:26:50.605 8_15364200: ** [Honeybadger] Environment Info: [Ruby: 1.9.3] [Rails: 3.2.11] [Env: staging]\",\"@type\":\"syslog\"}",
|
41
|
+
"_sourcehost" : "logs.example.com",
|
42
|
+
"_receipttime" : 1389119226829,
|
43
|
+
"_sourcename" : "/media/ephemeral0/logs/swipely/s-ledger-2014-01-07.log",
|
44
|
+
"_messagetime" : 1389119214159
|
45
|
+
}
|
46
|
+
]
|
47
|
+
http_version:
|
48
|
+
recorded_at: Tue, 07 Jan 2014 18:32:19 GMT
|
49
|
+
recorded_with: VCR 2.8.0
|
@@ -0,0 +1,43 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: https://api.sumologic.com/api/v1/logs/search?from=never&q=Rails
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
User-Agent:
|
11
|
+
- excon/0.31.0
|
12
|
+
Authorization:
|
13
|
+
- |
|
14
|
+
Basic YWxhZGRpbkBzd2lwZWx5LmNvbTpvcGVuIHNlc2FtZQ==
|
15
|
+
response:
|
16
|
+
status:
|
17
|
+
code: 400
|
18
|
+
message:
|
19
|
+
headers:
|
20
|
+
Cache-control:
|
21
|
+
- no-cache="set-cookie"
|
22
|
+
Content-Type:
|
23
|
+
- application/json; charset=ISO-8859-1
|
24
|
+
Date:
|
25
|
+
- Tue, 07 Jan 2014 18:30:35 GMT
|
26
|
+
Set-Cookie:
|
27
|
+
- AWSELB=D5C1176F0665104977B708B0B48E6FFEC09E311CD1473A91FD800B5F0A2443FAC23088ACED0D807762BB3B769CEAADA14ED5C9BC9C86BA1749DC556A5C1B04944D462E0D92;PATH=/
|
28
|
+
Content-Length:
|
29
|
+
- '154'
|
30
|
+
Connection:
|
31
|
+
- Close
|
32
|
+
body:
|
33
|
+
encoding: UTF-8
|
34
|
+
string: |-
|
35
|
+
{
|
36
|
+
"status" : 400,
|
37
|
+
"id" : "9IJJK-HJQ9G-H09X3",
|
38
|
+
"code" : "search.invalid.timestamp.from",
|
39
|
+
"message" : "The 'from' field contains an invalid time."
|
40
|
+
}
|
41
|
+
http_version:
|
42
|
+
recorded_at: Tue, 07 Jan 2014 18:30:39 GMT
|
43
|
+
recorded_with: VCR 2.8.0
|
@@ -0,0 +1,43 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: https://api.sumologic.com/api/v1/logs/search?q=nginx&to=nonsense
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
User-Agent:
|
11
|
+
- excon/0.31.0
|
12
|
+
Authorization:
|
13
|
+
- |
|
14
|
+
Basic YWxhZGRpbkBzd2lwZWx5LmNvbTpvcGVuIHNlc2FtZQ==
|
15
|
+
response:
|
16
|
+
status:
|
17
|
+
code: 400
|
18
|
+
message:
|
19
|
+
headers:
|
20
|
+
Cache-control:
|
21
|
+
- no-cache="set-cookie"
|
22
|
+
Content-Type:
|
23
|
+
- application/json; charset=ISO-8859-1
|
24
|
+
Date:
|
25
|
+
- Tue, 07 Jan 2014 15:52:16 GMT
|
26
|
+
Set-Cookie:
|
27
|
+
- AWSELB=D5C1176F0665104977B708B0B48E6FFEC09E311CD10EE26CC6F86FEAE76E4BBB3D728D261CADA16D6256329826144A742E70346FDF2C8592D1FAF5BF2ABFF69B2FDD5A38FA;PATH=/
|
28
|
+
Content-Length:
|
29
|
+
- '150'
|
30
|
+
Connection:
|
31
|
+
- Close
|
32
|
+
body:
|
33
|
+
encoding: UTF-8
|
34
|
+
string: |-
|
35
|
+
{
|
36
|
+
"status" : 400,
|
37
|
+
"id" : "ELTN2-7BBZF-ZCZQ8",
|
38
|
+
"code" : "search.invalid.timestamp.to",
|
39
|
+
"message" : "The 'to' field contains an invalid time."
|
40
|
+
}
|
41
|
+
http_version:
|
42
|
+
recorded_at: Tue, 07 Jan 2014 15:52:18 GMT
|
43
|
+
recorded_with: VCR 2.8.0
|
@@ -0,0 +1,49 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: https://api.sumologic.com/api/v1/logs/search?q=nginx
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
User-Agent:
|
11
|
+
- excon/0.31.0
|
12
|
+
Authorization:
|
13
|
+
- |
|
14
|
+
Basic YWxhZGRpbkBzd2lwZWx5LmNvbTpvcGVuIHNlc2FtZQ==
|
15
|
+
response:
|
16
|
+
status:
|
17
|
+
code: 200
|
18
|
+
message:
|
19
|
+
headers:
|
20
|
+
Cache-control:
|
21
|
+
- no-cache="set-cookie"
|
22
|
+
Content-Type:
|
23
|
+
- application/json; charset=ISO-8859-1
|
24
|
+
Date:
|
25
|
+
- Tue, 07 Jan 2014 15:49:55 GMT
|
26
|
+
Expires:
|
27
|
+
- Thu, 01-Jan-1970 00:00:00 GMT
|
28
|
+
Set-Cookie:
|
29
|
+
- JSESSIONID=1dybd1qub96gu13g5a9qh9dyqc;Path=/api, AWSELB=D5C1176F0665104977B708B0B48E6FFEC09E311CD1473A91FD800B5F0A2443FAC23088ACEDDBBB8C99D44C2403D82B59D74368F2FCC1FFB75ECAF9255D4A40FE87F45B4364;PATH=/
|
30
|
+
transfer-encoding:
|
31
|
+
- ''
|
32
|
+
Connection:
|
33
|
+
- Close
|
34
|
+
body:
|
35
|
+
encoding: UTF-8
|
36
|
+
string: |-
|
37
|
+
[
|
38
|
+
{
|
39
|
+
"_sourcecategory" : "all",
|
40
|
+
"_raw" : "nonsense result",
|
41
|
+
"_sourcehost" : "ls01.logs.swipely.com",
|
42
|
+
"_receipttime" : 1389109434302,
|
43
|
+
"_sourcename" : "/media/ephemeral0/logs/swipely/p-ledger-lb-2014-01-07.log",
|
44
|
+
"_messagetime" : 1389109430911
|
45
|
+
}
|
46
|
+
]
|
47
|
+
http_version:
|
48
|
+
recorded_at: Tue, 07 Jan 2014 15:49:59 GMT
|
49
|
+
recorded_with: VCR 2.8.0
|
data/sumo-search.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/sumo/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Swipely, Inc."]
|
6
|
+
gem.email = %w{tomhulihan@swipely.com}
|
7
|
+
gem.description = %q{A CLI for querying the Sumo Logic search API}
|
8
|
+
gem.summary = %q{A CLI for querying the Sumo Logic search API}
|
9
|
+
gem.homepage = 'https://github.com/swipely/sumo-search'
|
10
|
+
gem.license = 'MIT'
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = 'sumo-search'
|
15
|
+
gem.require_paths = %w{lib}
|
16
|
+
gem.version = Sumo::VERSION
|
17
|
+
gem.required_ruby_version = '>= 2.0.0'
|
18
|
+
gem.add_dependency 'excon', '>= 0.28'
|
19
|
+
gem.add_dependency 'json'
|
20
|
+
gem.add_development_dependency 'rake'
|
21
|
+
gem.add_development_dependency 'rspec'
|
22
|
+
gem.add_development_dependency 'cane'
|
23
|
+
gem.add_development_dependency 'pry'
|
24
|
+
gem.add_development_dependency 'vcr', '>= 2.7.0'
|
25
|
+
gem.add_development_dependency 'simplecov'
|
26
|
+
end
|
metadata
ADDED
@@ -0,0 +1,192 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sumo-search
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Swipely, Inc.
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-01-08 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: excon
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.28'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.28'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: json
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
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
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: cane
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: pry
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: vcr
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 2.7.0
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 2.7.0
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: simplecov
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - '>='
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
description: A CLI for querying the Sumo Logic search API
|
126
|
+
email:
|
127
|
+
- tomhulihan@swipely.com
|
128
|
+
executables:
|
129
|
+
- sumo
|
130
|
+
extensions: []
|
131
|
+
extra_rdoc_files: []
|
132
|
+
files:
|
133
|
+
- .gitignore
|
134
|
+
- Gemfile
|
135
|
+
- README.md
|
136
|
+
- Rakefile
|
137
|
+
- bin/sumo
|
138
|
+
- lib/sumo.rb
|
139
|
+
- lib/sumo/config.rb
|
140
|
+
- lib/sumo/error.rb
|
141
|
+
- lib/sumo/formatter.rb
|
142
|
+
- lib/sumo/query_builder.rb
|
143
|
+
- lib/sumo/version.rb
|
144
|
+
- spec/fixtures/sumo_creds
|
145
|
+
- spec/lib/sumo/config_spec.rb
|
146
|
+
- spec/lib/sumo/formatter_spec.rb
|
147
|
+
- spec/lib/sumo/query_builder_spec.rb
|
148
|
+
- spec/lib/sumo_spec.rb
|
149
|
+
- spec/spec_helper.rb
|
150
|
+
- spec/support/vcr.rb
|
151
|
+
- spec/vcr/Sumo/_search/when_the_credentials_can_be_found/and_the_query_is_valid/parses_the_response.yml
|
152
|
+
- spec/vcr/Sumo/_search/when_the_credentials_can_be_found/but_the_query_is_invalid/raises_an_error.yml
|
153
|
+
- spec/vcr/Sumo_QueryBuilder/integration/when_the_request_is_invalid/raises_an_error.yml
|
154
|
+
- spec/vcr/Sumo_QueryBuilder/integration/when_the_request_is_valid/compiles_and_sends_the_query_to_the_server.yml
|
155
|
+
- sumo-search.gemspec
|
156
|
+
homepage: https://github.com/swipely/sumo-search
|
157
|
+
licenses:
|
158
|
+
- MIT
|
159
|
+
metadata: {}
|
160
|
+
post_install_message:
|
161
|
+
rdoc_options: []
|
162
|
+
require_paths:
|
163
|
+
- lib
|
164
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
165
|
+
requirements:
|
166
|
+
- - '>='
|
167
|
+
- !ruby/object:Gem::Version
|
168
|
+
version: 2.0.0
|
169
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - '>='
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
requirements: []
|
175
|
+
rubyforge_project:
|
176
|
+
rubygems_version: 2.0.7
|
177
|
+
signing_key:
|
178
|
+
specification_version: 4
|
179
|
+
summary: A CLI for querying the Sumo Logic search API
|
180
|
+
test_files:
|
181
|
+
- spec/fixtures/sumo_creds
|
182
|
+
- spec/lib/sumo/config_spec.rb
|
183
|
+
- spec/lib/sumo/formatter_spec.rb
|
184
|
+
- spec/lib/sumo/query_builder_spec.rb
|
185
|
+
- spec/lib/sumo_spec.rb
|
186
|
+
- spec/spec_helper.rb
|
187
|
+
- spec/support/vcr.rb
|
188
|
+
- spec/vcr/Sumo/_search/when_the_credentials_can_be_found/and_the_query_is_valid/parses_the_response.yml
|
189
|
+
- spec/vcr/Sumo/_search/when_the_credentials_can_be_found/but_the_query_is_invalid/raises_an_error.yml
|
190
|
+
- spec/vcr/Sumo_QueryBuilder/integration/when_the_request_is_invalid/raises_an_error.yml
|
191
|
+
- spec/vcr/Sumo_QueryBuilder/integration/when_the_request_is_valid/compiles_and_sends_the_query_to_the_server.yml
|
192
|
+
has_rdoc:
|