diff --git a/onedocker/repository/onedocker_package.py b/onedocker/repository/onedocker_package.py index 22a82ca7..0d926cb3 100644 --- a/onedocker/repository/onedocker_package.py +++ b/onedocker/repository/onedocker_package.py @@ -16,15 +16,15 @@ def __init__(self, storage_svc: StorageService, repository_path: str) -> None: self.storage_svc = storage_svc self.repository_path = repository_path - def _build_package_path(self, package_name: str, version: str) -> str: + def build_package_path(self, package_name: str, version: str) -> str: return f"{self.repository_path}{package_name}/{version}/{package_name.split('/')[-1]}" def upload(self, package_name: str, version: str, source: str) -> None: - package_path = self._build_package_path(package_name, version) + package_path = self.build_package_path(package_name, version) self.storage_svc.copy(source, package_path) def download(self, package_name: str, version: str, destination: str) -> None: - package_path = self._build_package_path(package_name, version) + package_path = self.build_package_path(package_name, version) self.storage_svc.copy(package_path, destination) def get_package_versions( @@ -35,7 +35,7 @@ def get_package_versions( return self.storage_svc.list_folders(package_parent_path) def get_package_info(self, package_name: str, version: str) -> PackageInfo: - package_path = self._build_package_path(package_name, version) + package_path = self.build_package_path(package_name, version) if not self.storage_svc.file_exists(package_path): raise ValueError( diff --git a/onedocker/repository/onedocker_repository_service.py b/onedocker/repository/onedocker_repository_service.py index 3d5de746..8c29c692 100644 --- a/onedocker/repository/onedocker_repository_service.py +++ b/onedocker/repository/onedocker_repository_service.py @@ -1,8 +1,10 @@ +#!/usr/bin/env python3 # Copyright (c) Meta Platforms, Inc. and affiliates. # # This source code is licensed under the MIT license found in the # LICENSE file in the root directory of this source tree. +from datetime import datetime from typing import Optional from fbpcp.service.storage import StorageService @@ -10,7 +12,7 @@ from onedocker.repository.onedocker_package import OneDockerPackageRepository -class OnedockerRepositoryService: +class OneDockerRepositoryService: def __init__( self, storage_svc: StorageService, @@ -32,10 +34,28 @@ def upload( source: str, metadata: Optional[dict] = None, ) -> None: - raise NotImplementedError + # TODO: T127441856 handle storing metadata + self.package_repo.upload(package_name, version, source) def download(self, package_name: str, version: str, destination: str) -> None: raise NotImplementedError def promote(self, package_name: str, old_version: str, new_version: str) -> None: - raise NotImplementedError + # Build the target path to promote the package to. + target_path = self.package_repo.build_package_path(package_name, new_version) + if new_version == "latest": + # Archive the existing files in the target path. + last_modified_date = self.package_repo.get_package_info( + package_name, new_version + ).last_modified + formatted_date = datetime.strftime( + datetime.strptime(last_modified_date, "%a %b %d %H:%M:%S %Y"), + "%Y-%m-%d", + ) + + archive_path = self.package_repo.build_package_path( + package_name, formatted_date + ) + self.storage_svc.copy(target_path, archive_path) + current_path = self.package_repo.build_package_path(package_name, old_version) + self.storage_svc.copy(current_path, target_path) diff --git a/onedocker/tests/repository/test_onedocker_repository_service.py b/onedocker/tests/repository/test_onedocker_repository_service.py new file mode 100644 index 00000000..bc3c4bfb --- /dev/null +++ b/onedocker/tests/repository/test_onedocker_repository_service.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 +# Copyright (c) Meta Platforms, Inc. and affiliates. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +import unittest +from datetime import datetime +from unittest.mock import call, MagicMock, patch + +from onedocker.entity.package_info import PackageInfo + +from onedocker.repository.onedocker_repository_service import OneDockerRepositoryService + + +class TestOneDockerRepositoryService(unittest.TestCase): + TEST_PACKAGE_PATH = "private_lift/lift" + TEST_PACKAGE_NAME = TEST_PACKAGE_PATH.split("/")[-1] + TEST_PACKAGE_VERSION = "test_version" + + @patch( + "onedocker.repository.onedocker_repository_service.OneDockerChecksumRepository" + ) + @patch( + "onedocker.repository.onedocker_repository_service.OneDockerPackageRepository" + ) + @patch("fbpcp.service.storage_s3.S3StorageService") + def setUp( + self, mockStorageService, mockPackageRepoCall, mockChecksumRepoCall + ) -> None: + self.package_repo_path = "/package_repo_path/" + checksum_repo_path = "/checksum_repo_path/" + self.package_repo = MagicMock() + mockPackageRepoCall.return_value = self.package_repo + self.repo_service = OneDockerRepositoryService( + mockStorageService, self.package_repo_path, checksum_repo_path + ) + self.storage_svc = mockStorageService + self.package_repo.build_package_path = MagicMock( + side_effect=self._build_package_path + ) + + def test_onedocker_repo_service_upload(self) -> None: + # Arrange + source_path = "test_source_path" + + # Act + self.repo_service.upload( + self.TEST_PACKAGE_PATH, self.TEST_PACKAGE_VERSION, source_path + ) + + # Assert + self.package_repo.upload.assert_called_with( + self.TEST_PACKAGE_PATH, self.TEST_PACKAGE_VERSION, source_path + ) + + def _build_package_path(self, package_name: str, version: str) -> str: + return f"{self.package_repo_path}{self.TEST_PACKAGE_NAME}/{version}/{package_name.split('/')[-1]}" + + def test_onedocker_repo_service_promote_to_latest(self) -> None: + # Arrange + old_version = self.TEST_PACKAGE_VERSION + new_version = "latest" + date = datetime.today().ctime() + formatted_date = datetime.strftime( + datetime.strptime(date, "%a %b %d %H:%M:%S %Y"), + "%Y-%m-%d", + ) + self.package_repo.get_package_info.return_value = PackageInfo( + package_name=self.TEST_PACKAGE_NAME, + version=old_version, + last_modified=date, + package_size=1, + ) + target_path = self._build_package_path(self.TEST_PACKAGE_NAME, new_version) + current_path = self._build_package_path(self.TEST_PACKAGE_NAME, old_version) + archive_path = self._build_package_path(self.TEST_PACKAGE_NAME, formatted_date) + # Act + self.repo_service.promote(self.TEST_PACKAGE_PATH, old_version, new_version) + + # Assert + self.storage_svc.copy.assert_has_calls( + [call(target_path, archive_path), call(current_path, target_path)] + ) + + def test_onedocker_repo_service_promote(self) -> None: + # Arrange + old_version = self.TEST_PACKAGE_VERSION + new_version = "rc" + target_path = self._build_package_path(self.TEST_PACKAGE_NAME, new_version) + current_path = self._build_package_path(self.TEST_PACKAGE_NAME, old_version) + # Act + self.repo_service.promote(self.TEST_PACKAGE_PATH, old_version, new_version) + + # Assert + self.storage_svc.copy.assert_called_once_with(current_path, target_path)