1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2025-01-18 05:18:24 +02:00

abapEnvironment automatically building the next package (#4148)

* api user scatch

* cv, pv +steps

* dust wipe

* escape odata values in filter

* use correct validation url

* headers

* add missing error check

* restrict to non revertable packages

* Correct dotted-version-string calculation + tests
This commit is contained in:
tiloKo 2023-01-12 15:47:11 +01:00 committed by GitHub
parent 74b5527f1c
commit 3867f37dcf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 739 additions and 297 deletions

View File

@ -1,10 +1,6 @@
package cmd
import (
"encoding/json"
"fmt"
"net/url"
"github.com/SAP/jenkins-library/pkg/abap/aakaas"
abapbuild "github.com/SAP/jenkins-library/pkg/abap/build"
"github.com/SAP/jenkins-library/pkg/abaputils"
@ -21,6 +17,11 @@ func abapAddonAssemblyKitCheckCVs(config abapAddonAssemblyKitCheckCVsOptions, te
}
func runAbapAddonAssemblyKitCheckCVs(config *abapAddonAssemblyKitCheckCVsOptions, telemetryData *telemetry.CustomData, utils *aakaas.AakUtils, cpe *abapAddonAssemblyKitCheckCVsCommonPipelineEnvironment) error {
log.Entry().Info("╔══════════════════════════════╗")
log.Entry().Info("║ abapAddonAssemblyKitCheckCVs ║")
log.Entry().Info("╚══════════════════════════════╝")
conn := new(abapbuild.Connector)
if err := conn.InitAAKaaS(config.AbapAddonAssemblyKitEndpoint, config.Username, config.Password, *utils); err != nil {
return err
@ -32,14 +33,22 @@ func runAbapAddonAssemblyKitCheckCVs(config *abapAddonAssemblyKitCheckCVsOptions
return err
}
for i := range addonDescriptor.Repositories {
var c componentVersion
c.initCV(addonDescriptor.Repositories[i], *conn)
err := c.validate()
if err != nil {
for i, repo := range addonDescriptor.Repositories {
componentVersion := new(aakaas.ComponentVersion)
if err := componentVersion.ConstructComponentVersion(addonDescriptor.Repositories[i], *conn); err != nil {
return err
}
c.copyFieldsToRepo(&addonDescriptor.Repositories[i])
if err := componentVersion.Validate(); err != nil {
return err
}
componentVersion.CopyVersionFieldsToRepo(&addonDescriptor.Repositories[i])
log.Entry().Infof("Using cCTS %t", repo.UseClassicCTS)
log.Entry().Infof("CommitId %s", repo.CommitID)
if !repo.UseClassicCTS && repo.CommitID == "" {
return errors.Errorf("CommitID missing in repo '%s' of the addon.yml", repo.Name)
}
}
// now Software Component Versions fields are valid, but maybe Product Version was checked before, so copy that part from CPE
@ -63,57 +72,3 @@ func combineYAMLRepositoriesWithCPEProduct(addonDescriptor abaputils.AddonDescri
addonDescriptorFromCPE.Repositories = addonDescriptor.Repositories
return addonDescriptorFromCPE
}
func (c *componentVersion) initCV(repo abaputils.Repository, conn abapbuild.Connector) {
c.Connector = conn
c.Name = repo.Name
c.VersionYAML = repo.VersionYAML
c.CommitID = repo.CommitID
c.UseClassicCTS = repo.UseClassicCTS
}
func (c *componentVersion) copyFieldsToRepo(initialRepo *abaputils.Repository) {
initialRepo.Version = c.Version
initialRepo.SpLevel = c.SpLevel
initialRepo.PatchLevel = c.PatchLevel
}
func (c *componentVersion) validate() error {
log.Entry().Infof("Validate component %s version %s and resolve version", c.Name, c.VersionYAML)
appendum := "/odata/aas_ocs_package/ValidateComponentVersion?Name='" + url.QueryEscape(c.Name) + "'&Version='" + url.QueryEscape(c.VersionYAML) + "'"
body, err := c.Connector.Get(appendum)
if err != nil {
return err
}
var jCV jsonComponentVersion
if err := json.Unmarshal(body, &jCV); err != nil {
return errors.Wrap(err, "Unexpected AAKaaS response for Validate Component Version: "+string(body))
}
c.Name = jCV.ComponentVersion.Name
c.Version = jCV.ComponentVersion.Version
c.SpLevel = jCV.ComponentVersion.SpLevel
c.PatchLevel = jCV.ComponentVersion.PatchLevel
log.Entry().Infof("Resolved version %s, splevel %s, patchlevel %s", c.Version, c.SpLevel, c.PatchLevel)
log.Entry().Infof("Using cCTS %t", c.UseClassicCTS)
if !c.UseClassicCTS && c.CommitID == "" {
return fmt.Errorf("CommitID missing in repo '%s' of the addon.yml", c.Name)
}
return nil
}
type jsonComponentVersion struct {
ComponentVersion *componentVersion `json:"d"`
}
type componentVersion struct {
abapbuild.Connector
Name string `json:"Name"`
VersionYAML string
Version string `json:"Version"`
SpLevel string `json:"SpLevel"`
PatchLevel string `json:"PatchLevel"`
UseClassicCTS bool
CommitID string
}

View File

@ -5,9 +5,7 @@ import (
"testing"
"github.com/SAP/jenkins-library/pkg/abap/aakaas"
abapbuild "github.com/SAP/jenkins-library/pkg/abap/build"
"github.com/SAP/jenkins-library/pkg/abaputils"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
)
@ -15,7 +13,7 @@ func TestCheckCVsStep(t *testing.T) {
var config abapAddonAssemblyKitCheckCVsOptions
var cpe abapAddonAssemblyKitCheckCVsCommonPipelineEnvironment
bundle := aakaas.NewAakBundleMock()
bundle.SetBody(responseCheckCVs)
bundle.SetBody(aakaas.ResponseCheckCVs)
utils := bundle.GetUtils()
config.Username = "dummyUser"
config.Password = "dummyPassword"
@ -52,78 +50,6 @@ func TestCheckCVsStep(t *testing.T) {
})
}
func TestInitCV(t *testing.T) {
t.Run("test init", func(t *testing.T) {
conn := new(abapbuild.Connector)
conn.Client = &abaputils.ClientMock{}
repo := abaputils.Repository{
Name: "/DRNMSPC/COMP01",
VersionYAML: "1.2.3",
}
var c componentVersion
c.initCV(repo, *conn)
assert.Equal(t, "/DRNMSPC/COMP01", c.Name)
assert.Equal(t, "1.2.3", c.VersionYAML)
})
}
func TestValidateCV(t *testing.T) {
conn := new(abapbuild.Connector)
t.Run("test validate - success", func(t *testing.T) {
conn.Client = &abaputils.ClientMock{
Body: responseCheckCVs,
}
c := componentVersion{
Connector: *conn,
Name: "/DRNMSPC/COMP01",
VersionYAML: "1.2.3",
CommitID: "HUGO1234",
}
conn.Client = &abaputils.ClientMock{
Body: responseCheckCVs,
}
err := c.validate()
assert.NoError(t, err)
assert.Equal(t, "0001", c.Version)
assert.Equal(t, "0002", c.SpLevel)
assert.Equal(t, "0003", c.PatchLevel)
})
t.Run("test validate - with error", func(t *testing.T) {
conn.Client = &abaputils.ClientMock{
Body: "ErrorBody",
Error: errors.New("Validation failed"),
}
c := componentVersion{
Connector: *conn,
Name: "/DRNMSPC/COMP01",
VersionYAML: "1.2.3",
CommitID: "HUGO1234",
}
err := c.validate()
assert.Error(t, err)
assert.Equal(t, "", c.Version)
assert.Equal(t, "", c.SpLevel)
assert.Equal(t, "", c.PatchLevel)
})
}
func TestCopyFieldsCV(t *testing.T) {
t.Run("test copyFieldsToRepo", func(t *testing.T) {
repo := abaputils.Repository{
Name: "/DRNMSPC/COMP01",
VersionYAML: "1.2.3",
}
var c componentVersion
c.Version = "0001"
c.SpLevel = "0002"
c.PatchLevel = "0003"
c.copyFieldsToRepo(&repo)
assert.Equal(t, "0001", repo.Version)
assert.Equal(t, "0002", repo.SpLevel)
assert.Equal(t, "0003", repo.PatchLevel)
})
}
func TestCombineYAMLRepositoriesWithCPEProduct(t *testing.T) {
t.Run("test combineYAMLRepositoriesWithCPEProduct", func(t *testing.T) {
addonDescriptor := abaputils.AddonDescriptor{
@ -151,19 +77,3 @@ func TestCombineYAMLRepositoriesWithCPEProduct(t *testing.T) {
assert.Equal(t, "3.2.1", finalAddonDescriptor.Repositories[1].VersionYAML)
})
}
var responseCheckCVs = `{
"d": {
"__metadata": {
"id": "https://W7Q.DMZWDF.SAP.CORP:443/odata/aas_ocs_package/SoftwareComponentVersionSet(Name='%2FDRNMSPC%2FCOMP01',Version='0001')",
"uri": "https://W7Q.DMZWDF.SAP.CORP:443/odata/aas_ocs_package/SoftwareComponentVersionSet(Name='%2FDRNMSPC%2FCOMP01',Version='0001')",
"type": "SSDA.AAS_ODATA_PACKAGE_SRV.SoftwareComponentVersion"
},
"Name": "/DRNMSPC/COMP01",
"Version": "0001",
"SpLevel": "0002",
"PatchLevel": "0003",
"Vendor": "",
"VendorType": ""
}
}`

View File

@ -1,16 +1,12 @@
package cmd
import (
"encoding/json"
"net/url"
"github.com/SAP/jenkins-library/pkg/abap/aakaas"
abapbuild "github.com/SAP/jenkins-library/pkg/abap/build"
"github.com/SAP/jenkins-library/pkg/abaputils"
"github.com/SAP/jenkins-library/pkg/log"
"github.com/SAP/jenkins-library/pkg/piperutils"
"github.com/SAP/jenkins-library/pkg/telemetry"
"github.com/pkg/errors"
)
func abapAddonAssemblyKitCheckPV(config abapAddonAssemblyKitCheckPVOptions, telemetryData *telemetry.CustomData, cpe *abapAddonAssemblyKitCheckPVCommonPipelineEnvironment) {
@ -22,6 +18,10 @@ func abapAddonAssemblyKitCheckPV(config abapAddonAssemblyKitCheckPVOptions, tele
}
func runAbapAddonAssemblyKitCheckPV(config *abapAddonAssemblyKitCheckPVOptions, telemetryData *telemetry.CustomData, utils aakaas.AakUtils, cpe *abapAddonAssemblyKitCheckPVCommonPipelineEnvironment) error {
log.Entry().Info("╔═════════════════════════════╗")
log.Entry().Info("║ abapAddonAssemblyKitCheckPV ║")
log.Entry().Info("╚═════════════════════════════╝")
conn := new(abapbuild.Connector)
if err := conn.InitAAKaaS(config.AbapAddonAssemblyKitEndpoint, config.Username, config.Password, utils); err != nil {
return err
@ -33,12 +33,14 @@ func runAbapAddonAssemblyKitCheckPV(config *abapAddonAssemblyKitCheckPVOptions,
return err
}
pv := new(productVersion).init(addonDescriptor, *conn)
err = pv.validateAndResolveVersionFields()
if err != nil {
pv := new(aakaas.ProductVersion)
if err := pv.ConstructProductversion(addonDescriptor, *conn); err != nil {
return err
}
pv.transferVersionFields(&addonDescriptor)
if err = pv.ValidateAndResolveVersionFields(); err != nil {
return err
}
pv.CopyVersionFieldsToDescriptor(&addonDescriptor)
// now Product Version fields are valid, but maybe Component Versions (Repositories) were checked before, so copy that part from CPE
// we don't care for errors
@ -64,50 +66,3 @@ func runAbapAddonAssemblyKitCheckPV(config *abapAddonAssemblyKitCheckPVOptions,
return nil
}
func (p *productVersion) init(desc abaputils.AddonDescriptor, conn abapbuild.Connector) *productVersion {
p.Connector = conn
p.Name = desc.AddonProduct
p.VersionYAML = desc.AddonVersionYAML
return p
}
func (p *productVersion) transferVersionFields(initialAddonDescriptor *abaputils.AddonDescriptor) {
initialAddonDescriptor.AddonVersion = p.Version
initialAddonDescriptor.AddonSpsLevel = p.SpsLevel
initialAddonDescriptor.AddonPatchLevel = p.PatchLevel
}
func (p *productVersion) validateAndResolveVersionFields() error {
log.Entry().Infof("Validate product '%s' version '%s' and resolve version", p.Name, p.VersionYAML)
appendum := "/odata/aas_ocs_package/ValidateProductVersion?Name='" + url.QueryEscape(p.Name) + "'&Version='" + url.QueryEscape(p.VersionYAML) + "'"
body, err := p.Connector.Get(appendum)
if err != nil {
return err
}
var jPV jsonProductVersion
if err := json.Unmarshal(body, &jPV); err != nil {
return errors.Wrap(err, "Unexpected AAKaaS response for Validate Product Version: "+string(body))
}
p.Name = jPV.ProductVersion.Name
p.Version = jPV.ProductVersion.Version
p.SpsLevel = jPV.ProductVersion.SpsLevel
p.PatchLevel = jPV.ProductVersion.PatchLevel
log.Entry().Infof("Resolved version %s, spslevel %s, patchlevel %s", p.Version, p.SpsLevel, p.PatchLevel)
return nil
}
type jsonProductVersion struct {
ProductVersion *productVersion `json:"d"`
}
type productVersion struct {
abapbuild.Connector
Name string `json:"Name"`
VersionYAML string
Version string `json:"Version"`
SpsLevel string `json:"SpsLevel"`
PatchLevel string `json:"PatchLevel"`
TargetVectorID string
}

View File

@ -5,9 +5,7 @@ import (
"testing"
"github.com/SAP/jenkins-library/pkg/abap/aakaas"
abapbuild "github.com/SAP/jenkins-library/pkg/abap/build"
"github.com/SAP/jenkins-library/pkg/abaputils"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
)
@ -15,7 +13,7 @@ func TestCheckPVStep(t *testing.T) {
var config abapAddonAssemblyKitCheckPVOptions
var cpe abapAddonAssemblyKitCheckPVCommonPipelineEnvironment
bundle := aakaas.NewAakBundleMock()
bundle.SetBody(responseCheckPV)
bundle.SetBody(aakaas.ResponseCheckPV)
utils := bundle.GetUtils()
config.Username = "dummyUser"
config.Password = "dummyPassword"
@ -44,87 +42,3 @@ func TestCheckPVStep(t *testing.T) {
assert.Error(t, err, "Did expect error")
})
}
func TestInitPV(t *testing.T) {
t.Run("test init", func(t *testing.T) {
conn := new(abapbuild.Connector)
conn.Client = &abaputils.ClientMock{}
prodvers := abaputils.AddonDescriptor{
AddonProduct: "/DRNMSPC/PRD01",
AddonVersionYAML: "3.2.1",
}
var pv productVersion
pv.init(prodvers, *conn)
assert.Equal(t, "/DRNMSPC/PRD01", pv.Name)
assert.Equal(t, "3.2.1", pv.VersionYAML)
})
}
func TestValidatePV(t *testing.T) {
conn := new(abapbuild.Connector)
t.Run("test validate - success", func(t *testing.T) {
conn.Client = &abaputils.ClientMock{
Body: responseCheckPV,
}
pv := productVersion{
Connector: *conn,
Name: "/DRNMSPC/PRD01",
VersionYAML: "3.2.1",
}
err := pv.validateAndResolveVersionFields()
assert.NoError(t, err)
assert.Equal(t, "0003", pv.Version)
assert.Equal(t, "0002", pv.SpsLevel)
assert.Equal(t, "0001", pv.PatchLevel)
})
t.Run("test validate - with error", func(t *testing.T) {
conn.Client = &abaputils.ClientMock{
Body: "ErrorBody",
Error: errors.New("Validation failed"),
}
pv := productVersion{
Connector: *conn,
Name: "/DRNMSPC/PRD01",
VersionYAML: "3.2.1",
}
err := pv.validateAndResolveVersionFields()
assert.Error(t, err)
assert.Equal(t, "", pv.Version)
assert.Equal(t, "", pv.SpsLevel)
assert.Equal(t, "", pv.PatchLevel)
})
}
func TestCopyFieldsPV(t *testing.T) {
t.Run("test copyFieldsToRepo", func(t *testing.T) {
prodVers := abaputils.AddonDescriptor{
AddonProduct: "/DRNMSPC/PRD01",
AddonVersionYAML: "1.2.3",
}
var pv productVersion
pv.Version = "0003"
pv.SpsLevel = "0002"
pv.PatchLevel = "0001"
pv.transferVersionFields(&prodVers)
assert.Equal(t, "0003", prodVers.AddonVersion)
assert.Equal(t, "0002", prodVers.AddonSpsLevel)
assert.Equal(t, "0001", prodVers.AddonPatchLevel)
})
}
var responseCheckPV = `{
"d": {
"__metadata": {
"id": "https://W7Q.DMZWDF.SAP.CORP:443/odata/aas_ocs_package/ProductVersionSet(Name='%2FDRNMSPC%2FPRD01',Version='0001')",
"uri": "https://W7Q.DMZWDF.SAP.CORP:443/odata/aas_ocs_package/ProductVersionSet(Name='%2FDRNMSPC%2FPRD01',Version='0001')",
"type": "SSDA.AAS_ODATA_PACKAGE_SRV.ProductVersion"
},
"Name": "/DRNMSPC/PRD01",
"Version": "0003",
"SpsLevel": "0002",
"PatchLevel": "0001",
"Vendor": "",
"VendorType": ""
}
}`

View File

@ -0,0 +1,69 @@
package aakaas
import (
"encoding/json"
"net/url"
abapbuild "github.com/SAP/jenkins-library/pkg/abap/build"
"github.com/SAP/jenkins-library/pkg/abaputils"
"github.com/SAP/jenkins-library/pkg/log"
"github.com/pkg/errors"
)
const cvQueryURL string = "/odata/aas_ocs_package/xSSDAxC_Component_Version"
const cvValidateURL string = "/odata/aas_ocs_package/ValidateComponentVersion"
type ComponentVersion struct {
versionable
}
func (c *ComponentVersion) ConstructComponentVersion(repo abaputils.Repository, conn abapbuild.Connector) error {
if err := c.constructVersionable(repo.Name, repo.VersionYAML, conn, pvQueryURL); err != nil {
return err
}
if err := c.resolveNext(); err != nil {
return err
}
return nil
}
func (c *ComponentVersion) CopyVersionFieldsToRepo(repo *abaputils.Repository) {
repo.Version = c.TechRelease
repo.SpLevel = c.TechSpLevel
repo.PatchLevel = c.TechPatchLevel
}
func (c *ComponentVersion) Validate() error {
log.Entry().Infof("Validate component %s version %s and resolve version", c.Name, c.Version)
values := url.Values{}
values.Set("Name", "'"+c.Name+"'")
values.Set("Version", "'"+c.Version+"'")
requestUrl := cvValidateURL + "?" + values.Encode()
body, err := c.connector.Get(requestUrl)
if err != nil {
return err
}
var response jsonComponentVersionValidationResponse
if err := json.Unmarshal(body, &response); err != nil {
return errors.Wrap(err, "Unexpected AAKaaS response for Validate Component Version: "+string(body))
}
c.Name = response.Wrapper.Name
c.TechRelease = response.Wrapper.TechRelease
c.TechSpLevel = response.Wrapper.TechSpLevel
c.TechPatchLevel = response.Wrapper.TechPatchLevel
log.Entry().Infof("Resolved version %s, splevel %s, patchlevel %s", c.TechRelease, c.TechSpLevel, c.TechPatchLevel)
return nil
}
type jsonComponentVersionValidationResponse struct {
Wrapper struct {
Name string `json:"Name"`
TechRelease string `json:"Version"`
TechSpLevel string `json:"SpLevel"`
TechPatchLevel string `json:"PatchLevel"`
} `json:"d"`
}

View File

@ -0,0 +1,64 @@
package aakaas
import (
"testing"
abapbuild "github.com/SAP/jenkins-library/pkg/abap/build"
"github.com/SAP/jenkins-library/pkg/abaputils"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
)
func TestInitCV(t *testing.T) {
conn := new(abapbuild.Connector)
conn.Client = &abaputils.ClientMock{}
repo := abaputils.Repository{
Name: "/DRNMSPC/COMP01",
VersionYAML: "1.2.3",
}
var c ComponentVersion
t.Run("test init", func(t *testing.T) {
c.ConstructComponentVersion(repo, *conn)
assert.Equal(t, "/DRNMSPC/COMP01", c.Name)
assert.Equal(t, "1.2.3", c.Version)
})
t.Run("test validate - success", func(t *testing.T) {
conn.Client = &abaputils.ClientMock{
Body: ResponseCheckCVs,
}
c.ConstructComponentVersion(repo, *conn)
err := c.Validate()
assert.NoError(t, err)
assert.Equal(t, "0001", c.TechRelease)
assert.Equal(t, "0002", c.TechSpLevel)
assert.Equal(t, "0003", c.TechPatchLevel)
})
t.Run("test validate - with error", func(t *testing.T) {
conn.Client = &abaputils.ClientMock{
Body: "ErrorBody",
Error: errors.New("Validation failed"),
}
c.ConstructComponentVersion(repo, *conn)
err := c.Validate()
assert.Error(t, err)
})
t.Run("test copyFieldsToRepo", func(t *testing.T) {
var c ComponentVersion
c.TechRelease = "0001"
c.TechSpLevel = "0002"
c.TechPatchLevel = "0003"
c.CopyVersionFieldsToRepo(&repo)
assert.Equal(t, "0001", repo.Version)
assert.Equal(t, "0002", repo.SpLevel)
assert.Equal(t, "0003", repo.PatchLevel)
})
}

View File

@ -0,0 +1,67 @@
package aakaas
import (
"encoding/json"
"net/url"
abapbuild "github.com/SAP/jenkins-library/pkg/abap/build"
"github.com/SAP/jenkins-library/pkg/abaputils"
"github.com/SAP/jenkins-library/pkg/log"
"github.com/pkg/errors"
)
const pvQueryURL string = "/odata/aas_ocs_package/xSSDAxC_Product_Version"
const pvValidateURL string = "/odata/aas_ocs_package/ValidateProductVersion"
type ProductVersion struct {
versionable
}
func (p *ProductVersion) ConstructProductversion(desc abaputils.AddonDescriptor, conn abapbuild.Connector) error {
if err := p.constructVersionable(desc.AddonProduct, desc.AddonVersionYAML, conn, pvQueryURL); err != nil {
return err
}
if err := p.resolveNext(); err != nil {
return err
}
return nil
}
func (p *ProductVersion) CopyVersionFieldsToDescriptor(desc *abaputils.AddonDescriptor) {
desc.AddonVersion = p.TechRelease
desc.AddonSpsLevel = p.TechSpLevel
desc.AddonPatchLevel = p.TechPatchLevel
}
func (p *ProductVersion) ValidateAndResolveVersionFields() error {
log.Entry().Infof("Validate product '%s' version '%s' and resolve version", p.Name, p.Version)
values := url.Values{}
values.Set("Name", "'"+p.Name+"'")
values.Set("Version", "'"+p.Version+"'")
requestUrl := pvValidateURL + "?" + values.Encode()
body, err := p.connector.Get(requestUrl)
if err != nil {
return err
}
var response jsonProductVersionValidationResponse
if err := json.Unmarshal(body, &response); err != nil {
return errors.Wrap(err, "Unexpected AAKaaS response for Validate Product Version: "+string(body))
}
p.Name = response.Wrapper.Name
p.TechRelease = response.Wrapper.TechRelease
p.TechSpLevel = response.Wrapper.TechSpLevel
p.TechPatchLevel = response.Wrapper.TechPatchLevel
log.Entry().Infof("Resolved version %s, spslevel %s, patchlevel %s", p.TechRelease, p.TechSpLevel, p.TechPatchLevel)
return nil
}
type jsonProductVersionValidationResponse struct {
Wrapper struct {
Name string `json:"Name"`
TechRelease string `json:"Version"`
TechSpLevel string `json:"SpsLevel"`
TechPatchLevel string `json:"PatchLevel"`
} `json:"d"`
}

View File

@ -0,0 +1,58 @@
package aakaas
import (
"testing"
abapbuild "github.com/SAP/jenkins-library/pkg/abap/build"
"github.com/SAP/jenkins-library/pkg/abaputils"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
)
func TestInitPV(t *testing.T) {
conn := new(abapbuild.Connector)
conn.Client = &abaputils.ClientMock{}
prodVers := abaputils.AddonDescriptor{
AddonProduct: "/DRNMSPC/PRD01",
AddonVersionYAML: "3.2.1",
}
var pv ProductVersion
t.Run("test init", func(t *testing.T) {
pv.ConstructProductversion(prodVers, *conn)
assert.Equal(t, "/DRNMSPC/PRD01", pv.Name)
assert.Equal(t, "3.2.1", pv.Version)
})
t.Run("test validate - success", func(t *testing.T) {
conn.Client = &abaputils.ClientMock{
Body: ResponseCheckPV,
}
pv.ConstructProductversion(prodVers, *conn)
err := pv.ValidateAndResolveVersionFields()
assert.NoError(t, err)
assert.Equal(t, "0003", pv.TechRelease)
assert.Equal(t, "0002", pv.TechSpLevel)
assert.Equal(t, "0001", pv.TechPatchLevel)
})
t.Run("test validate - with error", func(t *testing.T) {
conn.Client = &abaputils.ClientMock{
Body: "ErrorBody",
Error: errors.New("Validation failed"),
}
pv.ConstructProductversion(prodVers, *conn)
err := pv.ValidateAndResolveVersionFields()
assert.Error(t, err)
})
t.Run("test copyFieldsToRepo", func(t *testing.T) {
pv.TechRelease = "0003"
pv.TechSpLevel = "0002"
pv.TechPatchLevel = "0001"
pv.CopyVersionFieldsToDescriptor(&prodVers)
assert.Equal(t, "0003", prodVers.AddonVersion)
assert.Equal(t, "0002", prodVers.AddonSpsLevel)
assert.Equal(t, "0001", prodVers.AddonPatchLevel)
})
}

142
pkg/abap/aakaas/testData.go Normal file
View File

@ -0,0 +1,142 @@
//go:build !release
// +build !release
/*
** The Test Data is partly re-used by the steps in cmd folder, thus we Export them but remove the file from release build
*/
package aakaas
import abapbuild "github.com/SAP/jenkins-library/pkg/abap/build"
var ResponseCheckPV = `{
"d": {
"Name": "/DRNMSPC/PRD01",
"Version": "0003",
"SpsLevel": "0002",
"PatchLevel": "0001",
"Vendor": "",
"VendorType": ""
}
}`
var ResponseCheckCVs = `{
"d": {
"Name": "/DRNMSPC/COMP01",
"Version": "0001",
"SpLevel": "0002",
"PatchLevel": "0003",
"Vendor": "",
"VendorType": ""
}
}`
var emptyResultBody = `{
"d": {
"results": []
}
}`
var testDataAakaasCVGetReleaseExisting = abapbuild.MockData{
Method: `GET`,
Url: `/odata/aas_ocs_package/xSSDAxC_Component_Version?%24filter=Name+eq+%27DummyComp%27+and+TechSpLevel+eq+%270000%27+and+TechPatchLevel+eq+%270000%27+and+%28+DeliveryStatus+eq+%27R%27+or+DeliveryStatus+eq+%27C%27+or+DeliveryStatus+eq+%27T%27+or+DeliveryStatus+eq+%27P%27+%29&%24format=json&%24orderby=TechRelease+desc&%24select=Name%2CVersion%2CTechRelease%2CTechSpLevel%2CTechPatchLevel&%24top=1`,
Body: `{
"d": {
"results": [
{
"Name": "DummyComp",
"Version": "1.0.0",
"TechRelease": "1",
"TechSpLevel": "0000",
"TechPatchLevel": "0000"
}
]
}
}`,
StatusCode: 200,
}
var testDataAakaasCVGetReleaseNonExisting = abapbuild.MockData{
Method: `GET`,
Url: `/odata/aas_ocs_package/xSSDAxC_Component_Version?%24filter=Name+eq+%27DummyComp%27+and+TechSpLevel+eq+%270000%27+and+TechPatchLevel+eq+%270000%27+and+%28+DeliveryStatus+eq+%27R%27+or+DeliveryStatus+eq+%27C%27+or+DeliveryStatus+eq+%27T%27+or+DeliveryStatus+eq+%27P%27+%29&%24format=json&%24orderby=TechRelease+desc&%24select=Name%2CVersion%2CTechRelease%2CTechSpLevel%2CTechPatchLevel&%24top=1`,
Body: emptyResultBody,
StatusCode: 200,
}
var testDataAakaasCVGetSpLevelExisting = abapbuild.MockData{
Method: `GET`,
Url: `/odata/aas_ocs_package/xSSDAxC_Component_Version?%24filter=Name+eq+%27DummyComp%27+and+TechRelease+eq+%271%27+and+TechPatchLevel+eq+%270000%27++and+%28+DeliveryStatus+eq+%27R%27+or+DeliveryStatus+eq+%27C%27+or+DeliveryStatus+eq+%27T%27+or+DeliveryStatus+eq+%27P%27+%29&%24format=json&%24orderby=TechSpLevel+desc&%24select=Name%2CVersion%2CTechRelease%2CTechSpLevel%2CTechPatchLevel&%24top=1`,
Body: `{
"d": {
"results": [
{
"Name": "DummyComp",
"Version": "1.7.0",
"TechRelease": "1",
"TechSpLevel": "0007",
"TechPatchLevel": "0000"
}
]
}
}`,
StatusCode: 200,
}
var testDataAakaasCVGetSpLevelNonExisting = abapbuild.MockData{
Method: `GET`,
Url: `/odata/aas_ocs_package/xSSDAxC_Component_Version?%24filter=Name+eq+%27DummyComp%27+and+TechRelease+eq+%271%27+and+TechPatchLevel+eq+%270000%27++and+%28+DeliveryStatus+eq+%27R%27+or+DeliveryStatus+eq+%27C%27+or+DeliveryStatus+eq+%27T%27+or+DeliveryStatus+eq+%27P%27+%29&%24format=json&%24orderby=TechSpLevel+desc&%24select=Name%2CVersion%2CTechRelease%2CTechSpLevel%2CTechPatchLevel&%24top=1`,
Body: emptyResultBody,
StatusCode: 200,
}
var testDataAakaasCVGetPatchLevelExisting = abapbuild.MockData{
Method: `GET`,
Url: `/odata/aas_ocs_package/xSSDAxC_Component_Version?%24filter=Name+eq+%27DummyComp%27+and+TechRelease+eq+%271%27+and+TechSpLevel+eq+%270003%27+and+%28+DeliveryStatus+eq+%27R%27+or+DeliveryStatus+eq+%27C%27+or+DeliveryStatus+eq+%27T%27+or+DeliveryStatus+eq+%27P%27+%29&%24format=json&%24orderby=TechPatchLevel+desc&%24select=Name%2CVersion%2CTechRelease%2CTechSpLevel%2CTechPatchLevel&%24top=1`,
Body: `{
"d": {
"results": [
{
"Name": "DummyComp",
"Version": "1.3.46",
"TechRelease": "1",
"TechSpLevel": "0003",
"TechPatchLevel": "0046"
}
]
}
}`,
StatusCode: 200,
}
var testDataAakaasCVGetPatchLevelNonExisting = abapbuild.MockData{
Method: `GET`,
Url: `/odata/aas_ocs_package/xSSDAxC_Component_Version?%24filter=Name+eq+%27DummyComp%27+and+TechRelease+eq+%271%27+and+TechSpLevel+eq+%270003%27+and+%28+DeliveryStatus+eq+%27R%27+or+DeliveryStatus+eq+%27C%27+or+DeliveryStatus+eq+%27T%27+or+DeliveryStatus+eq+%27P%27+%29&%24format=json&%24orderby=TechPatchLevel+desc&%24select=Name%2CVersion%2CTechRelease%2CTechSpLevel%2CTechPatchLevel&%24top=1`,
Body: emptyResultBody,
StatusCode: 200,
}
var testDataAakaasPVGetReleaseExisting = abapbuild.MockData{
Method: `GET`,
Url: `/odata/aas_ocs_package/xSSDAxC_Product_Version?%24filter=Name+eq+%27DummyProd%27+and+TechSpLevel+eq+%270000%27+and+TechPatchLevel+eq+%270000%27+and+%28+DeliveryStatus+eq+%27R%27+or+DeliveryStatus+eq+%27C%27+or+DeliveryStatus+eq+%27T%27+or+DeliveryStatus+eq+%27P%27+%29&%24format=json&%24orderby=TechRelease+desc&%24select=Name%2CVersion%2CTechRelease%2CTechSpLevel%2CTechPatchLevel&%24top=1`,
Body: `{
"d": {
"results": [
{
"Name": "DummyProd",
"Version": "1.0.0",
"TechRelease": "0001",
"TechSpLevel": "0000",
"TechPatchLevel": "0000"
}
]
}
}`,
StatusCode: 200,
}
var testDataAakaasPVGetReleaseNonExisting = abapbuild.MockData{
Method: `GET`,
Url: `/odata/aas_ocs_package/xSSDAxC_Product_Version?%24filter=Name+eq+%27DummyProd%27+and+TechSpLevel+eq+%270000%27+and+TechPatchLevel+eq+%270000%27+and+%28+DeliveryStatus+eq+%27R%27+or+DeliveryStatus+eq+%27C%27+or+DeliveryStatus+eq+%27T%27+or+DeliveryStatus+eq+%27P%27+%29&%24format=json&%24orderby=TechRelease+desc&%24select=Name%2CVersion%2CTechRelease%2CTechSpLevel%2CTechPatchLevel&%24top=1`,
Body: emptyResultBody,
StatusCode: 200,
}

View File

@ -0,0 +1,177 @@
package aakaas
import (
"encoding/json"
"fmt"
"net/url"
"strconv"
"strings"
abapbuild "github.com/SAP/jenkins-library/pkg/abap/build"
"github.com/SAP/jenkins-library/pkg/log"
"github.com/pkg/errors"
)
const wildCard string = "NEXT"
type versionable struct {
Name string
Version string
TechRelease string
TechSpLevel string
TechPatchLevel string
connector abapbuild.Connector
queryUrl string
}
type versionables struct {
Wrapper struct {
Vs []versionable `json:"results"`
} `json:"d"`
}
func (v *versionable) constructVersionable(name string, dottedVersionString string, connector abapbuild.Connector, queryURL string) error {
if name == "" {
return errors.New("No Component/Product Name provided")
}
subStrings := strings.Split(dottedVersionString, ".")
if len(subStrings) != 3 {
return errors.New("Provide a dotted-version-string with 2 '.' [Release.SP.Patch]")
}
v.Name = name
v.TechRelease = subStrings[0]
v.TechSpLevel = fmt.Sprintf("%04s", subStrings[1])
v.TechPatchLevel = fmt.Sprintf("%04s", subStrings[2])
v.connector = connector
v.queryUrl = queryURL
v.Version = dottedVersionString
return nil
}
func (v *versionable) resolveNext() error {
switch strings.Count(v.Version, wildCard) {
case 0:
return nil
case 1:
log.Entry().Info("Wildcard detected in dotted-version-string. Looking up highest existing package in AAKaaS...")
var err error
switch wildCard {
case v.TechRelease:
err = v.resolveRelease()
case v.TechSpLevel:
err = v.resolveSpLevel()
case v.TechPatchLevel:
err = v.resolvePatchLevel()
}
if err != nil {
return err
}
if v.Version, err = v.getDottedVersionString(); err != nil {
return err
}
default:
return errors.New("The dotted-version-string must contain only one wildcard " + wildCard)
}
return nil
}
func (v *versionable) resolveRelease() error {
//take only unrevertable status R/C for packages and T/P for TargetVectors
filter := "Name eq '" + v.Name + "' and TechSpLevel eq '0000' and TechPatchLevel eq '0000' and ( DeliveryStatus eq 'R' or DeliveryStatus eq 'C' or DeliveryStatus eq 'T' or DeliveryStatus eq 'P' )"
orderBy := "TechRelease desc"
if queryResuult, err := v.queryVersion(filter, orderBy); err != nil {
return err
} else {
if newRelease, err := strconv.Atoi(queryResuult.TechRelease); err != nil {
return err
} else {
v.TechRelease = strconv.Itoa(newRelease + 1)
return nil
}
}
}
func (v *versionable) resolveSpLevel() error {
filter := "Name eq '" + v.Name + "' and TechRelease eq '" + v.TechRelease + "' and TechPatchLevel eq '0000' and ( DeliveryStatus eq 'R' or DeliveryStatus eq 'C' or DeliveryStatus eq 'T' or DeliveryStatus eq 'P' )"
orderBy := "TechSpLevel desc"
if queryResuult, err := v.queryVersion(filter, orderBy); err != nil {
return err
} else {
if newSpLevel, err := strconv.Atoi(queryResuult.TechSpLevel); err != nil {
return err
} else {
v.TechSpLevel = fmt.Sprintf("%04d", newSpLevel+1)
return nil
}
}
}
func (v *versionable) resolvePatchLevel() error {
filter := "Name eq '" + v.Name + "' and TechRelease eq '" + v.TechRelease + "' and TechSpLevel eq '" + v.TechSpLevel + "' and ( DeliveryStatus eq 'R' or DeliveryStatus eq 'C' or DeliveryStatus eq 'T' or DeliveryStatus eq 'P' )"
orderBy := "TechPatchLevel desc"
if queryResuult, err := v.queryVersion(filter, orderBy); err != nil {
return err
} else {
if newPatchLevel, err := strconv.Atoi(queryResuult.TechPatchLevel); err != nil {
return err
} else {
v.TechPatchLevel = fmt.Sprintf("%04d", newPatchLevel+1)
return nil
}
}
}
func (v *versionable) queryVersion(filter string, orderBy string) (*versionable, error) {
result := versionable{}
values := url.Values{}
values.Set("$filter", filter)
values.Set("$orderby", orderBy)
values.Set("$select", "Name,Version,TechRelease,TechSpLevel,TechPatchLevel")
values.Set("$format", "json")
values.Set("$top", "1")
requestUrl := v.queryUrl + "?" + values.Encode()
if body, err := v.connector.Get(requestUrl); err != nil {
return &result, err
} else {
Versions := versionables{}
if err := json.Unmarshal(body, &Versions); err != nil {
return &result, errors.Wrap(err, "Unexpected AAKaaS response for Component Version Query: "+string(body))
}
switch len(Versions.Wrapper.Vs) {
case 0:
result = versionable{
TechRelease: "0",
TechSpLevel: "0000",
TechPatchLevel: "0000",
}
case 1:
result = Versions.Wrapper.Vs[0]
default:
return &result, errors.New("Unexpected Number of CVs in result: " + fmt.Sprint(len(Versions.Wrapper.Vs)))
}
}
return &result, nil
}
func (v *versionable) getDottedVersionString() (string, error) {
var spLevelAsnumber int
var patchLevelAsNumber int
var err error
if spLevelAsnumber, err = strconv.Atoi(v.TechSpLevel); err != nil {
return "", err
}
if patchLevelAsNumber, err = strconv.Atoi(v.TechPatchLevel); err != nil {
return "", err
}
dottedVersionString := strings.Join([]string{v.TechRelease, strconv.Itoa(spLevelAsnumber), strconv.Itoa(patchLevelAsNumber)}, ".")
return dottedVersionString, nil
}

View File

@ -0,0 +1,131 @@
package aakaas
import (
"testing"
abapbuild "github.com/SAP/jenkins-library/pkg/abap/build"
"github.com/stretchr/testify/assert"
)
func TestCvResolve(t *testing.T) {
//arrange
conn := new(abapbuild.Connector)
mc := abapbuild.NewMockClient()
conn.Client = &mc
vers := versionable{}
t.Run("Factory Success", func(t *testing.T) {
//act
err := vers.constructVersionable("DummyComp", "1.2.3", *conn, "")
//assert
assert.NoError(t, err)
assert.Equal(t, "DummyComp", vers.Name)
assert.Equal(t, "1", vers.TechRelease)
assert.Equal(t, "0002", vers.TechSpLevel)
assert.Equal(t, "0003", vers.TechPatchLevel)
assert.Equal(t, "1.2.3", vers.Version)
})
t.Run("Factory No Name", func(t *testing.T) {
err := vers.constructVersionable("", "1.2.3", *conn, "")
assert.Error(t, err)
})
t.Run("Factory Version too long", func(t *testing.T) {
err := vers.constructVersionable("DummyComp", "1.0.0.0", *conn, "")
assert.Error(t, err)
})
t.Run("Factory Version too short", func(t *testing.T) {
err := vers.constructVersionable("DummyComp", "1.0", *conn, "")
assert.Error(t, err)
})
t.Run("ComponentVersion NEXT Release Existing", func(t *testing.T) {
mc.AddData(testDataAakaasCVGetReleaseExisting)
err := vers.constructVersionable("DummyComp", wildCard+".0.0", *conn, cvQueryURL)
assert.NoError(t, err)
err = vers.resolveNext()
assert.NoError(t, err)
assert.Equal(t, "2", vers.TechRelease)
assert.Equal(t, "0000", vers.TechSpLevel)
assert.Equal(t, "0000", vers.TechPatchLevel)
assert.Equal(t, "2.0.0", vers.Version)
})
t.Run("ComponentVersion NEXT Release Non Existing", func(t *testing.T) {
mc.AddData(testDataAakaasCVGetReleaseNonExisting)
err := vers.constructVersionable("DummyComp", wildCard+".0.0", *conn, cvQueryURL)
assert.NoError(t, err)
err = vers.resolveNext()
assert.NoError(t, err)
assert.Equal(t, "1", vers.TechRelease)
assert.Equal(t, "0000", vers.TechSpLevel)
assert.Equal(t, "0000", vers.TechPatchLevel)
assert.Equal(t, "1.0.0", vers.Version)
})
t.Run("ComponentVersion NEXT SP Level Existing", func(t *testing.T) {
mc.AddData(testDataAakaasCVGetSpLevelExisting)
err := vers.constructVersionable("DummyComp", "1."+wildCard+".0", *conn, cvQueryURL)
assert.NoError(t, err)
err = vers.resolveNext()
assert.NoError(t, err)
assert.Equal(t, "1", vers.TechRelease)
assert.Equal(t, "0008", vers.TechSpLevel)
assert.Equal(t, "0000", vers.TechPatchLevel)
assert.Equal(t, "1.8.0", vers.Version)
})
t.Run("ComponentVersion NEXT SP Level Non Existing", func(t *testing.T) {
//This one should lead to an error later on as AOI is needed - anyway we can't just produce a differen package then customized...
mc.AddData(testDataAakaasCVGetSpLevelNonExisting)
err := vers.constructVersionable("DummyComp", "1."+wildCard+".0", *conn, cvQueryURL)
assert.NoError(t, err)
err = vers.resolveNext()
assert.NoError(t, err)
assert.Equal(t, "1", vers.TechRelease)
assert.Equal(t, "0001", vers.TechSpLevel)
assert.Equal(t, "0000", vers.TechPatchLevel)
assert.Equal(t, "1.1.0", vers.Version)
})
t.Run("ComponentVersion NEXT Patch Level Existing", func(t *testing.T) {
mc.AddData(testDataAakaasCVGetPatchLevelExisting)
err := vers.constructVersionable("DummyComp", "1.3."+wildCard, *conn, cvQueryURL)
assert.NoError(t, err)
err = vers.resolveNext()
assert.NoError(t, err)
assert.Equal(t, "1", vers.TechRelease)
assert.Equal(t, "0003", vers.TechSpLevel)
assert.Equal(t, "0047", vers.TechPatchLevel)
assert.Equal(t, "1.3.47", vers.Version)
})
t.Run("ComponentVersion NEXT Patch Level Non Existing", func(t *testing.T) {
//This one should lead to an error later on as AOI is needed - anyway we can't just produce a differen package then customized...
mc.AddData(testDataAakaasCVGetPatchLevelNonExisting)
err := vers.constructVersionable("DummyComp", "1.3."+wildCard, *conn, cvQueryURL)
assert.NoError(t, err)
err = vers.resolveNext()
assert.NoError(t, err)
assert.Equal(t, "1", vers.TechRelease)
assert.Equal(t, "0003", vers.TechSpLevel)
assert.Equal(t, "0001", vers.TechPatchLevel)
assert.Equal(t, "1.3.1", vers.Version)
})
t.Run("Product Version NEXT Release Existing", func(t *testing.T) {
mc.AddData(testDataAakaasPVGetReleaseExisting)
err := vers.constructVersionable("DummyProd", wildCard+".0.0", *conn, pvQueryURL)
assert.NoError(t, err)
err = vers.resolveNext()
assert.NoError(t, err)
assert.Equal(t, "2", vers.TechRelease)
assert.Equal(t, "0000", vers.TechSpLevel)
assert.Equal(t, "0000", vers.TechPatchLevel)
assert.Equal(t, "2.0.0", vers.Version)
})
t.Run("Product Version NEXT Release Non Existing", func(t *testing.T) {
mc.AddData(testDataAakaasPVGetReleaseNonExisting)
err := vers.constructVersionable("DummyProd", wildCard+".0.0", *conn, pvQueryURL)
assert.NoError(t, err)
err = vers.resolveNext()
assert.NoError(t, err)
assert.Equal(t, "1", vers.TechRelease)
assert.Equal(t, "0000", vers.TechSpLevel)
assert.Equal(t, "0000", vers.TechPatchLevel)
assert.Equal(t, "1.0.0", vers.Version)
})
}