0
\$\begingroup\$

Code for writing a Writer that is used to dump data in a Spanner table.

Specifically concerned about the function: func (s *SpannerClient) Write(resp Response) (n int, err error) given it has too many ifs.

package spanner

import (
    "context"
    "encoding/json"
    "errors"

    "cloud.google.com/go/spanner"
)

// Writer is an interface for anything that can write data.
type Writer interface {
    Write(p Response) (n int, err error)
}

type SpannerWriterAdapter struct {
    Client Writer
}

// SpannerClient is a type that implements the Writer interface and writes to Spanner.
type SpannerClient struct {
    Client *spanner.Client
    Ctx    context.Context
}

type Metadata struct {
    Labels            map[string]string `json:"labels"`
    CreationTimestamp string            `json:"creationTimestamp"`
}

type Data struct {
    ApiKey string `json:"api-key"`
}

type Response struct {
    Payload Payload `json:"payload"`
}

type Payload struct {
    Metadata Metadata               `json:"metadata"`
    Data     map[string]interface{} `json:"data"`
}

// Declare Error constants
var (
    ErrApiKeyNotFound    = errors.New("api-key not found")
    ErrProjectIdNotFound = errors.New("project id not found")
    ErrUserLabelNotFound = errors.New("user label not found")
    ErrEnvNotFound       = errors.New("env not found")
)

// Declare key constants
var (
    APIKEY_KEY      = "api-key"
    PROJECT_KEY     = "project"
    USER_KEY        = "user"
    USER_LABEL_KEY  = "user_label"
    ENVIRONMENT_KEY = "env"
)

// Declare Spanner table and related constants
var (
    SPANNER_TABLE_NAME = "dump_table" // this name can't be changed
)

// New creates a new Spanner client and returns it as a Writer.
func New(ctx context.Context, db string) (Writer, error) {
    client, err := spanner.NewClient(ctx, db)
    if err != nil {
        return nil, err
    }
    return &SpannerClient{
        Client: client,
        Ctx:    ctx,
    }, nil
}

// Write writes a Payload to Spanner.
func (s *SpannerClient) Write(resp Response) (n int, err error) {
    apiKey, ok := resp.Payload.Data[APIKEY_KEY].(string)
    if !ok {
        return 0, ErrApiKeyNotFound
    }
    projectID, ok := resp.Payload.Metadata.Labels[PROJECT_KEY]
    if !ok {
        return 0, ErrProjectIdNotFound
    }
    userID, ok := resp.Payload.Metadata.Labels[USER_KEY]
    if !ok {
        return 0, ErrUserLabelNotFound
    }
    userLabel, ok := resp.Payload.Metadata.Labels[USER_LABEL_KEY]
    if !ok {
        return 0, ErrEnvNotFound
    }
    environment, ok := resp.Payload.Metadata.Labels[ENVIRONMENT_KEY]
    if !ok {
        return 0, ErrEnvNotFound
    }
    creationTimestamp := resp.Payload.Metadata.CreationTimestamp
    mutation := spanner.InsertOrUpdate(
        SPANNER_TABLE_NAME,
        []string{"id", "project_id", "user_id", "label", "value", "environment", "created_on", "updated_on", "deleted_on"},
        []interface{}{apiKey, projectID, userID, userLabel, apiKey, environment, creationTimestamp, spanner.CommitTimestamp, nil},
    )
    _, err = s.Client.Apply(s.Ctx, []*spanner.Mutation{mutation})
    if err != nil {
        return 0, err
    }
    return 1, nil
}

// This function is critical so that we can decouple tackling raw bytes and json response
func (s *SpannerWriterAdapter) Write(p []byte) (n int, err error) {
    // Convert byte slice to Payload. This assumes a simple JSON representation.
    // Adjust the conversion as per your actual data structure.
    var response Response
    if err := json.Unmarshal(p, &response); err != nil {
        return 0, err
    }
    data := response.Payload.Data
    // labels := response.Payload.Metadata.Labels
    apiKey, ok := data[APIKEY_KEY].(string)
    if !ok || apiKey == "" {
        return 0, nil // ignore this data as there is no api-key
    }
    _, ok = response.Payload.Metadata.Labels[PROJECT_KEY]
    if !ok {
        return 0, nil // ignore this data as there is no project key
    }
    // Write the data that is in the vault namespace and has both the keys: 'api-key' and 'project'
    return s.Client.Write(response)
}
\$\endgroup\$

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.