From 0190768719ec3ad879bf42c74517a99c8739e507 Mon Sep 17 00:00:00 2001 From: Piotr Konopka Date: Fri, 26 Apr 2024 16:21:29 +0200 Subject: [PATCH] [common] OCTRL-805 allow to group entries in subfolders --- apricot/local/servicehttp.go | 12 +++++ configuration/componentcfg/query.go | 56 ++++++++++-------------- configuration/componentcfg/query_test.go | 27 ++++++++++++ 3 files changed, 62 insertions(+), 33 deletions(-) diff --git a/apricot/local/servicehttp.go b/apricot/local/servicehttp.go index 8b208e30..21d6bdd2 100644 --- a/apricot/local/servicehttp.go +++ b/apricot/local/servicehttp.go @@ -100,10 +100,12 @@ func NewHttpService(service configuration.Service) (svr *http.Server) { // GET /components/{component}/{runtype}/{rolename}/{entry}/resolve, assumes this is not a raw path, returns a raw path // like {component}/{runtype}/{rolename}/{entry} apiComponentQuery.HandleFunc("/resolve", httpsvc.ApiResolveComponentQuery).Methods(http.MethodGet) + apiComponentQuery.HandleFunc("/{subentry}/resolve", httpsvc.ApiResolveComponentQuery).Methods(http.MethodGet) // GET /components/{component}/{runtype}/{rolename}/{entry}, accepts raw or non-raw path, returns payload // that may be processed or not depending on process=true or false apiComponentQuery.HandleFunc("", httpsvc.ApiGetComponentConfiguration).Methods(http.MethodGet) apiComponentQuery.HandleFunc("/", httpsvc.ApiGetComponentConfiguration).Methods(http.MethodGet) + apiComponentQuery.HandleFunc("/{subentry}", httpsvc.ApiGetComponentConfiguration).Methods(http.MethodGet) // inventory API @@ -406,6 +408,11 @@ func (httpsvc *HttpService) ApiResolveComponentQuery(w http.ResponseWriter, r *h return } + subentry, hasSubentry := queryParams["subentry"] + if hasSubentry { + entry = entry + "/" + subentry + } + resolved, err := httpsvc.svc.ResolveComponentQuery(&componentcfg.Query{ Component: component, RunType: runType, @@ -484,6 +491,11 @@ func (httpsvc *HttpService) ApiGetComponentConfiguration(w http.ResponseWriter, return } + subentry, hasSubentry := queryParams["subentry"] + if hasSubentry { + entry = entry + "/" + subentry + } + queryArgs := r.URL.Query() processS := queryArgs.Get("process") process, err := strconv.ParseBool(processS) diff --git a/configuration/componentcfg/query.go b/configuration/componentcfg/query.go index 66970424..12f67e1e 100644 --- a/configuration/componentcfg/query.go +++ b/configuration/componentcfg/query.go @@ -42,7 +42,7 @@ const ( var ( // component /RUNTYPE /rolename /entry @timestamp - inputFullRegex = regexp.MustCompile(`^([a-zA-Z0-9-_]+)(\/[A-Z0-9-_]+){1}(\/[a-z-A-Z0-9-_]+){1}(\/[a-z-A-Z0-9-_]+){1}(\@[0-9]+)?$`) + inputFullRegex = regexp.MustCompile(`^([a-zA-Z0-9-_]+)(\/[A-Z0-9-_]+){1}(\/[a-z-A-Z0-9-_]+){1}(\/[a-z-A-Z0-9-_/]+){1}(\@[0-9]+)?$`) // component /RUNTYPE /rolename inputEntriesRegex = regexp.MustCompile(`^([a-zA-Z0-9-_]+)(\/[A-Z0-9-_]+){1}(\/[a-z-A-Z0-9-_]+){1}$`) inputParametersRegex = regexp.MustCompile(`^([a-zA-Z0-9-_]+=[a-zA-Z0-9-_,]+)(&[a-zA-Z0-9-_]+=[a-zA-Z0-9-_,"\[\]]+)*$`) @@ -110,39 +110,29 @@ func NewQuery(path string) (p *Query, err error) { } path = strings.TrimSpace(path) if IsStringValidQueryPathWithOptionalTimestamp(path) { - if strings.Contains(path, "@") { - // coconut conf show component/FLAVOR/rolename/entry@timestamp - arg := strings.Replace(path, "@", SEPARATOR, 1) - params := strings.Split(arg, SEPARATOR) - p.Component = params[0] - // Convert FLAVOR to pb-provided enum - typedFlavor, ok := apricotpb.RunType_value[params[1]] - if !ok { - err = E_BAD_KEY - return - } - p.RunType = apricotpb.RunType(typedFlavor) - p.RoleName = params[2] - p.EntryKey = params[3] - p.Timestamp = params[4] - } else if strings.Contains(path, SEPARATOR) { - // coconut conf show component/FLAVOR/rolename/entry - params := strings.Split(path, SEPARATOR) - p.Component = params[0] - // Convert FLAVOR to pb-provided enum - typedFlavor, ok := apricotpb.RunType_value[params[1]] - if !ok { - err = E_BAD_KEY - return - } - p.RunType = apricotpb.RunType(typedFlavor) - p.RoleName = params[2] - p.EntryKey = params[3] - // and if we received a raw path (with / instead of @ before timestamp): - if len(params) > 4 && len(params[4]) > 0 { - p.Timestamp = params[4] - } + + matches := inputFullRegex.FindAllStringSubmatch(path, -1) + + if len(matches) != 1 { + err = E_BAD_KEY + return + } + captureGroups := matches[0][1:] // the first element is the full query, we don't need it + if len(captureGroups) < 4 && len(captureGroups) > 5 { + err = E_BAD_KEY + return + } + p.Component = captureGroups[0] + // Convert FLAVOR to pb-provided enum + typedFlavor, ok := apricotpb.RunType_value[strings.TrimPrefix(captureGroups[1], "/")] + if !ok { + err = E_BAD_KEY + return } + p.RunType = apricotpb.RunType(typedFlavor) + p.RoleName = strings.TrimPrefix(captureGroups[2], "/") + p.EntryKey = strings.TrimPrefix(captureGroups[3], "/") + p.Timestamp = strings.TrimPrefix(captureGroups[4], "@") } else { err = E_BAD_KEY return diff --git a/configuration/componentcfg/query_test.go b/configuration/componentcfg/query_test.go index e11fe30e..e1ce73ad 100644 --- a/configuration/componentcfg/query_test.go +++ b/configuration/componentcfg/query_test.go @@ -43,6 +43,21 @@ var _ = Describe("query", func() { It("should be able to generalize to a query for any role name", func() { Expect(q.WithFallbackRunType().Path()).To(Equal("qc/ANY/pp/ctp-raw-qc@1234")) }) + It("should recognize the RunType correctly", func() { + Expect(q.RunType.String()).To(Equal("PHYSICS")) + }) + It("should recognize the RoleName correctly", func() { + Expect(q.RoleName).To(Equal("pp")) + }) + It("should recognize the Component correctly", func() { + Expect(q.Component).To(Equal("qc")) + }) + It("should recognize the EntryKey correctly", func() { + Expect(q.EntryKey).To(Equal("ctp-raw-qc")) + }) + It("should recognize the Timestamp correctly", func() { + Expect(q.Timestamp).To(Equal("1234")) + }) }) When("creating a new query with the full path but no timestamp", func() { @@ -72,6 +87,18 @@ var _ = Describe("query", func() { }) }) + When("creating a new query with entry having a subdirectory", func() { + BeforeEach(func() { + q, err = componentcfg.NewQuery("qc/ANY/any/tpc/clusters") + }) + It("should be parsed without reporting errors", func() { + Expect(err).To(BeNil()) + }) + It("the entry should be correctly recognized", func() { + Expect(q.EntryKey).To(Equal("tpc/clusters")) + }) + }) + Describe("dealing with incorrectly formatted queries", func() { When("query is empty", func() { BeforeEach(func() {