Skip to content

Commit

Permalink
Use parsers v2 API
Browse files Browse the repository at this point in the history
  • Loading branch information
SaaldjorMike committed Jun 6, 2024
1 parent 5c006b6 commit 9408a6c
Show file tree
Hide file tree
Showing 9 changed files with 308 additions and 42 deletions.
25 changes: 25 additions & 0 deletions api/internal/humiographql/parsers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package humiographql

type ParserTestEventInput struct {
RawString string `graphql:"rawString"`
}

type FieldHasValueInput struct {
FieldName string `graphql:"fieldName"`
ExpectedValue string `graphql:"expectedValue"`
}

type ParserTestCaseOutputAssertionsInput struct {
FieldsNotPresent []string `graphql:"fieldsNotPresent"`
FieldsHaveValues []FieldHasValueInput `graphql:"fieldsHaveValues"`
}

type ParserTestCaseAssertionsForOutputInput struct {
OutputEventIndex int `graphql:"outputEventIndex"`
Assertions ParserTestCaseOutputAssertionsInput `graphql:"assertions"`
}

type ParserTestCaseInput struct {
Event ParserTestEventInput `graphql:"event"`
OutputAssertions []ParserTestCaseAssertionsForOutputInput `graphql:"outputAssertions"`
}
258 changes: 220 additions & 38 deletions api/parsers.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,43 @@
package api

import graphql "github.com/cli/shurcooL-graphql"
import (
graphql "github.com/cli/shurcooL-graphql"
"github.com/humio/cli/api/internal/humiographql"
)

const LogScaleVersionWithParserAPIv2 = "1.129.0"

type ParserTestEvent struct {
RawString string `graphql:"rawString"`
}

type FieldHasValue struct {
FieldName string `graphql:"fieldName"`
ExpectedValue string `graphql:"expectedValue"`
}

type ParserTestCaseOutputAssertions struct {
FieldsNotPresent []string `graphql:"fieldsNotPresent"`
FieldsHaveValues []FieldHasValue `graphql:"fieldsHaveValues"`
}

type ParserTestCaseAssertionsForOutput struct {
OutputEventIndex int `graphql:"outputEventIndex"`
Assertions ParserTestCaseOutputAssertions `graphql:"assertions"`
}

type ParserTestCase struct {
Input string
Output map[string]string
Event ParserTestEvent `graphql:"event"`
OutputAssertions []ParserTestCaseAssertionsForOutput `graphql:"outputAssertions"`
}

type Parser struct {
ID string
Name string
Tests []string `yaml:",omitempty"`
Example string `yaml:",omitempty"`
Script string `yaml:",flow"`
TagFields []string `yaml:",omitempty"`
ID string
Name string
Script string `yaml:",flow"`
TestCases []ParserTestCase `yaml:",omitempty"`
FieldsToTag []string `yaml:",omitempty"`
FieldsToBeRemovedBeforeParsing []string `yaml:",omitempty"`
}

type Parsers struct {
Expand Down Expand Up @@ -47,12 +71,39 @@ func (p *Parsers) List(repositoryName string) ([]ParserListItem, error) {
return parsers, err
}

func (p *Parsers) Remove(repositoryName string, parserName string) error {
func (p *Parsers) Delete(repositoryName string, parserName string) error {
status, err := p.client.Status()
if err != nil {
return err
}

atLeast, err := status.AtLeast(LogScaleVersionWithParserAPIv2)
if !atLeast {
var mutation struct {
RemoveParser struct {
// We have to make a selection, so just take __typename
Typename graphql.String `graphql:"__typename"`
} `graphql:"removeParser(input: { id: $id, repositoryName: $repositoryName })"`
}

parser, err := p.client.Parsers().Get(repositoryName, parserName)
if err != nil {
return err
}

variables := map[string]interface{}{
"repositoryName": graphql.String(repositoryName),
"id": graphql.String(parser.ID),
}

return p.client.Mutate(&mutation, variables)
}

var mutation struct {
RemoveParser struct {
DeleteParser struct {
// We have to make a selection, so just take __typename
Typename graphql.String `graphql:"__typename"`
} `graphql:"removeParser(input: { id: $id, repositoryName: $repositoryName })"`
} `graphql:"deleteParser(input: { id: $id, repositoryName: $repositoryName })"`
}

parser, err := p.client.Parsers().Get(repositoryName, parserName)
Expand All @@ -68,48 +119,178 @@ func (p *Parsers) Remove(repositoryName string, parserName string) error {
return p.client.Mutate(&mutation, variables)
}

func (p *Parsers) Add(repositoryName string, parser *Parser, force bool) error {
func (p *Parsers) Add(repositoryName string, parser *Parser, allowOverwritingExistingParser bool) error {
status, err := p.client.Status()
if err != nil {
return err
}

atLeast, err := status.AtLeast(LogScaleVersionWithParserAPIv2)
if !atLeast {
var mutation struct {
CreateParser struct {
// We have to make a selection, so just take __typename
Typename graphql.String `graphql:"__typename"`
} `graphql:"createParser(input: { name: $name, repositoryName: $repositoryName, testData: $testData, tagFields: $tagFields, sourceCode: $sourceCode, force: $force})"`
}

tagFieldsGQL := make([]graphql.String, len(parser.FieldsToTag))

for i, field := range parser.FieldsToTag {
tagFieldsGQL[i] = graphql.String(field)
}

testsGQL := make([]graphql.String, len(parser.TestCases))

for i, field := range parser.TestCases {
testsGQL[i] = graphql.String(field.Event.RawString)
}

variables := map[string]interface{}{
"name": graphql.String(parser.Name),
"sourceCode": graphql.String(parser.Script),
"repositoryName": graphql.String(repositoryName),
"testData": testsGQL,
"tagFields": tagFieldsGQL,
"force": graphql.Boolean(allowOverwritingExistingParser),
}

return p.client.Mutate(&mutation, variables)
}

var mutation struct {
CreateParser struct {
// We have to make a selection, so just take __typename
Typename graphql.String `graphql:"__typename"`
} `graphql:"createParser(input: { name: $name, repositoryName: $repositoryName, testData: $testData, tagFields: $tagFields, sourceCode: $sourceCode, force: $force})"`
} `graphql:"createParserV2(input: { name: $name, repositoryName: $repositoryName, testCases: $testCases, fieldsToTag: $fieldsToTag, fieldsToBeRemovedBeforeParsing: $fieldsToBeRemovedBeforeParsing, script: $script, allowOverwritingExistingParser: $allowOverwritingExistingParser})"`
}

tagFieldsGQL := make([]graphql.String, len(parser.TagFields))

for i, field := range parser.TagFields {
tagFieldsGQL[i] = graphql.String(field)
fieldsToTagGQL := make([]graphql.String, len(parser.FieldsToTag))
for i, field := range parser.FieldsToTag {
fieldsToTagGQL[i] = graphql.String(field)
}

testsGQL := make([]graphql.String, len(parser.Tests))
fieldsToBeRemovedBeforeParsingGQL := make([]graphql.String, len(parser.FieldsToBeRemovedBeforeParsing))
for i, field := range parser.FieldsToBeRemovedBeforeParsing {
fieldsToBeRemovedBeforeParsingGQL[i] = graphql.String(field)
}

for i, field := range parser.Tests {
testsGQL[i] = graphql.String(field)
testCasesGQL := make([]humiographql.ParserTestCaseInput, len(parser.TestCases))
for i := range parser.TestCases {
testCasesGQL[i] = mapParserTestCaseToInput(parser.TestCases[i])
}

variables := map[string]interface{}{
"name": graphql.String(parser.Name),
"sourceCode": graphql.String(parser.Script),
"repositoryName": graphql.String(repositoryName),
"testData": testsGQL,
"tagFields": tagFieldsGQL,
"force": graphql.Boolean(force),
"name": graphql.String(parser.Name),
"script": graphql.String(parser.Script),
"repositoryName": humiographql.RepoOrViewName(repositoryName),
"testCases": testCasesGQL,
"fieldsToTag": fieldsToTagGQL,
"fieldsToBeRemovedBeforeParsing": fieldsToBeRemovedBeforeParsingGQL,
"allowOverwritingExistingParser": graphql.Boolean(allowOverwritingExistingParser),
}

return p.client.Mutate(&mutation, variables)
}

// ParserTestEvent -> ParserTestEventInput
func mapParserTestEventToInput(p ParserTestEvent) humiographql.ParserTestEventInput {
return humiographql.ParserTestEventInput{RawString: p.RawString}
}

// FieldHasValue -> FieldHasValueInput
func mapFieldHasValueToInput(p FieldHasValue) humiographql.FieldHasValueInput {
return humiographql.FieldHasValueInput{
FieldName: p.FieldName,
ExpectedValue: p.ExpectedValue,
}
}

// ParserTestCase -> ParserTestCaseInput, this is where add() should call
func mapParserTestCaseToInput(p ParserTestCase) humiographql.ParserTestCaseInput {
parserTestCaseAssertionsForOutputInput := make([]humiographql.ParserTestCaseAssertionsForOutputInput, len(p.OutputAssertions))
for i := range p.OutputAssertions {
parserTestCaseAssertionsForOutputInput[i] = humiographql.ParserTestCaseAssertionsForOutputInput{
OutputEventIndex: p.OutputAssertions[i].OutputEventIndex,
Assertions: mapParserTestCaseOutputAssertionsToInput(p.OutputAssertions[i].Assertions),
}
}
return humiographql.ParserTestCaseInput{
Event: mapParserTestEventToInput(p.Event),
OutputAssertions: parserTestCaseAssertionsForOutputInput,
}
}

func mapParserTestCaseOutputAssertionsToInput(p ParserTestCaseOutputAssertions) humiographql.ParserTestCaseOutputAssertionsInput {
fieldsHaveValuesInput := make([]humiographql.FieldHasValueInput, len(p.FieldsHaveValues))
for i := range p.FieldsHaveValues {
fieldsHaveValuesInput[i] = mapFieldHasValueToInput(p.FieldsHaveValues[i])
}
return humiographql.ParserTestCaseOutputAssertionsInput{
FieldsNotPresent: p.FieldsNotPresent,
FieldsHaveValues: fieldsHaveValuesInput,
}
}

func (p *Parsers) Get(repositoryName string, parserName string) (*Parser, error) {
status, err := p.client.Status()
if err != nil {
return nil, err
}

atLeast, err := status.AtLeast(LogScaleVersionWithParserAPIv2)
if !atLeast {
var query struct {
Repository struct {
Parser *struct {
ID string
Name string
SourceCode string
TestData []string
TagFields []string
} `graphql:"parser(name: $parserName)"`
} `graphql:"repository(name: $repositoryName)"`
}

variables := map[string]interface{}{
"parserName": graphql.String(parserName),
"repositoryName": graphql.String(repositoryName),
}

err := p.client.Query(&query, variables)
if err != nil {
return nil, err
}

if query.Repository.Parser == nil {
return nil, ParserNotFound(parserName)
}

parser := Parser{
ID: query.Repository.Parser.ID,
Name: query.Repository.Parser.Name,
Script: query.Repository.Parser.SourceCode,
FieldsToTag: query.Repository.Parser.TagFields,
}
parser.TestCases = make([]ParserTestCase, len(query.Repository.Parser.TestData))
for i := range query.Repository.Parser.TestData {
parser.TestCases[i] = ParserTestCase{
Event: ParserTestEvent{RawString: query.Repository.Parser.TestData[i]},
}
}

return &parser, nil
}

var query struct {
Repository struct {
Parser *struct {
ID string
Name string
SourceCode string
TestData []string
TagFields []string
ID string
Name string
Script string
TestCases []ParserTestCase
FieldsToTag []string
FieldsToBeRemovedBeforeParsing []string
} `graphql:"parser(name: $parserName)"`
} `graphql:"repository(name: $repositoryName)"`
}
Expand All @@ -119,7 +300,7 @@ func (p *Parsers) Get(repositoryName string, parserName string) (*Parser, error)
"repositoryName": graphql.String(repositoryName),
}

err := p.client.Query(&query, variables)
err = p.client.Query(&query, variables)
if err != nil {
return nil, err
}
Expand All @@ -129,11 +310,12 @@ func (p *Parsers) Get(repositoryName string, parserName string) (*Parser, error)
}

parser := Parser{
ID: query.Repository.Parser.ID,
Name: query.Repository.Parser.Name,
Tests: query.Repository.Parser.TestData,
Script: query.Repository.Parser.SourceCode,
TagFields: query.Repository.Parser.TagFields,
ID: query.Repository.Parser.ID,
Name: query.Repository.Parser.Name,
TestCases: query.Repository.Parser.TestCases,
Script: query.Repository.Parser.Script,
FieldsToTag: query.Repository.Parser.FieldsToTag,
FieldsToBeRemovedBeforeParsing: query.Repository.Parser.FieldsToBeRemovedBeforeParsing,
}

return &parser, nil
Expand Down
18 changes: 18 additions & 0 deletions api/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import (
"fmt"
"io"
"net/http"
"strings"

"github.com/Masterminds/semver/v3"
)

type StatusResponse struct {
Expand All @@ -16,6 +19,21 @@ func (s StatusResponse) IsDown() bool {
return s.Status != "OK" && s.Status != "WARN"
}

func (s StatusResponse) AtLeast(ver string) (bool, error) {
assumeLatest := true
version := strings.Split(s.Version, "-")
constraint, err := semver.NewConstraint(fmt.Sprintf(">= %s", ver))
if err != nil {
return assumeLatest, fmt.Errorf("could not parse constraint of `%s`: %w", fmt.Sprintf(">= %s", ver), err)
}
semverVersion, err := semver.NewVersion(version[0])
if err != nil {
return assumeLatest, fmt.Errorf("could not parse version of `%s`: %w", version[0], err)
}

return constraint.Check(semverVersion), nil
}

func (c *Client) Status() (*StatusResponse, error) {
resp, err := c.HTTPRequest(http.MethodGet, "api/v1/status", nil)

Expand Down
1 change: 1 addition & 0 deletions cmd/humioctl/parsers.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ func newParsersCmd() *cobra.Command {
cmd.AddCommand(newParsersListCmd())
cmd.AddCommand(newParsersRemoveCmd())
cmd.AddCommand(newParsersExportCmd())
cmd.AddCommand(newParsersShowCmd())

return cmd
}
Loading

0 comments on commit 9408a6c

Please sign in to comment.