英文:
How can I return variants of an ActiveStorage image over GraphQL in Ruby on Rails?
问题
以下是翻译好的部分:
摘要
对于针对 Ruby on Rails 7 端点的 GraphQL 查询:
query {
posts(limit: 1) {
id
caption
image {
web # <- 这是一个变体名称
thumb # <- 另一个变体名称
}
}
}
我希望得到一个类似这样的响应:
{
"data": {
"posts": [
{
"id": "8d59b87c-c33b-4c29-b18f-a4d6aa2abf45",
"caption": "Nam ad incidunt. Nisi sequi quibusdam. Nihil quis veritatis. Beatae consequatur excepturi. Et at sapiente. Delectus deleniti unde. Id a quo. Qui aut quod. Est qui quo. Dolores beatae facilis. Sed error vero. Est est at. Alias aut sit. Consequuntur consequ."
"image": {
"web": "some-url-here",
"thumb": "another-url-here"
}
}
]
}
}
其中 object
具有一个带有多个 variants
的 image
属性。
详细信息
我正在使用 Rails 7、graphql-rails 以及我对 Ruby 的中级知识来处理特定的 GraphQL 查询端点,但我遇到了一个障碍。
我有一个项目,允许用户上传照片,然后以 ActiveStorage 变体的形式接收多个修改。用户每次发布帖子时只包括一张附加的图像以进行处理。上传后,后台作业根据一些模型指定的尺寸生成 5 种不同的变体。
为了简化起见,这是 Post
模型的相关部分:
class Post < ApplicationRecord
belongs_to :user
has_many :comments, dependent: :destroy
has_one_attached :image, dependent: :destroy do |attachable|
attachable.variant :web, resize_to_fill: [1200, 1200, { crop: :centre }]
attachable.variant :opengraph, resize_to_fill: [1200, 630, { crop: :attention }]
attachable.variant :twitter, resize_to_fill: [1024, 512, { crop: :attention }]
attachable.variant :mobile, resize_to_fill: [600, 600, { crop: :centre }]
attachable.variant :thumb, resize_to_fill: [100, 100, { crop: :centre }]
end
...
GraphQL 的 Post
类型设置为返回一个名为 image
的字段,其类型为 VariantType
,我已经在下面的当前版本中包含了它:
module Types
class VariantType < Types::BaseObject
field :web, String, null: false
field :opengraph, String, null: false
field :twitter, String, null: false
field :mobile, String, null: false
field :thumb, String, null: false
end
def web
url_for(:web)
end
def opengraph
url_for(:opengraph)
end
def twitter
url_for(:twitter)
end
def mobile
url_for(:mobile)
end
def thumb
url_for(:thumb)
end
...(为了清晰起见,省略了 url_for 的方法定义)
end
当我尝试查询端点时,而不是上面的 JSON 响应,我收到了以下错误:
未能实现 Variant.web,尝试了:
- `Types::VariantType#web`,但不存在
- `ActiveStorage::Attached::One#web`,但不存在
- 在 `<ActiveStorage::Attached::One:0x000000012511c5f8>` 上查找散列键 `:web` 或 `"web"`,但它不是 Hash
要实现此字段,请定义其中一个上述方法(并检查是否有拼写错误)
更新 1
url_for
方法定义如下:
def url_for(variant) # rubocop:disable Metrics/MethodLength
img = object.image.variant(variant)
# 如果 CDN_HOST 没有协议,请添加协议
cdn_host = ENV.fetch('CDN_HOST', nil)
cdn_host = "https://#{cdn_host}" unless cdn_host.nil? || cdn_host.start_with?('http')
if cdn_host.nil?
blob_type = if img.is_a?(ActiveStorage::Variant) || img.is_a?(ActiveStorage::VariantWithRecord)
:rails_representation
else
:rails_blob
end
Rails.application.routes.url_helpers.route_for(blob_type, img, only_path: true)
else
File.join(cdn_host, img.key) # 返回 blob 的 CDN 路径
end
英文:
Summary
For a given GraphQL query against a Ruby on Rails 7 endpoint:
query {
posts(limit: 1) {
id
caption
image {
web # <- this is a variant name
thumb # <- another variant name
}
}
}
I want a response that looks like this:
{
"data": {
"posts": [
{
"id": "8d59b87c-c33b-4c29-b18f-a4d6aa2abf45",
"caption": "Nam ad incidunt. Nisi sequi quibusdam. Nihil quis veritatis. Beatae consequatur excepturi. Et at sapiente. Delectus deleniti unde. Id a quo. Qui aut quod. Est qui quo. Dolores beatae facilis. Sed error vero. Est est at. Alias aut sit. Consequuntur consequ."
"image": {
"web": "some-url-here",
"thumb": "another-url-here"
}
}
]
}
}
Where the object
has an image
property with multiple variants
defined.
Details
I'm working on a specific GraphQL query endpoint using Rails 7, the graphql-rails, and my intermediate knowledge of Ruby, but I've happened upon a stumbling block.
I have a project that accepts user uploads of photos, which then receive multiple modifications in the form of ActiveStorage variants. Each post a user makes includes one (and only one) attached image to be processed. After upload, a background job generates 5 different variants based on some model-specified dimensions.
For the sake of simplicity, here's the relevant part of the Post
model:
class Post < ApplicationRecord
belongs_to :user
has_many :comments, dependent: :destroy
has_one_attached :image, dependent: :destroy do |attachable|
attachable.variant :web, resize_to_fill: [1200, 1200, { crop: :centre }]
attachable.variant :opengraph, resize_to_fill: [1200, 630, { crop: :attention }]
attachable.variant :twitter, resize_to_fill: [1024, 512, { crop: :attention }]
attachable.variant :mobile, resize_to_fill: [600, 600, { crop: :centre }]
attachable.variant :thumb, resize_to_fill: [100, 100, { crop: :centre }]
end
...
The Post
type for GraphQL is set to return a field named image
of type VariantType
, which I've included below in my current, inelegant version:
module Types
class VariantType < Types::BaseObject
field :web, String, null: false
field :opengraph, String, null: false
field :twitter, String, null: false
field :mobile, String, null: false
field :thumb, String, null: false
end
def web
url_for(:web)
end
def opengraph
url_for(:opengraph)
end
def twitter
url_for(:twitter)
end
def mobile
url_for(:mobile)
end
def thumb
url_for(:thumb)
end
... (method definition for url_for removed for clarity)
end
When I attempted to query the endpoint, instead of the JSON response above, I got the below error:
Failed to implement Variant.web, tried:
- `Types::VariantType#web`, which did not exist
- `ActiveStorage::Attached::One#web`, which did not exist
- Looking up hash key `:web` or `"web"` on `<ActiveStorage::Attached::One:0x000000012511c5f8>`, but it wasn't a Hash
To implement this field, define one of the methods above (and check for typos)
Update 1
The url_for
method is defined as:
def url_for(variant) # rubocop:disable Metrics/MethodLength
img = object.image.variant(variant)
# Add protocol if CDN_HOST doesn't have it
cdn_host = ENV.fetch('CDN_HOST', nil)
cdn_host = "https://#{cdn_host}" unless cdn_host.nil? || cdn_host.start_with?('http')
if cdn_host.nil?
blob_type = if img.is_a?(ActiveStorage::Variant) || img.is_a?(ActiveStorage::VariantWithRecord)
:rails_representation
else
:rails_blob
end
Rails.application.routes.url_helpers.route_for(blob_type, img, only_path: true)
else
File.join(cdn_host, img.key) # Returns CDN path for blob
end
It's a hack, I know, in lieu of a better approach to having my asset URLs appear as https://cdn.example.com/asset.jpg
instead of the ugly ActiveStorage proxy URLs.
答案1
得分: 1
以下是您要翻译的内容:
修复此问题的一种方法是使用ActiveStorage提供的variant方法来检索所需的变体。您可以将变体名称作为符号传递给variant方法,如下所示:
def web
object.image.variant(:web).service_url
end
def opengraph
object.image.variant(:opengraph).service_url
end
def twitter
object.image.variant(:twitter).service_url
end
def mobile
object.image.variant(:mobile).service_url
end
def thumb
object.image.variant(:thumb).service_url
end
这将检索您想要的变体并使用service_url方法返回其URL。
另外,您可以像这样定义您的VariantType:
module Types
class VariantType < Types::BaseObject
field :web, String, null: false, method: :web_url
field :opengraph, String, null: false, method: :opengraph_url
field :twitter, String, null: false, method: :twitter_url
field :mobile, String, null: false, method: :mobile_url
field :thumb, String, null: false, method: :thumb_url
def web_url
object.image.variant(:web).service_url
end
def opengraph_url
object.image.variant(:opengraph).service_url
end
def twitter_url
object.image.variant(:twitter).service_url
end
def mobile_url
object.image.variant(:mobile).service_url
end
def thumb_url
object.image.variant(:thumb).service_url
end
end
end
这样,您可以使用method选项定义字段,并指定要调用以解析字段值的方法。
英文:
One way to fix this issue would be to use the variant method provided by ActiveStorage to retrieve the variant you want. You can pass the variant name as a symbol to the variant method, like this:
def web
object.image.variant(:web).service_url
end
def opengraph
object.image.variant(:opengraph).service_url
end
def twitter
object.image.variant(:twitter).service_url
end
def mobile
object.image.variant(:mobile).service_url
end
def thumb
object.image.variant(:thumb).service_url
end
This will retrieve the variant you want and return its URL using the service_url method.
Alternatively, you could define your VariantType like this:
module Types
class VariantType < Types::BaseObject
field :web, String, null: false, method: :web_url
field :opengraph, String, null: false, method: :opengraph_url
field :twitter, String, null: false, method: :twitter_url
field :mobile, String, null: false, method: :mobile_url
field :thumb, String, null: false, method: :thumb_url
def web_url
object.image.variant(:web).service_url
end
def opengraph_url
object.image.variant(:opengraph).service_url
end
def twitter_url
object.image.variant(:twitter).service_url
end
def mobile_url
object.image.variant(:mobile).service_url
end
def thumb_url
object.image.variant(:thumb).service_url
end
end
end
This way, you can define your fields with the method option, which allows you to specify a method to be called to resolve the field's value.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论