# Blob storage service (gRPC)

Service for storing and retrieving blobs in object storage with workspace-based authorization.

## Endpoints

### Get signed URL for blob

gRPC `/nominal.blob_storage_service.v1.BlobStorageService/GetSignedUrlForBlob`

Retrieve a signed URL for downloading a blob. The URL is valid for 15 minutes.

Request: `GetSignedUrlForBlobRequest`

`owning_rid` [`ResourceIdentifier`](https://dev.nominal.io/api/grpc/blob-storage-service.md#resourceidentifier) required
:   ​

    Fields of `ResourceIdentifier`

    `rid` string required
    :   Matches `^ri\.[a-z][a-z0-9\-]*\.([a-z0-9][a-z0-9\-]*)?\.[a-z][a-z0-9\-]*\.[a-zA-Z0-9\-\._]+$`.

`file_name` string required
:   ​

Response: `GetSignedUrlForBlobResponse`

`url` string
:   This is a short lived signed url that can be used by the client to retrieve the object

Request

**Python**

```python
import grpc
from nominal.protos.blob_storage_service.v1 import blob_storage_service_pb2, blob_storage_service_pb2_grpc

channel = grpc.secure_channel("api.gov.nominal.io:443", grpc.ssl_channel_credentials())
blob_storage_service = blob_storage_service_pb2_grpc.BlobStorageServiceStub(channel)
auth = [("authorization", "Bearer <api key>")]

request = blob_storage_service_pb2.GetSignedUrlForBlobRequest(
    owning_rid={"rid": "<rid>"},
    file_name="<file name>",
)
response = blob_storage_service.GetSignedUrlForBlob(request, metadata=auth)
print(response)
```

**Go**

```go
package main

import (
	"context"
	"crypto/tls"
	"fmt"
	"log"

	blob_storage_servicev1 "github.com/nominal-io/nominal-api-protos-go/nominal/protos/blob_storage_service/v1"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/metadata"
)

func main() {
	conn, err := grpc.NewClient("api.gov.nominal.io:443",
		grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{})))
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()
	blobStorageService := blob_storage_servicev1.NewBlobStorageServiceClient(conn)
	ctx := metadata.AppendToOutgoingContext(context.Background(), "authorization", "Bearer <api key>")

	request := &blob_storage_servicev1.GetSignedUrlForBlobRequest{
		OwningRid: &blob_storage_servicev1.ResourceIdentifier{
			Rid: "<rid>",
		},
		FileName: "<file name>",
	}
	response, err := blobStorageService.GetSignedUrlForBlob(ctx, request)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(response)
}
```

Response

```json
{
  "url": "<url>"
}
```

### Upload blob stream

gRPC `/nominal.blob_storage_service.v1.BlobStorageService/UploadBlobStream` client streaming

Upload a blob using streaming. The blob will be stored with authorization checks based on the provided owningRid.

Request: a stream of `UploadBlobStreamRequest`

`owning_rid` [`ResourceIdentifier`](https://dev.nominal.io/api/grpc/blob-storage-service.md#resourceidentifier) required
:   ​

    Fields of `ResourceIdentifier`

    `rid` string required
    :   Matches `^ri\.[a-z][a-z0-9\-]*\.([a-z0-9][a-z0-9\-]*)?\.[a-z][a-z0-9\-]*\.[a-zA-Z0-9\-\._]+$`.

`file_name` string required
:   ​

`ensure_unique` boolean required
:   when set to true, ensure_unique will attach the current timestamp to the file name this will ensure that we do not overwrite an existing file in blob storage

`content` bytes required
:   ​

Response: `UploadBlobStreamResponse`

`file_name` string
:   This is the key that can be used in a retrieve blob request in order to retrieve this specific blob. If ensure_unique is not set, this will match the file name provided in UploadBlobStreamRequest

Request

**Python**

```python
import grpc
from nominal.protos.blob_storage_service.v1 import blob_storage_service_pb2, blob_storage_service_pb2_grpc

channel = grpc.secure_channel("api.gov.nominal.io:443", grpc.ssl_channel_credentials())
blob_storage_service = blob_storage_service_pb2_grpc.BlobStorageServiceStub(channel)
auth = [("authorization", "Bearer <api key>")]

request = blob_storage_service_pb2.UploadBlobStreamRequest(
    owning_rid={"rid": "<rid>"},
    file_name="<file name>",
    ensure_unique=True,
    content=b"<content>",
)
response = blob_storage_service.UploadBlobStream(iter([request]), metadata=auth)
print(response)
```

**Go**

```go
package main

import (
	"context"
	"crypto/tls"
	"fmt"
	"log"

	blob_storage_servicev1 "github.com/nominal-io/nominal-api-protos-go/nominal/protos/blob_storage_service/v1"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/metadata"
)

func main() {
	conn, err := grpc.NewClient("api.gov.nominal.io:443",
		grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{})))
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()
	blobStorageService := blob_storage_servicev1.NewBlobStorageServiceClient(conn)
	ctx := metadata.AppendToOutgoingContext(context.Background(), "authorization", "Bearer <api key>")

	request := &blob_storage_servicev1.UploadBlobStreamRequest{
		OwningRid: &blob_storage_servicev1.ResourceIdentifier{
			Rid: "<rid>",
		},
		FileName:     "<file name>",
		EnsureUnique: true,
		Content:      []byte("<content>"),
	}
	stream, err := blobStorageService.UploadBlobStream(ctx)
	if err != nil {
		log.Fatal(err)
	}
	if err := stream.Send(request); err != nil {
		log.Fatal(err)
	}
	response, err := stream.CloseAndRecv()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(response)
}
```

Response

```json
{
  "file_name": "<file name>"
}
```

## Types

### ResourceIdentifier

This is used to identify any type of blob that will be in object storage.

`rid` string required
:   Matches `^ri\.[a-z][a-z0-9\-]*\.([a-z0-9][a-z0-9\-]*)?\.[a-z][a-z0-9\-]*\.[a-zA-Z0-9\-\._]+$`.

Page design based on [Scalar](https://scalar.com), MIT License.
