Skip to content

Commit

Permalink
Add endpoints for enhanced dashboarding view
Browse files Browse the repository at this point in the history
Fix #17

* Fetch stack annotations alongside app annotations
* Add endpoint for retrieving hosts
* Add endpoint for retrieving confidence scores, and
  ability to filter by layer (default is app)

Signed-off-by: Ali Amin <[email protected]>
Signed-off-by: Ali Amin <[email protected]>
  • Loading branch information
Ali-Amin committed Sep 17, 2024
1 parent 0243dda commit 3a217a9
Show file tree
Hide file tree
Showing 4 changed files with 270 additions and 26 deletions.
123 changes: 115 additions & 8 deletions internal/db/arango.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright 2022 Dell Inc.
* Copyright 2024 Dell Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
Expand All @@ -20,6 +20,7 @@ import (

"github.com/arangodb/go-driver"
"github.com/arangodb/go-driver/http"
"github.com/project-alvarium/alvarium-sdk-go/pkg/contracts"
"github.com/project-alvarium/alvarium-sdk-go/pkg/interfaces"
"github.com/project-alvarium/scoring-apps-go/internal/config"
"github.com/project-alvarium/scoring-apps-go/pkg/documents"
Expand Down Expand Up @@ -88,7 +89,7 @@ func (c *ArangoClient) QueryScore(ctx context.Context, key string) (documents.Sc
}
defer cursor.Close()

//There should only be one document returned here
// There should only be one document returned here
var score documents.Score
for {
_, err := cursor.ReadDocument(ctx, &score)
Expand All @@ -106,26 +107,132 @@ func (c *ArangoClient) QueryAnnotations(ctx context.Context, key string) ([]docu
if err != nil {
return nil, err
}
query := "FOR a in annotations FILTER a.dataRef == @key RETURN a"
appLayerQuery := `
LET stackAnnotations = (
FOR a IN annotations FILTER a.dataRef == @key
LET hostAnnotation = (
FOR hostAn in annotations
FILTER a.host == hostAn.host
AND (hostAn.layer == @host OR hostAn.layer == @os)
RETURN hostAn
)
LET tagAnnotation = (
FOR tagAn IN annotations
FILTER a.tag == tagAn.tag AND tagAn.layer == @cicd
RETURN tagAn
)
RETURN DISTINCT APPEND(tagAnnotation, hostAnnotation)
)
LET appAnnotations = (FOR a IN annotations FILTER a.dataRef == @key RETURN a)
RETURN FLATTEN(APPEND(appAnnotations, stackAnnotations))
`
bindVars := map[string]interface{}{
"key": key,
"key": key,
"cicd": string(contracts.CiCd),
"os": string(contracts.Os),
"host": string(contracts.Host),
}
cursor, err := db.Query(ctx, query, bindVars)
cursor, err := db.Query(ctx, appLayerQuery, bindVars)
if err != nil {
return nil, err
}
defer cursor.Close()

var annotations []documents.Annotation
for {
var doc documents.Annotation
_, err := cursor.ReadDocument(ctx, &doc)
_, err := cursor.ReadDocument(ctx, &annotations)
if driver.IsNoMoreDocuments(err) {
break
} else if err != nil {
return nil, err
}
annotations = append(annotations, doc)
}

return annotations, nil
}

func (c *ArangoClient) QueryScoreByLayer(ctx context.Context, key string, layer contracts.LayerType) ([]documents.Score, error) {
db, err := c.instance.Database(ctx, c.cfg.DatabaseName)
if err != nil {
return nil, err
}

var query string
switch layer {
case contracts.Application:
query = `FOR s IN scores FILTER s.dataRef == @key AND s.layer == @layer RETURN [s]`
case contracts.CiCd:
query = `FOR appScore IN scores FILTER appScore.dataRef == @key
LET cicdScore = (
FOR s IN scores FILTER
s.layer == @layer AND s.tag ANY IN appScore.tag
RETURN s
)
RETURN cicdScore `
case contracts.Os, contracts.Host:
query = `FOR a in annotations FILTER a.dataRef == @key LIMIT 1
LET scores = (FOR s IN scores FILTER s.layer == @layer AND a.host IN s.tag RETURN s)
RETURN scores`

}
bindVars := map[string]interface{}{
"key": key,
"layer": layer,
}
cursor, err := db.Query(ctx, query, bindVars)
if err != nil {
return nil, err
}
defer cursor.Close()

// There may be multiple documents returned here, for example
// if a data score is affected by 2 workloads (2 CICD scores)
// then two scores will be returned
var scores []documents.Score
for {
_, err := cursor.ReadDocument(ctx, &scores)
if driver.IsNoMoreDocuments(err) {
break
} else if err != nil {
return nil, err
}
}

return scores, nil
}

func (c *ArangoClient) FetchHosts(ctx context.Context) ([]string, error) {
db, err := c.instance.Database(ctx, c.cfg.DatabaseName)
if err != nil {
return nil, err
}

query := `FOR a IN annotations LET hosts = (a.host) RETURN DISTINCT hosts`
cursor, err := db.Query(ctx, query, nil)
if err != nil {
return nil, err
}
defer cursor.Close()

var hosts []string
for {
// returning 1 result will expect a string value,
// if multiple values, expects a []string value
if cursor.Count() > 1 {
_, err = cursor.ReadDocument(ctx, &hosts)
} else {
var host string
_, err = cursor.ReadDocument(ctx, &host)
if err == nil {
hosts = append(hosts, host)
}
}
if driver.IsNoMoreDocuments(err) {
break
} else if err != nil {
return nil, err
}
}

return hosts, nil
}
5 changes: 3 additions & 2 deletions internal/hashprovider/types.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright 2022 Dell Inc.
* Copyright 2024 Dell Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
Expand All @@ -17,11 +17,12 @@ package hashprovider
import (
crypto "crypto/sha256"
"encoding/hex"
"strings"
)

func DeriveHash(data []byte) string {
h := crypto.Sum256(data)
hashEncoded := make([]byte, hex.EncodedLen(len(h)))
hex.Encode(hashEncoded, h[:])
return string(hashEncoded)
return strings.ToUpper(string(hashEncoded))
}
Loading

0 comments on commit 3a217a9

Please sign in to comment.