tiktok_business_api 0.1.0 → 0.2.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/README.md +50 -0
- data/lib/tiktok_business_api/client.rb +84 -44
- data/lib/tiktok_business_api/errors.rb +10 -2
- data/lib/tiktok_business_api/resources/ad.rb +102 -0
- data/lib/tiktok_business_api/resources/adgroup.rb +92 -0
- data/lib/tiktok_business_api/resources/base_resource.rb +15 -15
- data/lib/tiktok_business_api/resources/campaign.rb +9 -103
- data/lib/tiktok_business_api/resources/crud_resource.rb +202 -0
- data/lib/tiktok_business_api/resources/identity.rb +122 -0
- data/lib/tiktok_business_api/resources/image.rb +151 -0
- data/lib/tiktok_business_api/utils.rb +58 -0
- data/lib/tiktok_business_api/version.rb +1 -1
- data/lib/tiktok_business_api.rb +7 -0
- metadata +42 -11
- data/bin/console +0 -11
- data/bin/publish_gem +0 -29
- data/lib/tiktok_ads_api.rb +0 -40
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b6df153f640ad431a27d8f06557b1424f2846d8adda17fa1189dfe6bded264e0
|
4
|
+
data.tar.gz: '01822882ce8e801f197433adb20e35c74eae2fa156f66521492e71d3f7dd75d9'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2b1a377795b3a0cf988b83f1e0f2325166cad9ffbb19ee9d01b3e047e4afab9d9ef0a1824f4ba0faf0b971b5e58fb10b40e2c7a40c02633c383d2e1687f97a8a
|
7
|
+
data.tar.gz: 64840256b05f91aad973ad96b5a6ad980bd01d82e9086e0784df96abd9f5a08a59dc7cf3615a821ad8f2a63d0e29b555d45fcab7c71d42e81e9dacfdcaeff256
|
data/README.md
CHANGED
@@ -113,6 +113,56 @@ client.campaigns.list_all(advertiser_id) do |campaign|
|
|
113
113
|
end
|
114
114
|
```
|
115
115
|
|
116
|
+
### Working with Identities
|
117
|
+
|
118
|
+
The Identity feature allows you to create Spark Ads by working with authorized TikTok accounts.
|
119
|
+
|
120
|
+
```ruby
|
121
|
+
# List available identities
|
122
|
+
identities = client.identities.list(advertiser_id: 'your_advertiser_id')
|
123
|
+
|
124
|
+
# Get information about a specific identity
|
125
|
+
identity_info = client.identities.get_info(
|
126
|
+
advertiser_id: 'your_advertiser_id',
|
127
|
+
identity_id: 'identity_id',
|
128
|
+
identity_type: 'TT_USER'
|
129
|
+
)
|
130
|
+
|
131
|
+
# Create a custom user identity
|
132
|
+
new_identity = client.identities.create(
|
133
|
+
advertiser_id: 'your_advertiser_id',
|
134
|
+
display_name: 'My Custom Identity',
|
135
|
+
image_uri: 'image_id_from_uploaded_image' # Optional
|
136
|
+
)
|
137
|
+
|
138
|
+
# List all identities with pagination
|
139
|
+
client.identities.list_all(advertiser_id: 'your_advertiser_id') do |identity|
|
140
|
+
puts "Identity: #{identity['display_name']} (#{identity['identity_type']})"
|
141
|
+
end
|
142
|
+
```
|
143
|
+
|
144
|
+
### Working with Images
|
145
|
+
|
146
|
+
Upload and manage images for your ads:
|
147
|
+
|
148
|
+
```ruby
|
149
|
+
# Upload an image file
|
150
|
+
uploaded_image = client.images.upload(
|
151
|
+
advertiser_id: 'your_advertiser_id',
|
152
|
+
image_file: File.open('/path/to/image.jpg')
|
153
|
+
)
|
154
|
+
|
155
|
+
# Get image info
|
156
|
+
image_info = client.images.get_info('your_advertiser_id', uploaded_image['image_id'])
|
157
|
+
|
158
|
+
# Search for images
|
159
|
+
images = client.images.search(
|
160
|
+
advertiser_id: 'your_advertiser_id',
|
161
|
+
page: 1,
|
162
|
+
page_size: 20
|
163
|
+
)
|
164
|
+
```
|
165
|
+
|
116
166
|
## Logging Requests and Responses
|
117
167
|
|
118
168
|
You can enable debug logging to see all API requests and responses:
|
@@ -5,42 +5,42 @@ module TiktokBusinessApi
|
|
5
5
|
class Client
|
6
6
|
# @return [TiktokBusinessApi::Config] Client configuration
|
7
7
|
attr_reader :config
|
8
|
-
|
8
|
+
|
9
9
|
# @return [TiktokBusinessApi::Auth] Authentication handler
|
10
10
|
attr_reader :auth
|
11
|
-
|
11
|
+
|
12
12
|
# Initialize a new client
|
13
13
|
#
|
14
14
|
# @param options [Hash] Override configuration options
|
15
15
|
def initialize(options = {})
|
16
|
-
@config = TiktokBusinessApi.config.dup
|
17
|
-
|
16
|
+
@config = TiktokBusinessApi.config.dup || Config.new
|
17
|
+
|
18
18
|
# Override config with options
|
19
19
|
options.each do |key, value|
|
20
20
|
@config.send("#{key}=", value) if @config.respond_to?("#{key}=")
|
21
21
|
end
|
22
|
-
|
22
|
+
|
23
23
|
@auth = Auth.new(self)
|
24
24
|
@resources = {}
|
25
25
|
end
|
26
|
-
|
26
|
+
|
27
27
|
# Get or create a resource instance
|
28
28
|
#
|
29
29
|
# @param resource_name [Symbol] Name of the resource
|
30
30
|
# @return [BaseResource] Resource instance
|
31
31
|
def resource(resource_name)
|
32
32
|
@resources[resource_name] ||= begin
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
33
|
+
# Convert resource_name to class name (e.g., :campaign => Campaign)
|
34
|
+
class_name = resource_name.to_s.split('_').map(&:capitalize).join
|
35
|
+
|
36
|
+
# Get the resource class
|
37
|
+
resource_class = TiktokBusinessApi::Resources.const_get(class_name)
|
38
|
+
|
39
|
+
# Create an instance
|
40
|
+
resource_class.new(self)
|
41
|
+
end
|
42
42
|
end
|
43
|
-
|
43
|
+
|
44
44
|
# Make an HTTP request to the TikTok Business API
|
45
45
|
#
|
46
46
|
# @param method [Symbol] HTTP method (:get, :post, :put, :delete)
|
@@ -50,46 +50,83 @@ module TiktokBusinessApi
|
|
50
50
|
# @return [Hash] Parsed API response
|
51
51
|
def request(method, path, params = {}, headers = {})
|
52
52
|
url = File.join(@config.api_base_url, path)
|
53
|
-
|
53
|
+
|
54
54
|
# Set up default headers
|
55
55
|
default_headers = {
|
56
56
|
'Content-Type' => 'application/json'
|
57
57
|
}
|
58
|
-
|
58
|
+
|
59
59
|
# Add access token if available
|
60
60
|
if @config.access_token
|
61
61
|
default_headers['Access-Token'] = @config.access_token
|
62
62
|
end
|
63
|
-
|
63
|
+
|
64
64
|
# Merge with custom headers
|
65
65
|
headers = default_headers.merge(headers)
|
66
|
-
|
66
|
+
|
67
67
|
# Log the request
|
68
68
|
log_request(method, url, params, headers)
|
69
|
-
|
69
|
+
|
70
70
|
# Build the request
|
71
71
|
response = connection.run_request(method, url, nil, headers) do |req|
|
72
72
|
case method
|
73
73
|
when :get, :delete
|
74
74
|
req.params = params
|
75
75
|
when :post, :put
|
76
|
-
|
76
|
+
if headers['Content-Type'] == 'multipart/form-data'
|
77
|
+
# For multipart form data, let Faraday handle it
|
78
|
+
req.options.timeout = 120 # Extend timeout for file uploads
|
79
|
+
req.body = {} # Initialize the body as an empty hash
|
80
|
+
params.each do |key, value|
|
81
|
+
req.body[key.to_sym] = value
|
82
|
+
end
|
83
|
+
else
|
84
|
+
req.body = JSON.generate(params) unless params.empty?
|
85
|
+
end
|
77
86
|
end
|
78
87
|
end
|
79
|
-
|
88
|
+
|
80
89
|
# Parse and handle the response
|
81
90
|
handle_response(response)
|
82
91
|
end
|
83
|
-
|
92
|
+
|
84
93
|
# Access to campaign resource
|
85
94
|
#
|
86
95
|
# @return [TiktokBusinessApi::Resources::Campaign] Campaign resource
|
87
96
|
def campaigns
|
88
97
|
resource(:campaign)
|
89
98
|
end
|
90
|
-
|
99
|
+
|
100
|
+
# Access to ad group resource
|
101
|
+
#
|
102
|
+
# @return [TiktokBusinessApi::Resources::Adgroup] Ad group resource
|
103
|
+
def adgroups
|
104
|
+
resource(:adgroup)
|
105
|
+
end
|
106
|
+
|
107
|
+
# Access to ad resource
|
108
|
+
#
|
109
|
+
# @return [TiktokBusinessApi::Resources::Ad] Ad resource
|
110
|
+
def ads
|
111
|
+
resource(:ad)
|
112
|
+
end
|
113
|
+
|
114
|
+
# Access to image resource
|
115
|
+
#
|
116
|
+
# @return [TiktokBusinessApi::Resources::Image] Image resource
|
117
|
+
def images
|
118
|
+
resource(:image)
|
119
|
+
end
|
120
|
+
|
121
|
+
# Access to identity resource
|
122
|
+
#
|
123
|
+
# @return [TiktokBusinessApi::Resources::Identity] Identity resource
|
124
|
+
def identities
|
125
|
+
resource(:identity)
|
126
|
+
end
|
127
|
+
|
91
128
|
private
|
92
|
-
|
129
|
+
|
93
130
|
# Set up Faraday connection
|
94
131
|
#
|
95
132
|
# @return [Faraday::Connection] Faraday connection
|
@@ -97,16 +134,19 @@ module TiktokBusinessApi
|
|
97
134
|
@connection ||= Faraday.new do |conn|
|
98
135
|
conn.options.timeout = @config.timeout
|
99
136
|
conn.options.open_timeout = @config.open_timeout
|
100
|
-
|
137
|
+
|
101
138
|
# Set up middleware
|
102
139
|
conn.use Faraday::Response::Logger, @config.logger if @config.logger
|
103
140
|
conn.use Faraday::FollowRedirects::Middleware
|
104
141
|
conn.use Faraday::Retry::Middleware, max: 3
|
105
|
-
|
142
|
+
|
143
|
+
# Use multipart middleware for file uploads
|
144
|
+
conn.request :multipart
|
145
|
+
|
106
146
|
conn.adapter Faraday.default_adapter
|
107
147
|
end
|
108
148
|
end
|
109
|
-
|
149
|
+
|
110
150
|
# Parse and handle the API response
|
111
151
|
#
|
112
152
|
# @param response [Faraday::Response] HTTP response
|
@@ -115,26 +155,26 @@ module TiktokBusinessApi
|
|
115
155
|
def handle_response(response)
|
116
156
|
# Log the response
|
117
157
|
log_response(response)
|
118
|
-
|
158
|
+
|
119
159
|
# Parse the response body
|
120
160
|
body = if response.body && !response.body.empty?
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
161
|
+
begin
|
162
|
+
JSON.parse(response.body)
|
163
|
+
rescue JSON::ParserError
|
164
|
+
{ error: "Invalid JSON response: #{response.body}" }
|
165
|
+
end
|
166
|
+
else
|
167
|
+
{}
|
168
|
+
end
|
169
|
+
|
130
170
|
# Check for API errors
|
131
171
|
if !response.success? || (body.is_a?(Hash) && body['code'] != 0)
|
132
172
|
raise ErrorFactory.from_response(response)
|
133
173
|
end
|
134
|
-
|
174
|
+
|
135
175
|
body
|
136
176
|
end
|
137
|
-
|
177
|
+
|
138
178
|
# Log the request details
|
139
179
|
#
|
140
180
|
# @param method [Symbol] HTTP method
|
@@ -143,18 +183,18 @@ module TiktokBusinessApi
|
|
143
183
|
# @param headers [Hash] Request headers
|
144
184
|
def log_request(method, url, params, headers)
|
145
185
|
return unless @config.debug && @config.logger
|
146
|
-
|
186
|
+
|
147
187
|
@config.logger.debug "[TiktokBusinessApi] Request: #{method.upcase} #{url}"
|
148
188
|
@config.logger.debug "[TiktokBusinessApi] Parameters: #{params.inspect}"
|
149
189
|
@config.logger.debug "[TiktokBusinessApi] Headers: #{headers.inspect}"
|
150
190
|
end
|
151
|
-
|
191
|
+
|
152
192
|
# Log the response details
|
153
193
|
#
|
154
194
|
# @param response [Faraday::Response] HTTP response
|
155
195
|
def log_response(response)
|
156
196
|
return unless @config.debug && @config.logger
|
157
|
-
|
197
|
+
|
158
198
|
@config.logger.debug "[TiktokBusinessApi] Response Status: #{response.status}"
|
159
199
|
@config.logger.debug "[TiktokBusinessApi] Response Body: #{response.body}"
|
160
200
|
end
|
@@ -50,12 +50,20 @@ module TiktokBusinessApi
|
|
50
50
|
# @return [TiktokBusinessApi::Error] The appropriate error object
|
51
51
|
def self.from_response(response, request = nil)
|
52
52
|
status = response.status
|
53
|
-
body = response.body
|
53
|
+
body = if response.body && !response.body.empty?
|
54
|
+
begin
|
55
|
+
JSON.parse(response.body)
|
56
|
+
rescue JSON::ParserError
|
57
|
+
{ error: "Invalid JSON response: #{response.body}" }
|
58
|
+
end
|
59
|
+
else
|
60
|
+
{}
|
61
|
+
end
|
54
62
|
|
55
63
|
# Parse TikTok API response which has its own error structure
|
56
64
|
error_code = body.is_a?(Hash) ? body['code'] : nil
|
57
65
|
error_message = body.is_a?(Hash) ? body['message'] : nil
|
58
|
-
|
66
|
+
|
59
67
|
# Determine the error class based on status and error code
|
60
68
|
klass = case status
|
61
69
|
when 401
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TiktokBusinessApi
|
4
|
+
module Resources
|
5
|
+
# Ad resource for the TikTok Business API
|
6
|
+
class Ad < CrudResource
|
7
|
+
RESOURCE_NAME = 'ad'
|
8
|
+
|
9
|
+
def get(advertiser_id:, ad_id:)
|
10
|
+
list(advertiser_id: advertiser_id, filtering: {ad_ids: [ad_id]}).first
|
11
|
+
end
|
12
|
+
|
13
|
+
# Create a new ad
|
14
|
+
#
|
15
|
+
# @param advertiser_id [String] Advertiser ID
|
16
|
+
# @param adgroup_id [String] Ad group ID
|
17
|
+
# @param creatives [Array<Hash>] Array of creative objects
|
18
|
+
# @return [Hash] New ad data with created ad IDs
|
19
|
+
def create(advertiser_id:, adgroup_id:, creatives:)
|
20
|
+
params = {
|
21
|
+
advertiser_id: advertiser_id,
|
22
|
+
adgroup_id: adgroup_id,
|
23
|
+
creatives: creatives
|
24
|
+
}
|
25
|
+
|
26
|
+
response = _http_post(create_path, params)
|
27
|
+
response['data']
|
28
|
+
end
|
29
|
+
|
30
|
+
def list(advertiser_id:, campaign_id: nil, adgroup_id: nil, filtering: {}, page_size: nil, page: nil, **other_params, &block)
|
31
|
+
filtering[:campaign_ids] = [campaign_id] if campaign_id
|
32
|
+
filtering[:adgroup_ids] = [adgroup_id] if adgroup_id
|
33
|
+
super(filtering: filtering, page_size: page_size, page: page, **other_params.merge(advertiser_id: advertiser_id), &block)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Update an ad
|
37
|
+
#
|
38
|
+
# @param advertiser_id [String] Advertiser ID
|
39
|
+
# @param ad_id [String] Ad ID
|
40
|
+
# @param params [Hash] Ad parameters to update
|
41
|
+
# @return [Hash] Updated ad data
|
42
|
+
def update(advertiser_id:, ad_id:, **params)
|
43
|
+
params = params.merge(
|
44
|
+
advertiser_id: advertiser_id,
|
45
|
+
ad_id: ad_id
|
46
|
+
)
|
47
|
+
|
48
|
+
response = _http_post(update_path, params)
|
49
|
+
response['data']
|
50
|
+
end
|
51
|
+
|
52
|
+
# Update ad status (enable/disable)
|
53
|
+
#
|
54
|
+
# @param advertiser_id [String] Advertiser ID
|
55
|
+
# @param ad_id [String] Ad ID
|
56
|
+
# @param status [String] New status ('ENABLE' or 'DISABLE')
|
57
|
+
# @return [Hash] Result
|
58
|
+
def update_status(advertiser_id:, ad_id:, status:)
|
59
|
+
params = {
|
60
|
+
advertiser_id: advertiser_id,
|
61
|
+
ad_ids: [ad_id],
|
62
|
+
operation_status: status
|
63
|
+
}
|
64
|
+
|
65
|
+
response = _http_post('status/update/', params)
|
66
|
+
response['data']
|
67
|
+
end
|
68
|
+
|
69
|
+
# Delete an ad
|
70
|
+
#
|
71
|
+
# @param advertiser_id [String] Advertiser ID
|
72
|
+
# @param ad_id [String] Ad ID
|
73
|
+
# @return [Hash] Result
|
74
|
+
def delete(advertiser_id:, ad_id:)
|
75
|
+
params = {
|
76
|
+
advertiser_id: advertiser_id,
|
77
|
+
ad_ids: [ad_id]
|
78
|
+
}
|
79
|
+
|
80
|
+
response = _http_post(delete_path, params)
|
81
|
+
response['data']
|
82
|
+
end
|
83
|
+
|
84
|
+
# Create Smart Creative ads
|
85
|
+
#
|
86
|
+
# @param advertiser_id [String] Advertiser ID
|
87
|
+
# @param adgroup_id [String] Ad group ID
|
88
|
+
# @param creatives [Array<Hash>] Array of creative objects
|
89
|
+
# @return [Hash] New Smart Creative ad data
|
90
|
+
def create_aco(advertiser_id:, adgroup_id:, creatives:)
|
91
|
+
params = {
|
92
|
+
advertiser_id: advertiser_id,
|
93
|
+
adgroup_id: adgroup_id,
|
94
|
+
creatives: creatives
|
95
|
+
}
|
96
|
+
|
97
|
+
response = _http_post('aco/create/', params)
|
98
|
+
response['data']
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TiktokBusinessApi
|
4
|
+
module Resources
|
5
|
+
# AdGroup resource for the TikTok Business API
|
6
|
+
class Adgroup < CrudResource
|
7
|
+
RESOURCE_NAME = 'adgroup'
|
8
|
+
|
9
|
+
def get(advertiser_id:, adgroup_id:)
|
10
|
+
list(advertiser_id: advertiser_id, filtering: {adgroup_ids: [adgroup_id]}).first
|
11
|
+
end
|
12
|
+
|
13
|
+
# Create a new ad group
|
14
|
+
#
|
15
|
+
# @param advertiser_id [String] Advertiser ID
|
16
|
+
# @param params [Hash] Ad group parameters
|
17
|
+
# @return [Hash] New ad group data
|
18
|
+
def create(advertiser_id, params = {})
|
19
|
+
# Ensure advertiser_id is included in the params
|
20
|
+
params = params.merge(advertiser_id: advertiser_id)
|
21
|
+
|
22
|
+
response = _http_post(create_path, params)
|
23
|
+
response['data']
|
24
|
+
end
|
25
|
+
|
26
|
+
def list(advertiser_id:, campaign_id: nil, filtering: {}, page_size: nil, page: nil, **other_params, &block)
|
27
|
+
filtering[:campaign_ids] = [campaign_id] if campaign_id
|
28
|
+
super(filtering: filtering, page_size: page_size, page: page, **other_params.merge(advertiser_id: advertiser_id), &block)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Update an ad group
|
32
|
+
#
|
33
|
+
# @param advertiser_id [String] Advertiser ID
|
34
|
+
# @param adgroup_id [String] Ad group ID
|
35
|
+
# @param params [Hash] Ad group parameters to update
|
36
|
+
# @return [Hash] Updated ad group data
|
37
|
+
def update(advertiser_id, adgroup_id, params = {})
|
38
|
+
params = params.merge(
|
39
|
+
advertiser_id: advertiser_id,
|
40
|
+
adgroup_id: adgroup_id
|
41
|
+
)
|
42
|
+
|
43
|
+
response = _http_post(update_path, params)
|
44
|
+
response['data']
|
45
|
+
end
|
46
|
+
|
47
|
+
# Update ad group status (enable/disable)
|
48
|
+
#
|
49
|
+
# @param advertiser_id [String] Advertiser ID
|
50
|
+
# @param adgroup_id [String] Ad group ID
|
51
|
+
# @param status [String] New status ('ENABLE' or 'DISABLE')
|
52
|
+
# @return [Hash] Result
|
53
|
+
def update_status(advertiser_id, adgroup_id, status)
|
54
|
+
params = {
|
55
|
+
advertiser_id: advertiser_id,
|
56
|
+
adgroup_ids: [adgroup_id],
|
57
|
+
operation_status: status
|
58
|
+
}
|
59
|
+
|
60
|
+
response = _http_post('status/update/', params)
|
61
|
+
response['data']
|
62
|
+
end
|
63
|
+
|
64
|
+
# Delete an ad group
|
65
|
+
#
|
66
|
+
# @param advertiser_id [String] Advertiser ID
|
67
|
+
# @param adgroup_id [String] Ad group ID
|
68
|
+
# @return [Hash] Result
|
69
|
+
def delete(advertiser_id, adgroup_id)
|
70
|
+
params = {
|
71
|
+
advertiser_id: advertiser_id,
|
72
|
+
adgroup_ids: [adgroup_id]
|
73
|
+
}
|
74
|
+
|
75
|
+
response = _http_post(delete_path, params)
|
76
|
+
response['data']
|
77
|
+
end
|
78
|
+
|
79
|
+
# Estimate audience size for an ad group
|
80
|
+
#
|
81
|
+
# @param advertiser_id [String] Advertiser ID
|
82
|
+
# @param params [Hash] Targeting parameters for estimation
|
83
|
+
# @return [Hash] Audience size estimation
|
84
|
+
def estimate_audience_size(advertiser_id, params = {})
|
85
|
+
params = params.merge(advertiser_id: advertiser_id)
|
86
|
+
|
87
|
+
response = _http_post('audience_size/estimate/', params)
|
88
|
+
response['data']
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -6,57 +6,57 @@ module TiktokBusinessApi
|
|
6
6
|
class BaseResource
|
7
7
|
# @return [TiktokBusinessApi::Client] Client instance
|
8
8
|
attr_reader :client
|
9
|
-
|
9
|
+
|
10
10
|
# Initialize a new resource
|
11
11
|
#
|
12
12
|
# @param client [TiktokBusinessApi::Client] Client instance
|
13
13
|
def initialize(client)
|
14
14
|
@client = client
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
17
|
# Get the resource name (used for endpoint paths)
|
18
18
|
#
|
19
19
|
# @return [String] Resource name
|
20
20
|
def resource_name
|
21
21
|
self.class.name.split('::').last.downcase
|
22
22
|
end
|
23
|
-
|
23
|
+
|
24
24
|
# Get the API version
|
25
25
|
#
|
26
26
|
# @return [String] API version
|
27
27
|
def api_version
|
28
28
|
'v1.3'
|
29
29
|
end
|
30
|
-
|
30
|
+
|
31
31
|
# Get the base path for this resource
|
32
32
|
#
|
33
33
|
# @return [String] Base path
|
34
34
|
def base_path
|
35
35
|
"#{api_version}/#{resource_name}/"
|
36
36
|
end
|
37
|
-
|
37
|
+
|
38
38
|
# Make a GET request to the resource
|
39
39
|
#
|
40
40
|
# @param path [String] Path relative to the resource base path
|
41
41
|
# @param params [Hash] Query parameters
|
42
42
|
# @param headers [Hash] Custom headers
|
43
43
|
# @return [Hash] Response data
|
44
|
-
def
|
44
|
+
def _http_get(path, params = {}, headers = {})
|
45
45
|
full_path = File.join(base_path, path)
|
46
46
|
client.request(:get, full_path, params, headers)
|
47
47
|
end
|
48
|
-
|
48
|
+
|
49
49
|
# Make a POST request to the resource
|
50
50
|
#
|
51
51
|
# @param path [String] Path relative to the resource base path
|
52
52
|
# @param params [Hash] Body parameters
|
53
53
|
# @param headers [Hash] Custom headers
|
54
54
|
# @return [Hash] Response data
|
55
|
-
def
|
55
|
+
def _http_post(path, params = {}, headers = {})
|
56
56
|
full_path = File.join(base_path, path)
|
57
57
|
client.request(:post, full_path, params, headers)
|
58
58
|
end
|
59
|
-
|
59
|
+
|
60
60
|
# Handle pagination for list endpoints
|
61
61
|
#
|
62
62
|
# @param path [String] Path relative to the resource base path
|
@@ -71,28 +71,28 @@ module TiktokBusinessApi
|
|
71
71
|
page = 1
|
72
72
|
page_size = params[:page_size] || 10
|
73
73
|
has_more = true
|
74
|
-
|
74
|
+
|
75
75
|
while has_more
|
76
76
|
params[:page] = page
|
77
77
|
params[:page_size] = page_size
|
78
|
-
|
78
|
+
|
79
79
|
response = get(path, params, headers)
|
80
|
-
|
80
|
+
|
81
81
|
# Extract data from the response
|
82
82
|
current_items = response.dig('data', data_key) || []
|
83
|
-
|
83
|
+
|
84
84
|
if block_given?
|
85
85
|
current_items.each { |item| yield(item) }
|
86
86
|
else
|
87
87
|
items.concat(current_items)
|
88
88
|
end
|
89
|
-
|
89
|
+
|
90
90
|
# Check if there are more pages
|
91
91
|
page_info = response.dig('data', 'page_info') || {}
|
92
92
|
has_more = page_info['has_more'] == true
|
93
93
|
page += 1
|
94
94
|
end
|
95
|
-
|
95
|
+
|
96
96
|
block_given? ? nil : items
|
97
97
|
end
|
98
98
|
end
|