Skip to content

Commit

Permalink
FS-899 Add bom router handlers and client APIs to fetch bom by bmcMac…
Browse files Browse the repository at this point in the history
…Addree (#244)

Add bom router handlers and client APIs to fetch bom by bmcMacAddree
  • Loading branch information
Alva8756 authored Sep 8, 2023
1 parent 775a5de commit 9089164
Show file tree
Hide file tree
Showing 6 changed files with 250 additions and 1 deletion.
26 changes: 26 additions & 0 deletions pkg/api/v1/bom.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ type AocMacAddressBom struct {
SerialNum string `json:"serial_num"`
}

// BmcMacAddressBom provides a struct to map the bmc_mac_address table.
type BmcMacAddressBom struct {
BmcMacAddress string `json:"bmc_mac_address"`
SerialNum string `json:"serial_num"`
}

// toDBModel converts Bom to BomInfo.
func (b *Bom) toDBModel() (*models.BomInfo, error) {
if b.SerialNum == "" {
Expand Down Expand Up @@ -76,3 +82,23 @@ func (b *Bom) toAocMacAddressDBModels() ([]*models.AocMacAddress, error) {

return dbAs, nil
}

// toBmcMacAddressDBModels converts Bom to one or multiple BmcMacAddress.
func (b *Bom) toBmcMacAddressDBModels() ([]*models.BMCMacAddress, error) {
if b.BmcMacAddress == "" {
return nil, errors.Errorf("the primary key bmc-mac-address can not be blank")
}

dbBs := []*models.BMCMacAddress{}

BmcMacAddrs := strings.Split(b.BmcMacAddress, ",")
for _, bmcMacAddr := range BmcMacAddrs {
dbB := &models.BMCMacAddress{
SerialNum: b.SerialNum,
BMCMacAddress: bmcMacAddr,
}
dbBs = append(dbBs, dbB)
}

return dbBs, nil
}
6 changes: 5 additions & 1 deletion pkg/api/v1/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,11 @@ func (r *Router) Routes(rg *gin.RouterGroup) {
srvBomByAocMacAddress.GET("/:aoc_mac_address", amw.RequiredScopes(readScopes("aoc-mac-address")), r.getBomFromAocMacAddress)
}

// TODO: support query by bmc-mac-address
// /bill-of-materials/bmc-mac-address
srvBomByBmcMacAddress := srvBoms.Group("/bmc-mac-address")
{
srvBomByBmcMacAddress.GET("/:bmc_mac_address", amw.RequiredScopes(readScopes("bmc-mac-address")), r.getBomFromBmcMacAddress)
}
}
}

Expand Down
41 changes: 41 additions & 0 deletions pkg/api/v1/router_bom.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,17 @@ func (r *Router) bomsUpload(c *gin.Context) {
return err
}
}

dbBmcMacAddrsBoms, err := (bom).toBmcMacAddressDBModels()
if err != nil {
return err
}

for _, dbBmcMacAddrsBom := range dbBmcMacAddrsBoms {
if err := dbBmcMacAddrsBom.Insert(c.Request.Context(), r.DB, boil.Infer()); err != nil {
return err
}
}
}
return nil
})
Expand Down Expand Up @@ -79,3 +90,33 @@ func (r *Router) getBomFromAocMacAddress(c *gin.Context) {

itemResponse(c, bom)
}

func (r *Router) getBomFromBmcMacAddress(c *gin.Context) {
mods := []qm.QueryMod{
qm.Where("bmc_mac_address=?", c.Param("bmc_mac_address")),
}

bmcMacAddr, err := models.BMCMacAddresses(mods...).One(c.Request.Context(), r.DB)
if err != nil {
dbErrorResponse(c, err)
return
}

mods = []qm.QueryMod{
qm.Where("serial_num=?", bmcMacAddr.SerialNum),
}

bomInfo, err := models.BomInfos(mods...).One(c.Request.Context(), r.DB)
if err != nil {
dbErrorResponse(c, err)
return
}

bom := Bom{}
if err = bom.fromDBModel(bomInfo); err != nil {
dbErrorResponse(c, err)
return
}

itemResponse(c, bom)
}
147 changes: 147 additions & 0 deletions pkg/api/v1/router_bom_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,31 @@ func TestIntegrationBomUpload(t *testing.T) {
aocMacAddress: "fakeAocMacAddress3",
expectedAocMacAddressError: false,
},
{
testName: "upload duplicate BmcMacAddress",
uploadBoms: []serverservice.Bom{
{
SerialNum: "fakeSerialNum1",
AocMacAddress: "fakeAocMacAddress1,fakeAocMacAddress2",
BmcMacAddress: "fakeBmcMacAddress1,fakeBmcMacAddress2",
NumDefiPmi: "fakeNumDefipmi1",
NumDefPWD: "fakeNumDefpwd1",
Metro: "fakeMetro1",
},
{
SerialNum: "fakeSerialNum2",
AocMacAddress: "fakeAocMacAddress3,fakeAocMacAddress4",
BmcMacAddress: "fakeBmcMacAddress1,fakeBmcMacAddress3",
NumDefiPmi: "fakeNumDefipmi2",
NumDefPWD: "fakeNumDefpwd2",
Metro: "fakeMetro2",
},
},
expectedUploadErr: true,
expectedUploadErrorMsg: "unable to insert into bmc_mac_address: pq: duplicate key value violates unique constraint",
aocMacAddress: "fakeBmcMacAddress3",
expectedAocMacAddressError: false,
},
{
testName: "upload empty serial number",
uploadBoms: []serverservice.Bom{
Expand Down Expand Up @@ -144,6 +169,23 @@ func TestIntegrationBomUpload(t *testing.T) {
aocMacAddress: "fakeAocMacAddress3",
expectedAocMacAddressError: false,
},
{
testName: "upload empty BmcMacAddress",
uploadBoms: []serverservice.Bom{
{
SerialNum: "fakeSerialNum1",
AocMacAddress: "fakeAocMacAddress1,fakeAocMacAddress2",
BmcMacAddress: "",
NumDefiPmi: "fakeNumDefipmi1",
NumDefPWD: "fakeNumDefpwd1",
Metro: "fakeMetro1",
},
},
expectedUploadErr: true,
expectedUploadErrorMsg: "the primary key bmc-mac-address can not be blank",
aocMacAddress: "fakeAocMacAddress3",
expectedAocMacAddressError: false,
},
}

for _, tc := range testCases {
Expand Down Expand Up @@ -325,3 +367,108 @@ func TestIntegrationGetBomByAocMacAddr(t *testing.T) {
})
}
}

func TestIntegrationGetBomByBmcMacAddr(t *testing.T) {
s := serverTest(t)

uploadBoms := []serverservice.Bom{
{
SerialNum: "fakeSerialNum1",
AocMacAddress: "fakeAocMacAddress1,fakeAocMacAddress2",
BmcMacAddress: "fakeBmcMacAddress1,fakeBmcMacAddress2",
NumDefiPmi: "fakeNumDefipmi1",
NumDefPWD: "fakeNumDefpwd1",
Metro: "fakeMetro1",
},
{
SerialNum: "fakeSerialNum2",
AocMacAddress: "fakeAocMacAddress3,fakeAocMacAddress4",
BmcMacAddress: "fakeBmcMacAddress3,fakeBmcMacAddress4",
NumDefiPmi: "fakeNumDefipmi2",
NumDefPWD: "fakeNumDefpwd2",
Metro: "fakeMetro2",
},
}
authToken := validToken(adminScopes)
s.Client.SetToken(authToken)

_, err := s.Client.BillOfMaterialsBatchUpload(context.TODO(), uploadBoms)
if err != nil {
t.Fatalf("s.Client.BillOfMaterialsBatchUpload(%v) failed to upload, err %v", uploadBoms, err)
return
}

var testCases = []struct {
testName string
bmcMacAddress string
expectedBom serverservice.Bom
expectedBmcMacAddressError bool
expectedBmcMacAddressErrorMsg string
}{
{
testName: "get first bom by first bmc mac address",
bmcMacAddress: "fakeBmcMacAddress1",
expectedBom: uploadBoms[0],
expectedBmcMacAddressError: false,
expectedBmcMacAddressErrorMsg: "",
},
{
testName: "get first bom by second bmc mac address",
bmcMacAddress: "fakeBmcMacAddress2",
expectedBom: uploadBoms[0],
expectedBmcMacAddressError: false,
expectedBmcMacAddressErrorMsg: "",
},
{
testName: "get second bom by first bmc mac address",
bmcMacAddress: "fakeBmcMacAddress3",
expectedBom: uploadBoms[1],
expectedBmcMacAddressError: false,
expectedBmcMacAddressErrorMsg: "",
},
{
testName: "get second bom by second bmc mac address",
bmcMacAddress: "fakeBmcMacAddress3",
expectedBom: uploadBoms[1],
expectedBmcMacAddressError: false,
expectedBmcMacAddressErrorMsg: "",
},
{
testName: "non-exist bmc mac address",
bmcMacAddress: "random",
expectedBom: uploadBoms[1],
expectedBmcMacAddressError: true,
expectedBmcMacAddressErrorMsg: "sql: no rows in result set",
},
{
testName: "empty bmc mac address",
bmcMacAddress: "",
expectedBom: uploadBoms[1],
expectedBmcMacAddressError: true,
expectedBmcMacAddressErrorMsg: "route not found",
},
}

for _, tc := range testCases {
t.Run(tc.testName, func(t *testing.T) {
bom, _, err := s.Client.GetBomInfoByBMCMacAddr(context.TODO(), tc.bmcMacAddress)
if tc.expectedBmcMacAddressError {
if err == nil {
t.Fatalf("GetBomInfoByBMCMacAddr(%v) expect error, got nil", tc.bmcMacAddress)
}
if !strings.Contains(err.Error(), tc.expectedBmcMacAddressErrorMsg) {
t.Fatalf("GetBomInfoByBMCMacAddr(%v) expect error %v, got %v", tc.bmcMacAddress, tc.expectedBmcMacAddressErrorMsg, err)
}
return
}
if err != nil {
t.Fatalf("GetBomInfoByBMCMacAddr(%v) failed to upload, err %v", tc.bmcMacAddress, err)
return
}

if !reflect.DeepEqual(bom, &tc.expectedBom) {
t.Fatalf("got incorrect bom %v, expect %v", bom, tc.expectedBom)
}
})
}
}
15 changes: 15 additions & 0 deletions pkg/api/v1/server_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const (
bomInfoEndpoint = "bill-of-materials"
uploadFileEndpoint = "batch-upload"
bomByMacAOCAddressEndpoint = "aoc-mac-address"
bomByMacBMCAddressEndpoint = "bmc-mac-address"
)

// ClientInterface provides an interface for the expected calls to interact with a server service api
Expand Down Expand Up @@ -59,6 +60,7 @@ type ClientInterface interface {
ListServerCredentialTypes(context.Context) (*ServerResponse, error)
BillOfMaterialsBatchUpload(context.Context, []Bom) (*ServerResponse, error)
GetBomInfoByAOCMacAddr(context.Context, string) (*Bom, *ServerResponse, error)
GetBomInfoByBMCMacAddr(context.Context, string) (*Bom, *ServerResponse, error)
}

// Create will attempt to create a server in Hollow and return the new server's UUID
Expand Down Expand Up @@ -412,3 +414,16 @@ func (c *Client) GetBomInfoByAOCMacAddr(ctx context.Context, aocMacAddr string)

return bom, &r, nil
}

// GetBomInfoByBMCMacAddr will return the bom info object by the bmc mac address.
func (c *Client) GetBomInfoByBMCMacAddr(ctx context.Context, bmcMacAddr string) (*Bom, *ServerResponse, error) {
path := fmt.Sprintf("%s/%s/%s", bomInfoEndpoint, bomByMacBMCAddressEndpoint, bmcMacAddr)
bom := &Bom{}
r := ServerResponse{Record: bom}

if err := c.get(ctx, path, &r); err != nil {
return nil, nil, err
}

return bom, &r, nil
}
16 changes: 16 additions & 0 deletions pkg/api/v1/server_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -381,3 +381,19 @@ func TestGetBomInfoByAOCMacAddr(t *testing.T) {
return err
})
}

func TestGetBomInfoByBMCMacAddr(t *testing.T) {
mockClientTests(t, func(ctx context.Context, respCode int, expectError bool) error {
bom := hollow.Bom{SerialNum: "fakeSerialNum1", AocMacAddress: "fakeAocMacAddress1", BmcMacAddress: "fakeBmcMacAddress1"}
jsonResponse, err := json.Marshal(hollow.ServerResponse{Record: bom})
require.Nil(t, err)

c := mockClient(string(jsonResponse), respCode)
respBom, _, err := c.GetBomInfoByBMCMacAddr(ctx, "fakeBmcMacAddress1")
if !expectError {
assert.Equal(t, &bom, respBom)
}

return err
})
}

0 comments on commit 9089164

Please sign in to comment.