Skip to content

feat: Cria endpoint para criar Article a partir de PidProviderXML#1445

Open
samuelveigarangel wants to merge 9 commits into
scieloorg:mainfrom
samuelveigarangel:issue-1444
Open

feat: Cria endpoint para criar Article a partir de PidProviderXML#1445
samuelveigarangel wants to merge 9 commits into
scieloorg:mainfrom
samuelveigarangel:issue-1444

Conversation

@samuelveigarangel

@samuelveigarangel samuelveigarangel commented Jun 25, 2026

Copy link
Copy Markdown
Collaborator

O que esse PR faz?

  • Adiciona EndPoint para receber requisições POST para disparar processamento de um PidProviderXML específico
  • Adiciona roda /api/v1/published_article
  • Adiciona testes para este EndPoint

Onde a revisão poderia começar?

pelo commit 62edbcb

Como este poderia ser testado manualmente?

Criar a task task_dispatch_articles informando uma coleção, um periódico e uma data.
Ao ser executado a task, pegar um pid_v3 e sps_pkg_name.

realizar um curl para obter autenticacao:

curl -X POST http://localhost:8009/api/v2/auth/token/   -H 'Content-Type: application/x-www-form-urlencoded'   -d 'username=your_username&password=your_password'

Realizar outro curl para informar o pid_v3, sps_pkg_name e o token:

curl -X POST http://localhost:8000/api/v2/pid/published_article/ \
          -H 'Authorization: Bearer eyJhbGciOi...' \
          -H 'Content-Type: application/json' \
          -d '{
            "pid_v3": "67CrZnsyZLpV7dyR7dgp6Vt",
            "sps_pkg_name": "2236-8906-hoehnea-49-e1082020"
          }'

Executar os testes:

python manage.py test article.test_publish_article_api -v 3

Algum cenário de contexto que queira dar?

Endpoints como published_article e pid_provider (modo síncrono) executam fluxos grandes:

load_article() cria/atualiza Article, autores, afiliações, DOI, issue, journal, etc.
check_availability() verifica URLs externas via HTTP (check_url) enquanto ainda está dentro da view
Com ATOMIC_REQUESTS, tudo isso corre dentro de uma única transação. Segurar locks no PostgreSQL durante parsing de XML + dezenas de writes + chamadas HTTP externas é um anti-padrão clássico: piora concorrência, aumenta risco de deadlock e timeout.

Quais são tickets relevantes?

CLOSED #1444

Comment thread config/api_router.py Outdated
router.register("pid_provider", PidProviderViewSet, basename="pid_provider")
router.register("fix_pid_v2", FixPidV2ViewSet, basename="fix_pid_v2")
router.register(
"published_article",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@samuelveigarangel troque published_article para publish_article, pois está fazendo uma ação e não uma obtenção de dado

Comment thread pid_provider/api/v1/views.py Outdated
from tempfile import NamedTemporaryFile, TemporaryDirectory
from config.settings.base import TASK_EXPIRES, TASK_TIMEOUT, RUN_ASYNC

from article.models import Article

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@samuelveigarangel não ocorre dependÊncia circular?

Comment thread pid_provider/api/v1/views.py Outdated
Comment on lines +15 to +20
from pid_provider.models import PidProviderXML
from pid_provider.provider import PidProvider
from pid_provider.tasks import (
task_delete_provide_pid_tmp_zip,
task_provide_pid_for_xml_zip,
)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@samuelveigarangel fora do estilo recomendado de 3 blocos de import: nativas, externas, internas.

Comment thread pid_provider/api/v1/views.py Outdated
Comment on lines +42 to +50
class PublishedArticleRegistrationSerializer(serializers.Serializer):
pid_v3 = serializers.CharField(
required=True, allow_blank=False, max_length=23, min_length=23
)
sps_pkg_name = serializers.CharField(
required=True, allow_blank=False, max_length=100
)


Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@samuelveigarangel manter o padrão, isso fica no arquiv serializers.py

Comment thread pid_provider/api/v1/views.py Outdated
)


class PublishedArticleRegistrationViewSet(GenericViewSet):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@samuelveigarangel Era esperado que o código do endpoint fosse feito em article/api/v1/views.

Além disso, não seria necessário criar uma nova classe. Pode usar a ArticleViewSet. Veja o exemplo hipotético. Por outro lado, se realmente ficar grande no

@action(
        detail=False, 
        methods=["post"], 
        permission_classes=[IsAuthenticated], 
        url_path="publish"
    )
    def publish_article(self, request):
        """
        Busca o XML no pid_provider e realiza a publicação do Article.
        URL: POST /api/v1/article/publish/
        """
        serializer = PublishedArticleRegistrationSerializer(data=request.data)
        if not serializer.is_valid():
            return Response(serializer.errors, status=rest_framework_status.HTTP_400_BAD_REQUEST)

        identifiers = serializer.validated_data
        
        # 1. Busca o XML no pid_provider
        try:
            pp_xml = PidProviderXML.objects.select_related("current_version").get(
                v3=identifiers["pid_v3"],
                pkg_name=identifiers["sps_pkg_name"],
            )
        except PidProviderXML.DoesNotExist:
            return Response({
                "error": "PidProviderXML not found",
                "pid_v3": identifiers["pid_v3"],
                "sps_pkg_name": identifiers["sps_pkg_name"],
            }, status=rest_framework_status.HTTP_404_NOT_FOUND)

        # 2. Executa as regras de negócio de publicação do Article
        try:
            operation = (
                "updated"
                if models.Article.get_by_pid_v3_or_by_sps_pkg_name(
                    pid_v3=pp_xml.v3, sps_pkg_name=pp_xml.pkg_name
                ).exists()
                else "created"
            )
            
            # Carrega, salva e valida a disponibilidade do artigo
            article = load_article(request.user, pp_xml=pp_xml)
            pp_xml.collections.set(article.collections)
            article.check_availability(request.user)
            
        except Exception as e:
            logging.error(f"Erro ao publicar artigo: {e}", exc_info=True)
            return Response(
                {"error_type": str(type(e)), "error_message": str(e)},
                status=rest_framework_status.HTTP_400_BAD_REQUEST
            )

        # 3. Log e Retorno
        timestamp = timezone.now().isoformat()
        logging.info(f"Article published: id={article.id} operation={operation} user={request.user.username}")
        
        response_status = (
            rest_framework_status.HTTP_201_CREATED 
            if operation == "created" 
            else rest_framework_status.HTTP_200_OK
        )
        
        return Response({
            "article_id": article.id,
            "pid_v3": article.pid_v3,
            "sps_pkg_name": article.sps_pkg_name,
            "operation": operation,
            "data_status": article.data_status,
            "is_public": article.is_public,
            "timestamp": timestamp
        }, status=response_status)

@robertatakenaka robertatakenaka left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

A ação é sobre Article então o endpoint tem que ser em article

@robertatakenaka robertatakenaka left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@samuelveigarangel corrigir a documentação e o texto do PR

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 endpoint para criar Article a partir de PidProviderXML

2 participants