如何让GraphQL知道文件名是图像-自定义API不创建固定/流体图像

时间:2020-04-16 19:36:36

标签: gatsby gatsby-image

我有一个自定义api端点,正在使用gatsby-source-apiserver访问。我的数据库中有无法转换为图像节点的图像-即允许childimagesharp

在我的graphql中,我具有以下结构:

######################################################
# Models
######################################################
class Project(models.Model):
    name = models.CharField(max_length=250, null=False, blank=False,
                            verbose_name=_("name"))
    slug = models.SlugField(max_length=250, unique=True, null=False, blank=True,
                            verbose_name=_("slug"))
    description = models.TextField(null=False, blank=False,
                                   verbose_name=_("description"))

    logo = models.FileField(upload_to=get_project_logo_file_path,
                            max_length=500, null=True, blank=True,
                            verbose_name=_("logo"))

    created_date = models.DateTimeField(null=False, blank=False,
                                        verbose_name=_("created date"),
                                        default=timezone.now)
    modified_date = models.DateTimeField(null=False, blank=False,
                                         verbose_name=_("modified date"))
    owner = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True,
                              related_name="owned_projects", verbose_name=_("owner"), on_delete=models.CASCADE)

    members = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name="projects",
                                     through="Membership", verbose_name=_("members"),
                                     through_fields=("project", "user"))

    is_private = models.BooleanField(default=True, null=False, blank=True,
                                     verbose_name=_("is private"))
    anon_permissions = ChoiceArrayField(
        models.TextField(null=False, blank=False, choices=ANON_PERMISSIONS),
        null=True,
        blank=True,
        default=list,
        verbose_name=_("anonymous permissions")
    )
    public_permissions = ChoiceArrayField(models.TextField(null=False, blank=False, choices=MEMBERS_PERMISSIONS),
                                    null=True, blank=True, default=list, verbose_name=_("user permissions"))

    is_featured = models.BooleanField(default=False, null=False, blank=True,
                                      verbose_name=_("is featured"))

    class Meta:
        db_table = "projects"
        verbose_name = "project"
        verbose_name_plural = "projects"
        ordering = ["name", "id"]
        index_together = [
            ["name", "id"],
        ]


class Membership(models.Model):
    # This model stores all project memberships. Also
    # stores invitations to memberships that do not have
    # assigned user.

    user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, default=None,
                             related_name="memberships", on_delete=models.CASCADE)
    project = models.ForeignKey(Project, null=False, blank=False,
                                related_name="memberships", on_delete=models.CASCADE)
    role = models.ForeignKey('core.Role', null=False, blank=False,
                             related_name="memberships", on_delete=models.CASCADE)
    is_admin = models.BooleanField(default=False, null=False, blank=False)

    user_order = models.BigIntegerField(default=timestamp_ms, null=False, blank=False,
                                        verbose_name=_("user order"))

    class Meta:
        db_table = "memberships"
        verbose_name = "membership"
        verbose_name_plural = "memberships"
        unique_together = ("user", "project",)
        ordering = ["project", "user__full_name", "user__username", "user__email"]

    def get_related_people(self):
        related_people = get_user_model().objects.filter(id=self.user.id)
        return related_people

    def clean(self):
        # TODO: Review and do it more robust
        memberships = Membership.objects.filter(user=self.user, project=self.project)
        if self.user and memberships.count() > 0 and memberships[0].id != self.id:
            raise ValidationError(_('The user is already member of the project'))

sourceFilename是一个字符串,例如'thisisanimage.jpg'-如果我在URL上附加“ http://example.com/media”这样的图像,图像将正常工作。我不确定如何继续,我已经看过createResolvers,但不完全了解如何实现它。

这是我到目前为止一直在尝试的-似乎没有做很多(除了在终端中获得“正在发生的一切”注释,而在下一个注释中却没有...

query MyQuery {
  allSubmissions {
    edges {
      node {
        works {
          mediaItems {
            sourceFilename
          }
        }
      }
    }
  }
}

值得一提的是,作品是一个mediaItems数组,它本身是一个字段数组,其中sourceFilename是一个。查看所附的屏幕截图

graphQL result from above query

1 个答案:

答案 0 :(得分:1)

因此,在对Spectrum Gatsby聊天提出了一些很好的建议之后,我有了一些解决方案: https://spectrum.chat/gatsby-js/general/getting-relative-images-to-work-with-a-custom-api-using-gatsby-source-apiserver~a0805b02-6e2b-4be6-ab1a-ae6d3cc53fab

执行此操作的一种方法是将代码放入gatsby-node.js。这可能不是最好的方法,但是可以。

第一个问题是以GraphQL看到字段的方式查找字段的名称。您可以使用文档资源管理器浏览查询,以查找包含图像URL的媒体项目的类型名称。这通常在Pascal案例中。就我而言,这不是由于我设置事物的奇怪方式造成的!

为我找到一个更简单的方法是使用CLI:键入gatsby repl,然后发出schema命令。

在我的情况下,类型名称为submissionsWorksMediaItems

然后我发现我可以使用它来下载图像并为我提供childImageSharp功能:

const { createRemoteFileNode } = require("gatsby-source-filesystem")

exports.createResolvers = ({
  actions,
  cache,
  createNodeId,
  createResolvers,
  store,
  reporter,
}) => {
  const { createNode } = actions
  createResolvers({
    submissionsWorksMediaItems: {
      imageFile: {
        type: `File`,
        resolve(source, args, context, info) {
          return createRemoteFileNode({
            url:
              `https://example.com/media/${source.filename}`,
            store,
            cache,
            createNode,
            createNodeId,
            reporter,
            })

        },
      },
    },
  })
}

在我的情况下,我发现我遇到了一些文件不在服务器上的问题-这造成了404错误,导致构建失败(尽管它在开发模式下有效)。因此,我改为使用以下代码(再次从Spectrum.chat中读取线程以获取所有上下文:

const { createRemoteFileNode } = require("gatsby-source-filesystem")

const imageNodes = new Map()
const getImageUrl = source =>
  `https://example.com/media/${source.filename}`
exports.sourceNodes = ({
  actions,
  cache,
  createNodeId,
  getNodesByType,
  store,
  reporter,
}) => {
  const { createNode } = actions
  const imageDownloads = []
  const submissions = getNodesByType("submissions")
  submissions.forEach(node => {
    node.works &&
      node.works.forEach(({ mediaItems }) => {
        mediaItems &&
          mediaItems.forEach(mediaSource => {
            const imageUrl = getImageUrl(mediaSource)
            imageDownloads.push(
              createRemoteFileNode({
                url: imageUrl,
                store,
                cache,
                createNode,
                createNodeId,
                reporter,
              })
                .then(result => {
                  if (!result) {
                    return reporter.warn(`Could not download ${imageUrl}`)
                  }
                  imageNodes.set(imageUrl, result)
                })
                .catch(err => {
                  reporter.warn(err)
                })
            )
          })
      })
  })
  return Promise.all(imageDownloads)
}
exports.createResolvers = ({ createResolvers }) => {
  createResolvers({
    submissionsWorksMediaItems: {
      imageFile: {
        type: `File`,
        resolve(source, args, context, info) {
          const imageUrl = getImageUrl(source)
          if (imageNodes.has(imageUrl)) {
            return imageNodes.get(imageUrl)
          }
          return null
        },
      },
    },
  })
}