Adicionando o Openverse: o terceiro provedor de imagens, e o primeiro que ninguém precisa configurar
Eu estava retomando uma issue que tinha ficado bloqueada no backlog por um bom tempo. A ideia é simples: adicionar o Openverse (um projeto da WordPress, antigo CC Search) como uma terceira fonte de imagens de destaque, ao lado do Pexels e do Unsplash, dando acesso a mais de 800 milhões de obras Creative Commons e de domínio público. Ela estava bloqueada por duas coisas que precisavam existir antes: imagens de destaque hospedadas localmente (já que o Openverse só aponta para onde a imagem está originalmente, e esses links apodrecem) e atribuição nos posts publicados (já que as licenças CC exigem dar crédito ao criador). As duas coisas foram entregues semanas atrás, então essa issue finalmente ficou desbloqueada.
Antes de escrever qualquer código, bati direto na API real do Openverse em vez de simplesmente confiar na descrição da issue. Isso valeu a pena na hora: confirmei que a busca anônima realmente funciona sem nenhuma credencial, obtive os nomes exatos dos campos de volta (url, thumbnail, creator_url, license, license_version, tudo confirmado contra uma resposta real para uma foto do Flickr), e confirmei o formato do endpoint de token OAuth2 com um par de requisições deliberadamente inválidas (um 400 com erros de campo obrigatório em /v1/auth_tokens/register/, um 401 invalid_client em /v1/auth_tokens/token/) sem chegar a registrar um app ou tocar em nenhuma conta real.
O cliente segue de perto o estilo já estabelecido pelo Pexels e pelo Unsplash: mesmo padrão de requisição Net::HTTP, mesmo ponto de injeção de dependência connection: para os testes, mesmas classes aninhadas Error/AuthError. O único desvio deliberado é que credenciais em branco não geram erro, o nível anônimo do Openverse é uma forma legítima e suportada de usar a API, não um modo degradado, então Openverse::Client.new sem nada passado simplesmente funciona. Essa única decisão de design acabou tendo mais implicações do que eu esperava (mais sobre isso abaixo).
A outra decisão de design de verdade foi sobre como tratar a licença. Pexels e Unsplash usam uma única licença geral para todo resultado (uma foto do Pexels é sempre “Pexels License”, ponto final), então os clientes deles simplesmente retornam uma constante. O Openverse agrega obras sob todo tipo de variante CC, BY, BY-SA, CC0, Public Domain Mark, então a licença precisava ser lida resultado a resultado. Errei isso na primeira tentativa: montei o nome de exibição sempre prefixando “CC” no código que vinha de volta, o que produz “CC CC0” para imagens de domínio público. Meu próprio teste pegou isso no sentido de que eu escrevi o teste para validar exatamente esse valor errado, e depois a revisão automatizada pegou que o teste estava validando um bug, não uma funcionalidade. Corrigi com um mapa explícito para as licenças sem atribuição (cc0 → “CC0”, pdm → “Public Domain Mark”) e o caminho com prefixo CC só para as variantes que realmente exigem atribuição.
O cache de token tinha um bug mais sutil. Rails.cache.fetch não consegue definir um TTL derivado do que o próprio bloco retorna, então eu implementei uma leitura-depois-escrita na mão. Minha primeira versão limitava o TTL calculado à margem de segurança em vez de a zero, então um token com um expires_in curto (digamos, 30 segundos, com uma margem de 60 segundos) acabava sendo cacheado por 60 segundos mesmo assim, o que significa que o app continuaria usando um token já morto e receberia 401 silenciosamente na segunda metade dessa janela. A revisão também pegou que o caminho de erro da busca de token tinha se desviado do tratamento de erro do caminho de busca principal, um 500 do endpoint de token estava sendo reportado incorretamente como AuthError, a mesma classe usada para um client secret genuinamente errado, o que mandaria alguém para o caminho de debug errado.
O que mais me surpreendeu não foi um bug, porém. Foi um trade-off de design que eu não tinha pensado até o fim, até que o ângulo de altitude da revisão nomeou isso diretamente: como o Openverse funciona de forma anônima, ele é o primeiro provedor do registro que fica “configurado” incondicionalmente. Pexels e Unsplash só aparecem nos resultados de busca se um admin colou uma chave; o Openverse sempre aparece. Isso significa que uma instalação que nunca configurou nenhum provedor de imagens agora faz uma chamada HTTPS real de saída para api.openverse.org em toda busca de imagem de destaque, quando antes não fazia nada. Decidi não corrigir isso dentro do próprio PR, é exatamente o que a issue pedia (“anônimo por padrão”), e construir uma forma de desativar um provedor sem chave é uma funcionalidade maior do que o escopo dessa issue. Mas é uma mudança de comportamento real que vale a pena nomear explicitamente em vez de descobrir por acidente depois.
O que vem a seguir: nada diretamente bloqueado por isso. O caso extremo de credenciais parcialmente preenchidas (client_id definido sem um secret, caindo silenciosamente para o modo anônimo sem nenhuma indicação) e a exposição da tela de atribuição em lote de imagens de destaque ao rate limit do Openverse sob rolagem pesada estão documentados no PR como riscos aceitos e de baixa severidade, em vez de terem sido registrados como novas issues. Nenhum dos dois causa atualmente um comportamento incorreto, só uma experiência degradada num caso extremo.
Leitura relacionada
O bug da imagem de destaque que na verdade eram três bugs
Um card que mentia sobre um hero ausente, um caminho relativo resolvido contra a origem errada, e um bloqueio de hotlink do meu blog contra o meu próprio app.
Adicionando o Unsplash como um segundo provedor de imagem de destaque
Espelhando o client do Pexels linha por linha, um ping de rastreamento dispara-e-esquece, e uma URL do navegador que quase acabou com a chave de API anexada.
Construindo imagens de destaque auto-hospedadas antes de eu ter qualquer coisa que precisasse delas
Um pipeline de download e re-hospedagem para imagens CC: vips de um buffer, um force-encode UTF-8 que corromperia o JPEG, e uma fila velha parecendo bug.
Você também pode achar útil
RackNerd VPS
Hospedagem VPS econômica para serviços leves que funcionam continuamente.
Como afiliado da RackNerd, ganho com compras qualificadas.
Saiba maisProton Drive
Armazenamento em nuvem criptografado, da equipe por trás do Proton Mail.
Como parceiro da Proton, ganho com compras qualificadas dos serviços de privacidade e segurança da Proton (Pass, Mail, VPN, Drive).
Saiba maisProton Mail
E-mail criptografado de ponta a ponta, com arquitetura de acesso zero.
Como parceiro da Proton, ganho com compras qualificadas dos serviços de privacidade e segurança da Proton (Pass, Mail, VPN, Drive).
Saiba mais