Manual Audience Segments API read endpoints (#19542)
* getting and listing segments * get list of users in segment * docs???
This commit is contained in:
parent
bbb9fd2e1e
commit
453c2eeccb
7 changed files with 562 additions and 9 deletions
|
|
@ -1,12 +1,30 @@
|
|||
module Api
|
||||
module V1
|
||||
class AudienceSegmentsController < ApiController
|
||||
DEFAULT_PER_PAGE = 30
|
||||
MAX_USER_IDS = 10_000
|
||||
USER_ATTRIBUTES_FOR_SERIALIZATION = %i[
|
||||
id username name twitter_username github_username
|
||||
profile_image website_url location summary created_at
|
||||
].freeze
|
||||
|
||||
before_action :authenticate_with_api_key!
|
||||
before_action :require_admin
|
||||
before_action :restrict_user_ids, only: %i[add_users remove_users]
|
||||
|
||||
def index
|
||||
page, per_page = pagination_params
|
||||
@segments = scope.including_user_counts.order(id: :desc).page(page).per(per_page)
|
||||
|
||||
render json: @segments
|
||||
end
|
||||
|
||||
def show
|
||||
@segment = scope.including_user_counts.find(params[:id])
|
||||
|
||||
render json: @segment
|
||||
end
|
||||
|
||||
def create
|
||||
@segment = AudienceSegment.create!(type_of: "manual")
|
||||
|
||||
|
|
@ -25,6 +43,13 @@ module Api
|
|||
end
|
||||
end
|
||||
|
||||
def users
|
||||
@segment = scope.find(params[:id])
|
||||
|
||||
page, per_page = pagination_params
|
||||
@users = @segment.users.joins(:profile).select(USER_ATTRIBUTES_FOR_SERIALIZATION).page(page).per(per_page)
|
||||
end
|
||||
|
||||
def add_users
|
||||
@segment = scope.find(params[:id])
|
||||
|
||||
|
|
@ -50,6 +75,14 @@ module Api
|
|||
render json: { error: "Too many user IDs provided" }, status: :unprocessable_entity
|
||||
end
|
||||
|
||||
def pagination_params
|
||||
per_page_param = params[:per_page] || DEFAULT_PER_PAGE
|
||||
max_per_page = ApplicationConfig["API_PER_PAGE_MAX"] || 1000
|
||||
per_page = [per_page_param, max_per_page].map(&:to_i).min
|
||||
|
||||
[params[:page].to_i, per_page]
|
||||
end
|
||||
|
||||
def scope
|
||||
AudienceSegment.manual
|
||||
end
|
||||
|
|
|
|||
|
|
@ -58,6 +58,12 @@ class AudienceSegment < ApplicationRecord
|
|||
.map { |(id, type)| [I18n.t("models.#{model_name.i18n_key}.type_ofs.#{type}"), id] }
|
||||
end
|
||||
|
||||
def self.including_user_counts
|
||||
left_joins(:segmented_users)
|
||||
.select("audience_segments.*, COUNT(segmented_users.id) AS user_count")
|
||||
.group("audience_segments.id")
|
||||
end
|
||||
|
||||
def persist_recently_active_users
|
||||
self.users = self.class.recently_active_users_in_segment(type_of)
|
||||
end
|
||||
|
|
|
|||
3
app/views/api/v1/audience_segments/users.json.jbuilder
Normal file
3
app/views/api/v1/audience_segments/users.json.jbuilder
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
json.array! @users do |user|
|
||||
json.partial! "api/v1/shared/user_show", user: user
|
||||
end
|
||||
|
|
@ -55,7 +55,8 @@ Rails.application.routes.draw do
|
|||
put "unpublish", on: :member
|
||||
end
|
||||
|
||||
resources :segments, controller: "audience_segments", only: %i[create destroy] do
|
||||
resources :segments, controller: "audience_segments", only: %i[index show create destroy] do
|
||||
get "users", on: :member
|
||||
put "add_users", on: :member
|
||||
put "remove_users", on: :member
|
||||
end
|
||||
|
|
|
|||
|
|
@ -46,6 +46,87 @@ RSpec.describe "Api::V1::AudienceSegments" do
|
|||
end
|
||||
end
|
||||
|
||||
describe "GET /api/segments" do
|
||||
subject(:make_request) { get api_segments_path, headers: headers }
|
||||
|
||||
let(:first_manual_segment) { AudienceSegment.create!(type_of: "manual") }
|
||||
let(:second_manual_segment) { AudienceSegment.create!(type_of: "manual") }
|
||||
let(:automatic_segment) { AudienceSegment.create!(type_of: "trusted") }
|
||||
let(:users) { create_list(:user, 3) }
|
||||
|
||||
it_behaves_like "an admin-only protected resource"
|
||||
|
||||
it "returns only manual segments including their user counts" do
|
||||
first_manual_segment.users << users
|
||||
expected_data = [
|
||||
hash_including(
|
||||
"id" => first_manual_segment.id,
|
||||
"type_of" => "manual",
|
||||
"user_count" => 3,
|
||||
),
|
||||
hash_including(
|
||||
"id" => second_manual_segment.id,
|
||||
"type_of" => "manual",
|
||||
"user_count" => 0,
|
||||
),
|
||||
]
|
||||
excluded_data = hash_including("id" => automatic_segment.id)
|
||||
|
||||
make_request
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
expect(response.media_type).to eq("application/json")
|
||||
|
||||
expect(response.parsed_body).to include(*expected_data)
|
||||
expect(response.parsed_body).not_to include(excluded_data)
|
||||
end
|
||||
|
||||
it "supports pagination" do
|
||||
5.times.each { AudienceSegment.create!(type_of: :manual) }
|
||||
|
||||
get api_segments_path, params: { page: 1, per_page: 3 }, headers: headers
|
||||
expect(response.parsed_body.length).to eq(3)
|
||||
get api_segments_path, params: { page: 2, per_page: 4 }, headers: headers
|
||||
expect(response.parsed_body.length).to eq(1)
|
||||
end
|
||||
|
||||
it "respects API_PER_PAGE_MAX env variable limit" do
|
||||
5.times.each { AudienceSegment.create!(type_of: :manual) }
|
||||
|
||||
allow(ApplicationConfig).to receive(:[]).and_return(nil)
|
||||
allow(ApplicationConfig).to receive(:[]).with("APP_PROTOCOL").and_return("http://")
|
||||
allow(ApplicationConfig).to receive(:[]).with("API_PER_PAGE_MAX").and_return(3)
|
||||
|
||||
get api_segments_path, params: { per_page: 10 }, headers: headers
|
||||
expect(response.parsed_body.length).to eq(3)
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET /api/segments/:id" do
|
||||
subject(:make_request) { get api_segment_path(segment.id), headers: headers }
|
||||
|
||||
let(:segment) { AudienceSegment.create!(type_of: "manual") }
|
||||
let(:users) { create_list(:user, 3) }
|
||||
|
||||
it_behaves_like "an admin-only protected resource"
|
||||
|
||||
it_behaves_like "an endpoint for only manual audience segments"
|
||||
|
||||
it "returns the segment including its user count" do
|
||||
segment.users << users
|
||||
|
||||
make_request
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
expect(response.media_type).to eq("application/json")
|
||||
expect(response.parsed_body).to include(
|
||||
"id" => segment.id,
|
||||
"type_of" => "manual",
|
||||
"user_count" => 3,
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
describe "POST /api/segments" do
|
||||
subject(:make_request) { post api_segments_path, headers: headers }
|
||||
|
||||
|
|
@ -66,6 +147,60 @@ RSpec.describe "Api::V1::AudienceSegments" do
|
|||
end
|
||||
end
|
||||
|
||||
describe "GET /api/segments/:id/users" do
|
||||
subject(:make_request) { get users_api_segment_path(segment.id), headers: headers }
|
||||
|
||||
let(:segment) { AudienceSegment.create!(type_of: "manual") }
|
||||
let(:users) { create_list(:user, 5) }
|
||||
|
||||
it_behaves_like "an admin-only protected resource"
|
||||
|
||||
it_behaves_like "an endpoint for only manual audience segments"
|
||||
|
||||
it "returns a list of users in the segment" do
|
||||
segment.users << users
|
||||
|
||||
expected_data = users.map do |user|
|
||||
hash_including(
|
||||
"id" => user.id,
|
||||
"username" => user.username,
|
||||
"name" => user.name,
|
||||
"twitter_username" => user.twitter_username,
|
||||
"github_username" => user.github_username,
|
||||
"summary" => user.profile.summary,
|
||||
"location" => user.profile.location,
|
||||
"website_url" => user.profile.website_url,
|
||||
)
|
||||
end
|
||||
|
||||
make_request
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
expect(response.media_type).to eq("application/json")
|
||||
expect(response.parsed_body).to match_array(expected_data)
|
||||
end
|
||||
|
||||
it "supports pagination" do
|
||||
segment.users << users
|
||||
|
||||
get users_api_segment_path(segment.id), params: { page: 1, per_page: 3 }, headers: headers
|
||||
expect(response.parsed_body.length).to eq(3)
|
||||
get users_api_segment_path(segment.id), params: { page: 2, per_page: 4 }, headers: headers
|
||||
expect(response.parsed_body.length).to eq(1)
|
||||
end
|
||||
|
||||
it "respects API_PER_PAGE_MAX env variable limit" do
|
||||
segment.users << users
|
||||
|
||||
allow(ApplicationConfig).to receive(:[]).and_return(nil)
|
||||
allow(ApplicationConfig).to receive(:[]).with("APP_PROTOCOL").and_return("http://")
|
||||
allow(ApplicationConfig).to receive(:[]).with("API_PER_PAGE_MAX").and_return(3)
|
||||
|
||||
get users_api_segment_path(segment.id), params: { per_page: 5 }, headers: headers
|
||||
expect(response.parsed_body.length).to eq(3)
|
||||
end
|
||||
end
|
||||
|
||||
describe "PUT /api/segments/:id/add_users" do
|
||||
subject(:make_request) do
|
||||
put add_users_api_segment_path(segment.id), params: { user_ids: user_ids }, headers: headers, as: :json
|
||||
|
|
|
|||
|
|
@ -25,11 +25,59 @@ RSpec.describe "Api::V1::Docs::AudienceSegments" do
|
|||
admin_api_secret.user.add_role(:admin)
|
||||
end
|
||||
|
||||
describe "POST /segments" do
|
||||
path "/api/segments" do
|
||||
path "/api/segments" do
|
||||
describe "GET /segments" do
|
||||
get "Manually managed audience segments" do
|
||||
tags "segments"
|
||||
description "This endpoint allows the client to retrieve a list of audience segments.
|
||||
|
||||
An audience segment is a group of users that can be targeted by a Billboard. This API only permits managing segments you create and maintain yourself.
|
||||
|
||||
The endpoint supports pagination, and each page will contain `30` segments by default."
|
||||
operationId "getSegments"
|
||||
produces "application/json"
|
||||
consumes "application/json"
|
||||
|
||||
parameter "$ref": "#/components/parameters/perPageParam30to1000"
|
||||
|
||||
response "200", "A List of manually managed audience segments" do
|
||||
let(:"api-key") { admin_api_secret.secret }
|
||||
let(:second_segment) { AudienceSegment.create!(type_of: "manual") }
|
||||
schema type: :array,
|
||||
items: { "$ref": "#/components/schemas/Segment" }
|
||||
|
||||
before do
|
||||
segment.users << users
|
||||
second_segment.users << create(:user)
|
||||
end
|
||||
|
||||
add_examples
|
||||
|
||||
run_test!
|
||||
end
|
||||
|
||||
response "401", "Unauthorized" do
|
||||
let(:"api-key") { nil }
|
||||
|
||||
add_examples
|
||||
|
||||
run_test!
|
||||
end
|
||||
|
||||
response "401", "Unauthorized" do
|
||||
let(:"api-key") { regular_api_secret.secret }
|
||||
|
||||
add_examples
|
||||
|
||||
run_test!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "POST /segments" do
|
||||
post "Create a manually managed audience segment" do
|
||||
tags "segments"
|
||||
description "This endpoint allows the client to create a new audience segment.\n\nAn audience segment is a group of users that can be targeted by a DisplayAd/Billboard/Widget. This API only permits managing segments you create and maintain yourself."
|
||||
description "This endpoint allows the client to create a new audience segment.\n\nAn audience segment is a group of users that can be targeted by a Billboard. This API only permits managing segments you create and maintain yourself."
|
||||
operationId "createSegment"
|
||||
produces "application/json"
|
||||
consumes "application/json"
|
||||
|
|
@ -61,11 +109,65 @@ RSpec.describe "Api::V1::Docs::AudienceSegments" do
|
|||
end
|
||||
end
|
||||
|
||||
describe "DELETE /segments/:id" do
|
||||
path "/api/segments/{id}" do
|
||||
path "/api/segments/{id}" do
|
||||
describe "GET /segments/:id" do
|
||||
get "A manually managed audience segment" do
|
||||
tags "segments"
|
||||
description "This endpoint allows the client to retrieve a single manually-managed audience segment specified by ID."
|
||||
operationId "getSegment"
|
||||
produces "application/json"
|
||||
consumes "application/json"
|
||||
|
||||
parameter name: :id, in: :path, required: true, schema: id_schema
|
||||
|
||||
response "200", "The audience segment" do
|
||||
let(:"api-key") { admin_api_secret.secret }
|
||||
let(:id) { segment.id }
|
||||
schema type: :object,
|
||||
items: { "$ref": "#/components/schemas/Segment" }
|
||||
|
||||
before do
|
||||
segment.users << users
|
||||
end
|
||||
|
||||
add_examples
|
||||
|
||||
run_test!
|
||||
end
|
||||
|
||||
response "401", "Unauthorized" do
|
||||
let(:"api-key") { nil }
|
||||
let(:id) { segment.id }
|
||||
|
||||
add_examples
|
||||
|
||||
run_test!
|
||||
end
|
||||
|
||||
response "401", "Unauthorized" do
|
||||
let(:"api-key") { regular_api_secret.secret }
|
||||
let(:id) { segment.id }
|
||||
|
||||
add_examples
|
||||
|
||||
run_test!
|
||||
end
|
||||
|
||||
response "404", "Audience Segment Not Found" do
|
||||
let(:"api-key") { admin_api_secret.secret }
|
||||
let(:id) { automatic_segment.id }
|
||||
|
||||
add_examples
|
||||
|
||||
run_test!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "DELETE /segments/:id" do
|
||||
delete "Delete a manually managed audience segment" do
|
||||
tags "segments"
|
||||
description "This endpoint allows the client to delete an audience segment specified by ID.\n\nAudience segments cannot be deleted if there are still any DisplayAds/Billboards/Widgets using them."
|
||||
description "This endpoint allows the client to delete an audience segment specified by ID.\n\nAudience segments cannot be deleted if there are still any Billboards using them."
|
||||
operationId "deleteSegment"
|
||||
produces "application/json"
|
||||
consumes "application/json"
|
||||
|
|
@ -125,6 +227,63 @@ RSpec.describe "Api::V1::Docs::AudienceSegments" do
|
|||
end
|
||||
end
|
||||
|
||||
describe "GET /segments/:id/users" do
|
||||
path "/api/segments/{id}/users" do
|
||||
get "Users in a manually managed audience segment" do
|
||||
tags "segments"
|
||||
description "This endpoint allows the client to retrieve a list of the users in an audience segment specified by ID. The endpoint supports pagination, and each page will contain `30` users by default."
|
||||
operationId "getUsersInSegment"
|
||||
produces "application/json"
|
||||
consumes "application/json"
|
||||
|
||||
parameter name: :id, in: :path, required: true, schema: id_schema
|
||||
parameter "$ref": "#/components/parameters/perPageParam30to1000"
|
||||
|
||||
response "200", "A List of users in the audience segment" do
|
||||
let(:"api-key") { admin_api_secret.secret }
|
||||
let(:id) { segment.id }
|
||||
schema type: :array,
|
||||
items: { "$ref": "#/components/schemas/User" }
|
||||
|
||||
before do
|
||||
segment.users << users
|
||||
end
|
||||
|
||||
add_examples
|
||||
|
||||
run_test!
|
||||
end
|
||||
|
||||
response "401", "Unauthorized" do
|
||||
let(:"api-key") { nil }
|
||||
let(:id) { segment.id }
|
||||
|
||||
add_examples
|
||||
|
||||
run_test!
|
||||
end
|
||||
|
||||
response "401", "Unauthorized" do
|
||||
let(:"api-key") { regular_api_secret.secret }
|
||||
let(:id) { segment.id }
|
||||
|
||||
add_examples
|
||||
|
||||
run_test!
|
||||
end
|
||||
|
||||
response "404", "Audience Segment Not Found" do
|
||||
let(:"api-key") { admin_api_secret.secret }
|
||||
let(:id) { automatic_segment.id }
|
||||
|
||||
add_examples
|
||||
|
||||
run_test!
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "PUT /segments/:id/add_users" do
|
||||
path "/api/segments/{id}/add_users" do
|
||||
put "Add users to a manually managed audience segment" do
|
||||
|
|
|
|||
|
|
@ -929,10 +929,63 @@
|
|||
}
|
||||
},
|
||||
"/api/segments": {
|
||||
"get": {
|
||||
"summary": "Manually managed audience segments",
|
||||
"tags": ["segments"],
|
||||
"description": "This endpoint allows the client to retrieve a list of audience segments.\n\nAn audience segment is a group of users that can be targeted by a Billboard. This API only permits managing segments you create and maintain yourself.\n\nThe endpoint supports pagination, and each page will contain `30` segments by default.",
|
||||
"operationId": "getSegments",
|
||||
"parameters": [
|
||||
{
|
||||
"$ref": "#/components/parameters/perPageParam30to1000"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A List of manually managed audience segments",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"example": [
|
||||
{
|
||||
"id": 127,
|
||||
"created_at": "2023-05-19T13:29:19.013-05:00",
|
||||
"type_of": "manual",
|
||||
"updated_at": "2023-05-19T13:29:19.013-05:00",
|
||||
"user_count": 1
|
||||
},
|
||||
{
|
||||
"id": 126,
|
||||
"created_at": "2023-05-19T13:29:19.013-05:00",
|
||||
"type_of": "manual",
|
||||
"updated_at": "2023-05-19T13:29:19.013-05:00",
|
||||
"user_count": 3
|
||||
}
|
||||
],
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/Segment"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"example": {
|
||||
"error": "unauthorized",
|
||||
"status": 401
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"summary": "Create a manually managed audience segment",
|
||||
"tags": ["segments"],
|
||||
"description": "This endpoint allows the client to create a new audience segment.\n\nAn audience segment is a group of users that can be targeted by a DisplayAd/Billboard/Widget. This API only permits managing segments you create and maintain yourself.",
|
||||
"description": "This endpoint allows the client to create a new audience segment.\n\nAn audience segment is a group of users that can be targeted by a Billboard. This API only permits managing segments you create and maintain yourself.",
|
||||
"operationId": "createSegment",
|
||||
"responses": {
|
||||
"201": {
|
||||
|
|
@ -963,10 +1016,72 @@
|
|||
}
|
||||
},
|
||||
"/api/segments/{id}": {
|
||||
"get": {
|
||||
"summary": "A manually managed audience segment",
|
||||
"tags": ["segments"],
|
||||
"description": "This endpoint allows the client to retrieve a single manually-managed audience segment specified by ID.",
|
||||
"operationId": "getSegment",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"minimum": 1
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "The audience segment",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"example": {
|
||||
"id": 131,
|
||||
"created_at": "2023-05-19T13:29:19.481-05:00",
|
||||
"type_of": "manual",
|
||||
"updated_at": "2023-05-19T13:29:19.481-05:00",
|
||||
"user_count": 3
|
||||
},
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/Segment"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"example": {
|
||||
"error": "unauthorized",
|
||||
"status": 401
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Audience Segment Not Found",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"example": {
|
||||
"error": "not found",
|
||||
"status": 404
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"summary": "Delete a manually managed audience segment",
|
||||
"tags": ["segments"],
|
||||
"description": "This endpoint allows the client to delete an audience segment specified by ID.\n\nAudience segments cannot be deleted if there are still any DisplayAds/Billboards/Widgets using them.",
|
||||
"description": "This endpoint allows the client to delete an audience segment specified by ID.\n\nAudience segments cannot be deleted if there are still any Billboards using them.",
|
||||
"operationId": "deleteSegment",
|
||||
"parameters": [
|
||||
{
|
||||
|
|
@ -1029,6 +1144,107 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/api/segments/{id}/users": {
|
||||
"get": {
|
||||
"summary": "Users in a manually managed audience segment",
|
||||
"tags": ["segments"],
|
||||
"description": "This endpoint allows the client to retrieve a list of the users in an audience segment specified by ID. The endpoint supports pagination, and each page will contain `30` users by default.",
|
||||
"operationId": "getUsersInSegment",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"minimum": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/parameters/perPageParam30to1000"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A List of users in the audience segment",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"example": [
|
||||
{
|
||||
"type_of": "user",
|
||||
"id": 1032,
|
||||
"username": "username586",
|
||||
"name": "Katrina \"Kirsten\" \\:/ Schiller",
|
||||
"twitter_username": "twitter586",
|
||||
"github_username": "github586",
|
||||
"summary": null,
|
||||
"location": null,
|
||||
"website_url": null,
|
||||
"joined_at": "May 19, 2023",
|
||||
"profile_image": "/uploads/user/profile_image/1947/02641cc1-4268-4203-8f2d-59cf53d4433a.jpeg"
|
||||
},
|
||||
{
|
||||
"type_of": "user",
|
||||
"id": 1033,
|
||||
"username": "username587",
|
||||
"name": "Breanne \"Steve\" \\:/ Marquardt",
|
||||
"twitter_username": "twitter587",
|
||||
"github_username": "github587",
|
||||
"summary": null,
|
||||
"location": null,
|
||||
"website_url": null,
|
||||
"joined_at": "May 19, 2023",
|
||||
"profile_image": "/uploads/user/profile_image/1948/621613c2-2990-4b92-8a26-8dac49725a95.jpeg"
|
||||
},
|
||||
{
|
||||
"type_of": "user",
|
||||
"id": 1034,
|
||||
"username": "username588",
|
||||
"name": "Eugene \"Jamar\" \\:/ Keebler",
|
||||
"twitter_username": "twitter588",
|
||||
"github_username": "github588",
|
||||
"summary": null,
|
||||
"location": null,
|
||||
"website_url": null,
|
||||
"joined_at": "May 19, 2023",
|
||||
"profile_image": "/uploads/user/profile_image/1949/7d5a54bc-faf2-408b-a988-3ea5b8ef40f1.jpeg"
|
||||
}
|
||||
],
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/User"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"example": {
|
||||
"error": "unauthorized",
|
||||
"status": 401
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Audience Segment Not Found",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"example": {
|
||||
"error": "not found",
|
||||
"status": 404
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/segments/{id}/add_users": {
|
||||
"put": {
|
||||
"summary": "Add users to a manually managed audience segment",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue