spotted 0.10.0 → 0.11.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 +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +1 -1
- data/lib/spotted/client.rb +62 -15
- data/lib/spotted/version.rb +1 -1
- data/rbi/spotted/client.rbi +20 -4
- data/sig/spotted/client.rbs +10 -3
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 53171d2924e8b65667348df43a8315b13834283411bcc57260a3dee25ed1b5bb
|
|
4
|
+
data.tar.gz: 4a987cf805446e513e6d977a00aa999f48d64ca881e7adc6f7a3ae9352169348
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 13b3fab4d5ead1e934399769d8556ba0521e2bebcbe5a0da61fc4cefba97ebd92801afc3d4136bc70f7efea09b7a3ac0689e41e8633968d4f983d11a7f4bc1b2
|
|
7
|
+
data.tar.gz: 0ce09546710593e818c08530438f4ab6918fd9e103ad54d3376af6362da5bb197298bdec2a931cc6868259aa49eacd08a827108d4375a5075922a1295594667e
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.11.0 (2025-11-10)
|
|
4
|
+
|
|
5
|
+
Full Changelog: [v0.10.0...v0.11.0](https://github.com/cjavdev/spotted/compare/v0.10.0...v0.11.0)
|
|
6
|
+
|
|
7
|
+
### Features
|
|
8
|
+
|
|
9
|
+
* **api:** manual updates ([feb8221](https://github.com/cjavdev/spotted/commit/feb822194d2e961c6469ca5e36c8d0cccdb0cb5a))
|
|
10
|
+
|
|
3
11
|
## 0.10.0 (2025-11-10)
|
|
4
12
|
|
|
5
13
|
Full Changelog: [v0.9.0...v0.10.0](https://github.com/cjavdev/spotted/compare/v0.9.0...v0.10.0)
|
data/README.md
CHANGED
data/lib/spotted/client.rb
CHANGED
|
@@ -15,12 +15,15 @@ module Spotted
|
|
|
15
15
|
# Default max retry delay in seconds.
|
|
16
16
|
DEFAULT_MAX_RETRY_DELAY = 8.0
|
|
17
17
|
|
|
18
|
-
# @return [String]
|
|
18
|
+
# @return [String, nil]
|
|
19
19
|
attr_reader :client_id
|
|
20
20
|
|
|
21
|
-
# @return [String]
|
|
21
|
+
# @return [String, nil]
|
|
22
22
|
attr_reader :client_secret
|
|
23
23
|
|
|
24
|
+
# @return [String, nil]
|
|
25
|
+
attr_reader :access_token
|
|
26
|
+
|
|
24
27
|
# @return [Spotted::Resources::Albums]
|
|
25
28
|
attr_reader :albums
|
|
26
29
|
|
|
@@ -69,6 +72,22 @@ module Spotted
|
|
|
69
72
|
# @return [Spotted::Resources::Markets]
|
|
70
73
|
attr_reader :markets
|
|
71
74
|
|
|
75
|
+
# @api private
|
|
76
|
+
#
|
|
77
|
+
# @return [Hash{String=>String}]
|
|
78
|
+
private def auth_headers
|
|
79
|
+
{**bearer_auth, **oauth_2_0}
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# @api private
|
|
83
|
+
#
|
|
84
|
+
# @return [Hash{String=>String}]
|
|
85
|
+
private def bearer_auth
|
|
86
|
+
return {} if @access_token.nil?
|
|
87
|
+
|
|
88
|
+
{"authorization" => "Bearer #{@access_token}"}
|
|
89
|
+
end
|
|
90
|
+
|
|
72
91
|
# @api private
|
|
73
92
|
# @return [Spotted::Internal::OAuth2ClientCredentials]
|
|
74
93
|
attr_reader :oauth_2_0_state
|
|
@@ -76,7 +95,7 @@ module Spotted
|
|
|
76
95
|
# @api private
|
|
77
96
|
#
|
|
78
97
|
# @return [Hash{String=>String}]
|
|
79
|
-
private def
|
|
98
|
+
private def oauth_2_0
|
|
80
99
|
return @oauth_2_0_state.auth_headers if @oauth_2_0_state
|
|
81
100
|
|
|
82
101
|
return {} unless @client_id && @client_secret
|
|
@@ -106,6 +125,8 @@ module Spotted
|
|
|
106
125
|
#
|
|
107
126
|
# @param client_secret [String, nil] Defaults to `ENV["SPOTIFY_CLIENT_SECRET"]`
|
|
108
127
|
#
|
|
128
|
+
# @param access_token [String, nil] Defaults to `ENV["SPOTIFY_ACCESS_TOKEN"]`
|
|
129
|
+
#
|
|
109
130
|
# @param base_url [String, nil] Override the default base URL for the API, e.g.,
|
|
110
131
|
# `"https://api.example.com/v2/"`. Defaults to `ENV["SPOTTED_BASE_URL"]`
|
|
111
132
|
#
|
|
@@ -119,6 +140,7 @@ module Spotted
|
|
|
119
140
|
def initialize(
|
|
120
141
|
client_id: ENV["SPOTIFY_CLIENT_ID"],
|
|
121
142
|
client_secret: ENV["SPOTIFY_CLIENT_SECRET"],
|
|
143
|
+
access_token: ENV["SPOTIFY_ACCESS_TOKEN"],
|
|
122
144
|
base_url: ENV["SPOTTED_BASE_URL"],
|
|
123
145
|
max_retries: self.class::DEFAULT_MAX_RETRIES,
|
|
124
146
|
timeout: self.class::DEFAULT_TIMEOUT_IN_SECONDS,
|
|
@@ -127,18 +149,9 @@ module Spotted
|
|
|
127
149
|
)
|
|
128
150
|
base_url ||= "https://api.spotify.com/v1"
|
|
129
151
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
if client_secret.nil?
|
|
134
|
-
# Ignore Layout/LineLength
|
|
135
|
-
# rubocop:disable Layout/LineLength
|
|
136
|
-
raise ArgumentError.new("client_secret is required, and can be set via environ: \"SPOTIFY_CLIENT_SECRET\"")
|
|
137
|
-
# rubocop:enable Layout/LineLength
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
@client_id = client_id.to_s
|
|
141
|
-
@client_secret = client_secret.to_s
|
|
152
|
+
@client_id = client_id&.to_s
|
|
153
|
+
@client_secret = client_secret&.to_s
|
|
154
|
+
@access_token = access_token&.to_s
|
|
142
155
|
|
|
143
156
|
super(
|
|
144
157
|
base_url: base_url,
|
|
@@ -165,5 +178,39 @@ module Spotted
|
|
|
165
178
|
@recommendations = Spotted::Resources::Recommendations.new(client: self)
|
|
166
179
|
@markets = Spotted::Resources::Markets.new(client: self)
|
|
167
180
|
end
|
|
181
|
+
|
|
182
|
+
# Generates the Spotify authorization URL for OAuth2 authorization code flow.
|
|
183
|
+
#
|
|
184
|
+
# @param redirect_uri [String] The URI to redirect to after authorization
|
|
185
|
+
# @param scope [String, Array<String>, nil] Space-delimited string or array of authorization scopes
|
|
186
|
+
# @param state [String, nil] Optional state parameter for CSRF protection
|
|
187
|
+
# @param show_dialog [Boolean] Whether to force the user to approve the app again
|
|
188
|
+
#
|
|
189
|
+
# @return [String] The authorization URL to redirect the user to
|
|
190
|
+
#
|
|
191
|
+
# @example Basic usage
|
|
192
|
+
# client = Spotted::Client.new(client_id: "...", client_secret: "...")
|
|
193
|
+
# url = client.authorization_url(redirect_uri: "http://localhost:3000/callback")
|
|
194
|
+
#
|
|
195
|
+
# @example With scopes and state
|
|
196
|
+
# url = client.authorization_url(
|
|
197
|
+
# redirect_uri: "http://localhost:3000/callback",
|
|
198
|
+
# scope: ["user-read-private", "user-read-email"],
|
|
199
|
+
# state: "random_state_string"
|
|
200
|
+
# )
|
|
201
|
+
def authorization_url(redirect_uri:, scope: nil, state: nil, show_dialog: false)
|
|
202
|
+
params = {
|
|
203
|
+
client_id: @client_id,
|
|
204
|
+
response_type: "code",
|
|
205
|
+
redirect_uri: redirect_uri
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
params[:scope] = scope.is_a?(Array) ? scope.join(" ") : scope if scope
|
|
209
|
+
params[:state] = state if state
|
|
210
|
+
params[:show_dialog] = "true" if show_dialog
|
|
211
|
+
|
|
212
|
+
query_string = URI.encode_www_form(params)
|
|
213
|
+
"https://accounts.spotify.com/authorize?#{query_string}"
|
|
214
|
+
end
|
|
168
215
|
end
|
|
169
216
|
end
|
data/lib/spotted/version.rb
CHANGED
data/rbi/spotted/client.rbi
CHANGED
|
@@ -10,12 +10,15 @@ module Spotted
|
|
|
10
10
|
|
|
11
11
|
DEFAULT_MAX_RETRY_DELAY = T.let(8.0, Float)
|
|
12
12
|
|
|
13
|
-
sig { returns(String) }
|
|
13
|
+
sig { returns(T.nilable(String)) }
|
|
14
14
|
attr_reader :client_id
|
|
15
15
|
|
|
16
|
-
sig { returns(String) }
|
|
16
|
+
sig { returns(T.nilable(String)) }
|
|
17
17
|
attr_reader :client_secret
|
|
18
18
|
|
|
19
|
+
sig { returns(T.nilable(String)) }
|
|
20
|
+
attr_reader :access_token
|
|
21
|
+
|
|
19
22
|
sig { returns(Spotted::Resources::Albums) }
|
|
20
23
|
attr_reader :albums
|
|
21
24
|
|
|
@@ -64,13 +67,23 @@ module Spotted
|
|
|
64
67
|
sig { returns(Spotted::Resources::Markets) }
|
|
65
68
|
attr_reader :markets
|
|
66
69
|
|
|
70
|
+
# @api private
|
|
71
|
+
sig { override.returns(T::Hash[String, String]) }
|
|
72
|
+
private def auth_headers
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# @api private
|
|
76
|
+
sig { returns(T::Hash[String, String]) }
|
|
77
|
+
private def bearer_auth
|
|
78
|
+
end
|
|
79
|
+
|
|
67
80
|
# @api private
|
|
68
81
|
sig { returns(Spotted::Internal::OAuth2ClientCredentials) }
|
|
69
82
|
attr_reader :oauth_2_0_state
|
|
70
83
|
|
|
71
84
|
# @api private
|
|
72
|
-
sig {
|
|
73
|
-
private def
|
|
85
|
+
sig { returns(T::Hash[String, String]) }
|
|
86
|
+
private def oauth_2_0
|
|
74
87
|
end
|
|
75
88
|
|
|
76
89
|
# Creates and returns a new client for interacting with the API.
|
|
@@ -78,6 +91,7 @@ module Spotted
|
|
|
78
91
|
params(
|
|
79
92
|
client_id: T.nilable(String),
|
|
80
93
|
client_secret: T.nilable(String),
|
|
94
|
+
access_token: T.nilable(String),
|
|
81
95
|
base_url: T.nilable(String),
|
|
82
96
|
max_retries: Integer,
|
|
83
97
|
timeout: Float,
|
|
@@ -90,6 +104,8 @@ module Spotted
|
|
|
90
104
|
client_id: ENV["SPOTIFY_CLIENT_ID"],
|
|
91
105
|
# Defaults to `ENV["SPOTIFY_CLIENT_SECRET"]`
|
|
92
106
|
client_secret: ENV["SPOTIFY_CLIENT_SECRET"],
|
|
107
|
+
# Defaults to `ENV["SPOTIFY_ACCESS_TOKEN"]`
|
|
108
|
+
access_token: ENV["SPOTIFY_ACCESS_TOKEN"],
|
|
93
109
|
# Override the default base URL for the API, e.g.,
|
|
94
110
|
# `"https://api.example.com/v2/"`. Defaults to `ENV["SPOTTED_BASE_URL"]`
|
|
95
111
|
base_url: ENV["SPOTTED_BASE_URL"],
|
data/sig/spotted/client.rbs
CHANGED
|
@@ -8,9 +8,11 @@ module Spotted
|
|
|
8
8
|
|
|
9
9
|
DEFAULT_MAX_RETRY_DELAY: Float
|
|
10
10
|
|
|
11
|
-
attr_reader client_id: String
|
|
11
|
+
attr_reader client_id: String?
|
|
12
12
|
|
|
13
|
-
attr_reader client_secret: String
|
|
13
|
+
attr_reader client_secret: String?
|
|
14
|
+
|
|
15
|
+
attr_reader access_token: String?
|
|
14
16
|
|
|
15
17
|
attr_reader albums: Spotted::Resources::Albums
|
|
16
18
|
|
|
@@ -44,14 +46,19 @@ module Spotted
|
|
|
44
46
|
|
|
45
47
|
attr_reader markets: Spotted::Resources::Markets
|
|
46
48
|
|
|
49
|
+
private def auth_headers: -> ::Hash[String, String]
|
|
50
|
+
|
|
51
|
+
private def bearer_auth: -> ::Hash[String, String]
|
|
52
|
+
|
|
47
53
|
# @api private
|
|
48
54
|
attr_reader oauth_2_0_state: Spotted::Internal::OAuth2ClientCredentials
|
|
49
55
|
|
|
50
|
-
private def
|
|
56
|
+
private def oauth_2_0: -> ::Hash[String, String]
|
|
51
57
|
|
|
52
58
|
def initialize: (
|
|
53
59
|
?client_id: String?,
|
|
54
60
|
?client_secret: String?,
|
|
61
|
+
?access_token: String?,
|
|
55
62
|
?base_url: String?,
|
|
56
63
|
?max_retries: Integer,
|
|
57
64
|
?timeout: Float,
|