如何在Ruby on Rails中通过GraphQL返回ActiveStorage图像的变体?

huangapple go评论93阅读模式
英文:

How can I return variants of an ActiveStorage image over GraphQL in Ruby on Rails?

问题

以下是翻译好的部分:

摘要

对于针对 Ruby on Rails 7 端点的 GraphQL 查询:

  1. query {
  2. posts(limit: 1) {
  3. id
  4. caption
  5. image {
  6. web # <- 这是一个变体名称
  7. thumb # <- 另一个变体名称
  8. }
  9. }
  10. }

我希望得到一个类似这样的响应:

  1. {
  2. "data": {
  3. "posts": [
  4. {
  5. "id": "8d59b87c-c33b-4c29-b18f-a4d6aa2abf45",
  6. "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."
  7. "image": {
  8. "web": "some-url-here",
  9. "thumb": "another-url-here"
  10. }
  11. }
  12. ]
  13. }
  14. }

其中 object 具有一个带有多个 variantsimage 属性。

详细信息

我正在使用 Rails 7、graphql-rails 以及我对 Ruby 的中级知识来处理特定的 GraphQL 查询端点,但我遇到了一个障碍。

我有一个项目,允许用户上传照片,然后以 ActiveStorage 变体的形式接收多个修改。用户每次发布帖子时只包括一张附加的图像以进行处理。上传后,后台作业根据一些模型指定的尺寸生成 5 种不同的变体。

为了简化起见,这是 Post 模型的相关部分:

  1. class Post < ApplicationRecord
  2. belongs_to :user
  3. has_many :comments, dependent: :destroy
  4. has_one_attached :image, dependent: :destroy do |attachable|
  5. attachable.variant :web, resize_to_fill: [1200, 1200, { crop: :centre }]
  6. attachable.variant :opengraph, resize_to_fill: [1200, 630, { crop: :attention }]
  7. attachable.variant :twitter, resize_to_fill: [1024, 512, { crop: :attention }]
  8. attachable.variant :mobile, resize_to_fill: [600, 600, { crop: :centre }]
  9. attachable.variant :thumb, resize_to_fill: [100, 100, { crop: :centre }]
  10. end
  11. ...

GraphQL 的 Post 类型设置为返回一个名为 image 的字段,其类型为 VariantType,我已经在下面的当前版本中包含了它:

  1. module Types
  2. class VariantType < Types::BaseObject
  3. field :web, String, null: false
  4. field :opengraph, String, null: false
  5. field :twitter, String, null: false
  6. field :mobile, String, null: false
  7. field :thumb, String, null: false
  8. end
  9. def web
  10. url_for(:web)
  11. end
  12. def opengraph
  13. url_for(:opengraph)
  14. end
  15. def twitter
  16. url_for(:twitter)
  17. end
  18. def mobile
  19. url_for(:mobile)
  20. end
  21. def thumb
  22. url_for(:thumb)
  23. end
  24. ...(为了清晰起见,省略了 url_for 的方法定义)
  25. end

当我尝试查询端点时,而不是上面的 JSON 响应,我收到了以下错误:

  1. 未能实现 Variant.web,尝试了:
  2. - `Types::VariantType#web`,但不存在
  3. - `ActiveStorage::Attached::One#web`,但不存在
  4. - `<ActiveStorage::Attached::One:0x000000012511c5f8>` 上查找散列键 `:web` `"web"`,但它不是 Hash
  5. 要实现此字段,请定义其中一个上述方法(并检查是否有拼写错误)

更新 1

url_for 方法定义如下:

  1. def url_for(variant) # rubocop:disable Metrics/MethodLength
  2. img = object.image.variant(variant)
  3. # 如果 CDN_HOST 没有协议,请添加协议
  4. cdn_host = ENV.fetch('CDN_HOST', nil)
  5. cdn_host = "https://#{cdn_host}" unless cdn_host.nil? || cdn_host.start_with?('http')
  6. if cdn_host.nil?
  7. blob_type = if img.is_a?(ActiveStorage::Variant) || img.is_a?(ActiveStorage::VariantWithRecord)
  8. :rails_representation
  9. else
  10. :rails_blob
  11. end
  12. Rails.application.routes.url_helpers.route_for(blob_type, img, only_path: true)
  13. else
  14. File.join(cdn_host, img.key) # 返回 blob 的 CDN 路径
  15. end
英文:

Summary

For a given GraphQL query against a Ruby on Rails 7 endpoint:

  1. query {
  2. posts(limit: 1) {
  3. id
  4. caption
  5. image {
  6. web # <- this is a variant name
  7. thumb # <- another variant name
  8. }
  9. }
  10. }

I want a response that looks like this:

  1. {
  2. "data": {
  3. "posts": [
  4. {
  5. "id": "8d59b87c-c33b-4c29-b18f-a4d6aa2abf45",
  6. "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."
  7. "image": {
  8. "web": "some-url-here",
  9. "thumb": "another-url-here"
  10. }
  11. }
  12. ]
  13. }
  14. }

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:

  1. class Post < ApplicationRecord
  2. belongs_to :user
  3. has_many :comments, dependent: :destroy
  4. has_one_attached :image, dependent: :destroy do |attachable|
  5. attachable.variant :web, resize_to_fill: [1200, 1200, { crop: :centre }]
  6. attachable.variant :opengraph, resize_to_fill: [1200, 630, { crop: :attention }]
  7. attachable.variant :twitter, resize_to_fill: [1024, 512, { crop: :attention }]
  8. attachable.variant :mobile, resize_to_fill: [600, 600, { crop: :centre }]
  9. attachable.variant :thumb, resize_to_fill: [100, 100, { crop: :centre }]
  10. end
  11. ...

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:

  1. module Types
  2. class VariantType < Types::BaseObject
  3. field :web, String, null: false
  4. field :opengraph, String, null: false
  5. field :twitter, String, null: false
  6. field :mobile, String, null: false
  7. field :thumb, String, null: false
  8. end
  9. def web
  10. url_for(:web)
  11. end
  12. def opengraph
  13. url_for(:opengraph)
  14. end
  15. def twitter
  16. url_for(:twitter)
  17. end
  18. def mobile
  19. url_for(:mobile)
  20. end
  21. def thumb
  22. url_for(:thumb)
  23. end
  24. ... (method definition for url_for removed for clarity)
  25. end

When I attempted to query the endpoint, instead of the JSON response above, I got the below error:

  1. Failed to implement Variant.web, tried:
  2. - `Types::VariantType#web`, which did not exist
  3. - `ActiveStorage::Attached::One#web`, which did not exist
  4. - Looking up hash key `:web` or `"web"` on `<ActiveStorage::Attached::One:0x000000012511c5f8>`, but it wasn't a Hash
  5. To implement this field, define one of the methods above (and check for typos)

Update 1

The url_for method is defined as:

  1. def url_for(variant) # rubocop:disable Metrics/MethodLength
  2. img = object.image.variant(variant)
  3. # Add protocol if CDN_HOST doesn't have it
  4. cdn_host = ENV.fetch('CDN_HOST', nil)
  5. cdn_host = "https://#{cdn_host}" unless cdn_host.nil? || cdn_host.start_with?('http')
  6. if cdn_host.nil?
  7. blob_type = if img.is_a?(ActiveStorage::Variant) || img.is_a?(ActiveStorage::VariantWithRecord)
  8. :rails_representation
  9. else
  10. :rails_blob
  11. end
  12. Rails.application.routes.url_helpers.route_for(blob_type, img, only_path: true)
  13. else
  14. File.join(cdn_host, img.key) # Returns CDN path for blob
  15. 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方法,如下所示:

  1. def web
  2. object.image.variant(:web).service_url
  3. end
  4. def opengraph
  5. object.image.variant(:opengraph).service_url
  6. end
  7. def twitter
  8. object.image.variant(:twitter).service_url
  9. end
  10. def mobile
  11. object.image.variant(:mobile).service_url
  12. end
  13. def thumb
  14. object.image.variant(:thumb).service_url
  15. end

这将检索您想要的变体并使用service_url方法返回其URL。

另外,您可以像这样定义您的VariantType:

  1. module Types
  2. class VariantType < Types::BaseObject
  3. field :web, String, null: false, method: :web_url
  4. field :opengraph, String, null: false, method: :opengraph_url
  5. field :twitter, String, null: false, method: :twitter_url
  6. field :mobile, String, null: false, method: :mobile_url
  7. field :thumb, String, null: false, method: :thumb_url
  8. def web_url
  9. object.image.variant(:web).service_url
  10. end
  11. def opengraph_url
  12. object.image.variant(:opengraph).service_url
  13. end
  14. def twitter_url
  15. object.image.variant(:twitter).service_url
  16. end
  17. def mobile_url
  18. object.image.variant(:mobile).service_url
  19. end
  20. def thumb_url
  21. object.image.variant(:thumb).service_url
  22. end
  23. end
  24. 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:

  1. def web
  2. object.image.variant(:web).service_url
  3. end
  4. def opengraph
  5. object.image.variant(:opengraph).service_url
  6. end
  7. def twitter
  8. object.image.variant(:twitter).service_url
  9. end
  10. def mobile
  11. object.image.variant(:mobile).service_url
  12. end
  13. def thumb
  14. object.image.variant(:thumb).service_url
  15. 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:

  1. module Types
  2. class VariantType < Types::BaseObject
  3. field :web, String, null: false, method: :web_url
  4. field :opengraph, String, null: false, method: :opengraph_url
  5. field :twitter, String, null: false, method: :twitter_url
  6. field :mobile, String, null: false, method: :mobile_url
  7. field :thumb, String, null: false, method: :thumb_url
  8. def web_url
  9. object.image.variant(:web).service_url
  10. end
  11. def opengraph_url
  12. object.image.variant(:opengraph).service_url
  13. end
  14. def twitter_url
  15. object.image.variant(:twitter).service_url
  16. end
  17. def mobile_url
  18. object.image.variant(:mobile).service_url
  19. end
  20. def thumb_url
  21. object.image.variant(:thumb).service_url
  22. end
  23. end
  24. 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.

huangapple
  • 本文由 发表于 2023年1月6日 12:21:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/75026893.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定