diff --git a/presenter/access/html/handlers.go b/presenter/access/html/handlers.go index 3a2b339..e68b311 100644 --- a/presenter/access/html/handlers.go +++ b/presenter/access/html/handlers.go @@ -40,7 +40,7 @@ func (h *handlers) ContainerIndex(c echo.Context) error { func (h *handlers) VersionIndex(c echo.Context) error { container := c.Param("container") - versions, err := h.svc.ListVersions(c.Request().Context(), container) + versions, err := h.svc.ListPublishedVersions(c.Request().Context(), container) if err != nil { if err == service.ErrNotFound { return c.Render(http.StatusNotFound, "404.html", nil) diff --git a/presenter/access/html/handlers_test.go b/presenter/access/html/handlers_test.go index 3573f63..9eb5e4e 100644 --- a/presenter/access/html/handlers_test.go +++ b/presenter/access/html/handlers_test.go @@ -20,7 +20,7 @@ func (s *handlersTestSuite) TestContainerIndex() { } func (s *handlersTestSuite) TestVersionIndex() { - s.serviceMock.On("ListVersions", "test-container-1").Return([]string{"20241011121314"}, nil).Once() + s.serviceMock.On("ListPublishedVersions", "test-container-1").Return([]string{"20241011121314"}, nil).Once() s.compareHTMLResponse(s.srv.URL+"/test-container-1/", "testdata/versions.html.sample") } @@ -51,7 +51,7 @@ func (s *handlersTestSuite) TestGetObject() { } func (s *handlersTestSuite) TestErrNotFound() { - s.serviceMock.On("ListVersions", "test-container-1").Return([]string(nil), service.ErrNotFound).Once() + s.serviceMock.On("ListPublishedVersions", "test-container-1").Return([]string(nil), service.ErrNotFound).Once() s.compareHTMLResponse(s.srv.URL+"/test-container-1/", "testdata/404.html.sample") s.serviceMock.On("ListObjects", "test-container-1", "20240101010101").Return([]string(nil), service.ErrNotFound).Once() @@ -65,7 +65,7 @@ func (s *handlersTestSuite) TestErrNotFound() { } func (s *handlersTestSuite) TestErr5xx() { - s.serviceMock.On("ListVersions", "test-container-1").Panic("blah").Once() + s.serviceMock.On("ListPublishedVersions", "test-container-1").Panic("blah").Once() s.compareHTMLResponse(s.srv.URL+"/test-container-1/", "testdata/5xx.html.sample") } diff --git a/service/mock.go b/service/mock.go index b23c966..09e4b19 100644 --- a/service/mock.go +++ b/service/mock.go @@ -2,12 +2,14 @@ package service import ( "context" - "io" "github.com/stretchr/testify/mock" ) -var _ ManageService = (*Mock)(nil) +var ( + _ ManageService = (*Mock)(nil) + _ AccessService = (*Mock)(nil) +) type Mock struct { mock.Mock @@ -37,7 +39,12 @@ func (m *Mock) CreateVersion(_ context.Context, container string) (id string, er return args.Get(0).(string), args.Error(1) } -func (m *Mock) ListVersions(_ context.Context, container string) ([]string, error) { +func (m *Mock) ListAllVersions(_ context.Context, container string) ([]string, error) { + args := m.Called(container) + return args.Get(0).([]string), args.Error(1) +} + +func (m *Mock) ListPublishedVersions(_ context.Context, container string) ([]string, error) { args := m.Called(container) return args.Get(0).([]string), args.Error(1) } @@ -52,16 +59,16 @@ func (m *Mock) DeleteVersion(_ context.Context, container, id string) error { return args.Error(0) } -func (m *Mock) AddObject(_ context.Context, container, versionID, key string, objReader io.Reader) error { - data, err := io.ReadAll(objReader) - if err != nil { - return err - } - - args := m.Called(container, versionID, key, data) +func (m *Mock) AddObject(_ context.Context, container, versionID, key, casKey string) error { + args := m.Called(container, versionID, key, casKey) return args.Error(0) } +func (m *Mock) EnsureBLOBPresenceOrGetUploadURL(ctx context.Context, checksum string, size int64) (string, error) { + args := m.Called(checksum, size) + return args.String(0), args.Error(1) +} + func (m *Mock) ListObjects(_ context.Context, container, versionID string) ([]string, error) { args := m.Called(container, versionID) return args.Get(0).([]string), args.Error(1) diff --git a/service/service.go b/service/service.go index 6daf48e..bfb0c5a 100644 --- a/service/service.go +++ b/service/service.go @@ -2,7 +2,6 @@ package service import ( "context" - "io" "github.com/pkg/errors" @@ -19,17 +18,20 @@ type ManageService interface { DeleteContainer(ctx context.Context, name string) error CreateVersion(ctx context.Context, container string) (id string, err error) + ListAllVersions(ctx context.Context, container string) ([]string, error) PublishVersion(ctx context.Context, container, id string) error DeleteVersion(ctx context.Context, container, id string) error - AddObject(ctx context.Context, container, versionID, key string, objReader io.Reader) error + AddObject(ctx context.Context, container, versionID, key string, casKey string) error DeleteObject(ctx context.Context, container, versionID, key string) error + + EnsureBLOBPresenceOrGetUploadURL(ctx context.Context, checksum string, size int64) (string, error) } type AccessService interface { ListContainers(ctx context.Context) ([]string, error) - ListVersions(ctx context.Context, container string) ([]string, error) + ListPublishedVersions(ctx context.Context, container string) ([]string, error) ListObjects(ctx context.Context, container, versionID string) ([]string, error) GetObjectURL(ctx context.Context, container, versionID, key string) (string, error) @@ -74,24 +76,31 @@ func (s *service) DeleteContainer(ctx context.Context, name string) error { } func (s *service) CreateVersion(ctx context.Context, container string) (id string, err error) { - panic("not implemented") + version, err := s.mdRepo.CreateVersion(ctx, container) + return version, mapMetadataErrors(err) } -func (s *service) ListVersions(ctx context.Context, container string) ([]string, error) { +func (s *service) ListPublishedVersions(ctx context.Context, container string) ([]string, error) { versions, err := s.mdRepo.ListPublishedVersionsByContainer(ctx, container) return versions, mapMetadataErrors(err) } +func (s *service) ListAllVersions(ctx context.Context, container string) ([]string, error) { + versions, err := s.mdRepo.ListAllVersionsByContainer(ctx, container) + return versions, mapMetadataErrors(err) +} + func (s *service) PublishVersion(ctx context.Context, container, id string) error { - panic("not implemented") + err := s.mdRepo.MarkVersionPublished(ctx, container, id) + return mapMetadataErrors(err) } func (s *service) DeleteVersion(ctx context.Context, container, id string) error { panic("not implemented") } -func (s *service) AddObject(ctx context.Context, container, versionID, key string, objReader io.Reader) error { - panic("not implemented") +func (s *service) AddObject(ctx context.Context, container, versionID, key, casKey string) error { + return s.mdRepo.CreateObject(ctx, container, versionID, key, casKey) } func (s *service) ListObjects(ctx context.Context, container, versionID string) ([]string, error) { @@ -108,6 +117,23 @@ func (s *service) GetObjectURL(ctx context.Context, container, versionID, key st return s.blobRepo.GetBlobURL(ctx, key) } +func (s *service) EnsureBLOBPresenceOrGetUploadURL(ctx context.Context, checksum string, size int64) (string, error) { + err := s.mdRepo.EnsureBlobKey(ctx, checksum, uint64(size)) + if err == nil { + return "", nil + } + + if err == metadata.ErrNotFound { + url, err := s.blobRepo.PutBlobURL(ctx, checksum) + if err != nil { + return "", err + } + return url, s.mdRepo.CreateBLOB(ctx, checksum, uint64(size), "application/octet-stream") + } + + return "", err +} + func (s *service) DeleteObject(ctx context.Context, container, versionID, key string) error { panic("not implemented") } diff --git a/service/service_test.go b/service/service_test.go index d683325..be6245c 100644 --- a/service/service_test.go +++ b/service/service_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/suite" blobRepoMock "github.com/teran/archived/repositories/blob/mock" + "github.com/teran/archived/repositories/metadata" mdRepoMock "github.com/teran/archived/repositories/metadata/mock" ) @@ -34,15 +35,18 @@ func (s *serviceTestSuite) TestDeleteContainer() { } func (s *serviceTestSuite) TestCreateVersion() { - s.Require().PanicsWithValue("not implemented", func() { - s.svc.CreateVersion(s.ctx, "container") - }) + s.mdRepoMock.On("CreateVersion", "container").Return("versionID", nil).Once() + + id, err := s.svc.CreateVersion(s.ctx, "container") + s.Require().NoError(err) + s.Require().Equal("versionID", id) } func (s *serviceTestSuite) TestPublishVersion() { - s.Require().PanicsWithValue("not implemented", func() { - s.svc.PublishVersion(s.ctx, "container", "version") - }) + s.mdRepoMock.On("MarkVersionPublished", "container", "version").Return(nil).Once() + + err := s.svc.PublishVersion(s.ctx, "container", "version") + s.Require().NoError(err) } func (s *serviceTestSuite) TestDeleteVersion() { @@ -52,9 +56,10 @@ func (s *serviceTestSuite) TestDeleteVersion() { } func (s *serviceTestSuite) TestAddObject() { - s.Require().PanicsWithValue("not implemented", func() { - s.svc.AddObject(s.ctx, "container", "versionID", "key", nil) - }) + s.mdRepoMock.On("CreateObject", "container", "versionID", "key", "cas_key").Return(nil).Once() + + err := s.svc.AddObject(s.ctx, "container", "versionID", "key", "cas_key") + s.Require().NoError(err) } func (s *serviceTestSuite) TestDeleteObject() { @@ -88,7 +93,7 @@ func (s *serviceTestSuite) TestListVersions() { "version1", "version2", }, nil).Once() - versions, err := s.svc.ListVersions(s.ctx, "container") + versions, err := s.svc.ListPublishedVersions(s.ctx, "container") s.Require().NoError(err) s.Require().Equal([]string{ "version1", "version2", @@ -125,6 +130,24 @@ func (s *serviceTestSuite) TestGetObjectURL() { s.Require().Equal("url", url) } +func (s *serviceTestSuite) TestEnsureBLOBPresenceOrGetUploadURL() { + // Blob exists + s.mdRepoMock.On("EnsureBlobKey", "checksum", uint64(1234)).Return(nil).Once() + + url, err := s.svc.EnsureBLOBPresenceOrGetUploadURL(s.ctx, "checksum", 1234) + s.Require().NoError(err) + s.Require().Equal("", url) + + // Blob doesn't exist + s.mdRepoMock.On("EnsureBlobKey", "checksum", uint64(1234)).Return(metadata.ErrNotFound).Once() + s.blobRepoMock.On("PutBlobURL", "checksum").Return("https://example.com", nil).Once() + s.mdRepoMock.On("CreateBLOB", "checksum", uint64(1234), "application/octet-stream").Return(nil).Once() + + url, err = s.svc.EnsureBLOBPresenceOrGetUploadURL(s.ctx, "checksum", 1234) + s.Require().NoError(err) + s.Require().Equal("https://example.com", url) +} + // Definitions type serviceTestSuite struct { suite.Suite