diff --git a/netbox/client.go b/netbox/client.go index 194079a2cbebf2eac8414ee5895dcac28b69d4be..f1274d0d540298fa51da335645297d5089a548b2 100644 --- a/netbox/client.go +++ b/netbox/client.go @@ -69,7 +69,7 @@ func NewClient(addr string, client *http.Client) (*Client, error) { client: client, } - c.DCIM = &DCIMService{c: c} + c.DCIM = NewDCIMService(c) c.IPAM = &IPAMService{c: c} c.Tenancy = NewTenancyService(c) diff --git a/netbox/dcim.go b/netbox/dcim.go index a7f4dffd91eef822c77d72c6a028753fd08aec37..0f955bd01e51c7197122fadd79123d353df3ecf6 100644 --- a/netbox/dcim.go +++ b/netbox/dcim.go @@ -16,7 +16,18 @@ package netbox // A DCIMService is used in a Client to access NetBox's DCIM API methods. type DCIMService struct { - c *Client + c *Client + InventoryItems *InventoryItemsService +} + +// NewDCIMService returns a DCIMService initialized with all sub-services. +func NewDCIMService(client *Client) *DCIMService { + return &DCIMService{ + c: client, + InventoryItems: &InventoryItemsService{ + c: client, + }, + } } // SimpleIdentifier represents a simple object that consists of only an ID, diff --git a/netbox/dcim_inventory-items.go b/netbox/dcim_inventory-items.go new file mode 100644 index 0000000000000000000000000000000000000000..73a9ff4eaa92be178e1f01d4275ca27c61dd264e --- /dev/null +++ b/netbox/dcim_inventory-items.go @@ -0,0 +1,118 @@ +// Copyright 2017 The go-netbox Authors. +// +// 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. + +// Code generated by generate_functions.go. DO NOT EDIT. + +package netbox + +import ( + "encoding/json" + "fmt" + "net/http" +) + +// InventoryItemsService is used in a Client to access NetBox's dcim/inventory-items API methods. +type InventoryItemsService struct { + c *Client +} + +// Get retrieves an InventoryItem object from NetBox by its ID. +func (s *InventoryItemsService) Get(id int) (*InventoryItem, error) { + req, err := s.c.NewRequest( + http.MethodGet, + fmt.Sprintf("api/dcim/inventory-items/%d/", id), + nil, + ) + if err != nil { + return nil, err + } + + t := new(InventoryItem) + err = s.c.Do(req, t) + if err != nil { + return nil, err + } + return t, nil +} + +// List returns a Page associated with an NetBox API Endpoint. +func (s *InventoryItemsService) List(options *ListInventoryItemOptions) *Page { + return NewPage(s.c, "api/dcim/inventory-items/", options) +} + +// Extract retrives a list of InventoryItem objects from page. +func (s *InventoryItemsService) Extract(page *Page) ([]*InventoryItem, error) { + if err := page.Err(); err != nil { + return nil, err + } + + var groups []*InventoryItem + if err := json.Unmarshal(page.data.Results, &groups); err != nil { + return nil, err + } + return groups, nil +} + +// Create creates a new InventoryItem object in NetBox and returns the ID of the new object. +func (s *InventoryItemsService) Create(data *InventoryItem) (int, error) { + req, err := s.c.NewJSONRequest(http.MethodPost, "api/dcim/inventory-items/", nil, data) + if err != nil { + return 0, err + } + + g := new(writableInventoryItem) + err = s.c.Do(req, g) + if err != nil { + return 0, err + } + return g.ID, nil +} + +// Update changes an existing InventoryItem object in NetBox, and returns the ID of the new object. +func (s *InventoryItemsService) Update(data *InventoryItem) (int, error) { + req, err := s.c.NewJSONRequest( + http.MethodPatch, + fmt.Sprintf("api/dcim/inventory-items/%d/", data.ID), + nil, + data, + ) + if err != nil { + return 0, err + } + + // g is just used to verify correct api result. + // data is not changed, because the g is not the full representation that one would + // get with Get. But if the response was unmarshaled into writableInventoryItem correctly, + // everything went fine, and we do not need to update data. + g := new(writableInventoryItem) + err = s.c.Do(req, g) + if err != nil { + return 0, err + } + return g.ID, nil +} + +// Delete deletes an existing InventoryItem object from NetBox. +func (s *InventoryItemsService) Delete(data *InventoryItem) error { + req, err := s.c.NewRequest( + http.MethodDelete, + fmt.Sprintf("api/dcim/inventory-items/%d/", data.ID), + nil, + ) + if err != nil { + return err + } + + return s.c.Do(req, nil) +} diff --git a/netbox/dcim_inventory-items_basic_test.go b/netbox/dcim_inventory-items_basic_test.go new file mode 100644 index 0000000000000000000000000000000000000000..fbf62e8f9a7c3e5924fb019d56b0eabf57181022 --- /dev/null +++ b/netbox/dcim_inventory-items_basic_test.go @@ -0,0 +1,299 @@ +// Copyright 2017 The go-netbox Authors. +// +// 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. + +// Code generated by generate_basic_tests.go. DO NOT EDIT. + +package netbox + +import ( + "encoding/json" + "errors" + "fmt" + "net/http" + "reflect" + "testing" +) + +// Using this to override MarshalJSON +// In all cases when posting data to netbox-API, the InventoryItem.MarshalJSON is what you want, +// but not here as a return in testHandler +type serverDataInventoryItem InventoryItem + +func convertToServerDataInventoryItem(data []*InventoryItem) []*serverDataInventoryItem { + dataWant := make([]*serverDataInventoryItem, len(data)) + for i := range data { + tmp := serverDataInventoryItem(*data[i]) + dataWant[i] = &tmp + } + return dataWant +} + +func TestBasicInventoryItemGet(t *testing.T) { + var tests = []struct { + desc string + want *InventoryItem + }{ + { + desc: "Simple InventoryItem", + want: testInventoryItem(1), + }, + } + + for i, tt := range tests { + t.Run(fmt.Sprintf("[%d] %s", i, tt.desc), func(t *testing.T) { + serverData := serverDataInventoryItem(*tt.want) + + c, done := testClient(t, testHandler(t, http.MethodGet, "/api/dcim/inventory-items/1/", &serverData)) + defer done() + + res, err := c.DCIM.InventoryItems.Get(1) + if err != nil { + t.Fatalf("unexpected error from Client.DCIM.InventoryItems.Get: %v", err) + } + + if want, got := tt.want, res; !reflect.DeepEqual(want, got) { + t.Fatalf("unexpected InventoryItem:\n- want: %v\n- got: %v", want, got) + } + }) + } +} + +func TestBasicInventoryItemGet404(t *testing.T) { + c, done := testClient(t, testStatusHandler(t, http.MethodGet, "/api/dcim/inventory-items/1/", &struct { + Detail string `json:"detail"` + }{ + Detail: "Not found.", + }, + http.StatusNotFound)) + defer done() + + res, err := c.DCIM.InventoryItems.Get(1) + errstr := "404 - Not found." + if want, got := errors.New(errstr), err; !reflect.DeepEqual(want, got) { + t.Fatalf("unexpected error from Client.DCIM.InventoryItems.Get:\n- want: %v\n- got: %v", want, got) + } + + if res != nil { + t.Fatalf("unexpected result:\n- want: %v\n- got: %v", nil, res) + } +} + +func TestBasicListExtractInventoryItem(t *testing.T) { + want := []*InventoryItem{ + testInventoryItem(1), + testInventoryItem(2), + } + serverWant := convertToServerDataInventoryItem(want) + serverData, _ := json.Marshal(serverWant) + c, done := testClient(t, testHandler(t, http.MethodGet, "/api/dcim/inventory-items/", &pageData{ + Count: 2, + NextURL: "", + PreviousURL: "", + Results: serverData, + })) + defer done() + + page := c.DCIM.InventoryItems.List(nil) + + if page == nil { + t.Fatalf("unexpexted result from c.DCIM.InventoryItems.List.") + } + + got := []*InventoryItem{} + counter := 0 + for page.Next() { + var err error + got, err = c.DCIM.InventoryItems.Extract(page) + if err != nil { + t.Fatalf("unexpected error from c.DCIM.InventoryItems.Extract: %v", err) + } + counter = counter + 1 + if counter > 2 { // Safe guard + break + } + } + if counter != 1 { + t.Fatalf("unexpected page count:\n- want: 1\n- got: %d", counter) + } + + if !reflect.DeepEqual(want, got) { + t.Fatalf("unexpected result:\n- want: %v\n- got: %v", want, got) + } + + if page.Err() != nil { + t.Fatalf("unexpected error from page:\n- want: %v\n- got: %v", want, got) + } +} + +func TestBasicCreateInventoryItem(t *testing.T) { + var tests = []struct { + desc string + data *InventoryItem + want int + serverData interface{} + status int + errstr string + }{ + { + desc: "Create with ID 0", + data: testInventoryItemCreate(1), + want: 1, + status: 0, + errstr: "", + serverData: testInventoryItem(1), + }, + { + desc: "Create duplicate", + data: testInventoryItemCreate(1), + want: 0, + status: http.StatusBadRequest, + errstr: "400 - {\"name\":[\"InventoryItemsService with this name already exists.\"]}\n", + serverData: &struct { + Name []string `json:"name"` + }{ + Name: []string{"InventoryItemsService with this name already exists."}, + }, + }, + } + + for i, tt := range tests { + t.Run(fmt.Sprintf("[%d] %s", i, tt.desc), func(t *testing.T) { + c, done := testClient(t, testStatusHandler(t, http.MethodPost, "/api/dcim/inventory-items/", tt.serverData, tt.status)) + defer done() + + var terr error + if tt.errstr != "" { + terr = errors.New(tt.errstr) // Using errstr and initialize real err here, to satisfy golint + } + + res, err := c.DCIM.InventoryItems.Create(tt.data) + if want, got := terr, err; !reflect.DeepEqual(want, got) { + t.Fatalf("unexpected error:\n- want: %v\n- got: %v", want, got) + } + if want, got := tt.want, res; !reflect.DeepEqual(want, got) { + t.Fatalf("unexpected InventoryItem:\n- want: %v\n- got: %v", want, got) + } + }) + } +} + +func TestBasicUpdateInventoryItem(t *testing.T) { + var tests = []struct { + desc string + data *InventoryItem + want int + serverData interface{} + status int + errstr string + }{ + { + desc: "Update with ID 1", + data: testInventoryItem(1), + want: 1, + serverData: testInventoryItem(1), + status: 0, + errstr: "", + }, + { + desc: "Update not found", + data: testInventoryItem(1), + want: 0, + serverData: &struct { + Detail string + }{ + Detail: "Not found.", + }, + status: http.StatusNotFound, + errstr: "404 - Not found.", + }, + { + desc: "Update to duplicate", + data: testInventoryItem(1), + want: 0, + serverData: &struct { + Name []string `json:"name"` + }{ + Name: []string{"InventoryItemsService with this name already exists."}, + }, + status: http.StatusBadRequest, + errstr: "400 - {\"name\":[\"InventoryItemsService with this name already exists.\"]}\n", + }, + } + + for i, tt := range tests { + t.Run(fmt.Sprintf("[%d] %s", i, tt.desc), func(t *testing.T) { + c, done := testClient(t, testStatusHandler(t, http.MethodPatch, "/api/dcim/inventory-items/1/", tt.serverData, tt.status)) + defer done() + + var terr error + if tt.errstr != "" { + terr = errors.New(tt.errstr) // Using errstr and initialize real err here, to satisfy golint + } + + res, err := c.DCIM.InventoryItems.Update(tt.data) + if want, got := terr, err; !reflect.DeepEqual(want, got) { + t.Fatalf("unexpected error:\n- want: %v\n- got: %v", want, got) + } + if want, got := tt.want, res; !reflect.DeepEqual(want, got) { + t.Fatalf("unexpected InventoryItem:\n- want: %v\n- got: %v", want, got) + } + }) + } +} + +func TestBasicDeleteInventoryItem(t *testing.T) { + var tests = []struct { + desc string + data *InventoryItem + serverData interface{} + status int + errstr string + }{ + { + desc: "Delete ID 1", + data: testInventoryItem(1), + serverData: testInventoryItem(1), + status: 0, + errstr: "", + }, + { + desc: "Delete not Found", + data: testInventoryItem(1), + serverData: &struct { + Detail string `json:"detail"` + }{ + Detail: "Not found.", + }, + status: http.StatusNotFound, + errstr: "404 - Not found.", + }, + } + + for i, tt := range tests { + t.Run(fmt.Sprintf("[%d] %s", i, tt.desc), func(t *testing.T) { + c, done := testClient(t, testStatusHandler(t, http.MethodDelete, "/api/dcim/inventory-items/1/", tt.serverData, tt.status)) + defer done() + + var terr error + if tt.errstr != "" { + terr = errors.New(tt.errstr) // Using errstr and initialize real err here, to satisfy golint + } + + err := c.DCIM.InventoryItems.Delete(tt.data) + if want, got := terr, err; !reflect.DeepEqual(want, got) { + t.Fatalf("unexpected error:\n- want: %v\n- got: %v", want, got) + } + }) + } +} diff --git a/netbox/dcim_inventory-items_test.go b/netbox/dcim_inventory-items_test.go new file mode 100644 index 0000000000000000000000000000000000000000..527a6d9b33c26760b098cc9cec191b3598d7c122 --- /dev/null +++ b/netbox/dcim_inventory-items_test.go @@ -0,0 +1,294 @@ +// Copyright 2017 The go-netbox Authors. +// +// 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 netbox + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + "net/url" + "reflect" + "testing" +) + +func TestInventoryItemGet(t *testing.T) { + var tests = []struct { + desc string + want *InventoryItem + wantManufacturer *NestedManufacturer + }{ + { + desc: "InventoryItem without Manufacturer", + want: testInventoryItem(1), + }, + { + desc: "InventoryItem with Manufacturer", + want: testInventoryItemWithManufacturer(1), + }, + } + + for idx, ii := range tests { + t.Run(fmt.Sprintf("[%d] %s", idx, ii.desc), func(t *testing.T) { + serverData := serverDataInventoryItem(*ii.want) + + c, done := testClient(t, testHandler(t, http.MethodGet, "/api/dcim/inventory-items/1/", &serverData)) + defer done() + + res, err := c.DCIM.InventoryItems.Get(1) + if err != nil { + t.Fatalf("unexpected error from c.DCIM.InventoryItems.Get: %v", err) + } + + if want, got := ii.want, res; !reflect.DeepEqual(want, got) { + t.Fatalf("unexpected InventoryItem\n- want: %v\n- got: %v", want, got) + } + }) + } +} + +func TestInventoryItemUnmarshalJSON(t *testing.T) { + parentID := 4 + var tests = []struct { + desc string + data []byte + want *InventoryItem + }{ + { + desc: "Minimal inventory item", + data: []byte(`{ "id": 4, "device": { "id": 1, "url": "http://localhost:8000/api/dcim/devices/1/", "name": "Test Device 1", "display_name": "Test Device 1"}, "parent": null, "name": "device name", "manufacturer": null }`), + want: &InventoryItem{ + ID: 4, + Device: NestedDevice{ + ID: 1, + URL: "http://localhost:8000/api/dcim/devices/1/", + Name: "Test Device 1", + DisplayName: "Test Device 1", + }, + Name: "device name", + Parent: nil, + Manufacturer: nil, + }, + }, + { + desc: "Maximal inventory item", + data: []byte(`{ "id": 2, "device": { "id": 1, "url": "http://localhost:8000/api/dcim/devices/1/", "name": "Test Device 1", "display_name": "Test Device 1" }, "parent": 4, "name": "the device name", "manufacturer": { "id": 10, "url": "http://localhost:8000/api/dcim/manufacturers/10/", "name": "manufacturer name", "slug": "mfg-name" }, "part_id": "the part ID", "serial": "the serial", "discovered": true }`), + want: &InventoryItem{ + ID: 2, + Device: NestedDevice{ + ID: 1, + URL: "http://localhost:8000/api/dcim/devices/1/", + Name: "Test Device 1", + DisplayName: "Test Device 1", + }, + Parent: &parentID, + Name: "the device name", + Manufacturer: &NestedManufacturer{ + ID: 10, + URL: "http://localhost:8000/api/dcim/manufacturers/10/", + Name: "manufacturer name", + Slug: "mfg-name", + }, + PartID: "the part ID", + Serial: "the serial", + Discovered: true, + }, + }, + } + + for idx, ii := range tests { + t.Run(fmt.Sprintf("[%d] %s", idx, ii.desc), func(t *testing.T) { + result := new(InventoryItem) + err := json.Unmarshal(ii.data, result) + if err != nil { + t.Fatalf("unexpected error from InventoryItem.UnmarshalJSON: %v", err) + } + + if want, got := ii.want, result; !reflect.DeepEqual(want, got) { + t.Fatalf("unexpected InventoryItem:\n- want: %v\n- got: %v", want, got) + } + }) + } +} + +func TestInventoryItemMarshalJSON(t *testing.T) { + var tests = []struct { + desc string + data *InventoryItem + want []byte + }{ + { + desc: "Inventory item without manufacturer", + data: testInventoryItem(1), + want: []byte(`{"id":1,"device":10001,"name":"Inventory Item 1","part_id":"Part ID 1","serial":"Serial 1","discovered":true}`), + }, + { + desc: "Inventory item with manufacturer", + data: testInventoryItemWithManufacturer(2), + want: []byte(`{"device":10002,"name":"Inventory Item 2","manufacturer":20002,"part_id":"Part ID 2","serial":"Serial 2","discovered":true}`), + }, + } + + for idx, ii := range tests { + t.Run(fmt.Sprintf("[%d] %s", idx, ii.desc), func(t *testing.T) { + result, err := json.Marshal(ii.data) + if err != nil { + t.Fatalf("unexpected error from writableInventoryItem.MarshalJSON: %v", err) + } + + if want, got := ii.want, result; bytes.Compare(want, got) != 0 { + t.Fatalf("unexpected JSON:\n- want: %v\n- got: %v", string(want), string(got)) + } + }) + } +} + +func TestWritableInventoryItemUnmarshalJSON(t *testing.T) { + parentID := 4 + manufacturerID := 6 + var tests = []struct { + desc string + data []byte + want *writableInventoryItem + }{ + { + desc: "Minimal inventory item", + data: []byte(`{ "device": 1, "parent": null, "name": "device name" }`), + want: &writableInventoryItem{Device: 1, Name: "device name"}, + }, + { + desc: "Maximal inventory item", + data: []byte(`{ "id": 2, "device": 3, "parent": 4, "name": "the device name", "manufacturer": 6, "part_id": "the part ID", "serial": "the serial", "discovered": true }`), + want: &writableInventoryItem{ + ID: 2, + Device: 3, + Parent: &parentID, + Name: "the device name", + Manufacturer: &manufacturerID, + PartID: "the part ID", + Serial: "the serial", + Discovered: true, + }, + }, + } + + for idx, ii := range tests { + t.Run(fmt.Sprintf("[%d] %s", idx, ii.desc), func(t *testing.T) { + result := new(writableInventoryItem) + err := json.Unmarshal(ii.data, result) + if err != nil { + t.Fatalf("unexpected error from writableInventoryItem.UnmarshalJSON: %v", err) + } + + if want, got := ii.want, result; !reflect.DeepEqual(want, got) { + t.Fatalf("unexpected writableInventoryItem:\n- want: %v\n- got: %v", want, got) + } + }) + } +} + +func TestListInventoryItemOptions(t *testing.T) { + var tests = []struct { + desc string + o *ListInventoryItemOptions + v url.Values + }{ + { + desc: "empty options", + }, + { + desc: "full options", + o: &ListInventoryItemOptions{ + Name: "Hello", + DeviceID: 1, + Device: "node_name_1", + }, + v: url.Values{ + "name": []string{"Hello"}, + "device_id": []string{"1"}, + }, + }, + { + desc: "device name", + o: &ListInventoryItemOptions{ + Name: "Hello", + Device: "node_name_1", + }, + v: url.Values{ + "name": []string{"Hello"}, + "device": []string{"node_name_1"}, + }, + }, + } + + for i, tt := range tests { + t.Run(fmt.Sprintf("[%d] %s", i, tt.desc), func(t *testing.T) { + v, err := tt.o.Values() + if err != nil { + t.Fatalf("unexpected Values error: %v", err) + } + + if want, got := tt.v, v; !reflect.DeepEqual(want, got) { + t.Fatalf("unexpected url.Values map:\n- want: %v\n- got: %v", want, got) + } + }) + } +} + +func testInventoryItem(id int) *InventoryItem { + return testInventoryItemHelper(id, false, nil) +} + +func testInventoryItemCreate(id int) *InventoryItem { + return testInventoryItemHelper(id, true, nil) +} + +func testInventoryItemWithManufacturer(id int) *InventoryItem { + return testInventoryItemHelper(id, true, testNestedManufacturer(id+20000)) +} + +func testNestedManufacturer(id int) *NestedManufacturer { + return &NestedManufacturer{ + ID: id, + URL: fmt.Sprintf("http://localhost:8000/api/dcim/manufacturers/%d/", id), + Name: fmt.Sprintf("Test nested manufacturer %d", id), + Slug: fmt.Sprintf("test-nested-manufacturer-%d", id), + } +} + +func testInventoryItemHelper(id int, create bool, manufacturer *NestedManufacturer) *InventoryItem { + deviceID := id + 10000 + device := NestedDevice{ + ID: deviceID, + URL: fmt.Sprintf("http://example.host/api/dcim/devices/%d/", deviceID), + Name: fmt.Sprintf("Device %d", deviceID), + DisplayName: fmt.Sprintf("Display Device %d", deviceID), + } + + itemID := id + if create { + itemID = 0 + } + return &InventoryItem{ + ID: itemID, + Device: device, + Parent: nil, + Name: fmt.Sprintf("Inventory Item %d", id), + Manufacturer: manufacturer, + PartID: fmt.Sprintf("Part ID %d", id), + Serial: fmt.Sprintf("Serial %d", id), + Discovered: true, + } +} diff --git a/netbox/dcim_inventory-items_types.go b/netbox/dcim_inventory-items_types.go new file mode 100644 index 0000000000000000000000000000000000000000..0df73921ed687dc53355c8b531ea57fc371efbcf --- /dev/null +++ b/netbox/dcim_inventory-items_types.go @@ -0,0 +1,123 @@ +// Copyright 2017 The go-netbox Authors. +// +// 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 netbox + +import ( + "encoding/json" + "net/url" + "strconv" +) + +// A NestedManufacturer corresponds to the Netbox API's +// nested serializer, when a manufacturer appears as a +// parent of another object. +// +// https://netbox.readthedocs.io/en/stable/api/overview/#serialization +type NestedManufacturer struct { + ID int `json:"id"` + URL string `json:"url"` + Name string `json:"name"` + Slug string `json:"slug"` +} + +// A NestedDevice corresponds to the Netbox API's +// nested serializer, when a device appears as a +// parent of another object. +type NestedDevice struct { + ID int `json:"id"` + URL string `json:"url"` + Name string `json:"name"` + DisplayName string `json:"display_name"` +} + +// An InventoryItem corresponds to the Netbox API's +// base serializer for Inventory Items. +type InventoryItem struct { + ID int `json:"id,omitempty"` + Device NestedDevice `json:"device"` + Parent *int `json:"parent,omitempty"` + Name string `json:"name"` + Manufacturer *NestedManufacturer `json:"manufacturer,omitempty"` + PartID string `json:"part_id,omitempty"` + Serial string `json:"serial,omitempty"` + Discovered bool `json:"discovered,omitempty"` +} + +// A writableInventoryItem corresponds to the Netbox API's +// writable serializer for an InventoryItem. It is used transparently +// when IventoryItems are serialized in to JSON. +type writableInventoryItem struct { + ID int `json:"id,omitempty"` + Device int `json:"device"` + Parent *int `json:"parent,omitempty"` + Name string `json:"name"` + Manufacturer *int `json:"manufacturer,omitempty"` + PartID string `json:"part_id,omitempty"` + Serial string `json:"serial,omitempty"` + Discovered bool `json:"discovered,omitempty"` +} + +// MarshalJSON marshals an InventoryItem into JSON bytes, +// and is used by the standard json package. +func (i *InventoryItem) MarshalJSON() ([]byte, error) { + var manufacturerID *int + if i.Manufacturer != nil { + manufacturerID = &i.Manufacturer.ID + } + return json.Marshal(writableInventoryItem{ + ID: i.ID, + Device: i.Device.ID, + Parent: i.Parent, + Name: i.Name, + Manufacturer: manufacturerID, + PartID: i.PartID, + Serial: i.Serial, + Discovered: i.Discovered, + }) +} + +// ListInventoryItemOptions is used as an argument for Client.DCIM.InventoryItems.List. +// Integer fields with an *ID suffix are preferred over their string +// counterparts, and if both are set, only the *ID field will be used. +type ListInventoryItemOptions struct { + Name string + DeviceID int + Device string +} + +// Values generates a url.Values map from the data in ListTenantOptions. +func (o *ListInventoryItemOptions) Values() (url.Values, error) { + if o == nil { + return nil, nil + } + + v := url.Values{} + + if o.Name != "" { + v.Set("name", o.Name) + } + + switch { + case o.DeviceID != 0: + v.Set("device_id", strconv.Itoa(o.DeviceID)) + case o.Device != "": + v.Set("device", o.Device) + } + + return v, nil +} + +//go:generate go run generate_functions.go -type-name InventoryItem -update-type-name writableInventoryItem -service-name InventoryItemsService -endpoint dcim -service inventory-items +//go:generate go run generate_basic_tests.go -type-name InventoryItem -service-name InventoryItemsService -endpoint dcim -service inventory-items -client-endpoint DCIM -client-service InventoryItems