sentry-api 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 +12 -0
- data/.travis.yml +7 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +24 -0
- data/README.md +82 -0
- data/Rakefile +2 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/sentry-api.rb +45 -0
- data/lib/sentry-api/api.rb +17 -0
- data/lib/sentry-api/client.rb +10 -0
- data/lib/sentry-api/client/events.rb +91 -0
- data/lib/sentry-api/client/organizations.rb +76 -0
- data/lib/sentry-api/client/projects.rb +185 -0
- data/lib/sentry-api/client/teams.rb +24 -0
- data/lib/sentry-api/configuration.rb +39 -0
- data/lib/sentry-api/error.rb +98 -0
- data/lib/sentry-api/objectified_hash.rb +35 -0
- data/lib/sentry-api/page_links.rb +33 -0
- data/lib/sentry-api/paginated_response.rb +75 -0
- data/lib/sentry-api/request.rb +133 -0
- data/lib/sentry-api/version.rb +3 -0
- data/sentry-api.gemspec +35 -0
- metadata +164 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 63d5a7432a7a86a1c8077fb4d702701632640a76
|
4
|
+
data.tar.gz: 7aee07004fab408b8a662e3ed394090408ecbdda
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ada67008ed2eaa102a87ff5b2e3bd370aaf57f408371261149984e8f4f220319c68c28180ca691958b2cba5911d67fa25366b1f8bce132b480fe7a9f1b9a2517
|
7
|
+
data.tar.gz: 31879008fa89a01e1601f19dc0fc0754b5677a5b726bfefcf745efa20815b7b53a946be315baf090c7d2661e0ebe5e9835e04ea1c9c3e741c4130fa96fb41de4
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
Copyright (c) 2015-2016 Thierry Xing <thierry.xing@gmail.com>
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
5
|
+
modification, are permitted provided that the following conditions are met:
|
6
|
+
|
7
|
+
1. Redistributions of source code must retain the above copyright notice,
|
8
|
+
this list of conditions and the following disclaimer.
|
9
|
+
|
10
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
this list of conditions and the following disclaimer in the documentation
|
12
|
+
and/or other materials provided with the distribution.
|
13
|
+
|
14
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
15
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
16
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
17
|
+
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
18
|
+
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
19
|
+
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
20
|
+
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
21
|
+
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
22
|
+
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
23
|
+
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
24
|
+
POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
# Sentry Ruby API
|
2
|
+
[](https://travis-ci.org/thierryxing/sentry-ruby-api)
|
3
|
+
[](https://github.com/thierryxing/sentry-ruby-api/blob/master/LICENSE.txt)
|
4
|
+
|
5
|
+
Sentry Ruby API is a Ruby wrapper for the [getsentry/sentry API](https://docs.sentry.io/hosted/api/).
|
6
|
+
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
add to a Gemfile:
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
gem 'sentry', :git => "git@github.com:thierryxing/sentry-ruby-api.git"
|
14
|
+
```
|
15
|
+
|
16
|
+
## Usage
|
17
|
+
|
18
|
+
Configuration example:
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
SentryApi.configure do |config|
|
22
|
+
config.endpoint = 'http://example.com/api/0'
|
23
|
+
config.auth_token = 'your_auth_token'
|
24
|
+
config.default_org_slug = 'sentry-sc'
|
25
|
+
end
|
26
|
+
```
|
27
|
+
|
28
|
+
(Note: If you are using getsentry.com's hosted service, your endpoint will be `https://app.getsentry.com/api/0`)
|
29
|
+
|
30
|
+
Usage examples:
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
# set an API endpoint
|
34
|
+
SentryApi.endpoint = 'http://example.com/api/0'
|
35
|
+
# => "http://example.com/api/0"
|
36
|
+
|
37
|
+
# set a user private token
|
38
|
+
SentryApi.auth_token = 'your_auth_token'
|
39
|
+
# => "your_auth_token"
|
40
|
+
|
41
|
+
# configure a proxy server
|
42
|
+
SentryApi.http_proxy('proxyhost', 8888)
|
43
|
+
# proxy server w/ basic auth
|
44
|
+
SentryApi.http_proxy('proxyhost', 8888, 'user', 'pass')
|
45
|
+
|
46
|
+
# list projects
|
47
|
+
SentryApi.projects
|
48
|
+
|
49
|
+
# initialize a new client
|
50
|
+
s = SentryApi.client(endpoint: 'https://api.example.com', auth_token: 'your_auth_token', default_org_slug: 'sentry-sc')
|
51
|
+
|
52
|
+
# a paginated response
|
53
|
+
projects = SentryApi.projects
|
54
|
+
|
55
|
+
# check existence of the next page
|
56
|
+
projects.has_next_page?
|
57
|
+
|
58
|
+
# retrieve the next page
|
59
|
+
projects.next_page
|
60
|
+
|
61
|
+
# iterate all projects
|
62
|
+
projects.auto_paginate do |project|
|
63
|
+
# do something
|
64
|
+
end
|
65
|
+
|
66
|
+
# retrieve all projects as an array
|
67
|
+
projects.auto_paginate
|
68
|
+
```
|
69
|
+
|
70
|
+
## Development
|
71
|
+
The basic framework had been finished, meanwhile the APIs of Organizations,Projects and Events had been added, more APIs will be added later. You are welcome to help me with it.
|
72
|
+
|
73
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
74
|
+
`rake spec` to run the tests. You can also run `bin/console` for an interactive
|
75
|
+
prompt that will allow you to experiment.
|
76
|
+
|
77
|
+
## License
|
78
|
+
|
79
|
+
Released under the BSD 2-clause license. See LICENSE.txt for details.
|
80
|
+
|
81
|
+
## Special Thank
|
82
|
+
Thanks to NARKOZ's [gitlab](https://github.com/NARKOZ/gitlab) ruby wrapper which really gives me a lot of inspiration.
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "sentry-api"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/lib/sentry-api.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'sentry-api/version'
|
2
|
+
require 'sentry-api/objectified_hash'
|
3
|
+
require 'sentry-api/configuration'
|
4
|
+
require 'sentry-api/error'
|
5
|
+
require 'sentry-api/page_links'
|
6
|
+
require 'sentry-api/paginated_response'
|
7
|
+
require 'sentry-api/request'
|
8
|
+
require 'sentry-api/api'
|
9
|
+
require 'sentry-api/client'
|
10
|
+
|
11
|
+
module SentryApi
|
12
|
+
extend Configuration
|
13
|
+
|
14
|
+
# Alias for Sentry::Client.new
|
15
|
+
#
|
16
|
+
# @return [Sentry::Client]
|
17
|
+
def self.client(options={})
|
18
|
+
SentryApi::Client.new(options)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Delegate to Sentry::Client
|
22
|
+
def self.method_missing(method, *args, &block)
|
23
|
+
return super unless client.respond_to?(method)
|
24
|
+
client.send(method, *args, &block)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Delegate to Sentry::Client
|
28
|
+
def respond_to_missing?(method_name, include_private = false)
|
29
|
+
client.respond_to?(method_name) || super
|
30
|
+
end
|
31
|
+
|
32
|
+
# Delegate to HTTParty.http_proxy
|
33
|
+
def self.http_proxy(address=nil, port=nil, username=nil, password=nil)
|
34
|
+
SentryApi::Request.http_proxy(address, port, username, password)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Returns an unsorted array of available client methods.
|
38
|
+
#
|
39
|
+
# @return [Array<Symbol>]
|
40
|
+
def self.actions
|
41
|
+
hidden = /endpoint|auth_token|default_org_slug|get|post|put|delete|validate|set_request_defaults|httparty/
|
42
|
+
(SentryApi::Client.instance_methods - Object.methods).reject { |e| e[hidden] }
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module SentryApi
|
2
|
+
# @private
|
3
|
+
class API < Request
|
4
|
+
# @private
|
5
|
+
attr_accessor(*Configuration::VALID_OPTIONS_KEYS)
|
6
|
+
|
7
|
+
# Creates a new API.
|
8
|
+
# @raise [Error:MissingCredentials]
|
9
|
+
def initialize(options={})
|
10
|
+
options = SentryApi.options.merge(options)
|
11
|
+
(Configuration::VALID_OPTIONS_KEYS).each do |key|
|
12
|
+
send("#{key}=", options[key]) if options[key]
|
13
|
+
end
|
14
|
+
set_request_defaults
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
class SentryApi::Client
|
2
|
+
|
3
|
+
module Events
|
4
|
+
|
5
|
+
# Retrieve an Issue
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# SentryApi.issue('120732258')
|
9
|
+
#
|
10
|
+
# @param issue_id [String] the ID of the issue to retrieve.
|
11
|
+
# @return [SentryApi::ObjectifiedHash]
|
12
|
+
def issue(issue_id)
|
13
|
+
get("/issues/#{issue_id}/")
|
14
|
+
end
|
15
|
+
|
16
|
+
# List an Issue’s Events
|
17
|
+
#
|
18
|
+
# @example
|
19
|
+
# SentryApi.issue_events('120732258')
|
20
|
+
#
|
21
|
+
# @param issue_id [String] the ID of the issue to retrieve.
|
22
|
+
# @return [Array<SentryApi::ObjectifiedHash>]
|
23
|
+
def issue_events(issue_id)
|
24
|
+
get("/issues/#{issue_id}/events/")
|
25
|
+
end
|
26
|
+
|
27
|
+
# List an Issue’s Hashes
|
28
|
+
#
|
29
|
+
# @example
|
30
|
+
# SentryApi.issues_hashes('120732258')
|
31
|
+
#
|
32
|
+
# @param issue_id [String] the ID of the issue to retrieve.
|
33
|
+
# @return [Array<SentryApi::ObjectifiedHash>]
|
34
|
+
def issue_hashes(issue_id)
|
35
|
+
get("/issues/#{issue_id}/hashes/")
|
36
|
+
end
|
37
|
+
|
38
|
+
# Removes an individual issue.
|
39
|
+
#
|
40
|
+
# @example
|
41
|
+
# SentryApi.remove_issue('120732258')
|
42
|
+
#
|
43
|
+
# @param issue_id [String] the ID of the issue to retrieve.
|
44
|
+
def remove_issue(issue_id)
|
45
|
+
delete("/issues/#{issue_id}/")
|
46
|
+
end
|
47
|
+
|
48
|
+
# Update an individual issue.
|
49
|
+
#
|
50
|
+
# @example
|
51
|
+
# SentryApi.update_issue('120732258')
|
52
|
+
# SentryApi.update_issue('120732258',{status:'resolved'})
|
53
|
+
# SentryApi.update_issue('120732258',{status:'resolved', assignedTo:'thierry.xing@gmail.com'})
|
54
|
+
#
|
55
|
+
# @param issue_id [String] the ID of the issue to retrieve.
|
56
|
+
# @param [Hash] options A customizable set of options.
|
57
|
+
# @option options [String] :status the new status for the groups. Valid values are "resolved", "unresolved" and "muted".
|
58
|
+
# @option options [String] :assignedTo the username of the user that should be assigned to this issue.
|
59
|
+
# @option options [Boolean] :hasSeen in case this API call is invoked with a user context this allows changing of the flag that indicates if the user has seen the event.
|
60
|
+
# @option options [Boolean] :isBookmarked in case this API call is invoked with a user context this allows changing of the bookmark flag.
|
61
|
+
# @option options [Boolean] :isSubscribed in case this API call is invoked with a user context this allows changing of the subscribed flag.
|
62
|
+
# @return <SentryApi::ObjectifiedHash>
|
63
|
+
def update_issue(issue_id, options={})
|
64
|
+
put("/issues/#{issue_id}/", body: options)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Retrieves the details of the latest event.
|
68
|
+
#
|
69
|
+
# @example
|
70
|
+
# SentryApi.latest_event('120633628')
|
71
|
+
#
|
72
|
+
# @param issue_id [String] the ID of the issue to retrieve.
|
73
|
+
# @return [SentryApi::ObjectifiedHash]
|
74
|
+
def latest_event(issue_id)
|
75
|
+
get("/issues/#{issue_id}/events/latest/")
|
76
|
+
end
|
77
|
+
|
78
|
+
# Retrieves the details of the oldest event.
|
79
|
+
#
|
80
|
+
# @example
|
81
|
+
# SentryApi.oldest_event('120633628')
|
82
|
+
#
|
83
|
+
# @param issue_id [String] the ID of the issue to retrieve.
|
84
|
+
# @return [SentryApi::ObjectifiedHash]
|
85
|
+
def oldest_event(issue_id)
|
86
|
+
get("/issues/#{issue_id}/events/oldest/")
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
class SentryApi::Client
|
2
|
+
|
3
|
+
module Organizations
|
4
|
+
# List your Organizations.
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# SentryApi.organizations
|
8
|
+
#
|
9
|
+
# @param member [Boolean] Restrict results to organizations which you have membership
|
10
|
+
# @return [Array<SentryApi::ObjectifiedHash>]
|
11
|
+
def organizations(member=false)
|
12
|
+
get("/organizations/", query: {member: member})
|
13
|
+
end
|
14
|
+
|
15
|
+
# List an Organization’s Projects
|
16
|
+
#
|
17
|
+
# @example
|
18
|
+
# SentryApi.organization_projects
|
19
|
+
# SentryApi.organization_projects('slug')
|
20
|
+
#
|
21
|
+
# @param organization_slug [String] the slug of the organization for which the projects should be listed.
|
22
|
+
# @return [Array<SentryApi::ObjectifiedHash>]
|
23
|
+
def organization_projects(organization_slug="")
|
24
|
+
organization_slug = @default_org_slug if organization_slug == ""
|
25
|
+
get("/organizations/#{organization_slug}/projects/")
|
26
|
+
end
|
27
|
+
|
28
|
+
# Retrieve an Organization
|
29
|
+
#
|
30
|
+
# @example
|
31
|
+
# SentryApi.organization
|
32
|
+
# SentryApi.organization('slug')
|
33
|
+
#
|
34
|
+
# @param organization_slug [String] the slug of the organization the team should be created for.
|
35
|
+
# @return [SentryApi::ObjectifiedHash]
|
36
|
+
def organization(organization_slug="")
|
37
|
+
organization_slug = @default_org_slug if organization_slug == ""
|
38
|
+
get("/organizations/#{organization_slug}/")
|
39
|
+
end
|
40
|
+
|
41
|
+
# Update an Organization
|
42
|
+
#
|
43
|
+
# @example
|
44
|
+
# SentryApi.update_organization('slug')
|
45
|
+
# SentryApi.update_organization('slug',{name:'new-name'})
|
46
|
+
# SentryApi.update_organization('slug',{name:'new-name', slug:'new-slug'})
|
47
|
+
#
|
48
|
+
# @param organization_slug [String] the slug of the organization the team should be created for.
|
49
|
+
# @param [Hash] options A customizable set of options.
|
50
|
+
# @option options [String] :name an optional new name for the organization.
|
51
|
+
# @option options [String] :slug an optional new slug for the organization. Needs to be available and unique.
|
52
|
+
# @return [SentryApi::ObjectifiedHash]
|
53
|
+
def update_organization(organization_slug, options={})
|
54
|
+
put("/organizations/#{organization_slug}/", body: options)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Retrieve Event Counts for an Organization
|
58
|
+
#
|
59
|
+
# @example
|
60
|
+
# SentryApi.organization_stats('slug')
|
61
|
+
# SentryApi.organization_stats('slug', {stat:'received', since:'1472158800'})
|
62
|
+
#
|
63
|
+
# @param organization_slug [String] the slug of the organization for which the stats should be retrieved.
|
64
|
+
# @param [Hash] options A customizable set of options.
|
65
|
+
# @option options [String] :stat the name of the stat to query ("received", "rejected", "blacklisted")
|
66
|
+
# @option options [Timestamp] :since a timestamp to set the start of the query in seconds since UNIX epoch.
|
67
|
+
# @option options [Timestamp] :until a timestamp to set the end of the query in seconds since UNIX epoch.
|
68
|
+
# @option options [String] :resolution an explicit resolution to search for (eg: 10s). This should not be used unless you are familiar with Sentry’s internals as it’s restricted to pre-defined values.
|
69
|
+
# @return [Array<Array>]
|
70
|
+
def organization_stats(organization_slug, options={})
|
71
|
+
get("/organizations/#{organization_slug}/stats/", query: options)
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
@@ -0,0 +1,185 @@
|
|
1
|
+
class SentryApi::Client
|
2
|
+
|
3
|
+
module Projects
|
4
|
+
# List your Projects
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# SentryApi.projects
|
8
|
+
#
|
9
|
+
# @return [Array<SentryApi::ObjectifiedHash>]
|
10
|
+
def projects
|
11
|
+
get("/projects/")
|
12
|
+
end
|
13
|
+
|
14
|
+
# Retrieve a Project
|
15
|
+
#
|
16
|
+
# @example
|
17
|
+
# SentryApi.project('project-slug')
|
18
|
+
#
|
19
|
+
# @param project_slug [String] the slug of the project to retrieve.
|
20
|
+
# @param organization_slug [String] the slug of the organization the project belong to.
|
21
|
+
# @return [SentryApi::ObjectifiedHash]
|
22
|
+
def project(project_slug, organization_slug="")
|
23
|
+
organization_slug = @default_org_slug if organization_slug == ""
|
24
|
+
get("/projects/#{organization_slug}/#{project_slug}/")
|
25
|
+
end
|
26
|
+
|
27
|
+
# Update a Project
|
28
|
+
#
|
29
|
+
# @example
|
30
|
+
# SentryApi.update_project('project-slug', {name:'new-name', slug:'new-slug', is_bookmarked:false})
|
31
|
+
#
|
32
|
+
# @param project_slug [String] the slug of the project to retrieve.
|
33
|
+
# @param [Hash] options A customizable set of options.
|
34
|
+
# @option options [String] :name the new name for the project.
|
35
|
+
# @option options [String] :slug the new slug for the project.
|
36
|
+
# @option options [String] :isBookmarked in case this API call is invoked with a user context this allows changing of the bookmark flag.
|
37
|
+
# @option options [Hash] optional options to override in the project settings.
|
38
|
+
# @param organization_slug [String] the slug of the organization the project belong to.
|
39
|
+
# @return [SentryApi::ObjectifiedHash]
|
40
|
+
def update_project(project_slug, options={}, organization_slug="")
|
41
|
+
organization_slug = @default_org_slug if organization_slug == ""
|
42
|
+
put("/projects/#{organization_slug}/#{project_slug}/", body: options)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Delete a Project.
|
46
|
+
#
|
47
|
+
# @example
|
48
|
+
# SentryApi.delete_project('project-slug')
|
49
|
+
#
|
50
|
+
# @param project_slug [String] the slug of the project to delete.
|
51
|
+
# @param organization_slug [String] the slug of the organization the project belong to.
|
52
|
+
def delete_project(project_slug, organization_slug="")
|
53
|
+
organization_slug = @default_org_slug if organization_slug == ""
|
54
|
+
delete("/projects/#{organization_slug}/#{project_slug}/")
|
55
|
+
end
|
56
|
+
|
57
|
+
# Retrieve Event Counts for an Project
|
58
|
+
#
|
59
|
+
# @example
|
60
|
+
# SentryApi.project_stats('slug')
|
61
|
+
# SentryApi.project_stats('slug', {stat:'received', since:'1472158800'})
|
62
|
+
#
|
63
|
+
# @param project_slug [String] the slug of the project.
|
64
|
+
# @param [Hash] options A customizable set of options.
|
65
|
+
# @option options [String] :stat the name of the stat to query ("received", "rejected", "blacklisted")
|
66
|
+
# @option options [Timestamp] :since a timestamp to set the start of the query in seconds since UNIX epoch.
|
67
|
+
# @option options [Timestamp] :until a timestamp to set the end of the query in seconds since UNIX epoch.
|
68
|
+
# @option options [String] :resolution an explicit resolution to search for (eg: 10s). This should not be used unless you are familiar with Sentry’s internals as it’s restricted to pre-defined values.
|
69
|
+
# @param organization_slug [String] the slug of the organization the project belong to.
|
70
|
+
# @return [Array<Array>]
|
71
|
+
def project_stats(project_slug, options={}, organization_slug="")
|
72
|
+
get("/projects/#{organization_slug}/#{project_slug}/stats/", query: options)
|
73
|
+
end
|
74
|
+
|
75
|
+
# List a Project’s DSym Files.
|
76
|
+
#
|
77
|
+
# @example
|
78
|
+
# SentryApi.project_dsym_files('project-slug')
|
79
|
+
#
|
80
|
+
# @param organization_slug [String] the slug of the organization.
|
81
|
+
# @param project_slug [String] the slug of the project to list the dsym files of.
|
82
|
+
# @return [Array<SentryApi::ObjectifiedHash>]
|
83
|
+
def project_dsym_files(project_slug, organization_slug="")
|
84
|
+
organization_slug = @default_org_slug if organization_slug == ""
|
85
|
+
get("/projects/#{organization_slug}/#{project_slug}/files/dsyms/")
|
86
|
+
end
|
87
|
+
|
88
|
+
# List a Project’s Client Keys.
|
89
|
+
#
|
90
|
+
# @example
|
91
|
+
# SentryApi.client_keys('project-slug')
|
92
|
+
#
|
93
|
+
# @param project_slug [String] the slug of the project the client keys belong to.
|
94
|
+
# @param organization_slug [String] the slug of the organization the client keys belong to.
|
95
|
+
# @return [Array<SentryApi::ObjectifiedHash>]
|
96
|
+
def client_keys(project_slug, organization_slug="")
|
97
|
+
organization_slug = @default_org_slug if organization_slug == ""
|
98
|
+
get("/projects/#{organization_slug}/#{project_slug}/keys/")
|
99
|
+
end
|
100
|
+
|
101
|
+
# Create a new Client Key.
|
102
|
+
#
|
103
|
+
# @example
|
104
|
+
# SentryApi.create_client_key('project-slug','new-name')
|
105
|
+
#
|
106
|
+
# @param project_slug [String] the slug of the project the client keys belong to.
|
107
|
+
# @param [Hash] options A customizable set of options.
|
108
|
+
# @option options [String] :name the name for the new key.
|
109
|
+
# @param organization_slug [String] the slug of the organization the client keys belong to.
|
110
|
+
# @return [SentryApi::ObjectifiedHash]
|
111
|
+
def create_client_key(project_slug, options={}, organization_slug="")
|
112
|
+
organization_slug = @default_org_slug if organization_slug == ""
|
113
|
+
post("/projects/#{organization_slug}/#{project_slug}/keys/", body: options)
|
114
|
+
end
|
115
|
+
|
116
|
+
# Delete a Client Key.
|
117
|
+
#
|
118
|
+
# @example
|
119
|
+
# SentryApi.delete_client_key('project-slug','87c990582e07446b9907b357fc27730e')
|
120
|
+
#
|
121
|
+
# @param project_slug [String] the slug of the project the client keys belong to.
|
122
|
+
# @param key_id [String] the ID of the key to delete.
|
123
|
+
# @param organization_slug [String] the slug of the organization the client keys belong to.
|
124
|
+
def delete_client_key(project_slug, key_id, organization_slug="")
|
125
|
+
organization_slug = @default_org_slug if organization_slug == ""
|
126
|
+
delete("/projects/#{organization_slug}/#{project_slug}/keys/#{key_id}/")
|
127
|
+
end
|
128
|
+
|
129
|
+
# Update a Client Key
|
130
|
+
#
|
131
|
+
# @example
|
132
|
+
# SentryApi.update_client_key('project-slug','87c990582e07446b9907b357fc27730e',{name:'new-name'})
|
133
|
+
#
|
134
|
+
# @param project_slug [String] the slug of the project the client keys belong to.
|
135
|
+
# @param key_id [String] the ID of the key to update.
|
136
|
+
# @param [Hash] options A customizable set of options.
|
137
|
+
# @option options [String] :name the new name for the client key.
|
138
|
+
# @param organization_slug [String] the slug of the organization the client keys belong to.
|
139
|
+
# @return [Array<SentryApi::ObjectifiedHash>]
|
140
|
+
def update_client_key(project_slug, key_id, options={}, organization_slug="")
|
141
|
+
organization_slug = @default_org_slug if organization_slug == ""
|
142
|
+
put("/projects/#{organization_slug}/#{project_slug}/keys/#{key_id}/", body: options)
|
143
|
+
end
|
144
|
+
|
145
|
+
# Return a list of sampled events bound to a project.
|
146
|
+
#
|
147
|
+
# @example
|
148
|
+
# SentryApi.project_events('project-slug')
|
149
|
+
#
|
150
|
+
# @param project_slug [String] the slug of the project the client keys belong to.
|
151
|
+
# @param organization_slug [String] the slug of the organization the client keys belong to.
|
152
|
+
# @return [Array<SentryApi::ObjectifiedHash>]
|
153
|
+
def project_events(project_slug, organization_slug="")
|
154
|
+
organization_slug = @default_org_slug if organization_slug == ""
|
155
|
+
get("/projects/#{organization_slug}/#{project_slug}/events/")
|
156
|
+
end
|
157
|
+
|
158
|
+
# Retrieve an Event for a Project
|
159
|
+
#
|
160
|
+
# @example
|
161
|
+
# SentryApi.project_event('project-slug', 'event-id')
|
162
|
+
#
|
163
|
+
# @param project_slug [String] the slug of the project the client keys belong to.
|
164
|
+
# @param event_id [String] the slug of the project the event belongs to.
|
165
|
+
# @param organization_slug [String] the slug of the organization the client keys belong to.
|
166
|
+
# @return [SentryApi::ObjectifiedHash]
|
167
|
+
def project_event(project_slug, event_id, organization_slug="")
|
168
|
+
organization_slug = @default_org_slug if organization_slug == ""
|
169
|
+
get("/projects/#{organization_slug}/#{project_slug}/events/#{event_id}/")
|
170
|
+
end
|
171
|
+
|
172
|
+
# Return a list of aggregates bound to a project
|
173
|
+
#
|
174
|
+
# @example
|
175
|
+
# SentryApi.project_issues('project-slug')
|
176
|
+
#
|
177
|
+
# @return [Array<SentryApi::ObjectifiedHash>]
|
178
|
+
def project_issues(project_slug, organization_slug="")
|
179
|
+
organization_slug = @default_org_slug if organization_slug == ""
|
180
|
+
get("/projects/#{organization_slug}/#{project_slug}/issues/")
|
181
|
+
end
|
182
|
+
|
183
|
+
end
|
184
|
+
|
185
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class SentryApi::Client
|
2
|
+
|
3
|
+
module Teams
|
4
|
+
|
5
|
+
# Create a new project bound to a team.
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# SentryApi.create_project('team-slug',{name:'new-name'})
|
9
|
+
#
|
10
|
+
# @param team_slug [String] the slug of the team to create a new project for.
|
11
|
+
# @param organization_slug [String] the slug of the organization the team belongs to.
|
12
|
+
# @param [Hash] options A customizable set of options.
|
13
|
+
# @option options [String] :name the name for the new project.
|
14
|
+
# @option options [String] :slug optionally a slug for the new project. If it’s not provided a slug is generated from the name.
|
15
|
+
# @return [SentryApi::ObjectifiedHash]
|
16
|
+
def create_project(team_slug, options={}, organization_slug="")
|
17
|
+
organization_slug = @default_org_slug if organization_slug == ""
|
18
|
+
post("/teams/#{organization_slug}/#{team_slug}/projects/", body: options)
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module SentryApi
|
2
|
+
# Defines constants and methods related to configuration.
|
3
|
+
module Configuration
|
4
|
+
# An array of valid keys in the options hash when configuring a Sentry::API.
|
5
|
+
VALID_OPTIONS_KEYS = [:endpoint, :auth_token, :default_org_slug, :httparty].freeze
|
6
|
+
|
7
|
+
# The user agent that will be sent to the API endpoint if none is set.
|
8
|
+
DEFAULT_USER_AGENT = "Sentry Ruby Gem #{SentryApi::VERSION}".freeze
|
9
|
+
|
10
|
+
# @private
|
11
|
+
attr_accessor(*VALID_OPTIONS_KEYS)
|
12
|
+
|
13
|
+
# Sets all configuration options to their default values
|
14
|
+
# when this module is extended.
|
15
|
+
def self.extended(base)
|
16
|
+
base.reset
|
17
|
+
end
|
18
|
+
|
19
|
+
# Convenience method to allow configuration options to be set in a block.
|
20
|
+
def configure
|
21
|
+
yield self
|
22
|
+
end
|
23
|
+
|
24
|
+
# Creates a hash of options and their values.
|
25
|
+
def options
|
26
|
+
VALID_OPTIONS_KEYS.inject({}) do |option, key|
|
27
|
+
option.merge!(key => send(key))
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Resets all configuration options to the defaults.
|
32
|
+
def reset
|
33
|
+
self.endpoint = ENV['SENTRY_API_ENDPOINT']
|
34
|
+
self.auth_token = ENV['SENTRY_API_AUTH_TOKEN']
|
35
|
+
self.default_org_slug = ENV['SENTRY_API_DEFAULT_ORG_SLUG']
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
module SentryApi
|
2
|
+
module Error
|
3
|
+
# Custom error class for rescuing from all Sentry errors.
|
4
|
+
class Error < StandardError;
|
5
|
+
end
|
6
|
+
|
7
|
+
# Raised when API endpoint credentials not configured.
|
8
|
+
class MissingCredentials < Error;
|
9
|
+
end
|
10
|
+
|
11
|
+
# Raised when impossible to parse response body.
|
12
|
+
class Parsing < Error;
|
13
|
+
end
|
14
|
+
|
15
|
+
# Custom error class for rescuing from HTTP response errors.
|
16
|
+
class ResponseError < Error
|
17
|
+
def initialize(response)
|
18
|
+
@response = response
|
19
|
+
super(build_error_message)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Status code returned in the http response.
|
23
|
+
#
|
24
|
+
# @return [Integer]
|
25
|
+
def response_status
|
26
|
+
@response.code
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
# Human friendly message.
|
32
|
+
#
|
33
|
+
# @return [String]
|
34
|
+
def build_error_message
|
35
|
+
parsed_response = @response.parsed_response
|
36
|
+
message = parsed_response.message || parsed_response.error
|
37
|
+
|
38
|
+
"Server responded with code #{@response.code}, message: " \
|
39
|
+
"#{handle_message(message)}. " \
|
40
|
+
"Request URI: #{@response.request.base_uri}#{@response.request.path}"
|
41
|
+
end
|
42
|
+
|
43
|
+
# Handle error response message in case of nested hashes
|
44
|
+
def handle_message(message)
|
45
|
+
case message
|
46
|
+
when SentryApi::ObjectifiedHash
|
47
|
+
message.to_h.sort.map do |key, val|
|
48
|
+
"'#{key}' #{(val.is_a?(Hash) ? val.sort.map { |k, v| "(#{k}: #{v.join(' ')})" } : val).join(' ')}"
|
49
|
+
end.join(', ')
|
50
|
+
when Array
|
51
|
+
message.join(' ')
|
52
|
+
else
|
53
|
+
message
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Raised when API endpoint returns the HTTP status code 400.
|
59
|
+
class BadRequest < ResponseError;
|
60
|
+
end
|
61
|
+
|
62
|
+
# Raised when API endpoint returns the HTTP status code 401.
|
63
|
+
class Unauthorized < ResponseError;
|
64
|
+
end
|
65
|
+
|
66
|
+
# Raised when API endpoint returns the HTTP status code 403.
|
67
|
+
class Forbidden < ResponseError;
|
68
|
+
end
|
69
|
+
|
70
|
+
# Raised when API endpoint returns the HTTP status code 404.
|
71
|
+
class NotFound < ResponseError;
|
72
|
+
end
|
73
|
+
|
74
|
+
# Raised when API endpoint returns the HTTP status code 405.
|
75
|
+
class MethodNotAllowed < ResponseError;
|
76
|
+
end
|
77
|
+
|
78
|
+
# Raised when API endpoint returns the HTTP status code 409.
|
79
|
+
class Conflict < ResponseError;
|
80
|
+
end
|
81
|
+
|
82
|
+
# Raised when API endpoint returns the HTTP status code 422.
|
83
|
+
class Unprocessable < ResponseError;
|
84
|
+
end
|
85
|
+
|
86
|
+
# Raised when API endpoint returns the HTTP status code 500.
|
87
|
+
class InternalServerError < ResponseError;
|
88
|
+
end
|
89
|
+
|
90
|
+
# Raised when API endpoint returns the HTTP status code 502.
|
91
|
+
class BadGateway < ResponseError;
|
92
|
+
end
|
93
|
+
|
94
|
+
# Raised when API endpoint returns the HTTP status code 503.
|
95
|
+
class ServiceUnavailable < ResponseError;
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module SentryApi
|
2
|
+
# Converts hashes to the objects.
|
3
|
+
class ObjectifiedHash
|
4
|
+
# Creates a new ObjectifiedHash object.
|
5
|
+
def initialize(hash)
|
6
|
+
@hash = hash
|
7
|
+
@data = hash.inject({}) do |data, (key, value)|
|
8
|
+
value = ObjectifiedHash.new(value) if value.is_a? Hash
|
9
|
+
data[key.to_s] = value
|
10
|
+
data
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# @return [Hash] The original hash.
|
15
|
+
def to_hash
|
16
|
+
@hash
|
17
|
+
end
|
18
|
+
|
19
|
+
alias_method :to_h, :to_hash
|
20
|
+
|
21
|
+
# @return [String] Formatted string with the class name, object id and original hash.
|
22
|
+
def inspect
|
23
|
+
"#<#{self.class}:#{object_id} {hash: #{@hash.inspect}}"
|
24
|
+
end
|
25
|
+
|
26
|
+
# Delegate to ObjectifiedHash.
|
27
|
+
def method_missing(key)
|
28
|
+
@data.key?(key.to_s) ? @data[key.to_s] : nil
|
29
|
+
end
|
30
|
+
|
31
|
+
def respond_to_missing?(method_name, include_private = false)
|
32
|
+
@hash.keys.map(&:to_sym).include?(method_name.to_sym) || super
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module SentryApi
|
2
|
+
# Parses link header.
|
3
|
+
#
|
4
|
+
# @private
|
5
|
+
class PageLinks
|
6
|
+
HEADER_LINK = 'Link'.freeze
|
7
|
+
DELIM_LINKS = ','.freeze
|
8
|
+
LINK_REGEX = /<([^>]+)>; rel=\"([^\"]+)\"; results=\"([^\"]+)\"/
|
9
|
+
METAS = %w(previous next)
|
10
|
+
|
11
|
+
attr_accessor(*METAS)
|
12
|
+
|
13
|
+
def initialize(headers)
|
14
|
+
link_header = headers[HEADER_LINK]
|
15
|
+
|
16
|
+
if link_header && link_header =~ /(previous|next)/
|
17
|
+
extract_links(link_header)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def extract_links(header)
|
24
|
+
header.split(DELIM_LINKS).each do |link|
|
25
|
+
LINK_REGEX.match(link.strip) do |match|
|
26
|
+
url, meta, results = match[1], match[2], match[3]
|
27
|
+
next if !url or !meta or !(results == "true") or METAS.index(meta).nil?
|
28
|
+
self.send("#{meta}=", url)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module SentryApi
|
2
|
+
# Wrapper class of paginated response.
|
3
|
+
class PaginatedResponse
|
4
|
+
attr_accessor :client
|
5
|
+
|
6
|
+
def initialize(array)
|
7
|
+
@array = array
|
8
|
+
end
|
9
|
+
|
10
|
+
def ==(other)
|
11
|
+
@array == other
|
12
|
+
end
|
13
|
+
|
14
|
+
def inspect
|
15
|
+
@array.inspect
|
16
|
+
end
|
17
|
+
|
18
|
+
def method_missing(name, *args, &block)
|
19
|
+
if @array.respond_to?(name)
|
20
|
+
@array.send(name, *args, &block)
|
21
|
+
else
|
22
|
+
super
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def respond_to_missing?(method_name, include_private = false)
|
27
|
+
super || @array.respond_to?(method_name, include_private)
|
28
|
+
end
|
29
|
+
|
30
|
+
def parse_headers!(headers)
|
31
|
+
@links = PageLinks.new headers
|
32
|
+
end
|
33
|
+
|
34
|
+
def each_page
|
35
|
+
current = self
|
36
|
+
yield current
|
37
|
+
while current.has_next_page?
|
38
|
+
current = current.next_page
|
39
|
+
yield current
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def auto_paginate
|
44
|
+
response = block_given? ? nil : []
|
45
|
+
each_page do |page|
|
46
|
+
if block_given?
|
47
|
+
page.each do |item|
|
48
|
+
yield item
|
49
|
+
end
|
50
|
+
else
|
51
|
+
response += page
|
52
|
+
end
|
53
|
+
end
|
54
|
+
response
|
55
|
+
end
|
56
|
+
|
57
|
+
def has_next_page?
|
58
|
+
!(@links.nil? || @links.next.nil?)
|
59
|
+
end
|
60
|
+
|
61
|
+
def next_page
|
62
|
+
return nil if @client.nil? || !has_next_page?
|
63
|
+
@links.next
|
64
|
+
end
|
65
|
+
|
66
|
+
def has_prev_page?
|
67
|
+
!(@links.nil? || @links.previous.nil?)
|
68
|
+
end
|
69
|
+
|
70
|
+
def prev_page
|
71
|
+
return nil if @client.nil? || !has_prev_page?
|
72
|
+
@links.previous
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module SentryApi
|
5
|
+
# @private
|
6
|
+
class Request
|
7
|
+
include HTTParty
|
8
|
+
format :json
|
9
|
+
headers 'Content-Type' => 'application/json'
|
10
|
+
parser proc { |body, _| parse(body) }
|
11
|
+
|
12
|
+
attr_accessor :auth_token, :endpoint, :default_org_slug
|
13
|
+
|
14
|
+
# Converts the response body to an ObjectifiedHash.
|
15
|
+
def self.parse(body)
|
16
|
+
body = decode(body)
|
17
|
+
|
18
|
+
if body.is_a? Hash
|
19
|
+
ObjectifiedHash.new body
|
20
|
+
elsif body.is_a? Array
|
21
|
+
if body[0].is_a? Array
|
22
|
+
body
|
23
|
+
else
|
24
|
+
PaginatedResponse.new(body.collect! { |e| ObjectifiedHash.new(e) })
|
25
|
+
end
|
26
|
+
elsif body
|
27
|
+
true
|
28
|
+
elsif !body
|
29
|
+
false
|
30
|
+
elsif body.nil?
|
31
|
+
false
|
32
|
+
else
|
33
|
+
raise Error::Parsing.new "Couldn't parse a response body"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Decodes a JSON response into Ruby object.
|
38
|
+
def self.decode(response)
|
39
|
+
JSON.load response
|
40
|
+
rescue JSON::ParserError
|
41
|
+
raise Error::Parsing.new "The response is not a valid JSON"
|
42
|
+
end
|
43
|
+
|
44
|
+
def get(path, options={})
|
45
|
+
set_httparty_config(options)
|
46
|
+
set_authorization_header(options)
|
47
|
+
validate self.class.get(@endpoint + path, options)
|
48
|
+
end
|
49
|
+
|
50
|
+
def post(path, options={})
|
51
|
+
set_httparty_config(options)
|
52
|
+
set_json_body(options)
|
53
|
+
set_authorization_header(options, path)
|
54
|
+
validate self.class.post(@endpoint + path, options)
|
55
|
+
end
|
56
|
+
|
57
|
+
def put(path, options={})
|
58
|
+
set_httparty_config(options)
|
59
|
+
set_json_body(options)
|
60
|
+
set_authorization_header(options)
|
61
|
+
validate self.class.put(@endpoint + path, options)
|
62
|
+
end
|
63
|
+
|
64
|
+
def delete(path, options={})
|
65
|
+
set_httparty_config(options)
|
66
|
+
set_authorization_header(options)
|
67
|
+
validate self.class.delete(@endpoint + path, options)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Checks the response code for common errors.
|
71
|
+
# Returns parsed response for successful requests.
|
72
|
+
def validate(response)
|
73
|
+
error_klass = case response.code
|
74
|
+
when 400 then
|
75
|
+
Error::BadRequest
|
76
|
+
when 401 then
|
77
|
+
Error::Unauthorized
|
78
|
+
when 403 then
|
79
|
+
Error::Forbidden
|
80
|
+
when 404 then
|
81
|
+
Error::NotFound
|
82
|
+
when 405 then
|
83
|
+
Error::MethodNotAllowed
|
84
|
+
when 409 then
|
85
|
+
Error::Conflict
|
86
|
+
when 422 then
|
87
|
+
Error::Unprocessable
|
88
|
+
when 500 then
|
89
|
+
Error::InternalServerError
|
90
|
+
when 502 then
|
91
|
+
Error::BadGateway
|
92
|
+
when 503 then
|
93
|
+
Error::ServiceUnavailable
|
94
|
+
end
|
95
|
+
|
96
|
+
fail error_klass.new(response) if error_klass
|
97
|
+
|
98
|
+
parsed = response.parsed_response
|
99
|
+
parsed.client = self if parsed.respond_to?(:client=)
|
100
|
+
parsed.parse_headers!(response.headers) if parsed.respond_to?(:parse_headers!)
|
101
|
+
parsed
|
102
|
+
end
|
103
|
+
|
104
|
+
# Sets a base_uri and default_params for requests.
|
105
|
+
# @raise [Error::MissingCredentials] if endpoint not set.
|
106
|
+
def set_request_defaults
|
107
|
+
self.class.default_params
|
108
|
+
raise Error::MissingCredentials.new("Please set an endpoint to API") unless @endpoint
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
# Sets a Authorization header for requests.
|
114
|
+
# @raise [Error::MissingCredentials] if auth_token and auth_token are not set.
|
115
|
+
def set_authorization_header(options, path=nil)
|
116
|
+
unless path == '/session'
|
117
|
+
raise Error::MissingCredentials.new("Please provide a auth_token for user") unless @auth_token
|
118
|
+
options[:headers] = {'Authorization' => "Bearer #{@auth_token}"}
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# Set http post or put body as json string
|
123
|
+
def set_json_body(options)
|
124
|
+
options[:body] = options[:body].to_json
|
125
|
+
end
|
126
|
+
|
127
|
+
# Set HTTParty configuration
|
128
|
+
# @see https://github.com/jnunemaker/httparty
|
129
|
+
def set_httparty_config(options)
|
130
|
+
options.merge!(httparty) if httparty
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
data/sentry-api.gemspec
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'sentry-api/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "sentry-api"
|
8
|
+
spec.version = SentryApi::VERSION
|
9
|
+
spec.authors = ["Thierry Xing"]
|
10
|
+
spec.email = ["thierry.xing@gmail.com"]
|
11
|
+
spec.licenses = ['MIT']
|
12
|
+
spec.summary = %q{Ruby client for Sentry API}
|
13
|
+
spec.description = %q{A Ruby wrapper for the Sentry API}
|
14
|
+
spec.homepage = "https://github.com/thierryxing/sentry-ruby-api"
|
15
|
+
|
16
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
17
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
18
|
+
if spec.respond_to?(:metadata)
|
19
|
+
spec.metadata['allowed_push_host'] = "https://rubygems.org"
|
20
|
+
else
|
21
|
+
raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
|
22
|
+
end
|
23
|
+
|
24
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
25
|
+
spec.bindir = "exe"
|
26
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
27
|
+
spec.require_paths = ["lib"]
|
28
|
+
|
29
|
+
spec.add_runtime_dependency 'httparty', "~> 0.14.0"
|
30
|
+
spec.add_development_dependency "bundler", "~> 1.12"
|
31
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
32
|
+
spec.add_development_dependency 'rspec', "~> 3.5.0", '>= 3.5.0'
|
33
|
+
spec.add_development_dependency 'webmock', "~> 2.1.0", '>= 2.1.0'
|
34
|
+
spec.add_development_dependency 'yard', "~> 0.9.5"
|
35
|
+
end
|
metadata
ADDED
@@ -0,0 +1,164 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sentry-api
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Thierry Xing
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-08-31 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.14.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.14.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.12'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.12'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '10.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '10.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: 3.5.0
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: 3.5.0
|
65
|
+
type: :development
|
66
|
+
prerelease: false
|
67
|
+
version_requirements: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - "~>"
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: 3.5.0
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: 3.5.0
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: webmock
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - "~>"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: 2.1.0
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: 2.1.0
|
85
|
+
type: :development
|
86
|
+
prerelease: false
|
87
|
+
version_requirements: !ruby/object:Gem::Requirement
|
88
|
+
requirements:
|
89
|
+
- - "~>"
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: 2.1.0
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: 2.1.0
|
95
|
+
- !ruby/object:Gem::Dependency
|
96
|
+
name: yard
|
97
|
+
requirement: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - "~>"
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: 0.9.5
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
requirements:
|
106
|
+
- - "~>"
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: 0.9.5
|
109
|
+
description: A Ruby wrapper for the Sentry API
|
110
|
+
email:
|
111
|
+
- thierry.xing@gmail.com
|
112
|
+
executables: []
|
113
|
+
extensions: []
|
114
|
+
extra_rdoc_files: []
|
115
|
+
files:
|
116
|
+
- ".gitignore"
|
117
|
+
- ".travis.yml"
|
118
|
+
- Gemfile
|
119
|
+
- LICENSE.txt
|
120
|
+
- README.md
|
121
|
+
- Rakefile
|
122
|
+
- bin/console
|
123
|
+
- bin/setup
|
124
|
+
- lib/sentry-api.rb
|
125
|
+
- lib/sentry-api/api.rb
|
126
|
+
- lib/sentry-api/client.rb
|
127
|
+
- lib/sentry-api/client/events.rb
|
128
|
+
- lib/sentry-api/client/organizations.rb
|
129
|
+
- lib/sentry-api/client/projects.rb
|
130
|
+
- lib/sentry-api/client/teams.rb
|
131
|
+
- lib/sentry-api/configuration.rb
|
132
|
+
- lib/sentry-api/error.rb
|
133
|
+
- lib/sentry-api/objectified_hash.rb
|
134
|
+
- lib/sentry-api/page_links.rb
|
135
|
+
- lib/sentry-api/paginated_response.rb
|
136
|
+
- lib/sentry-api/request.rb
|
137
|
+
- lib/sentry-api/version.rb
|
138
|
+
- sentry-api.gemspec
|
139
|
+
homepage: https://github.com/thierryxing/sentry-ruby-api
|
140
|
+
licenses:
|
141
|
+
- MIT
|
142
|
+
metadata:
|
143
|
+
allowed_push_host: https://rubygems.org
|
144
|
+
post_install_message:
|
145
|
+
rdoc_options: []
|
146
|
+
require_paths:
|
147
|
+
- lib
|
148
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
154
|
+
requirements:
|
155
|
+
- - ">="
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: '0'
|
158
|
+
requirements: []
|
159
|
+
rubyforge_project:
|
160
|
+
rubygems_version: 2.5.1
|
161
|
+
signing_key:
|
162
|
+
specification_version: 4
|
163
|
+
summary: Ruby client for Sentry API
|
164
|
+
test_files: []
|