-
Notifications
You must be signed in to change notification settings - Fork 10
Adiciona trigger para atualizar os dados de coleção no opac_5 #1156
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
base: main
Are you sure you want to change the base?
Adiciona trigger para atualizar os dados de coleção no opac_5 #1156
Conversation
…ara endpoint no opac ao salvar objeto collection
️✅ There are no secrets present in this pull request anymore.If these secrets were true positive and are still valid, we highly recommend you to revoke them. 🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request. |
There was a problem hiding this 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 JWT token generation and a webhook trigger system to automatically notify opac_5 when collection data is updated in the core system. The integration uses RS256 JWT authentication and sends HTTP POST requests to update collection information.
- Implements JWT token generation using RS256 algorithm for secure service-to-service communication
- Adds a post-save signal trigger that sends collection data to opac_5 via webhook when collections are created or updated
- Enhances collection logo model by replacing size field with purpose field for better organization
Reviewed Changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| requirements/base.txt | Adds JWT dependencies (cryptography and PyJWT) |
| core/utils/jwt.py | New JWT token generation utility using RS256 algorithm |
| collection/tasks.py | Celery tasks for sending collection data webhooks to opac_5 |
| collection/signals.py | Post-save signal handler to trigger webhook on collection changes |
| collection/models.py | Updates CollectionLogo model to use purpose instead of size field |
| collection/api/v1/serializers.py | Enhanced collection serializers with logo grouping and URL generation |
| config/settings/base.py | JWT configuration settings and webhook endpoint configuration |
| .envs/.local/.jwt | JWT environment variables for local development |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
config/settings/base.py
Outdated
| with open(JWT_PRIVATE_KEY_PATH, "rb") as f: | ||
| JWT_PRIVATE_KEY = f.read() |
Copilot
AI
Oct 1, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reading the JWT private key file at settings import time will cause the application to crash if the file doesn't exist. This should be moved to a function that's called when needed, or wrapped in a try/except block with appropriate error handling.
| with open(JWT_PRIVATE_KEY_PATH, "rb") as f: | |
| JWT_PRIVATE_KEY = f.read() | |
| def get_jwt_private_key(): | |
| try: | |
| with open(JWT_PRIVATE_KEY_PATH, "rb") as f: | |
| return f.read() | |
| except (FileNotFoundError, IOError) as e: | |
| # You may want to log the error or handle it differently | |
| return None |
collection/tasks.py
Outdated
| for schema in ["http", "https"]: | ||
| url = f"{schema}://{host_or_url}" | ||
| try: | ||
| resp = requests.post(url, timeout=timeout) |
Copilot
AI
Oct 1, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function fetch_with_schema_guess is sending a POST request without any data or headers to test connectivity, but it should use a GET request instead since it's only checking if the URL is reachable.
| resp = requests.post(url, timeout=timeout) | |
| resp = requests.get(url, timeout=timeout) |
core/models.py
Outdated
| ordering = ['sort_order', 'language'] | ||
|
|
||
| def __str__(self): | ||
| return f"{self.collection} - {self.language} ({self.size})" |
Copilot
AI
Oct 1, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The __str__ method references self.size which was removed from the model. This should be updated to reference the new field or removed entirely.
| return f"{self.collection} - {self.language} ({self.size})" | |
| return f"{self.collection} - {self.language}" |
collection/tasks.py
Outdated
| if resp.status_code != 200: | ||
| logging.error(f"Erro ao enviar dados de coleção para {url}. Status: {resp.status_code}. Body: {resp.text}") | ||
| resp.raise_for_status() | ||
| return resp.json() | ||
| except Exception as e: |
Copilot
AI
Oct 1, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error logging occurs before raise_for_status(), which means the function will continue and potentially raise an exception after logging. The logging should be inside an exception handler or the raise_for_status() should be moved before the logging.
| if resp.status_code != 200: | |
| logging.error(f"Erro ao enviar dados de coleção para {url}. Status: {resp.status_code}. Body: {resp.text}") | |
| resp.raise_for_status() | |
| return resp.json() | |
| except Exception as e: | |
| resp.raise_for_status() | |
| return resp.json() | |
| except Exception as e: | |
| logging.error(f"Erro ao enviar dados de coleção para {url}. Exception: {e}") |
collection/api/v1/serializers.py
Outdated
| return rendition.url | ||
| domain = Site.objects.get(is_default_site=True).hostname | ||
| domain = f"http://{domain}" | ||
| return f"{domain}{obj.logo.file.url}" |
Copilot
AI
Oct 1, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using obj.logo.file.url directly bypasses Wagtail's image rendition system and may expose internal file paths. Consider using obj.logo.url instead for consistency with the original implementation.
| return f"{domain}{obj.logo.file.url}" | |
| return f"{domain}{obj.logo.url}" |
| {% trans 'Sobre o SciELO' %} | ||
| {% endif %} | ||
| </span> | ||
| </span> |
Copilot
AI
Oct 1, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a duplicate closing </span> tag. The closing span tag on line 94 should be removed as it creates invalid HTML structure.
| </span> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Copilot reviewed 17 out of 17 changed files in this pull request and generated 7 comments.
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
collection/tasks.py
Outdated
| try: | ||
| resp = requests.post(pattern_url, json=payload, headers=headers, timeout=5) | ||
| resp.raise_for_status() | ||
| return resp.json() | ||
| except Exception as e: | ||
| logging.error(f"Erro ao enviar dados de coleção para {url}. Status: {resp.status_code}. Body: {resp.text}") |
Copilot
AI
Oct 7, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The variable resp is referenced before it's guaranteed to be defined. If the exception occurs during the requests.post() call, resp won't exist, causing a NameError.
| try: | |
| resp = requests.post(pattern_url, json=payload, headers=headers, timeout=5) | |
| resp.raise_for_status() | |
| return resp.json() | |
| except Exception as e: | |
| logging.error(f"Erro ao enviar dados de coleção para {url}. Status: {resp.status_code}. Body: {resp.text}") | |
| resp = None | |
| try: | |
| resp = requests.post(pattern_url, json=payload, headers=headers, timeout=5) | |
| resp.raise_for_status() | |
| return resp.json() | |
| except Exception as e: | |
| if resp is not None: | |
| logging.error(f"Erro ao enviar dados de coleção para {url}. Status: {resp.status_code}. Body: {resp.text}") | |
| else: | |
| logging.error(f"Erro ao enviar dados de coleção para {url}. Nenhuma resposta recebida. Exception: {e}") |
| Collection.load(user) | ||
|
|
||
|
|
||
| def fetch_with_protocol_guess(host_or_url, timeout=10): |
Copilot
AI
Oct 7, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Function is missing a return type annotation and docstring explanation of what happens when both protocols fail.
| collection = Collection.objects.get(acron3=collection_acron, is_active=True) | ||
|
|
||
| if not collection.domain: | ||
| return None |
Copilot
AI
Oct 7, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Function returns None when both HTTP and HTTPS protocols fail, but the caller expects a URL string. This could cause issues in _send_payload when concatenating with settings.ENDPOINT_COLLECTION.
organization/api/v1/serializers.py
Outdated
| domain = Site.objects.get(is_default_site=True).hostname | ||
| domain = f"http://{domain}" | ||
| return f"{domain}{obj.logo.url}" |
Copilot
AI
Oct 7, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code assumes there's always a default site. If no default site exists, this will raise a DoesNotExist exception. Consider using get_or_create() or handling the exception.
| domain = Site.objects.get(is_default_site=True).hostname | |
| domain = f"http://{domain}" | |
| return f"{domain}{obj.logo.url}" | |
| try: | |
| domain = Site.objects.get(is_default_site=True).hostname | |
| domain = f"http://{domain}" | |
| return f"{domain}{obj.logo.url}" | |
| except Site.DoesNotExist: | |
| return None |
collection/api/v1/serializers.py
Outdated
| domain = Site.objects.get(is_default_site=True).hostname | ||
| domain = f"http://{domain}" |
Copilot
AI
Oct 7, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same issue as in organization serializer - assumes there's always a default site. If no default site exists, this will raise a DoesNotExist exception.
|
|
||
|
|
||
| LOGO_SIZE_CHOICES = [ |
Copilot
AI
Oct 7, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LOGO_SIZE_CHOICES is still defined but appears to be unused after the refactoring to purpose-based logos. Consider removing this unused constant.
| ordering = ['sort_order', 'language'] | ||
| unique_together = [ | ||
| ("collection", "size", "language"), | ||
| ('collection', 'language', 'purpose'), |
Copilot
AI
Oct 7, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The unique constraint uses single quotes while other parts of the codebase use double quotes for consistency. Consider using double quotes.
| ('collection', 'language', 'purpose'), | |
| ("collection", "language", "purpose"), |
…to issue-opac-339
O que esse PR faz?
Onde a revisão poderia começar?
pelos commits
Como este poderia ser testado manualmente?
Pré-requisitos:
Algum cenário de contexto que queira dar?
Screenshots
N/A
Quais são tickets relevantes?
#339
Referências
N/A