Skip to content

Commit

Permalink
feat!: add snap_branch method to support branch-level snapshots
Browse files Browse the repository at this point in the history
Implemented the `snap_branch` method in `PackageService` for handling branch-level
snapshots. Updated `PackageController` to expose the new `/api/packages/snap/branch`
endpoint. The frontend now uses this endpoint for branch snapshots.

BREAKING CHANGE: The old API endpoint `/api/packages/snap` is now moved to
`/api/advanced/packages/snap`. Additionally, permissions for snapshot operations have been
updated to reflect the new endpoint structure.

Implements anydistro#119
  • Loading branch information
LordTermor committed Sep 2, 2024
1 parent 95095a2 commit 25399be
Show file tree
Hide file tree
Showing 8 changed files with 139 additions and 10 deletions.
4 changes: 4 additions & 0 deletions daemon/core/application/services/PackageService.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ class PackageService {
virtual coro::task<Result<void>>
snap(const PackageSectionDTO from_section,
const PackageSectionDTO to_section) = 0;

virtual coro::task<Result<void>> snap_branch(const std::string from_branch,
const std::string to_branch,
const std::string arch) = 0;
};

} // namespace bxt::Core::Application
1 change: 1 addition & 0 deletions daemon/di.h
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ namespace Infrastructure {
bxt::Infrastructure::PackageService,
kgr::dependency<di::Utilities::EventBusDispatcher,
di::Core::Domain::PackageRepositoryBase,
di::Core::Domain::ReadOnlySectionRepository,
di::Core::Domain::UnitOfWorkBaseFactory>>,
kgr::overrides<di::Core::Application::PackageService> {};

Expand Down
71 changes: 71 additions & 0 deletions daemon/infrastructure/PackageService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@
#include "core/application/errors/CrudError.h"
#include "core/application/events/CommitEvent.h"
#include "core/application/events/IntegrationEventBase.h"
#include "core/domain/entities/Section.h"
#include "coro/task.hpp"
#include "coro/when_all.hpp"
#include "utilities/Error.h"
#include "utilities/StaticDTOMapper.h"

#include <algorithm>
#include <iterator>
#include <ranges>
#include <vector>

namespace bxt::Infrastructure {
Expand Down Expand Up @@ -293,4 +295,73 @@ coro::task<PackageService::Result<void>>
std::move(to_copy)));
co_return {};
}

coro::task<PackageService::Result<void>>
PackageService::snap_branch(const std::string from_branch,
const std::string to_branch,
const std::string arch) {
auto uow = co_await m_uow_factory(true);

auto from_sections = co_await m_section_repository.find_async(
[from_branch, arch](const auto& section) {
return section.architecture() == arch
&& section.branch() == from_branch;
},
uow);

if (!from_sections.has_value()) {
co_return bxt::make_error_with_source<CrudError>(
std::move(from_sections.error()),
CrudError::ErrorType::InternalError);
}

auto to_sections = co_await m_section_repository.find_async(
[to_branch, arch](const auto& section) {
return section.architecture() == arch
&& section.branch() == to_branch;
},
uow);

for (const auto& section : *to_sections) {
auto to_delete_packages =
co_await m_repository.find_by_section_async(section, uow);
if (!to_delete_packages.has_value()) {
co_return bxt::make_error_with_source<CrudError>(
std::move(to_delete_packages.error()),
CrudError::ErrorType::InternalError);
}
auto to_delete_ids = *to_delete_packages
| std::views::transform([](const auto& package) {
return package.id();
})
| std::ranges::to<std::vector>();

auto deleted = co_await m_repository.delete_async(to_delete_ids, uow);
}

std::vector<Package> packages;
for (const auto& section : *from_sections) {
auto from_packages =
co_await m_repository.find_by_section_async(section, uow);

if (!from_packages.has_value()) {
co_return bxt::make_error_with_source<CrudError>(
std::move(from_packages.error()),
CrudError::ErrorType::InternalError);
}

packages = *from_packages | std::views::transform([&](auto&& package) {
auto package_section = package.section();
package_section.set_branch(to_branch);
package.set_section(package_section);
return package;
}) | std::ranges::to<std::vector>();
}

auto saved = co_await m_repository.save_async(packages, uow);

co_await uow->commit_async();

co_return {};
}
} // namespace bxt::Infrastructure
10 changes: 10 additions & 0 deletions daemon/infrastructure/PackageService.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
#include "PackageServiceOptions.h"
#include "core/application/dtos/PackageDTO.h"
#include "core/application/services/PackageService.h"
#include "core/domain/entities/Section.h"
#include "core/domain/repositories/PackageRepositoryBase.h"
#include "core/domain/repositories/ReadOnlyRepositoryBase.h"
#include "core/domain/repositories/UnitOfWorkBase.h"
#include "coro/task.hpp"
#include "utilities/eventbus/EventBusDispatcher.h"
Expand All @@ -23,9 +25,12 @@ class PackageService : public Core::Application::PackageService {
public:
PackageService(Utilities::EventBusDispatcher& dispatcher,
Core::Domain::PackageRepositoryBase& repository,
Core::Domain::ReadOnlyRepositoryBase<Core::Domain::Section>&
section_repository,
UnitOfWorkBaseFactory& uow_factory)
: m_dispatcher(dispatcher),
m_repository(repository),
m_section_repository(section_repository),
m_uow_factory(uow_factory) {}

virtual coro::task<Result<void>>
Expand All @@ -41,6 +46,10 @@ class PackageService : public Core::Application::PackageService {
coro::task<Result<void>> push(const Transaction transaction,
const RequestContext context) override;

coro::task<Result<void>> snap_branch(const std::string from_branch,
const std::string to_branch,
const std::string arch) override;

private:
coro::task<Result<void>> add_package(const PackageDTO package,
std::shared_ptr<UnitOfWorkBase> uow);
Expand All @@ -60,6 +69,7 @@ class PackageService : public Core::Application::PackageService {
PackageServiceOptions m_options;
Utilities::EventBusDispatcher& m_dispatcher;
Core::Domain::PackageRepositoryBase& m_repository;
Core::Domain::ReadOnlyRepositoryBase<Section>& m_section_repository;
UnitOfWorkBaseFactory& m_uow_factory;
};

Expand Down
6 changes: 6 additions & 0 deletions daemon/presentation/messages/PackageMessages.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ struct SnapRequest {
SectionRequest target;
};

struct SnapBranchRequest {
std::string source_branch;
std::string target_branch;
std::string architecture;
};

struct PoolEntryResponse {
std::string version;
bool has_signature;
Expand Down
32 changes: 31 additions & 1 deletion daemon/presentation/web-controllers/PackageController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ drogon::Task<drogon::HttpResponsePtr>

BXT_JWT_CHECK_PERMISSIONS(
(std::vector<std::string_view> {
fmt::format("packages.snap.{}.{}.{}", source_branch.branch,
fmt::format("advanced.packages.snap.{}.{}.{}", source_branch.branch,
source_branch.repository, source_branch.architecture),
fmt::format("sections.{}.{}.{}", source_branch.branch,
source_branch.repository, source_branch.architecture)}),
Expand All @@ -318,4 +318,34 @@ drogon::Task<drogon::HttpResponsePtr>
co_return drogon_helpers::make_ok_response();
}

drogon::Task<drogon::HttpResponsePtr>
PackageController::snap_branch(drogon::HttpRequestPtr req) {
const auto snap_request =
rfl::json::read<SnapBranchRequest>(std::string(req->getBody()));

if (snap_request.error()) {
co_return drogon_helpers::make_error_response("Invalid arguments");
}
auto &source_branch = (*snap_request).source_branch;
auto &target_branch = (*snap_request).target_branch;
auto &arch = (*snap_request).architecture;

BXT_JWT_CHECK_PERMISSIONS(
(std::vector<std::string_view> {
fmt::format("sections.{}.*.{}", source_branch, arch),
fmt::format("packages.snap.from.{}", source_branch),
fmt::format("sections.{}.*.{}", target_branch, arch),
fmt::format("packages.snap.to.{}", target_branch)}),
req)

const auto snap_ok = co_await m_package_service.snap_branch(
source_branch, target_branch, arch);

if (!snap_ok.has_value()) {
co_return drogon_helpers::make_error_response(
fmt::format("Snap section failed: {}", snap_ok.error().what()));
}

co_return drogon_helpers::make_ok_response();
}
} // namespace bxt::Presentation
11 changes: 9 additions & 2 deletions daemon/presentation/web-controllers/PackageController.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,14 @@ class PackageController
"/api/packages/sync",
drogon::Post);

BXT_JWT_ADD_METHOD_TO(PackageController::snap,
"/api/packages/snap",
BXT_JWT_ADD_METHOD_TO(PackageController::snap_branch,
"/api/packages/snap/branch",
drogon::Post);

// Methods for advanced operations. These are not exposed to the frontend.
BXT_JWT_ADD_METHOD_TO(PackageController::snap,
"/api/advanced/packages/snap",
drogon::Post);
METHOD_LIST_END

drogon::Task<drogon::HttpResponsePtr> sync(drogon::HttpRequestPtr req);
Expand All @@ -64,6 +68,9 @@ class PackageController

drogon::Task<drogon::HttpResponsePtr> snap(drogon::HttpRequestPtr req);

drogon::Task<drogon::HttpResponsePtr>
snap_branch(drogon::HttpRequestPtr req);

private:
Core::Application::PackageService &m_package_service;
Core::Application::SyncService &m_sync_service;
Expand Down
14 changes: 7 additions & 7 deletions web/src/modals/SnapshotModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,17 +64,17 @@ export const SnapshotModal = forwardRef<HTMLDialogElement, ISnapshotModalProps>(

const doSnap = useCallback(async () => {
if (
!sourceSection ||
!targetSection ||
Object.values(sourceSection).some((el) => el === undefined) ||
Object.values(targetSection).some((el) => el === undefined)
!sourceSection?.branch ||
!targetSection?.branch ||
!sourceSection?.architecture
) {
return;
}
try {
await axios.post("/api/packages/snap", {
source: sourceSection,
target: targetSection
await axios.post("/api/packages/snap/branch", {
sourceBranch: sourceSection.branch,
targetBranch: targetSection.branch,
architecture: sourceSection.architecture
});
internalRef?.current?.close();
} catch (error) {}
Expand Down

0 comments on commit 25399be

Please sign in to comment.