DisplayAd API: Read endpoints (#18726)
* DisplayAd API: Read endpoints * Updated swagger docs for display_ads read endpoints * Update swagger with type_of * Remove extraneous comment * Restrict authentication to strictly api_key
This commit is contained in:
parent
db39270539
commit
7c82d128ff
5 changed files with 322 additions and 30 deletions
25
app/controllers/api/v1/display_ads_controller.rb
Normal file
25
app/controllers/api/v1/display_ads_controller.rb
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
module Api
|
||||
module V1
|
||||
class DisplayAdsController < ApiController
|
||||
before_action :authenticate_with_api_key!
|
||||
before_action :require_admin
|
||||
|
||||
def index
|
||||
@display_ads = DisplayAd.order(id: :desc).page(params[:page]).per(50)
|
||||
@display_ads = @display_ads.search_ads(params[:search]) if params[:search].present?
|
||||
render json: @display_ads
|
||||
end
|
||||
|
||||
def show
|
||||
@display_ad = DisplayAd.find(params[:id])
|
||||
render json: @display_ad
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def require_admin
|
||||
authorize DisplayAd, :access?, policy_class: InternalPolicy
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -51,6 +51,8 @@ Rails.application.routes.draw do
|
|||
post "/reactions", to: "reactions#create"
|
||||
post "/reactions/toggle", to: "reactions#toggle"
|
||||
|
||||
resources :display_ads, only: %i[index show]
|
||||
|
||||
draw :api
|
||||
end
|
||||
|
||||
|
|
|
|||
65
spec/requests/api/v1/display_ads_spec.rb
Normal file
65
spec/requests/api/v1/display_ads_spec.rb
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
require "rails_helper"
|
||||
|
||||
# rubocop:disable RSpec/InstanceVariable
|
||||
|
||||
RSpec.describe "Api::V1::DisplayAds" do
|
||||
let!(:v1_headers) { { "content-type" => "application/json", "Accept" => "application/vnd.forem.api-v1+json" } }
|
||||
|
||||
before do
|
||||
allow(FeatureFlag).to receive(:enabled?).with(:api_v1).and_return(true)
|
||||
@ad1 = create(:display_ad, published: true, approved: true)
|
||||
end
|
||||
|
||||
shared_context "when user is authorized" do
|
||||
let(:api_secret) { create(:api_secret) }
|
||||
let(:user) { api_secret.user }
|
||||
let(:auth_header) { v1_headers.merge({ "api-key" => api_secret.secret }) }
|
||||
before { user.add_role(:admin) }
|
||||
end
|
||||
|
||||
context "when authenticated and authorized and get to index" do
|
||||
include_context "when user is authorized"
|
||||
|
||||
describe "GET /api/display_ads" do
|
||||
it "returns json response" do
|
||||
get api_display_ads_path, headers: auth_header
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
expect(response.media_type).to eq("application/json")
|
||||
expect(response.parsed_body.size).to eq(1)
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET /api/display_ads/:id" do
|
||||
it "returns json response" do
|
||||
get api_display_ad_path(@ad1.id), headers: auth_header
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
expect(response.media_type).to eq("application/json")
|
||||
expect(response.parsed_body).to include(
|
||||
"id" => @ad1.id,
|
||||
"published" => true,
|
||||
"approved" => true,
|
||||
"cached_tag_list" => "",
|
||||
"clicks_count" => 0,
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when unauthenticated and get to index" do
|
||||
it "returns unauthorized" do
|
||||
get api_display_ads_path, headers: v1_headers
|
||||
expect(response).to have_http_status(:unauthorized)
|
||||
end
|
||||
end
|
||||
|
||||
context "when unauthorized and get to show" do
|
||||
it "returns unauthorized" do
|
||||
get api_display_ads_path, params: { id: @ad1.id },
|
||||
headers: v1_headers.merge({ "api-key" => "invalid api key" })
|
||||
expect(response).to have_http_status(:unauthorized)
|
||||
end
|
||||
end
|
||||
end
|
||||
# rubocop:enable RSpec/InstanceVariable
|
||||
91
spec/requests/api/v1/docs/display_ads_spec.rb
Normal file
91
spec/requests/api/v1/docs/display_ads_spec.rb
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
require "rails_helper"
|
||||
require "swagger_helper"
|
||||
|
||||
# rubocop:disable RSpec/EmptyExampleGroup
|
||||
# rubocop:disable RSpec/VariableName
|
||||
|
||||
RSpec.describe "api/v1/display_ads" do
|
||||
let(:Accept) { "application/vnd.forem.api-v1+json" }
|
||||
let(:api_secret) { create(:api_secret) }
|
||||
let(:display_ad) { create(:display_ad) }
|
||||
let(:user) { api_secret.user }
|
||||
|
||||
before { user.add_role(:admin) }
|
||||
|
||||
path "/api/display_ads" do
|
||||
describe "get all display ads" do
|
||||
get("display ads") do
|
||||
tags "display ads"
|
||||
description(<<-DESCRIBE.strip)
|
||||
This endpoint allows the client to retrieve a list of all display ads.
|
||||
DESCRIBE
|
||||
|
||||
produces "application/json"
|
||||
|
||||
response(200, "successful") do
|
||||
let(:"api-key") { api_secret.secret }
|
||||
add_examples
|
||||
|
||||
run_test!
|
||||
end
|
||||
|
||||
response "401", "unauthorized" do
|
||||
let(:"api-key") { "invalid" }
|
||||
add_examples
|
||||
|
||||
run_test!
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
path "/api/display_ads/{id}" do
|
||||
describe "GET a single display ad" do
|
||||
get("display ad") do
|
||||
tags "display ads"
|
||||
description(<<-DESCRIBE.strip)
|
||||
This endpoint allows the client to retrieve a single display ad, via its id.
|
||||
DESCRIBE
|
||||
|
||||
produces "application/json"
|
||||
parameter name: :id,
|
||||
in: :path,
|
||||
required: true,
|
||||
description: "The ID of the user to unpublish.",
|
||||
schema: {
|
||||
type: :integer,
|
||||
format: :int32,
|
||||
minimum: 1
|
||||
},
|
||||
example: 123
|
||||
|
||||
response(200, "successful") do
|
||||
let(:id) { display_ad.id }
|
||||
let(:"api-key") { api_secret.secret }
|
||||
add_examples
|
||||
|
||||
run_test!
|
||||
end
|
||||
|
||||
response "401", "unauthorized" do
|
||||
let(:"api-key") { "invalid" }
|
||||
let(:id) { 10_000 }
|
||||
add_examples
|
||||
|
||||
run_test!
|
||||
end
|
||||
|
||||
response "404", "Unknown DisplayAd ID" do
|
||||
let(:"api-key") { api_secret.secret }
|
||||
let(:id) { 10_000 }
|
||||
add_examples
|
||||
|
||||
run_test!
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# rubocop:enable RSpec/VariableName
|
||||
# rubocop:enable RSpec/EmptyExampleGroup
|
||||
|
|
@ -103,45 +103,45 @@
|
|||
"example": [
|
||||
{
|
||||
"type_of": "article",
|
||||
"id": 1,
|
||||
"title": "Mr Standfast1",
|
||||
"description": "Fixie crucifix vegan vinyl cleanse stumptown. Ennui chicharrones actually heirloom taxidermy selfies...",
|
||||
"readable_publish_date": "Sep 3",
|
||||
"slug": "mr-standfast1-o1j",
|
||||
"path": "/username1/mr-standfast1-o1j",
|
||||
"url": "http://localhost:3000/username1/mr-standfast1-o1j",
|
||||
"id": 191,
|
||||
"title": "An Acceptable Time172",
|
||||
"description": "Shabby chic wayfarers normcore marfa carry. Schlitz kinfolk +1 kickstarter pitchfork cardigan 90's....",
|
||||
"readable_publish_date": "Nov 17",
|
||||
"slug": "an-acceptable-time172-25ik",
|
||||
"path": "/username378/an-acceptable-time172-25ik",
|
||||
"url": "http://localhost:3000/username378/an-acceptable-time172-25ik",
|
||||
"comments_count": 0,
|
||||
"public_reactions_count": 0,
|
||||
"collection_id": null,
|
||||
"published_timestamp": "2022-09-02T19:24:23Z",
|
||||
"published_timestamp": "2022-11-17T09:30:31Z",
|
||||
"positive_reactions_count": 0,
|
||||
"cover_image": "http://localhost:3000/assets/19-ed58d3e8defcefc445020631589697a05e725243e834b5192aee4e6b91a3e927.png",
|
||||
"social_image": "http://localhost:3000/assets/19-ed58d3e8defcefc445020631589697a05e725243e834b5192aee4e6b91a3e927.png",
|
||||
"canonical_url": "http://localhost:3000/username1/mr-standfast1-o1j",
|
||||
"created_at": "2022-09-02T19:24:24Z",
|
||||
"cover_image": "http://localhost:3000/assets/23-33ea5a2b5af3dc15b9ed90de0b850f67e2390eaec3361d6687d3b9750c699f84.png",
|
||||
"social_image": "http://localhost:3000/assets/23-33ea5a2b5af3dc15b9ed90de0b850f67e2390eaec3361d6687d3b9750c699f84.png",
|
||||
"canonical_url": "http://localhost:3000/username378/an-acceptable-time172-25ik",
|
||||
"created_at": "2022-11-17T09:30:31Z",
|
||||
"edited_at": null,
|
||||
"crossposted_at": null,
|
||||
"published_at": "2022-09-02T19:24:23Z",
|
||||
"last_comment_at": "2022-09-02T19:24:23Z",
|
||||
"published_at": "2022-11-17T09:30:31Z",
|
||||
"last_comment_at": "2022-11-17T09:30:31Z",
|
||||
"reading_time_minutes": 1,
|
||||
"tag_list": ["discuss"],
|
||||
"tags": "discuss",
|
||||
"user": {
|
||||
"name": "Lavona \"Lenna\" \\:/ Watsica",
|
||||
"username": "username1",
|
||||
"twitter_username": "twitter1",
|
||||
"github_username": "github1",
|
||||
"user_id": 1,
|
||||
"name": "Melaine \"Quinn\" \\:/ Goodwin",
|
||||
"username": "username378",
|
||||
"twitter_username": "twitter378",
|
||||
"github_username": "github378",
|
||||
"user_id": 386,
|
||||
"website_url": null,
|
||||
"profile_image": "/uploads/user/profile_image/1/4e030890-caa7-4c5b-836a-432e7b1bfdfc.jpeg",
|
||||
"profile_image_90": "/uploads/user/profile_image/1/4e030890-caa7-4c5b-836a-432e7b1bfdfc.jpeg"
|
||||
"profile_image": "/uploads/user/profile_image/386/93f316c4-1287-45b3-b1c7-49861128e477.jpeg",
|
||||
"profile_image_90": "/uploads/user/profile_image/386/93f316c4-1287-45b3-b1c7-49861128e477.jpeg"
|
||||
},
|
||||
"organization": {
|
||||
"name": "Weimann-Toy",
|
||||
"username": "org1",
|
||||
"slug": "org1",
|
||||
"profile_image": "/uploads/organization/profile_image/1/2df76e17-f420-434a-8027-2e90ebfb89bf.png",
|
||||
"profile_image_90": "/uploads/organization/profile_image/1/2df76e17-f420-434a-8027-2e90ebfb89bf.png"
|
||||
"name": "Baumbach Group",
|
||||
"username": "org60",
|
||||
"slug": "org60",
|
||||
"profile_image": "/uploads/organization/profile_image/65/0bccbabf-3f76-473b-8c5e-bac56178315b.png",
|
||||
"profile_image_90": "/uploads/organization/profile_image/65/0bccbabf-3f76-473b-8c5e-bac56178315b.png"
|
||||
},
|
||||
"flare_tag": {
|
||||
"name": "discuss",
|
||||
|
|
@ -180,6 +180,16 @@
|
|||
"minimum": 1
|
||||
},
|
||||
"example": 1
|
||||
},
|
||||
{
|
||||
"name": "note",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"description": "Content for the note that's created along with unpublishing",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"example": "Admin requested unpublishing all articles via API"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
|
|
@ -211,6 +221,105 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/api/display_ads": {
|
||||
"get": {
|
||||
"summary": "display ads",
|
||||
"tags": ["display ads"],
|
||||
"description": "This endpoint allows the client to retrieve a list of all display ads.",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "successful",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"example": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "unauthorized",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"example": {
|
||||
"error": "unauthorized",
|
||||
"status": 401
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/display_ads/{id}": {
|
||||
"get": {
|
||||
"summary": "display ad",
|
||||
"tags": ["display ads"],
|
||||
"description": "This endpoint allows the client to retrieve a single display ad, via its id.",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"description": "The ID of the user to unpublish.",
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"minimum": 1
|
||||
},
|
||||
"example": 123
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "successful",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"example": {
|
||||
"id": 10,
|
||||
"approved": false,
|
||||
"body_markdown": "Hello _hey_ Hey hey 5",
|
||||
"cached_tag_list": "",
|
||||
"clicks_count": 0,
|
||||
"created_at": "2022-11-17T10:30:32.066+01:00",
|
||||
"display_to": "all",
|
||||
"impressions_count": 0,
|
||||
"name": "Display Ad 10",
|
||||
"organization_id": 66,
|
||||
"placement_area": "sidebar_left",
|
||||
"processed_html": "<p>Hello <em>hey</em> Hey hey 5</p>",
|
||||
"published": false,
|
||||
"success_rate": 0.0,
|
||||
"type_of": "in_house",
|
||||
"updated_at": "2022-11-17T10:30:32.069+01:00",
|
||||
"tag_list": []
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "unauthorized",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"example": {
|
||||
"error": "unauthorized",
|
||||
"status": 401
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Unknown DisplayAd ID",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"example": {
|
||||
"error": "not found",
|
||||
"status": 404
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/reactions/toggle": {
|
||||
"post": {
|
||||
"summary": "toggle reaction",
|
||||
|
|
@ -223,7 +332,7 @@
|
|||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"enum": ["like", "readinglist", "unicorn", "thinking", "hands"]
|
||||
"enum": ["like", "readinglist", "unicorn"]
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -254,7 +363,7 @@
|
|||
"result": "create",
|
||||
"category": "like",
|
||||
"id": 1,
|
||||
"reactable_id": 5,
|
||||
"reactable_id": 195,
|
||||
"reactable_type": "Article"
|
||||
}
|
||||
}
|
||||
|
|
@ -286,7 +395,7 @@
|
|||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"enum": ["like", "readinglist", "unicorn", "thinking", "hands"]
|
||||
"enum": ["like", "readinglist", "unicorn"]
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -317,7 +426,7 @@
|
|||
"result": "none",
|
||||
"category": "like",
|
||||
"id": 3,
|
||||
"reactable_id": 7,
|
||||
"reactable_id": 197,
|
||||
"reactable_type": "Article"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue