Skip to content

Commit

Permalink
Merge pull request #1026 from neicnordic/feature/sda-admin-cli
Browse files Browse the repository at this point in the history
sda-admin cli
  • Loading branch information
jbygdell authored Sep 18, 2024
2 parents d72fdd9 + 95c6bf9 commit 26fc9fe
Show file tree
Hide file tree
Showing 15 changed files with 1,254 additions and 4 deletions.
25 changes: 24 additions & 1 deletion .github/workflows/code-linter.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,27 @@ jobs:
uses: golangci/[email protected]
with:
args: -E bodyclose,gocritic,gofmt,gosec,govet,nestif,nlreturn,rowserrcheck -e G401,G501,G107,G115 --timeout 5m
working-directory: sda
working-directory: sda

lint_sda_admin:
name: Lint sda-admin code
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
go-version: ['1.22']
steps:
- name: Set up Go ${{ matrix.go-version }}
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go-version }}
id: go

- name: Check out code into the Go module directory
uses: actions/checkout@v4

- name: Run golangci-lint
uses: golangci/[email protected]
with:
args: -E bodyclose,gocritic,gofmt,gosec,govet,nestif,nlreturn,rowserrcheck -e G401,G501,G107,G115 --timeout 5m
working-directory: sda-admin
40 changes: 39 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ jobs:

- name: Get dependencies
run: |
cd sda-download
cd sda
go get -v -t -d ./...
if [ -f Gopkg.toml ]; then
curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
Expand All @@ -104,3 +104,41 @@ jobs:
file: ./sda/coverage.txt
flags: unittests
fail_ci_if_error: false

test_sda_admin:
name: Test SDA Admin
runs-on: ubuntu-latest
strategy:
matrix:
go-version: ['1.22']
steps:

- name: Set up Go ${{ matrix.go-version }}
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go-version }}
id: go

- name: Check out code into the Go module directory
uses: actions/checkout@v4

- name: Get dependencies
run: |
cd sda-admin
go get -v -t -d ./...
if [ -f Gopkg.toml ]; then
curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
dep ensure
fi
- name: Test
run: |
cd sda-admin
go test -v -coverprofile=coverage.txt -covermode=atomic ./...
- name: Codecov
uses: codecov/[email protected]
with:
token: ${{ secrets.CODECOV_TOKEN }}
file: ./sda-admin/coverage.txt
flags: unittests
fail_ci_if_error: false
11 changes: 9 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ bootstrap: go-version-check docker-version-check
GO111MODULE=off go get golang.org/x/tools/cmd/goimports

# build containers
build-all: build-postgresql build-rabbitmq build-sda build-sda-download build-sda-sftp-inbox
build-all: build-postgresql build-rabbitmq build-sda build-sda-download build-sda-sftp-inbox build-sda-admin
build-postgresql:
@cd postgresql && docker build -t ghcr.io/neicnordic/sensitive-data-archive:PR$$(date +%F)-postgres .
build-rabbitmq:
Expand All @@ -34,6 +34,8 @@ build-sda-download:
@cd sda-download && docker build -t ghcr.io/neicnordic/sensitive-data-archive:PR$$(date +%F)-download .
build-sda-sftp-inbox:
@cd sda-sftp-inbox && docker build -t ghcr.io/neicnordic/sensitive-data-archive:PR$$(date +%F)-sftp-inbox .
build-sda-admin:
@cd sda-admin && go build


go-version-check: SHELL:=/bin/bash
Expand Down Expand Up @@ -81,13 +83,16 @@ integrationtest-sda: build-all
@PR_NUMBER=$$(date +%F) docker compose -f .github/integration/sda-posix-integration.yml down -v --remove-orphans

# lint go code
lint-all: lint-sda lint-sda-download
lint-all: lint-sda lint-sda-download lint-sda-admin
lint-sda:
@echo 'Running golangci-lint in the `sda` folder'
@cd sda && golangci-lint run $(LINT_INCLUDE) $(LINT_EXCLUDE)
lint-sda-download:
@echo 'Running golangci-lint in the `sda-download` folder'
@cd sda-download && golangci-lint run $(LINT_INCLUDE) $(LINT_EXCLUDE)
lint-sda-admin:
@echo 'Running golangci-lint in the `sda-admin` folder'
@cd sda-admin && golangci-lint run $(LINT_INCLUDE) $(LINT_EXCLUDE)

# run static code tests
test-all: test-sda test-sda-download test-sda-sftp-inbox
Expand All @@ -97,3 +102,5 @@ test-sda-download:
@cd sda-download && go test ./... -count=1
test-sda-sftp-inbox:
@docker run --rm -v ./sda-sftp-inbox:/inbox maven:3.9.4-eclipse-temurin-21-alpine sh -c "cd /inbox && mvn test -B"
test-sda-admin:
@cd sda-admin && go test ./... -count=1
97 changes: 97 additions & 0 deletions sda-admin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# sda-admin

`sda-admin` is a command-line tool for managing sensitive data archives. It provides functionalities to list users and files, ingest and set accession IDs for files, and create or release datasets.

## General Usage

```sh
sda-admin [-uri URI] [-token TOKEN] <command> [options]
```

## Global Options
- `-uri URI`
Set the URI for the API server (optional if the environmental variable `API_HOST` is set).
- `-token TOKEN`
Set the authentication token (optional if the environmental variable `ACCESS_TOKEN` is set).

## List all users

Use the following command to return all users with active uploads
```sh
sda-admin user list
```

## List all files for a specified user

Use the following command to return all files belonging to the specified user `[email protected]`
```sh
sda-admin file list -user [email protected]
```

## Ingest a file

Use the following command to trigger the ingesting of a given file `/path/to/file.c4gh` that belongs to the user `[email protected]`

```sh
sda-admin file ingest -filepath /path/to/file.c4gh -user [email protected]
```

## Assign an accession ID to a file

Use the following command to assign an accession ID `my-accession-id-1` to a given file `/path/to/file.c4gh` that belongs to the user `[email protected]`

```sh
sda-admin file set-accession -filepath /path/to/file.c4gh -user [email protected] -accession-id my-accession-id-1
```

## Create a dataset from a list of accession IDs and a dataset ID

Use the following command to create a dataset `dataset001` from accession IDs `my-accession-id-1` and `my-accession-id-2`

```sh
sda-admin dataset create -dataset-id dataset001 my-accession-id-1 my-accession-id-2
```


## Release a dataset for downloading

Use the following command to release the dataset `dataset001` for downloading

```sh
sda-admin dataset release -dataset-id dataset001
```

## Show version information

Use the following command to show the version information for sda-admin.

```sh
sda-admin version
```

## Help

For detailed usage information about specific commands or options, use:

```sh
sda-admin help <command>
```

### Examples

To get help on the `file` command:
```sh
sda-admin help file
```

To get help on the `file ingest` command:

```sh
sda-admin help file ingest
```

To get help on the `dataset create` command:

```sh
sda-admin help dataset create
```
57 changes: 57 additions & 0 deletions sda-admin/dataset/dataset.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package dataset

import (
"encoding/json"
"fmt"
"net/url"
"path"

"github.com/neicnordic/sensitive-data-archive/sda-admin/helpers"
)

type RequestBodyDataset struct {
AccessionIDs []string `json:"accession_ids"`
DatasetID string `json:"dataset_id"`
}

// Create creates a dataset from a list of accession IDs and a dataset ID.
func Create(apiURI, token, datasetID string, accessionIDs []string) error {
parsedURL, err := url.Parse(apiURI)
if err != nil {
return err
}
parsedURL.Path = path.Join(parsedURL.Path, "dataset/create")

requestBody := RequestBodyDataset{
AccessionIDs: accessionIDs,
DatasetID: datasetID,
}

jsonBody, err := json.Marshal(requestBody)
if err != nil {
return fmt.Errorf("failed to marshal JSON, reason: %v", err)
}

_, err = helpers.PostRequest(parsedURL.String(), token, jsonBody)
if err != nil {
return err
}

return nil
}

// Release releases a dataset for downloading
func Release(apiURI, token, datasetID string) error {
parsedURL, err := url.Parse(apiURI)
if err != nil {
return err
}
parsedURL.Path = path.Join(parsedURL.Path, "dataset/release") + "/" + datasetID

_, err = helpers.PostRequest(parsedURL.String(), token, nil)
if err != nil {
return err
}

return nil
}
93 changes: 93 additions & 0 deletions sda-admin/dataset/dataset_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package dataset

import (
"errors"
"testing"

"github.com/neicnordic/sensitive-data-archive/sda-admin/helpers"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)

// MockHelpers is a mock implementation of the helpers package functions
type MockHelpers struct {
mock.Mock
}

func (m *MockHelpers) PostRequest(url, token string, jsonBody []byte) ([]byte, error) {
args := m.Called(url, token, jsonBody)

return args.Get(0).([]byte), args.Error(1)
}

func TestCreate_Success(t *testing.T) {
mockHelpers := new(MockHelpers)
originalFunc := helpers.PostRequest
helpers.PostRequest = mockHelpers.PostRequest
defer func() { helpers.PostRequest = originalFunc }() // Restore original after test

expectedURL := "http://example.com/dataset/create"
token := "test-token"
datasetID := "dataset-123"
accessionIDs := []string{"accession-1", "accession-2"}
jsonBody := []byte(`{"accession_ids":["accession-1","accession-2"],"dataset_id":"dataset-123"}`)

mockHelpers.On("PostRequest", expectedURL, token, jsonBody).Return([]byte(`{}`), nil)

err := Create("http://example.com", token, datasetID, accessionIDs)
assert.NoError(t, err)
mockHelpers.AssertExpectations(t)
}

func TestCreate_PostRequestFailure(t *testing.T) {
mockHelpers := new(MockHelpers)
originalFunc := helpers.PostRequest
helpers.PostRequest = mockHelpers.PostRequest
defer func() { helpers.PostRequest = originalFunc }() // Restore original after test

expectedURL := "http://example.com/dataset/create"
token := "test-token"
datasetID := "dataset-123"
accessionIDs := []string{"accession-1", "accession-2"}
jsonBody := []byte(`{"accession_ids":["accession-1","accession-2"],"dataset_id":"dataset-123"}`)

mockHelpers.On("PostRequest", expectedURL, token, jsonBody).Return([]byte(nil), errors.New("failed to send request"))

err := Create("http://example.com", token, datasetID, accessionIDs)
assert.Error(t, err)
assert.EqualError(t, err, "failed to send request")
mockHelpers.AssertExpectations(t)
}

func TestRelease_Success(t *testing.T) {
mockHelpers := new(MockHelpers)
originalFunc := helpers.PostRequest
helpers.PostRequest = mockHelpers.PostRequest
defer func() { helpers.PostRequest = originalFunc }() // Restore original after test

expectedURL := "http://example.com/dataset/release/dataset-123"
token := "test-token"

mockHelpers.On("PostRequest", expectedURL, token, []byte(nil)).Return([]byte(`{}`), nil)

err := Release("http://example.com", token, "dataset-123")
assert.NoError(t, err)
mockHelpers.AssertExpectations(t)
}

func TestRelease_PostRequestFailure(t *testing.T) {
mockHelpers := new(MockHelpers)
originalFunc := helpers.PostRequest
helpers.PostRequest = mockHelpers.PostRequest
defer func() { helpers.PostRequest = originalFunc }() // Restore original after test

expectedURL := "http://example.com/dataset/release/dataset-123"
token := "test-token"

mockHelpers.On("PostRequest", expectedURL, token, []byte(nil)).Return([]byte(nil), errors.New("failed to send request"))

err := Release("http://example.com", token, "dataset-123")
assert.Error(t, err)
assert.EqualError(t, err, "failed to send request")
mockHelpers.AssertExpectations(t)
}
Loading

0 comments on commit 26fc9fe

Please sign in to comment.