Skip to content

Add POST/PUT /api/v1/crossmarkpolicy endpoint para registrar CrossmarkPage#418

Open
Copilot wants to merge 6 commits intomasterfrom
copilot/consult-api-for-crossmark-policy
Open

Add POST/PUT /api/v1/crossmarkpolicy endpoint para registrar CrossmarkPage#418
Copilot wants to merge 6 commits intomasterfrom
copilot/consult-api-for-crossmark-policy

Conversation

Copy link
Contributor

Copilot AI commented Mar 6, 2026

Adds support for persisting journal crossmark (update policy) data, queried from an external core API at https://{domain}/api/v1/crossmarkpolicy.

O que esse PR faz?

  • Importa o modelo CrossmarkPage diretamente de opac_schema.v1.models (campos: doi, is_doi_active, language, journal (ref → Journal, cascade delete), url, text, created_at, updated_at)
  • Adiciona o endpoint REST POST/PUT /api/v1/crossmarkpolicy (protegido por token) que cria ou faz upsert de um registro CrossmarkPage identificado pela combinação (journal, language)
  • Lookup do journal aceita qualquer variante de ISSN (eISSN, pISSN, scielo_issn)
  • url e text são campos obrigatórios no payload; em atualização, todos os campos incluindo doi, url e text são sobrescritos
  • Pina opac-schema==2.10.0 em requirements.txt

Exemplo de payload:

{
    "doi": "10.1234/example.doi",
    "is_doi_active": true,
    "language": "pt",
    "journal_id": "1678-4464",
    "url": "https://example.com/crossmark",
    "text": "Crossmark policy text"
}

Onde a revisão poderia começar?

  • opac/webapp/controllers.py — import de CrossmarkPage, funções add_crossmark_page / get_crossmark_page_by_doi (fim do arquivo)
  • opac/webapp/main/views.py — endpoint crossmarkpolicy (fim do arquivo)
  • opac/tests/test_restapi.pyRestAPICrossmarkPageTestCase
  • requirements.txt — dependência opac-schema==2.10.0

Como este poderia ser testado manualmente?

  1. Inicie a aplicação com uma instância MongoDB em execução
  2. Crie um journal via POST /api/v1/journal
  3. Faça POST em /api/v1/crossmarkpolicy com um payload válido (incluindo url e text) referenciando o ISSN do journal
  4. Verifique resposta 200 com {"failed": false, "doi": "..."} e o documento persistido na coleção crossmark_page
  5. Faça PUT com o mesmo journal_id + language e campos atualizados — confirme atualização no lugar, sem registro duplicado

Algum cenário de contexto que queira dar?

CrossmarkPage é importado diretamente de opac_schema.v1.models, disponível a partir da versão 2.10.0. O requirements.txt agora pina opac-schema==2.10.0. O modelo segue o mesmo padrão MongoEngine utilizado por PressRelease, News, etc.

A chave de upsert é (journal, language) — cada combinação journal/language mapeia para exatamente um CrossmarkPage. Ao chamar o endpoint novamente para o mesmo par journal+language, os campos doi, is_doi_active, url e text do registro existente são todos atualizados.

Screenshots

N/A

Quais são tickets relevantes?

Consultar a API do core para obter os dados da política de atualização (crossmark)

Referências

Original prompt

This section details on the original issue you should resolve

<issue_title>Consultar a API do core (gestão de dados do periódico) para obter os dados da política de atualização (crossmark)</issue_title>
<issue_description>- [ ] Cada Journal tem (ou não) sua política de atualização (CrossmarkPolicy)

  • Consultar a API cuja rota é https://{domain}/api/v1/crossmarkpolicy
  • Para /api/v1/crossmarkpolicy considere filtrar por qualquer issn (eissn ou pissn) e/ou por collection e journal acronym
  • Armazenar os dados em:
class CrossmarkPage(Document):
    doi = StringField(required=True)
    is_doi_active = BooleanField(required=True, default=True)
    language = StringField(max_length=5, required=True)
    journal = ReferenceField(Journal, reverse_delete_rule=CASCADE, required=True)
    created_at = DateTimeField()
    updated_at = DateTimeField()
```</issue_description>

## Comments on the Issue (you are @copilot in this section)

<comments>
</comments>

💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Copilot AI and others added 2 commits March 6, 2026 19:26
- Add CrossmarkPage MongoEngine Document model in crossmark_models.py
- Add add_crossmark_page and get_crossmark_page_by_doi controller functions
- Add POST/PUT /api/v1/crossmarkpolicy REST API endpoint
- Add RestAPICrossmarkPageTestCase tests for the new endpoint

Co-authored-by: robertatakenaka <505143+robertatakenaka@users.noreply.github.com>
Co-authored-by: robertatakenaka <505143+robertatakenaka@users.noreply.github.com>
Copilot AI changed the title [WIP] Consult API for Crossmark policy updates Add CrossmarkPage model and POST/PUT /api/v1/crossmarkpolicy endpoint Mar 6, 2026
from opac_schema.v1.models import Journal


class CrossmarkPage(Document):
Copy link
Member

Choose a reason for hiding this comment

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

@copilot elimine a definição da classe aqui, pois ela existe em from opac_schema.v1 import models. E considere que também há url e text

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed crossmark_models.pyCrossmarkPage is now imported directly from opac_schema.v1.models. Updated requirements.txt to pin opac_schema to commit ebef33fb which introduces CrossmarkPage. Also added url and text field support to both the endpoint payload and add_crossmark_page controller. Commit: 60cb8cf.


Retorna a instância de CrossmarkPage salva.
"""
crossmark = CrossmarkPage.objects(doi=doi).first()
Copy link
Member

Choose a reason for hiding this comment

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

@copilot troque CrossmarkPage.objects(doi=doi).first() por CrossmarkPage.objects(journal=journal, language=language).first() e atualize os campos url e text também

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed lookup to CrossmarkPage.objects(journal=journal, language=language).first(). On existing records, doi, url, and text are all updated. Commit: 5c28a56.


O campo ``journal_id`` pode ser um eISSN, pISSN ou scielo_issn do periódico.
"""
payload = request.get_json()
Copy link
Member

Choose a reason for hiding this comment

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

@copilot payload tem que ter url e text

Copy link
Contributor Author

Choose a reason for hiding this comment

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

url and text are now required fields — both validated with a 400 response if missing. Commit: 5c28a56.

- Remove local crossmark_models.py: CrossmarkPage now imported from opac_schema.v1.models
- Update requirements.txt to pin opac_schema to commit ebef33fb (which introduces CrossmarkPage)
- Update controllers.py to import CrossmarkPage from opac_schema.v1.models
- Add url and text field support in add_crossmark_page controller and endpoint
- Update test import to use opac_schema.v1.models.CrossmarkPage

Co-authored-by: robertatakenaka <505143+robertatakenaka@users.noreply.github.com>
- controllers.py: change CrossmarkPage.objects(doi=doi) to
  CrossmarkPage.objects(journal=journal, language=language); update doi on
  existing records
- views.py: make url and text required fields in POST/PUT payload
- test_restapi.py: add url/text to base payload; add missing-url and
  missing-text test cases; fix update test to keep same language (since
  lookup key is now journal+language)

Co-authored-by: robertatakenaka <505143+robertatakenaka@users.noreply.github.com>
Copilot AI changed the title Add CrossmarkPage model and POST/PUT /api/v1/crossmarkpolicy endpoint Add POST/PUT /api/v1/crossmarkpolicy endpoint with CrossmarkPage from opac_schema Mar 7, 2026
@robertatakenaka robertatakenaka changed the title Add POST/PUT /api/v1/crossmarkpolicy endpoint with CrossmarkPage from opac_schema Add POST/PUT /api/v1/crossmarkpolicy endpoint para registrar CrossmarkPage Mar 7, 2026
@robertatakenaka robertatakenaka marked this pull request as ready for review March 7, 2026 20:55
Copilot AI review requested due to automatic review settings March 7, 2026 20:55
requirements.txt Outdated
PyJWT==2.8.0
tenacity==8.2.3
-e git+https://git@github.com/scieloorg/opac_schema@v2.9.0#egg=Opac_Schema
-e git+https://git@github.com/scieloorg/opac_schema@ebef33fb368e93c223b1cad31959809d6f3d5018#egg=Opac_Schema
Copy link
Member

Choose a reason for hiding this comment

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

@copilot trocar ebef33fb368e93c223b1cad31959809d6f3d5018 por 2.10.0

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed to opac-schema==2.10.0. Commit: e15a5c0.

Co-authored-by: robertatakenaka <505143+robertatakenaka@users.noreply.github.com>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds support for persisting and updating journal Crossmark policy content (CrossmarkPage) via a token-protected REST API endpoint, leveraging the new CrossmarkPage model introduced in opac_schema.

Changes:

  • Pinned opac_schema dependency to a specific commit that introduces CrossmarkPage.
  • Added POST/PUT /api/v1/crossmarkpolicy endpoint to create/update Crossmark policy data keyed by (journal, language).
  • Added controller helpers and REST API tests for CrossmarkPage upsert behavior.

Reviewed changes

Copilot reviewed 3 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
requirements.txt Pins opac_schema to the commit that contains CrossmarkPage.
opac/webapp/main/views.py Adds the /crossmarkpolicy REST endpoint and payload validation.
opac/webapp/controllers.py Imports CrossmarkPage and adds create/update + lookup helpers.
opac/tests/test_restapi.py Adds REST API tests for creating/updating CrossmarkPage and validation errors.
.gitignore Ignores src/ (pip editable install sources).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


O campo ``journal_id`` pode ser um eISSN, pISSN ou scielo_issn do periódico.
"""
payload = request.get_json()
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

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

request.get_json() can raise a BadRequest (returning Flask’s default 400 HTML) when the request has Content-Type: application/json but an empty/invalid body. That would bypass the intended {"failed": true, "error": "missing payload"} response (and makes response.get_json() unreliable in tests). Use request.get_json(silent=True) (or catch BadRequest) and handle None explicitly to always return a JSON error payload.

Suggested change
payload = request.get_json()
payload = request.get_json(silent=True)

Copilot uses AI. Check for mistakes.
Comment on lines +1968 to +1986
crossmark = CrossmarkPage.objects(journal=journal, language=language).first()

if crossmark is None:
crossmark = CrossmarkPage(
doi=doi,
is_doi_active=is_doi_active,
language=language,
journal=journal,
)
else:
crossmark.doi = doi
crossmark.is_doi_active = is_doi_active

crossmark.url = url
crossmark.text = text

return crossmark.save()


Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

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

This upsert is implemented as a read-then-write (first() then save()), which is not atomic. Under concurrent POST/PUT calls for the same (journal, language) pair it can create duplicate documents and update only one of them. Prefer an atomic modify(..., upsert=True, new=True) / update_one(upsert=True, ...) on the queryset filtered by (journal, language), setting all fields in one operation.

Suggested change
crossmark = CrossmarkPage.objects(journal=journal, language=language).first()
if crossmark is None:
crossmark = CrossmarkPage(
doi=doi,
is_doi_active=is_doi_active,
language=language,
journal=journal,
)
else:
crossmark.doi = doi
crossmark.is_doi_active = is_doi_active
crossmark.url = url
crossmark.text = text
return crossmark.save()
crossmark = CrossmarkPage.objects(journal=journal, language=language).modify(
upsert=True,
new=True,
set__doi=doi,
set__is_doi_active=is_doi_active,
set__url=url,
set__text=text,
set_on_insert__journal=journal,
set_on_insert__language=language,
)
return crossmark

Copilot uses AI. Check for mistakes.
Comment on lines +598 to +603
response = client.post(
url_for("restapi.crossmarkpolicy"),
data=json.dumps(self.crossmark_payload),
follow_redirects=True,
content_type="application/json",
)
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

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

The /api/v1/crossmarkpolicy endpoint is decorated with @helper.token_required (token must be provided as a token query param). These tests call url_for("restapi.crossmarkpolicy") without supplying a token, so they will receive 401 responses from the decorator. Update the test requests to include a valid token (e.g., url_for(..., token=<jwt>) or query_string={"token": ...}), consistent with how the endpoint is authenticated.

Copilot uses AI. Check for mistakes.
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.

Criar a API POST para receber e guardar os dados da política de atualização (crossmark)

3 participants