[deploy] Introduce /api/profile_images/:username endpoint (#10547)

* Documents the new /profile-images/:username API endpoint

* Introduces the /api/profile_images/:username endpoint

This endpoint receives either an user or organization username as input
via the URL path and returns the profile image information for that
user/orgnization.

* Updates the /api/profile_images/:username response

Adapts the response according to the review comments:

1. Removes the cache directives
1. Tweaks how the user query is being done
1. Adds a `image_of` property to the response

* Bump API version

Co-authored-by: rhymes <rhymes@hey.com>
Co-authored-by: Molly Struve <mollylbs@gmail.com>
This commit is contained in:
Diogo Osório 2020-10-14 20:24:25 +01:00 committed by GitHub
parent 9ff7f82664
commit 2b72a35d3d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 166 additions and 1 deletions

View file

@ -0,0 +1,27 @@
module Api
module V0
class ProfileImagesController < ApiController
def show
not_found unless profile_image_owner
@profile_image_owner = profile_image_owner
end
private
def profile_image_owner
user || organization
end
def user
@user ||= User.registered.select(:id, :profile_image)
.find_by(username: params[:username])
end
def organization
@organization ||= Organization.select(:id, :profile_image)
.find_by(username: params[:username])
end
end
end
end

View file

@ -0,0 +1,5 @@
json.type_of "profile_image"
json.image_of @profile_image_owner.class.name.downcase
json.profile_image Images::Profile.call(@profile_image_owner.profile_image_url, length: 640)
json.profile_image_90 Images::Profile.call(@profile_image_owner.profile_image_url, length: 90)

View file

@ -194,6 +194,8 @@ Rails.application.routes.draw do
get :cache
end
end
resources :profile_images, only: %i[show], param: :username
end
end

View file

@ -17,7 +17,7 @@ info:
Dates and date times, unless otherwise specified, must be in
the [RFC 3339](https://tools.ietf.org/html/rfc3339) format.
version: "0.9.0"
version: "0.9.1"
termsOfService: https://dev.to/terms
contact:
name: DEV Team
@ -1086,6 +1086,24 @@ components:
user:
$ref: "#/components/schemas/SharedUser"
ProfileImage:
description: Profile image
type: object
properties:
type_of:
type: string
enum: [profile_image]
image_of:
description: "Discriminates what is the type of the profile image owner (user or organization)"
type: string
enum: [user, organization]
profile_image:
type: string
description: Profile image (640x640)
profile_image_90:
type: string
description: Profile image (90x90)
examples:
ErrorBadRequest:
value:
@ -1642,6 +1660,13 @@ components:
profile_image: "..."
profile_image_90: "..."
ProfileImage:
value:
type_of: profile_image
image_of: user
profile_image: https://res.cloudinary.com/...jpeg
profile_image_90: https://res.cloudinary.com/...jpeg
tags:
- name: articles
description: Articles are all the posts users create on DEV
@ -1665,6 +1690,8 @@ tags:
description: Video articles
- name: webhooks
description: Webhooks are HTTP endpoints registered to receive events
- name: profile images
description: User or organization profile images
paths:
/articles:
@ -3080,6 +3107,48 @@ paths:
source: |
curl -H "api-key: API_KEY" https://dev.to/api/users/me
/profile_images/{username}:
get:
operationId: getProfileImage
summary: User or organization profile picture
description: |
This endpoint allows the client to retrieve a user or organization
profile image information by its corresponding username
tags:
- profile images
parameters:
- name: username
in: path
required: true
description: Username of the user or organization
schema:
type: string
example: "diogoosorio"
responses:
"200":
description: The profile image
content:
application/json:
schema:
$ref: "#/components/schemas/ProfileImage"
examples:
profile-image-success:
$ref: "#/components/examples/ProfileImage"
"404":
description: Resource not found
content:
application/json:
schema:
$ref: "#/components/schemas/APIError"
examples:
profile-image-not-found:
$ref: "#/components/examples/ErrorNotFound"
x-code-samples:
- lang: Shell
label: curl
source: |
curl https://dev.to/api/profile_images/diogoosorio
/videos:
get:
operationId: getArticlesWithVideo

View file

@ -83,6 +83,13 @@ FactoryBot.define do
after(:build) { |user| user.add_role(:banned) }
end
trait :invited do
after(:build) do |user|
user.registered = false
user.registered_at = nil
end
end
trait :ignore_mailchimp_subscribe_callback do
after(:build) do |user|
user.define_singleton_method(:subscribe_to_mailchimp_newsletter) {}

View file

@ -0,0 +1,55 @@
require "rails_helper"
RSpec.describe "Api::V0::ProfileImages", type: :request do
describe "GET /api/profile_images/:username" do
it "returns 404 if the username is not taken" do
get api_profile_image_path("invalid-username")
expect(response).to have_http_status(:not_found)
end
context "when the username relates to an user" do
let(:user) { create(:user) }
it "returns the user profile image information" do
get api_profile_image_path(user.username)
expect(response.parsed_body).to eq(
"type_of" => "profile_image",
"image_of" => "user",
"profile_image" => profile_image(user.profile_image_url, 640),
"profile_image_90" => profile_image(user.profile_image_url, 90),
)
end
end
context "when the username relates to an invited user" do
let(:user) { create(:user, :invited) }
it "returns a 404" do
get api_profile_image_path(user.username)
expect(response).to have_http_status(:not_found)
end
end
context "when the username relates to an organization" do
let(:organization) { create(:organization) }
it "returns the organization's profile image information" do
get api_profile_image_path(organization.username)
expect(response.parsed_body).to eq(
"type_of" => "profile_image",
"image_of" => "organization",
"profile_image" => profile_image(organization.profile_image_url, 640),
"profile_image_90" => profile_image(organization.profile_image_url, 90),
)
end
end
end
def profile_image(url, length)
Images::Profile.call(url, length: length)
end
end