Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 1 addition & 17 deletions legal-api/poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions legal-api/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "legal-api"
version = "3.1.12"
version = "3.1.13"
description = ""
authors = [
{name = "thor",email = "1042854+thorwolpert@users.noreply.github.com"}
Expand All @@ -25,7 +25,6 @@ dependencies = [
# FUTURE: look at removing
"strict-rfc3339 (==0.7)",
# FUTURE: look at removing restriction
"minio (==7.0.2)",
"pypdf (>=6.12.1)",
"reportlab (>=4.5.0)",
# FUTURE: look at removing restriction
Expand Down
12 changes: 0 additions & 12 deletions legal-api/src/legal_api/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,12 +137,6 @@ class _Config: # pylint: disable=too-few-public-methods
# legislative timezone for future effective dating
LEGISLATIVE_TIMEZONE = os.getenv("LEGISLATIVE_TIMEZONE", "America/Vancouver")

# Minio configuration values
MINIO_ENDPOINT = os.getenv("MINIO_ENDPOINT")
MINIO_ACCESS_KEY = os.getenv("MINIO_ACCESS_KEY")
MINIO_ACCESS_SECRET = os.getenv("MINIO_ACCESS_SECRET")
MINIO_BUCKET_BUSINESSES = os.getenv("MINIO_BUCKET_BUSINESSES", "businesses")
MINIO_SECURE = True

# determines which year of NAICS data will be used to drive NAICS search
NAICS_YEAR = int(os.getenv("NAICS_YEAR", "2022"))
Expand Down Expand Up @@ -303,12 +297,6 @@ class TestConfig(_Config): # pylint: disable=too-few-public-methods
4H8UZcVFN95vEKxJiLRjAmj6g273pu9kK4ymXNEjWWJn
-----END RSA PRIVATE KEY-----"""

# Minio variables
MINIO_ENDPOINT = "http://dummy-minio-url"
MINIO_ACCESS_KEY = "minio"
MINIO_ACCESS_SECRET = "minio123"
MINIO_BUCKET_BUSINESSES = "businesses"
MINIO_SECURE = False

# determines which year of NAICS data will be used to drive NAICS search;
# matches the test seed data loaded by business_model_migrations
Expand Down
23 changes: 0 additions & 23 deletions legal-api/src/legal_api/reports/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,41 +78,18 @@ def _get_static_report(self):
document_type = ReportMeta.static_reports[self._report_key]["documentType"]
document: Document = self._filing.documents.filter(Document.type == document_type).first()
# DRS-backed keys are "{documentClass}-{documentServiceId}", e.g. "COOP-DS0000101951";
# legacy Minio keys (UUIDs) and bare DRS ids ("DS...") do not match.
if match := re.match(r"^([A-Z]+)-(DS\d+)$", document.file_key or ""):
# the DRS applies the certified copy stamp itself for configured combinations
# (e.g. COOP-COSD), so DRS-served documents must not be stamped again here
from legal_api.services import doc_service
drs_response = doc_service.get_document(match.group(2), match.group(1), doc_binary=True)
document_data, status = drs_response.content, drs_response.status_code
else:
from legal_api.services import MinioService
minio_response = MinioService.get_file(document.file_key)
document_data, status = minio_response.data, minio_response.status
if self._report_key == "affidavit" and status == HTTPStatus.OK:
# legacy storage never stamps, so the registrar's certification stamp is applied here
document_data = self._certify_uploaded_document(document_data)
return current_app.response_class(
response=document_data,
status=status,
mimetype="application/pdf"
)

def _certify_uploaded_document(self, document_bytes: bytes) -> bytes:
"""Apply the registrar's certification stamp when the document is downloaded.

Documents are never stamped on upload; the stamp is applied to each
served copy and the stored original is left unchanged.
"""
from legal_api.services import PdfService
from legal_api.services.pdf_service import RegistrarStampData
business = self._business
if not business and self._filing.business_id:
business = Business.find_by_internal_id(self._filing.business_id)
identifier = business.identifier if business else self._filing.temp_reg
stamp_data = RegistrarStampData(self._filing.filing_date, identifier)
return PdfService().create_certified_copy(document_bytes, stamp_data).read()

def _get_report(self, regenerate: bool = False):
# Try to get report from DRS first: get to here if duplicate UI request before refreshing filing documents.
if self._filing.business_id:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
from legal_api.reports import get_pdf
from legal_api.reports.document_service import DocumentService
from legal_api.resources.v2.business.bp import bp
from legal_api.services import MinioService, authorized
from legal_api.services import authorized
from legal_api.services import doc_service as client_doc_service
from legal_api.services.request_context import add_account_linking_key_header
from legal_api.utils.auth import jwt
Expand Down Expand Up @@ -122,22 +122,14 @@ def get_documents(identifier: str, # noqa: PLR0911, PLR0912

return get_pdf(filing.storage, legal_filing_name)
elif file_key and (document := Document.find_by_file_key(file_key)):
if document.filing_id == filing.id: # make sure the file belongs to this filing
# DRS-backed keys are "{documentClass}-{documentServiceId}", e.g. "COOP-DS0000101951";
# legacy Minio keys (UUIDs) do not match.
if match := re.match(r"^([A-Z]+)-(DS\d+)$", document.file_key):
drs_response = client_doc_service.get_document(match.group(2), match.group(1), doc_binary=True)
return current_app.response_class(
response=drs_response.content,
status=drs_response.status_code,
mimetype=APP_PDF
)
response = MinioService.get_file(document.file_key)
if document.filing_id == filing.id and (match := re.match(r"^([A-Z]+)-(DS\d+)$", document.file_key)): # make sure the file belongs to this filing
drs_response = client_doc_service.get_document(match.group(2), match.group(1), doc_binary=True)
return current_app.response_class(
response=response.data,
status=response.status,
response=drs_response.content,
status=drs_response.status_code,
mimetype=APP_PDF
)


return {}, HTTPStatus.NOT_FOUND

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@
from legal_api.services import (
STAFF_ROLE,
SYSTEM_ROLE,
MinioService,
RegistrationBootstrapService,
authorized,
doc_service,
Expand Down Expand Up @@ -226,7 +225,7 @@ def delete_filings(identifier, filing_id=None):
filing.delete()

with suppress(Exception):
ListFilingResource.delete_from_minio(filing_type, filing_json)
ListFilingResource.delete_uploaded_documents(filing_type, filing_json)

if identifier.startswith("T") and filing.filing_type != Filing.FILINGS["noticeOfWithdrawal"]["name"]:
bootstrap = RegistrationBootstrap.find_by_identifier(identifier)
Expand Down Expand Up @@ -1118,19 +1117,17 @@ def is_future_effective_filing(filing_json: dict) -> bool:

@staticmethod
def delete_uploaded_file(file_key: str):
"""Delete an uploaded file from the DRS or Minio based on the file key shape.
"""Delete an uploaded file from the DRS based on the file key shape.

DRS-backed keys are "{documentClass}-{documentServiceId}", e.g. "COOP-DS0000101951";
legacy Minio keys (UUIDs) do not match.
"""
if re.match(r"^([A-Z]+)-(DS\d+)$", file_key):
doc_service.delete_document(Document(file_key=file_key))
else:
MinioService.delete_file(file_key)


@staticmethod
def delete_from_minio(filing_type: str, filing_json: dict):
"""Delete the filing's uploaded files from the DRS or Minio."""
def delete_uploaded_documents(filing_type: str, filing_json: dict):
"""Delete the filing's uploaded files from the DRS."""
if (filing_type == Filing.FILINGS["incorporationApplication"].get("name")
and (cooperative := filing_json
.get("filing", {})
Expand Down Expand Up @@ -1161,7 +1158,7 @@ def delete_from_minio(filing_type: str, filing_json: dict):

@staticmethod
def delete_continuation_in_files(filing_json: dict):
"""Delete continuation in files from minio."""
"""Delete continuation in files from DRS."""
continuation_in = filing_json.get("filing", {}).get("continuationIn", {})

# Delete affidavit file
Expand Down
40 changes: 0 additions & 40 deletions legal-api/src/legal_api/resources/v2/document.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@

from business_model.models import Document, Filing
from legal_api.services import doc_service
from legal_api.services.minio import MinioService
from legal_api.utils.auth import jwt

bp = Blueprint("DOCUMENTS2", __name__, url_prefix="/api/v2/documents")
Expand Down Expand Up @@ -87,12 +86,6 @@
}


@bp.route("/<string:file_name>/signatures", methods=["GET"])
@cross_origin()
@jwt.requires_auth
def get_signatures(file_name: str):
"""Return a pre-signed URL for the new document."""
return MinioService.create_signed_put_url(file_name), HTTPStatus.OK


def is_draft_filing(file_key: str) -> bool:
Expand All @@ -104,40 +97,7 @@ def is_draft_filing(file_key: str) -> bool:
return filing and filing.status == Filing.Status.DRAFT.value


@bp.route("/<string:document_key>", methods=["DELETE"])
@cross_origin()
@jwt.requires_auth
def delete_minio_document(document_key):
"""Delete Minio document based on the provided document key and if it is a draft filing."""
try:
if is_draft_filing(document_key):
MinioService.delete_file(document_key)
return jsonify({"message": f"File {document_key} deleted successfully."}), HTTPStatus.OK
return jsonify({"message": "Filing is not a draft."}), HTTPStatus.FORBIDDEN
except Exception as e:
current_app.logger.error(f"Error deleting file {document_key}: {e}")
return jsonify(
message=f"Error deleting file {document_key}."
), HTTPStatus.INTERNAL_SERVER_ERROR


@bp.route("/<string:document_key>", methods=["GET"])
@cross_origin()
@jwt.requires_auth
def get_minio_document(document_key: str):
"""Get the document from Minio."""
try:
response = MinioService.get_file(document_key)
return current_app.response_class(
response=response.data,
status=response.status,
mimetype="application/pdf"
)
except Exception as e:
current_app.logger.error(f"Error getting file {document_key}: {e}")
return jsonify(
message=f"Error getting file {document_key}."
), HTTPStatus.INTERNAL_SERVER_ERROR


@bp.route("/client/<string:filing_type>/<string:entity_type>/<string:document_type>", methods=["POST"])
Expand Down
2 changes: 0 additions & 2 deletions legal-api/src/legal_api/services/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
from .business_details_version import VersionedBusinessDetailsService
from .colin import ColinService
from .furnishing_documents_service import FurnishingDocumentsService
from .minio import MinioService
from .mras_service import MrasService
from .naics import NaicsService
from .namex import NameXService
Expand Down Expand Up @@ -71,7 +70,6 @@
"DigitalCredentialsRulesService",
"Flags",
"FurnishingDocumentsService",
"MinioService",
"MrasService",
"NaicsService",
"NameXService",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
from business_model.models import Address, Business, PartyRole
from legal_api.core.filing import Filing as CoreFiling
from legal_api.errors import Error
from legal_api.services import STAFF_ROLE, MinioService, colin, doc_service, flags, namex
from legal_api.services import STAFF_ROLE, colin, doc_service, flags, namex
from legal_api.services.permissions import ListActionsPermissionsAllowed, PermissionService
from legal_api.services.request_context import get_request_context
from legal_api.services.utils import get_str
Expand Down Expand Up @@ -591,19 +591,14 @@ def validate_pdf(file_key: str, file_key_path: str, verify_paper_size: bool = Tr
return None

def _get_file_data(file_key: str) -> tuple[bytes, int]:
"""Return (file_bytes, file_size) for a file_key, whether DRS-backed or legacy Minio."""
enabled_features: list[str] = flags.value("enable-new-feature", [])

if "drs-upload" in enabled_features and (match := DRS_KEY_PATTERN.match(file_key)):
"""Return (file_bytes, file_size) for a DRS-backed file_key."""
if match := DRS_KEY_PATTERN.match(file_key):
doc_class, drs_id = match.group(1), match.group(2)
response = doc_service.get_document(drs_id, doc_class, doc_binary=True)
if not response.ok:
raise ValueError(f"DRS get_document failed: status={response.status_code}")
return response.content, len(response.content)

file = MinioService.get_file(file_key)
file_info = MinioService.get_file_info(file_key)
return file.data, file_info.size

def validate_parties_names(filing_json: dict, filing_type: str, legal_type: str) -> list:
"""Validate the parties name for COLIN sync."""
Expand Down
Loading