Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Solicitar conteúdos root e child de um usuário separadamente #747

Merged
merged 6 commits into from
Nov 9, 2022
Merged

Solicitar conteúdos root e child de um usuário separadamente #747

merged 6 commits into from
Nov 9, 2022

Conversation

33gustavo33
Copy link
Contributor

@33gustavo33 33gustavo33 commented Sep 21, 2022

Com esse PR é possível solicitar os conteúdos root e conteúdos child de um usuário separadamente. (Ou obter todos os conteúdos do usuário)

  • GET /api/v1/contents/[username]/root: retorna os conteúdos root do usuário
  • GET /api/v1/contents/[username]/: retorna todos os conteúdos do usuário
  • GET /api/v1/contents/[username]/children: retorna os conteúdos child do usuário

@vercel
Copy link

vercel bot commented Sep 21, 2022

@33gustavo33 is attempting to deploy a commit to the TabNews Team on Vercel.

To accomplish this, @33gustavo33 needs to request access to the Team.

Afterwards, an owner of the Team is required to accept their membership request.

If you're already a member of the respective Vercel Team, make sure that your Personal Vercel Account is connected to your GitHub account.

@filipedeschamps
Copy link
Owner

@33gustavo33 contribuição sensacional! Muito obrigado por ter chego inclusive a implementar os testes 😍 eu demorei para participar desse PR, pois estava montando o novo estúdio do canal, de qualquer forma, eu já tinha lido o código nos primeiros dias que enviou e ele e fiquei pensando bastante sobre a interface pública e qual a melhor forma de interagir com esses dados.

Não consegui chegar a uma conclusão, mas gostaria de compartilhar as duas principais estratégias que acredito existir:

  1. Retornar em duas requests separadas os conteúdos root e children (que você implementou)
  2. Retornar na mesma request conteúdos root e children (conforme o filtro utilizado)

Opção 1

Na primeira opção, me peguei pensando se não seria melhor ao invés de usar query parameters para filtrar, usar o padrão de /children que foi usado no endpoint do conteúdo em si, mas agora para o nível do username.

Então por exemplo, se já conseguimos acessar os conteúdos children de um conteúdo dessa forma:

.../contents/[username]/[slug]/children

Talvez acessar os conteúdos children de um username poderia ser assim:

.../contents/[username]/children

E daí na interface quando alguém entra na Home do Usuário, ele veria uma lista das últimas publicações children (com a navegação dela) e mais uma lista abaixo com as últimas publicações children (também com a sua navegação). Isso levaria cada navegação para sua página específica de navegação.

Opção 2

Nessa opção, ela usa o query parameter que você criou, mas com uma abordagem de também conseguir retornar tudo na mesma lista, onde a pessoa pode escolher receber dados de 3 formas:

  1. Somente root
  2. Somente children
  3. Ambos root e children

Dado a isso, talvez ao invés de criarmos um query parameter com novo nome, poderíamos controlar o que queremos fazer com o parent_id (que é o ponto central dessa implementação), mas confesso que não consegui bolar uma interface legal para todos os casos e travei. Na verdade só consegui pensar num caso:

  1. Somente root: ?parent_id=null

Do resto, tudo que rabisquei aqui ficou muito feio.

Fora que isso pode também complicar na hora do validator entender o que é um parent_id, pois se enviarmos valores como true ou false, a validação irá quebrar, pois parent_id espera um uuid.

Conclusão

Não cheguei a conclusão, mas gostaria de registrar aqui meus pensamentos enquanto isso. Mas aproveito para perguntar o que você achou da interface pública da Opção 1?

@33gustavo33
Copy link
Contributor Author

33gustavo33 commented Sep 26, 2022

@filipedeschamps A opção 1 é mais interessante porque ficaria um "padrão" com o resto da API
Pelo que eu entendi
/contents/[username]/children retornaria os conteúdos child do usuário
/contents/[username]/ retornaria os conteúdos root do usuário
Certo?

@filipedeschamps
Copy link
Owner

Isso! Mas mesmo assim, eu vejo um furo nessa minha sugestão, porque seguindo ela à risca deveria ser assim:

  • /contents/[username]/children - retorna somente children (com o campo body)
  • /contents/[username]/root - retorna somente root (sem o campo body)
  • /contents/[username]/ - retorna tudo

Mas isso ao mesmo tempo impacta no endpoint /contents que por padrão somente retorna conteúdos root. Então para seguir o mesmo padrão, teríamos que fazer uma breaking change na raiz do path de conteúdo:

  • /contents/children - retorna somente children de todos os usernames (com o campo body)
  • /contents/root - retorna somente root de todos os usernames (que é o retorno de hoje, sem o body)
  • /contents/[username]/ - retorna tudo deste username (este é o mesmo endpoint lá de cima)

Fazer o design de uma API pública é difícil 😂

Se mais alguém estiver observando a discussão e quiser dar alguma sugestão, é super bem vindo 🤝

@33gustavo33
Copy link
Contributor Author

Mas isso ao mesmo tempo impacta no endpoint /contents que por padrão somente retorna conteúdos root. Então
para seguir o mesmo padrão, teríamos que fazer uma breaking change na raiz do path de conteúdo:

@filipedeschamps Acho que vale a pena fazer essa breaking change por 3 motivos:

  • O Tabnews ainda está em "beta" e eu imagino que não tenha muitos Clients consumindo a API,
  • Isso permitiria obter todos os conteúdos children, e não ficar preso somente aos conteúdos root em /contents seria util.
  • Ter um padrão na API sempre é bom.

Fazer o design de uma API pública é difícil 😂

Nunca concordei tanto com uma frase....

@33gustavo33
Copy link
Contributor Author

Pronto, com esse commit que adicionei agora o children e root são possíveis de acessar assim:

  • /contents/[username]/children - retorna somente children
  • /contents/[username]/root - retorna somente root
  • /contents/[username]/ - retorna tudo

@aprendendofelipe
Copy link
Collaborator

Acho que faz sentido /contents/[username] e /contents seguirem o mesmo padrão, pois o primeiro é só uma restrição aplicada ao segundo. Mas não precisamos tentar manter o mesmo padrão de /contents/[username]/[slug], pois esse último é bem diferente.

Estou em dúvida se seria útil um endpoint que retornasse só os children. Talvez seja suficiente ter duas alternativas, onde pedimos ou não os children, mas nos dois casos recebemos os root:

  1. root (aqui mantendo o padrão atual).
  2. children + root (aqui criando um novo caminho, por exemplo, /all_contents no lugar de /contents).

Só tentando contribuir, mas....

Fazer o design de uma API pública é difícil 😂

Nunca concordei tanto com uma frase....

Também concordo demais!!! hahaha

E daí na interface quando alguém entra na Home do Usuário, ele veria uma lista das últimas publicações children (com a navegação dela) e mais uma lista abaixo com as últimas publicações children (também com a sua navegação). Isso levaria cada navegação para sua página específica de navegação.

Nessa opção de duas navegações independentes provavelmente só vai compensar usar páginas estáticas para a página 1/1. E o restante das navegações compensaria mais fazer pelo client chamando a API. Por isso eu não gosto muito da ideia.

O que acham de o padrão na home do usuário ser mostrar root e children tudo junto (classificado por tempo ou outra estratégia) e permitir filtrar os resultados pelo client?

@filipedeschamps
Copy link
Owner

O que acham de o padrão na home do usuário ser mostrar root e children tudo junto (classificado por tempo ou outra estratégia) e permitir filtrar os resultados pelo client?

Gosto desta ideia! Podemos mostrar tudo na mesma lista e diferenciar com um ícone quando é uma resposta (quando tem parent_id).

2. children + root (aqui criando um novo caminho, por exemplo, /all_contents no lugar de /contents).

Eu imagino que em REST há uma alternativa que consiga ainda respeitar o objeto limpo na raiz, possivelmente com as query strings mesmo.


Uma sugestão do que fazer, que é o mesmo princípio adotado no passado quando as coisas ficam complexas que é dar o menor passo possível, que para esse caso eu sugiro o seguinte:

/contents/[username]/ - retornar tudo e anunciar uma breaking change na API.

Essa não é a pior breaking change do mundo, porque o objeto retornado é exatamente o mesmo, porém agora com dados que poderão estar null (que é o title no caso das respostas).

Interessae notar que na página do usuário, body já estava sendo retornado e não deveria, porque até então não estava sendo usado. Essa otimização foi feita no /contents e esqueci de repassar para o /contents/[username]:

O que me faz pensar se no novo retorno do /contents/[username]/ a gente deveria remover o body quando o parent_id=null e manter quando tiver parent_id. Eu destaco isso, pois quando removi o body do /content me lembro que houve um ganho de performance interessante na velocidade de retorno do endpoint. Ou a gente retorna o body de tudo e vida que segue?

@aprendendofelipe
Copy link
Collaborator

Podemos mostrar tudo na mesma lista e diferenciar com um ícone quando é uma resposta (quando tem parent_id).

Sim, gosto da ideia do ícone e também seria legal mostrar mais informações, como as que aparecem quando acessamos uma resposta. Como nesse exemplo:

image Respondendo a "Acho que a parte legal desse site é ensinar uma li..." dentro da publicação Aprenda Web Design em 4 minutos

Talvez também mostrar um pedaço do body. O que nos leva ao seu questionamento:

O que me faz pensar se no novo retorno do /contents/[username]/ a gente deveria remover o body quando o parent_id=null e manter quando tiver parent_id. Eu destaco isso, pois quando removi o body do /content me lembro que houve um ganho de performance interessante na velocidade de retorno do endpoint. Ou a gente retorna o body de tudo e vida que segue?

Podemos começar retornando só o que fizer sentido para montar a página do usuário. E só retornar outros dados se surgir necessidade.

E pensando na sugestão acima de mostrar o "Em resposta a...", percebo que, ao contrário do que eu disse antes, temos sim semelhanças com a rota /contents/[username]/[slug]

Que vontade de GraphQL! hahaha

@33gustavo33
Copy link
Contributor Author

O que acham de o padrão na home do usuário ser mostrar root e children tudo junto (classificado por tempo ou outra estratégia) e permitir filtrar os resultados pelo client?

Acho que o único problema com essa ideia é que não teria 30 itens por página.

Como a API retorna 30 itens por padrão
Se um usuário tivesse 28 conteúdos Child e 4 conteúdo root, quando ele filtrasse pelo Client por conteúdos Root só iria aparecer 2 conteúdos Root na 1 página, já que 2+28=30

@aprendendofelipe
Copy link
Collaborator

O que acham de o padrão na home do usuário ser mostrar root e children tudo junto (classificado por tempo ou outra estratégia) e permitir filtrar os resultados pelo client?

Acho que o único problema com essa ideia é que não teria 30 itens por página.

Como a API retorna 30 itens por padrão Se um usuário tivesse 28 conteúdos Child e 4 conteúdo root, quando ele filtrasse pelo Client por conteúdos Root só iria aparecer 2 conteúdos Root na 1 página, já que 2+28=30

@33gustavo33, você diz o problema do filtro, né? É verdade! Por enquanto é melhor sem esse filtro. Só o fato de diferenciar a apresentação dos conteúdos já é suficiente.

Eu gostei da ideia do @filipedeschamps de mostrar um ícone. Daí precisamos pensar se vamos só mostrar o início do body ou se mostramos também dados do parent e do root relacionados com a resposta.

@33gustavo33
Copy link
Contributor Author

33gustavo33 commented Sep 28, 2022

Eu gostei da ideia do @filipedeschamps de mostrar um ícone. Daí precisamos pensar se vamos só mostrar o início do body ou se mostramos também dados do parent e do root relacionados com a resposta.

@aprendendofelipe Pessoalmente eu prefiro mostrar o início do body, ou o body inteiro mesmo.
Também gostei da ideia de mostrar um ícone, vai ajudar a identificar se é um child ou se é um root

Estou em dúvida se seria útil um endpoint que retornasse só os children. Talvez seja suficiente ter duas alternativas, onde pedimos ou não os children, mas nos dois casos recebemos os root:

  1. root (aqui mantendo o padrão atual).
  2. children + root (aqui criando um novo caminho, por exemplo, /all_contents no lugar de /contents).

Porque não ao invés de /all_contents algo como /contents/all?

@aprendendofelipe
Copy link
Collaborator

Pessoalmente eu prefiro mostrar o início do body, ou o body inteiro mesmo

Se for mostrar o body inteiro, então vai ser algo mais no estilo de RenderChildrenTree do que de ContentList. É uma opção, mas prefiro algo mais próximo de ContentList para termos uma visão geral da lista antes de clicar em um item específico. Mas sim, mostrando o começo do body é melhor do que só mostrar o "Em resposta".

Porque não ao invés de /all_contents algo como /contents/all?

Não tenho nada contra... haha... Mas também não tenho nada contra as outras sugestões de caminhos... Todos fazem sentido 🤝

@33gustavo33
Copy link
Contributor Author

Se for mostrar o body inteiro, então vai ser algo mais no estilo de RenderChildrenTree do que de ContentList. É uma opção, mas prefiro algo mais próximo de ContentList para termos uma visão geral da lista antes de clicar em um item específico. Mas sim, mostrando o começo do body é melhor do que só mostrar o "Em resposta".

@aprendendofelipe Também prefiro mostrar só o começo do body, algo como os primeiros 35 caracteres seria interessante, não tem necessidade de mostrar mais eu imagino.

Porque não ao invés de /all_contents algo como /contents/all?

Não tenho nada contra... haha... Mas também não tenho nada contra as outras sugestões de caminhos... Todos fazem sentido 🤝

O problema aqui é que todas as sugestões fazem sentido, mesmo assim acho que vale a pena ir pelo caminho "fácil" e fazer a breaking change como o @filipedeschamps falou aqui

@filipedeschamps
Copy link
Owner

filipedeschamps commented Sep 29, 2022

Show! Sugiro também manter o espírito de ContentList sim e por hora retornar tudo em /contents/[username].

Sobre a raiz do /contents ou outras variações do /contents/[username] vamos manter a cabeça pensando e fazer isso em outra oportunidade 🤝

@aprendendofelipe
Copy link
Collaborator

Navegando pelas páginas de alguns usuários que publicam bastante eu percebi que não ia ficar tão interessante aquela minha sugestão de deixar os comentários desses usuários estivessem ali misturados com as publicações.

Então pensei em outra alternativa... Só adicionar na página do usuário um link para algo como:

https://www.tabnews.com.br/gugadeschamps/comentarios

E esse caminho pode ter sua própria paginação

https://www.tabnews.com.br/gugadeschamps/comentarios/pagina/2

Só para pensarmos, pois isso não é para esse PR 🤝

@33gustavo33
Copy link
Contributor Author

Adicionei um commit que:

  • Retira o body dos conteúdos root
  • Encurta o body em conteúdos child

@filipedeschamps
Copy link
Owner

@33gustavo33 eu vou primeiro fazer o merge do PR #783 do @aprendendofelipe e depois venho nesse aqui e vou carregar ele até a parte do frontend para colocarmos isso em produção 🤝 me veio mais coisas na cabeça sobre as rotas e assim que conseguir compartilho enviando novos comentários e commits 👍

Copy link
Owner

@filipedeschamps filipedeschamps left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Muito obrigado pela contribuição @33gustavo33 e vou seguir com ela fazendo alguns pequenos ajustes e também implementando a parte do frontend 🤝

@@ -173,6 +172,10 @@ async function findAll(values = {}, options = {}) {
return `contents.${columnName} IS NOT DISTINCT FROM $${globalIndex}`;
}

if (columnName === '$not_null') {
return `contents.${columnValue} IS NOT NULL`;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To pensando aqui, se com o que está no validator fica possível fazer um injection aqui caso algum dia esse valor seja exposto na interface pública. Vou colocar outro comentário no validator.


$not_null: function () {
return Joi.object({
$not_null: Joi.string()
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Esta é a parte que eu vejo ser possível fazer um injection no banco caso algum dia esse parâmetro for exposto na interface pública (o que não está hoje). Mas destaco isso dado que ele aceita uma string com qualquer valor e pelo que entendi, isso é concatenado na linha 176 do content. O que talvez temos que fazer aqui é limitar com um .valid() o que pode ser de fato enviado.

O model content é o que mais me faz querer voltar a usar um ORM, seria tudo muito mais fácil em alguns casos 😂


function shortifyText(text) {
const substring = text.substring(0, 35).trim();
return text.length < 35 ? substring : substring + '...';
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aqui vou alterar para 256 para ficar igual ao tamanho da propriedade title 🤝

@filipedeschamps
Copy link
Owner

To pensando novamente na idéia de que trazer ou não os conteúdos root ou child é um filtro por query string do endpoint /contents.

Então minha cabeça ta me jogando as sugestões abaixo, mas eu não estou seguro:

  • /contents - traz tudo
  • /contents?parent_id=true - traz só child contents, pois eles apontam para um parent_id.
  • /contents?parent_id=false - traz só root contents.

Mas não é semântico, dado que para o root contents deveria ser parent_id=null e tanto true quanto false não são valores de parent_id. Arggggghhh!!! 😂

@filipedeschamps filipedeschamps changed the base branch from main to users-route November 9, 2022 17:27
@filipedeschamps
Copy link
Owner

@33gustavo33 eu vou resolver o conflito e em seguida adicionar commits para reduzir o escopo da implementação, mas quero manter os commits originais para pesquisa depois 🤝 tomei a decisão de, enquanto não chegamos numa conclusão sobre a interface pública para filtro dos dados, não vamos implementar ela (os filtros), e vamos trabalhar apenas em fazer o endpoint /contents/users retornar os conteúdos root e child. Mais para frente quando for realmente necessário implementar os filtros, vamos conversar de novo 🤝

E para resolver o conflito no arquivo de teste, eu vou recuperar o arquivo original que está na main e depois só complementar ele com testes que garantem o que a implementação faz na raiz do endpoint 👍

@vercel
Copy link

vercel bot commented Nov 9, 2022

@filipedeschamps is attempting to deploy a commit to the TabNews Team on Vercel.

A member of the Team first needs to authorize it.

@filipedeschamps filipedeschamps merged commit cd47d48 into filipedeschamps:users-route Nov 9, 2022
@filipedeschamps filipedeschamps deleted the users-route-resolved branch November 9, 2022 17:39
filipedeschamps added a commit that referenced this pull request Nov 9, 2022
… `/children` endpoints for now

This was originally implemented by @33gustavo33 but as discussed in here
#747 (comment) we will
reimplement this in the future, probably using query filters
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants