From 31523a49207ac8cad106903224c3dae5013b26b0 Mon Sep 17 00:00:00 2001 From: srinandan <13950006+srinandan@users.noreply.github.com> Date: Tue, 25 Jul 2023 09:31:22 -0700 Subject: [PATCH] feat: gen bundle for integration targets #235 --- cmd/apis/integrationapis.go | 13 ++-- cmd/apis/oascrtapis.go | 41 +++++++++++-- internal/bundlegen/generateapi.go | 60 ++++++++++++------- internal/bundlegen/generateint.go | 15 ++++- internal/bundlegen/options.go | 33 ++++++++++ internal/bundlegen/proxybundle/proxybundle.go | 49 ++++++++++----- 6 files changed, 167 insertions(+), 44 deletions(-) create mode 100644 internal/bundlegen/options.go diff --git a/cmd/apis/integrationapis.go b/cmd/apis/integrationapis.go index 3de0a1e64..4966b52e4 100644 --- a/cmd/apis/integrationapis.go +++ b/cmd/apis/integrationapis.go @@ -18,6 +18,7 @@ import ( "os" "internal/apiclient" + "internal/client/apis" "internal/bundlegen" "internal/bundlegen/proxybundle" @@ -40,15 +41,17 @@ var IntegrationCmd = &cobra.Command{ defer os.RemoveAll(tmpDir) - if err = bundlegen.GenerateIntegrationAPIProxy(name, integration, apitrigger); err != nil { + if err = bundlegen.GenerateIntegrationAPIProxy(name, apitrigger); err != nil { return err } + if err = proxybundle.GenerateIntegrationAPIProxyBundle(name, integration, apitrigger, true); err != nil { return err } - /*if _, err = apis.CreateProxy(name, tmpDir); err != nil { - return err - }*/ + + if importProxy { + _, err = apis.CreateProxy(name, tmpDir) + } return err }, } @@ -62,6 +65,8 @@ func init() { "", "Integration name") IntegrationCmd.Flags().StringVarP(&apitrigger, "trigger", "", "", "API Trigger name; don't include 'api_trigger/'") + IntegrationCmd.Flags().BoolVarP(&importProxy, "import", "", + true, "Import API Proxy after generation") _ = IntegrationCmd.MarkFlagRequired("name") _ = IntegrationCmd.MarkFlagRequired("integration") diff --git a/cmd/apis/oascrtapis.go b/cmd/apis/oascrtapis.go index ef90114a5..1e123e999 100644 --- a/cmd/apis/oascrtapis.go +++ b/cmd/apis/oascrtapis.go @@ -39,12 +39,23 @@ var OasCreateCmd = &cobra.Command{ if targetURL != "" && targetURLRef != "" { return fmt.Errorf("either target-url or target-url-ref must be passed, not both") } + if integration != "" && apitrigger == "" { + return fmt.Errorf("apitrigger must be passed if integration is set") + } + if integration == "" && apitrigger != "" { + return fmt.Errorf("integration must be passed if apitrigger is set") + } + if (targetURL != "" || targetURLRef != "") && (integration != "" || apitrigger != "") { + return fmt.Errorf("integration or apitrigger cannot be set if targetURL or targetURLRef is set") + } return apiclient.SetApigeeOrg(org) }, RunE: func(cmd *cobra.Command, args []string) (err error) { var content []byte var oasDocName string + integrationEndpoint := false + if oasFile != "" { oasDocName, content, err = bundle.LoadDocumentFromFile(oasFile, validateSpec, formatValidation) } else { @@ -54,11 +65,31 @@ var OasCreateCmd = &cobra.Command{ return err } + targetOptions := bundle.TargetOptions{ + IntegrationBackend: bundle.IntegrationBackendOptions{ + IntegrationName: integration, + TriggerName: apitrigger, + }, + HttpBackend: bundle.HttpBackendOptions{ + OasGoogleAcessTokenScopeLiteral: oasGoogleAcessTokenScopeLiteral, + OasGoogleIDTokenAudLiteral: oasGoogleIDTokenAudLiteral, + OasGoogleIDTokenAudRef: oasGoogleIDTokenAudRef, + OasTargetURLRef: targetURLRef, + TargetURL: targetURL, + }, + } + + // check if integrationEndpoint is selected + if integration != "" { + integrationEndpoint = true + } + // Generate the apiproxy struct err = bundle.GenerateAPIProxyDefFromOAS(name, oasDocName, skipPolicy, addCORS, + integrationEndpoint, oasGoogleAcessTokenScopeLiteral, oasGoogleIDTokenAudLiteral, oasGoogleIDTokenAudRef, @@ -75,11 +106,7 @@ var OasCreateCmd = &cobra.Command{ oasDocName, skipPolicy, addCORS, - oasGoogleAcessTokenScopeLiteral, - oasGoogleIDTokenAudLiteral, - oasGoogleIDTokenAudRef, - targetURLRef, - targetURL) + targetOptions) if err != nil { return err @@ -116,6 +143,10 @@ func init() { "", "Set a reference variable containing the target endpoint") OasCreateCmd.Flags().StringVarP(&targetURL, "target-url", "", "", "Set a target URL for the target endpoint") + OasCreateCmd.Flags().StringVarP(&integration, "integration", "i", + "", "Integration name") + OasCreateCmd.Flags().StringVarP(&apitrigger, "trigger", "", + "", "API Trigger name; don't include 'api_trigger/'") OasCreateCmd.Flags().BoolVarP(&importProxy, "import", "", true, "Import API Proxy after generation from spec") OasCreateCmd.Flags().BoolVarP(&validateSpec, "validate", "", diff --git a/internal/bundlegen/generateapi.go b/internal/bundlegen/generateapi.go index 909759750..708e1e1ff 100644 --- a/internal/bundlegen/generateapi.go +++ b/internal/bundlegen/generateapi.go @@ -208,11 +208,12 @@ func GenerateAPIProxyDefFromOAS(name string, oasDocName string, skipPolicy bool, addCORS bool, + integrationEndpoint bool, oasGoogleAcessTokenScopeLiteral string, - oasGoogleIdTokenAudLiteral string, - oasGoogleIdTokenAudRef string, - oasTargetUrlRef string, - targetUrl string, + oasGoogleIDTokenAudLiteral string, + oasGoogleIDTokenAudRef string, + oasTargetURLRef string, + targetURL string, ) (err error) { if doc == nil { return fmt.Errorf("the Open API document not loaded") @@ -231,7 +232,11 @@ func GenerateAPIProxyDefFromOAS(name string, apiproxy.SetCreatedAt() apiproxy.SetLastModifiedAt() apiproxy.SetConfigurationVersion() - apiproxy.AddTargetEndpoint(NoAuthTargetName) + if integrationEndpoint { + apiproxy.AddIntegrationEndpoint("default") + } else { + apiproxy.AddTargetEndpoint(NoAuthTargetName) + } apiproxy.AddProxyEndpoint("default") if !skipPolicy { @@ -250,25 +255,40 @@ func GenerateAPIProxyDefFromOAS(name string, apiproxy.SetBasePath(u.Path) - // if target is not set, derive it from the OAS file - if targetUrl == "" { - targets.NewTargetEndpoint(NoAuthTargetName, u.Scheme+"://"+u.Hostname()+u.Path, oasGoogleAcessTokenScopeLiteral, oasGoogleIdTokenAudLiteral, oasGoogleIdTokenAudRef) - } else { // an explicit target url is set - if _, err = url.Parse(targetUrl); err != nil { - return fmt.Errorf("invalid target url: %v", err) + // decide on the type of target + if integrationEndpoint { // assume an integration endpoint + proxies.AddStepToPreFlowRequest("set-integration-request") + apiproxy.AddPolicy("set-integration-request") + proxies.NewProxyEndpoint(u.Path, false) + } else { + // if target is not set, derive it from the OAS file + if targetURL == "" { + targets.NewTargetEndpoint(NoAuthTargetName, + u.Scheme+"://"+u.Hostname()+u.Path, + oasGoogleAcessTokenScopeLiteral, + oasGoogleIDTokenAudLiteral, + oasGoogleIDTokenAudRef) + } else { // an explicit target url is set + if _, err = url.Parse(targetURL); err != nil { + return fmt.Errorf("invalid target url: %v", err) + } + targets.NewTargetEndpoint(NoAuthTargetName, + targetURL, + oasGoogleAcessTokenScopeLiteral, + oasGoogleIDTokenAudLiteral, + oasGoogleIDTokenAudRef) + } + + // set a dynamic target url + if oasTargetURLRef != "" { + targets.AddStepToPreFlowRequest("Set-Target-1", NoAuthTargetName) + apiproxy.AddPolicy("Set-Target-1") + generateSetTarget = true } - targets.NewTargetEndpoint(NoAuthTargetName, targetUrl, oasGoogleAcessTokenScopeLiteral, oasGoogleIdTokenAudLiteral, oasGoogleIdTokenAudRef) - } - // set a dynamic target url - if oasTargetUrlRef != "" { - targets.AddStepToPreFlowRequest("Set-Target-1", NoAuthTargetName) - apiproxy.AddPolicy("Set-Target-1") - generateSetTarget = true + proxies.NewProxyEndpoint(u.Path, true) } - proxies.NewProxyEndpoint(u.Path, true) - // add any preflow security schemes if securityScheme := getSecurityRequirements(doc.Security); securityScheme.SchemeName != "" { if securityScheme.APIKeyPolicy.APIKeyPolicyEnabled { diff --git a/internal/bundlegen/generateint.go b/internal/bundlegen/generateint.go index 26e268c4d..a85868759 100644 --- a/internal/bundlegen/generateint.go +++ b/internal/bundlegen/generateint.go @@ -1,3 +1,17 @@ +// Copyright 2022 Google LLC +// +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package bundlegen import ( @@ -6,7 +20,6 @@ import ( ) func GenerateIntegrationAPIProxy(name string, - integration string, apitrigger string, ) (err error) { apiproxy.SetDisplayName(name) diff --git a/internal/bundlegen/options.go b/internal/bundlegen/options.go new file mode 100644 index 000000000..ce8635aa1 --- /dev/null +++ b/internal/bundlegen/options.go @@ -0,0 +1,33 @@ +// Copyright 2023 Google LLC +// +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package bundlegen + +type IntegrationBackendOptions struct { + IntegrationName string + TriggerName string +} + +type HttpBackendOptions struct { + OasGoogleAcessTokenScopeLiteral string + OasGoogleIDTokenAudLiteral string + OasGoogleIDTokenAudRef string + OasTargetURLRef string + TargetURL string +} + +type TargetOptions struct { + IntegrationBackend IntegrationBackendOptions + HttpBackend HttpBackendOptions +} diff --git a/internal/bundlegen/proxybundle/proxybundle.go b/internal/bundlegen/proxybundle/proxybundle.go index 1917df0e9..81e60b83c 100644 --- a/internal/bundlegen/proxybundle/proxybundle.go +++ b/internal/bundlegen/proxybundle/proxybundle.go @@ -48,13 +48,9 @@ func GenerateAPIProxyBundleFromOAS(name string, fileName string, skipPolicy bool, addCORS bool, - oasGoogleAcessTokenScopeLiteral string, - oasGoogleIdTokenAudLiteral string, - oasGoogleIdTokenAudRef string, - oasTargetUrlRef string, - targetUrl string, + targetOptions bundlegen.TargetOptions, ) (err error) { - var apiProxyData, proxyEndpointData, targetEndpointData string + var apiProxyData, proxyEndpointData, targetEndpointData, integrationEndpointData string const resourceType = "oas" tmpDir, err := os.MkdirTemp("", "proxy") @@ -82,6 +78,7 @@ func GenerateAPIProxyBundleFromOAS(name string, policiesDirPath := rootDir + string(os.PathSeparator) + "policies" targetDirPath := rootDir + string(os.PathSeparator) + "targets" resDirPath := rootDir + string(os.PathSeparator) + "resources" + string(os.PathSeparator) + resourceType //"oas" + integrationDirPath := rootDir + string(os.PathSeparator) + "integration-endpoints" if err = os.Mkdir(proxiesDirPath, os.ModePerm); err != nil { return err @@ -96,18 +93,33 @@ func GenerateAPIProxyBundleFromOAS(name string, return err } - if err = os.Mkdir(targetDirPath, os.ModePerm); err != nil { - return err - } + if targetOptions.IntegrationBackend.IntegrationName != "" { - for _, targetEndpoint := range targets.TargetEndpoints { - if targetEndpointData, err = target.GetTargetEndpoint(targetEndpoint); err != nil { + if err = os.Mkdir(integrationDirPath, os.ModePerm); err != nil { return err } - if err = writeXMLData(targetDirPath+string(os.PathSeparator)+targetEndpoint.Name+".xml", targetEndpointData); err != nil { + // assume there is an integration target + integrationEndpointData = target.GetIntegrationEndpoint() + if err = writeXMLData(integrationDirPath+string(os.PathSeparator)+"default.xml", integrationEndpointData); err != nil { return err } + + } else { + + if err = os.Mkdir(targetDirPath, os.ModePerm); err != nil { + return err + } + + for _, targetEndpoint := range targets.TargetEndpoints { + if targetEndpointData, err = target.GetTargetEndpoint(targetEndpoint); err != nil { + return err + } + + if err = writeXMLData(targetDirPath+string(os.PathSeparator)+targetEndpoint.Name+".xml", targetEndpointData); err != nil { + return err + } + } } if !skipPolicy { @@ -124,15 +136,24 @@ func GenerateAPIProxyBundleFromOAS(name string, } // add set target url - if targetUrl == "" { + if targetOptions.HttpBackend.TargetURL == "" { if genapi.GenerateSetTargetPolicy() { if err = writeXMLData(policiesDirPath+string(os.PathSeparator)+"Set-Target-1.xml", - policies.AddSetTargetEndpointRef(oasTargetUrlRef)); err != nil { + policies.AddSetTargetEndpointRef(targetOptions.HttpBackend.OasTargetURLRef)); err != nil { return err } } } + if targetOptions.IntegrationBackend.IntegrationName != "" { + // add set integration request policy + if err = writeXMLData(policiesDirPath+string(os.PathSeparator)+"set-integration-request.xml", + policies.AddSetIntegrationRequestPolicy(targetOptions.IntegrationBackend.IntegrationName, + targetOptions.IntegrationBackend.TriggerName)); err != nil { + return err + } + } + // add security policies for _, securityScheme := range genapi.GetSecuritySchemesList() { if securityScheme.APIKeyPolicy.APIKeyPolicyEnabled {