From d4f2b69b974630dd58c0f75232174fcffdee64a3 Mon Sep 17 00:00:00 2001 From: gracewilcox Date: Fri, 26 Jan 2024 14:56:55 -0800 Subject: [PATCH 1/8] move code --- sdk/monitor/query/azmetrics/autorest.md | 76 ++++ sdk/monitor/query/azmetrics/build.go | 10 + sdk/monitor/query/azmetrics/client.go | 113 ++++++ sdk/monitor/query/azmetrics/constants.go | 61 ++++ sdk/monitor/query/azmetrics/go.mod | 11 + sdk/monitor/query/azmetrics/go.sum | 12 + sdk/monitor/query/azmetrics/models.go | 126 +++++++ sdk/monitor/query/azmetrics/models_serde.go | 336 ++++++++++++++++++ sdk/monitor/query/azmetrics/options.go | 55 +++ sdk/monitor/query/azmetrics/response_types.go | 15 + sdk/monitor/query/azmetrics/time_rfc3339.go | 86 +++++ 11 files changed, 901 insertions(+) create mode 100644 sdk/monitor/query/azmetrics/autorest.md create mode 100644 sdk/monitor/query/azmetrics/build.go create mode 100644 sdk/monitor/query/azmetrics/client.go create mode 100644 sdk/monitor/query/azmetrics/constants.go create mode 100644 sdk/monitor/query/azmetrics/go.mod create mode 100644 sdk/monitor/query/azmetrics/go.sum create mode 100644 sdk/monitor/query/azmetrics/models.go create mode 100644 sdk/monitor/query/azmetrics/models_serde.go create mode 100644 sdk/monitor/query/azmetrics/options.go create mode 100644 sdk/monitor/query/azmetrics/response_types.go create mode 100644 sdk/monitor/query/azmetrics/time_rfc3339.go diff --git a/sdk/monitor/query/azmetrics/autorest.md b/sdk/monitor/query/azmetrics/autorest.md new file mode 100644 index 000000000000..dc8c98a4c0e4 --- /dev/null +++ b/sdk/monitor/query/azmetrics/autorest.md @@ -0,0 +1,76 @@ +``` yaml +title: Metrics Query Client +clear-output-folder: false +go: true +input-file: https://github.com/Azure/azure-rest-api-specs/blob/0373f0edc4414fd402603fac51d0df93f1f70507/specification/monitor/data-plane/Microsoft.Insights/stable/2023-10-01/metricBatch.json +license-header: MICROSOFT_MIT_NO_VERSION +module: github.com/Azure/azure-sdk-for-go/sdk/monitor/query/azmetrics +openapi-type: "data-plane" +output-folder: ../azmetrics +security: "AADToken" +use: "@autorest/go@4.0.0-preview.61" +inject-spans: true +version: "^3.0.0" + +directive: + # rename Batch to QueryResources + - rename-operation: + from: MetricsBatch_Batch + to: Metrics_QueryResources + + # remove unused error models + - from: swagger-document + where: + - $.definitions..ErrorResponse + - $.definitions..ErrorDetail + - $.definitions..ErrorAdditionalInfo + transform: $["x-ms-external"] = true + + # Rename MetricResultsResponse + - rename-model: + from: MetricResultsResponse + to: MetricResults + - from: + - models.go + - models_serde.go + where: $ + transform: return $.replace(/MetricResultsValuesItem/g, "MetricValues"); + - from: swagger-document + where: $.definitions.MetricResults.properties.values.items + transform: $["description"] = "Metric data values." + + # fix casing, rename batch metric fields + - from: swagger-document + where: $.parameters.StartTimeParameter + transform: $["x-ms-client-name"] = "StartTime" + - from: swagger-document + where: $.parameters.EndTimeParameter + transform: $["x-ms-client-name"] = "EndTime" + - from: swagger-document + where: $.definitions.ResourceIdList.properties.resourceids + transform: $["x-ms-client-name"] = "ResourceIDs" + - from: swagger-document + where: $.definitions.MetricResults.properties.values.items.properties.starttime + transform: $["x-ms-client-name"] = "StartTime" + - from: swagger-document + where: $.definitions.MetricResults.properties.values.items.properties.endtime + transform: $["x-ms-client-name"] = "EndTime" + - from: swagger-document + where: $.definitions.MetricResults.properties.values.items.properties.resourceid + transform: $["x-ms-client-name"] = "ResourceID" + - from: swagger-document + where: $.definitions.MetricResults.properties.values.items.properties.resourceregion + transform: $["x-ms-client-name"] = "ResourceRegion" + - from: swagger-document + where: $.definitions.MetricResults.properties.values.items.properties.value + transform: $["x-ms-client-name"] = "Values" + - from: swagger-document + where: $.definitions.TimeSeriesElement.properties.metadatavalues + transform: $["x-ms-client-name"] = "MetadataValues" + - from: swagger-document + where: $.parameters.OrderByParameter + transform: $["x-ms-client-name"] = "OrderBy" + - from: swagger-document + where: $.parameters.RollUpByParameter + transform: $["x-ms-client-name"] = "RollUpBy" +``` \ No newline at end of file diff --git a/sdk/monitor/query/azmetrics/build.go b/sdk/monitor/query/azmetrics/build.go new file mode 100644 index 000000000000..4c89d9385aad --- /dev/null +++ b/sdk/monitor/query/azmetrics/build.go @@ -0,0 +1,10 @@ +//go:build go1.18 +// +build go1.18 + +//go:generate autorest ./autorest.md +//go:generate gofmt -w . + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +package azmetrics diff --git a/sdk/monitor/query/azmetrics/client.go b/sdk/monitor/query/azmetrics/client.go new file mode 100644 index 000000000000..fe82c352a66e --- /dev/null +++ b/sdk/monitor/query/azmetrics/client.go @@ -0,0 +1,113 @@ +//go:build go1.18 +// +build go1.18 + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. DO NOT EDIT. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +package azmetrics + +import ( + "context" + "errors" + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime" + "net/http" + "net/url" + "strconv" + "strings" +) + +// Client contains the methods for the Metrics group. +// Don't use this type directly, use a constructor function instead. +type Client struct { + internal *azcore.Client + endpoint string +} + +// QueryResources - Lists the metric values for multiple resources. +// If the operation fails it returns an *azcore.ResponseError type. +// +// Generated from API version 2023-10-01 +// - subscriptionID - The subscription identifier for the resources in this batch. +// - metricnamespace - Metric namespace that contains the requested metric names. +// - metricnames - The names of the metrics (comma separated) to retrieve. +// - batchRequest - Metrics batch body including the list of resource ids +// - options - ClientQueryResourcesOptions contains the optional parameters for the Client.QueryResources method. +func (client *Client) QueryResources(ctx context.Context, subscriptionID string, metricnamespace string, metricnames []string, batchRequest ResourceIDList, options *ClientQueryResourcesOptions) (ClientQueryResourcesResponse, error) { + var err error + ctx, endSpan := runtime.StartSpan(ctx, "Client.QueryResources", client.internal.Tracer(), nil) + defer func() { endSpan(err) }() + req, err := client.queryResourcesCreateRequest(ctx, subscriptionID, metricnamespace, metricnames, batchRequest, options) + if err != nil { + return ClientQueryResourcesResponse{}, err + } + httpResp, err := client.internal.Pipeline().Do(req) + if err != nil { + return ClientQueryResourcesResponse{}, err + } + if !runtime.HasStatusCode(httpResp, http.StatusOK) { + err = runtime.NewResponseError(httpResp) + return ClientQueryResourcesResponse{}, err + } + resp, err := client.queryResourcesHandleResponse(httpResp) + return resp, err +} + +// queryResourcesCreateRequest creates the QueryResources request. +func (client *Client) queryResourcesCreateRequest(ctx context.Context, subscriptionID string, metricnamespace string, metricnames []string, batchRequest ResourceIDList, options *ClientQueryResourcesOptions) (*policy.Request, error) { + urlPath := "/subscriptions/{subscriptionId}/metrics:getBatch" + if subscriptionID == "" { + return nil, errors.New("parameter subscriptionID cannot be empty") + } + urlPath = strings.ReplaceAll(urlPath, "{subscriptionId}", url.PathEscape(subscriptionID)) + req, err := runtime.NewRequest(ctx, http.MethodPost, runtime.JoinPaths(client.endpoint, urlPath)) + if err != nil { + return nil, err + } + reqQP := req.Raw().URL.Query() + if options != nil && options.StartTime != nil { + reqQP.Set("starttime", *options.StartTime) + } + if options != nil && options.EndTime != nil { + reqQP.Set("endtime", *options.EndTime) + } + if options != nil && options.Interval != nil { + reqQP.Set("interval", *options.Interval) + } + reqQP.Set("metricnamespace", metricnamespace) + reqQP.Set("metricnames", strings.Join(metricnames, ",")) + if options != nil && options.Aggregation != nil { + reqQP.Set("aggregation", *options.Aggregation) + } + if options != nil && options.Top != nil { + reqQP.Set("top", strconv.FormatInt(int64(*options.Top), 10)) + } + if options != nil && options.OrderBy != nil { + reqQP.Set("orderby", *options.OrderBy) + } + if options != nil && options.Filter != nil { + reqQP.Set("filter", *options.Filter) + } + if options != nil && options.RollUpBy != nil { + reqQP.Set("rollupby", *options.RollUpBy) + } + reqQP.Set("api-version", "2023-10-01") + req.Raw().URL.RawQuery = reqQP.Encode() + req.Raw().Header["Accept"] = []string{"application/json"} + if err := runtime.MarshalAsJSON(req, batchRequest); err != nil { + return nil, err + } + return req, nil +} + +// queryResourcesHandleResponse handles the QueryResources response. +func (client *Client) queryResourcesHandleResponse(resp *http.Response) (ClientQueryResourcesResponse, error) { + result := ClientQueryResourcesResponse{} + if err := runtime.UnmarshalAsJSON(resp, &result.MetricResults); err != nil { + return ClientQueryResourcesResponse{}, err + } + return result, nil +} diff --git a/sdk/monitor/query/azmetrics/constants.go b/sdk/monitor/query/azmetrics/constants.go new file mode 100644 index 000000000000..7b7f061c7077 --- /dev/null +++ b/sdk/monitor/query/azmetrics/constants.go @@ -0,0 +1,61 @@ +//go:build go1.18 +// +build go1.18 + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. DO NOT EDIT. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +package azmetrics + +// MetricUnit - The unit of the metric. +type MetricUnit string + +const ( + // MetricUnitBitsPerSecond - Rate unit of binary digits per second. + MetricUnitBitsPerSecond MetricUnit = "BitsPerSecond" + // MetricUnitByteSeconds - Unit of data transfer or storage. It is the size of the data in bytes multiplied by the time it + // takes to transfer or store the data in seconds. + MetricUnitByteSeconds MetricUnit = "ByteSeconds" + // MetricUnitBytes - Unit of memory in bytes. + MetricUnitBytes MetricUnit = "Bytes" + // MetricUnitBytesPerSecond - Rate unit of memory in bytes per second. + MetricUnitBytesPerSecond MetricUnit = "BytesPerSecond" + // MetricUnitCores - Unit of processing power. + MetricUnitCores MetricUnit = "Cores" + // MetricUnitCount - Unit of raw quantity. + MetricUnitCount MetricUnit = "Count" + // MetricUnitCountPerSecond - Rate unit of raw quantity per second. + MetricUnitCountPerSecond MetricUnit = "CountPerSecond" + // MetricUnitMilliCores - Unit of processing power in 1/1000th of a CPU core. + MetricUnitMilliCores MetricUnit = "MilliCores" + // MetricUnitMilliSeconds - Unit of time in 1/1000th of a second. + MetricUnitMilliSeconds MetricUnit = "MilliSeconds" + // MetricUnitNanoCores - Unit of processing power in one billionth of a CPU core. + MetricUnitNanoCores MetricUnit = "NanoCores" + // MetricUnitPercent - Percentage unit. + MetricUnitPercent MetricUnit = "Percent" + // MetricUnitSeconds - Unit of time in seconds. + MetricUnitSeconds MetricUnit = "Seconds" + // MetricUnitUnspecified - No specified unit. + MetricUnitUnspecified MetricUnit = "Unspecified" +) + +// PossibleMetricUnitValues returns the possible values for the MetricUnit const type. +func PossibleMetricUnitValues() []MetricUnit { + return []MetricUnit{ + MetricUnitBitsPerSecond, + MetricUnitByteSeconds, + MetricUnitBytes, + MetricUnitBytesPerSecond, + MetricUnitCores, + MetricUnitCount, + MetricUnitCountPerSecond, + MetricUnitMilliCores, + MetricUnitMilliSeconds, + MetricUnitNanoCores, + MetricUnitPercent, + MetricUnitSeconds, + MetricUnitUnspecified, + } +} diff --git a/sdk/monitor/query/azmetrics/go.mod b/sdk/monitor/query/azmetrics/go.mod new file mode 100644 index 000000000000..eba916f8e6a6 --- /dev/null +++ b/sdk/monitor/query/azmetrics/go.mod @@ -0,0 +1,11 @@ +module github.com/Azure/azure-sdk-for-go/sdk/monitor/query/azmetrics + +go 1.18 + +require github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.0 + +require ( + github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.0 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/text v0.13.0 // indirect +) diff --git a/sdk/monitor/query/azmetrics/go.sum b/sdk/monitor/query/azmetrics/go.sum new file mode 100644 index 000000000000..428cd71de305 --- /dev/null +++ b/sdk/monitor/query/azmetrics/go.sum @@ -0,0 +1,12 @@ +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.0 h1:fb8kj/Dh4CSwgsOzHeZY4Xh68cFVbzXx+ONXGMY//4w= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.0/go.mod h1:uReU2sSxZExRPBAg3qKzmAucSi51+SP1OhohieR821Q= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.0 h1:d81/ng9rET2YqdVkVwkb6EXeRrLJIwyGnJcAlAWKwhs= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.0/go.mod h1:s4kgfzA0covAXNicZHDMN58jExvcng2mC/DepXiF1EI= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/sdk/monitor/query/azmetrics/models.go b/sdk/monitor/query/azmetrics/models.go new file mode 100644 index 000000000000..b40e474985c6 --- /dev/null +++ b/sdk/monitor/query/azmetrics/models.go @@ -0,0 +1,126 @@ +//go:build go1.18 +// +build go1.18 + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. DO NOT EDIT. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +package azmetrics + +import "time" + +// LocalizableString - The localizable string class. +type LocalizableString struct { + // REQUIRED; The invariant value. + Value *string + + // The display name. + LocalizedValue *string +} + +// MetadataValue - Represents a metric metadata value. +type MetadataValue struct { + // The name of the metadata. + Name *LocalizableString + + // The value of the metadata. + Value *string +} + +// Metric - The result data of a query. +type Metric struct { + // REQUIRED; The metric Id. + ID *string + + // REQUIRED; The name and the display name of the metric, i.e. it is localizable string. + Name *LocalizableString + + // REQUIRED; The time series returned when a data query is performed. + Timeseries []*TimeSeriesElement + + // REQUIRED; The resource type of the metric resource. + Type *string + + // REQUIRED; The unit of the metric. + Unit *MetricUnit + + // Detailed description of this metric. + DisplayDescription *string + + // 'Success' or the error details on query failures for this metric. + ErrorCode *string + + // Error message encountered querying this specific metric. + ErrorMessage *string +} + +// MetricResults - The metrics result for a resource. +type MetricResults struct { + // The collection of metric data responses per resource, per metric. + Values []*MetricValues +} + +// MetricValues - Metric data values. +type MetricValues struct { + // REQUIRED; The end time, in datetime format, for which the data was retrieved. + EndTime *string + + // REQUIRED; The start time, in datetime format, for which the data was retrieved. + StartTime *string + + // REQUIRED; The value of the collection. + Values []*Metric + + // The interval (window size) for which the metric data was returned in ISO 8601 duration format with a special case for 'FULL' + // value that returns single datapoint for entire time span requested ( + // Examples: PT15M, PT1H, P1D, FULL). This may be adjusted and different from what was originally requested if AutoAdjustTimegrain=true + // is specified. + Interval *string + + // The namespace of the metrics been queried + Namespace *string + + // The resource that has been queried for metrics. + ResourceID *string + + // The region of the resource been queried for metrics. + ResourceRegion *string +} + +// MetricValue - Represents a metric value. +type MetricValue struct { + // REQUIRED; The timestamp for the metric value in ISO 8601 format. + TimeStamp *time.Time + + // The average value in the time range. + Average *float64 + + // The number of samples in the time range. Can be used to determine the number of values that contributed to the average + // value. + Count *float64 + + // The greatest value in the time range. + Maximum *float64 + + // The least value in the time range. + Minimum *float64 + + // The sum of all of the values in the time range. + Total *float64 +} + +// ResourceIDList - The comma separated list of resource IDs to query metrics for. +type ResourceIDList struct { + // The list of resource IDs to query metrics for. + ResourceIDs []*string +} + +// TimeSeriesElement - A time series result type. The discriminator value is always TimeSeries in this case. +type TimeSeriesElement struct { + // An array of data points representing the metric values. This is only returned if a result type of data is specified. + Data []*MetricValue + + // The metadata values returned if $filter was specified in the call. + MetadataValues []*MetadataValue +} diff --git a/sdk/monitor/query/azmetrics/models_serde.go b/sdk/monitor/query/azmetrics/models_serde.go new file mode 100644 index 000000000000..ecad9d419bec --- /dev/null +++ b/sdk/monitor/query/azmetrics/models_serde.go @@ -0,0 +1,336 @@ +//go:build go1.18 +// +build go1.18 + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. DO NOT EDIT. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +package azmetrics + +import ( + "encoding/json" + "fmt" + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "reflect" +) + +// MarshalJSON implements the json.Marshaller interface for type LocalizableString. +func (l LocalizableString) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "localizedValue", l.LocalizedValue) + populate(objectMap, "value", l.Value) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type LocalizableString. +func (l *LocalizableString) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", l, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "localizedValue": + err = unpopulate(val, "LocalizedValue", &l.LocalizedValue) + delete(rawMsg, key) + case "value": + err = unpopulate(val, "Value", &l.Value) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", l, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type MetadataValue. +func (m MetadataValue) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "name", m.Name) + populate(objectMap, "value", m.Value) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type MetadataValue. +func (m *MetadataValue) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", m, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "name": + err = unpopulate(val, "Name", &m.Name) + delete(rawMsg, key) + case "value": + err = unpopulate(val, "Value", &m.Value) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", m, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type Metric. +func (m Metric) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "displayDescription", m.DisplayDescription) + populate(objectMap, "errorCode", m.ErrorCode) + populate(objectMap, "errorMessage", m.ErrorMessage) + populate(objectMap, "id", m.ID) + populate(objectMap, "name", m.Name) + populate(objectMap, "timeseries", m.Timeseries) + populate(objectMap, "type", m.Type) + populate(objectMap, "unit", m.Unit) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type Metric. +func (m *Metric) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", m, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "displayDescription": + err = unpopulate(val, "DisplayDescription", &m.DisplayDescription) + delete(rawMsg, key) + case "errorCode": + err = unpopulate(val, "ErrorCode", &m.ErrorCode) + delete(rawMsg, key) + case "errorMessage": + err = unpopulate(val, "ErrorMessage", &m.ErrorMessage) + delete(rawMsg, key) + case "id": + err = unpopulate(val, "ID", &m.ID) + delete(rawMsg, key) + case "name": + err = unpopulate(val, "Name", &m.Name) + delete(rawMsg, key) + case "timeseries": + err = unpopulate(val, "Timeseries", &m.Timeseries) + delete(rawMsg, key) + case "type": + err = unpopulate(val, "Type", &m.Type) + delete(rawMsg, key) + case "unit": + err = unpopulate(val, "Unit", &m.Unit) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", m, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type MetricResults. +func (m MetricResults) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "values", m.Values) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type MetricResults. +func (m *MetricResults) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", m, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "values": + err = unpopulate(val, "Values", &m.Values) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", m, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type MetricValues. +func (m MetricValues) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "endtime", m.EndTime) + populate(objectMap, "interval", m.Interval) + populate(objectMap, "namespace", m.Namespace) + populate(objectMap, "resourceid", m.ResourceID) + populate(objectMap, "resourceregion", m.ResourceRegion) + populate(objectMap, "starttime", m.StartTime) + populate(objectMap, "value", m.Values) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type MetricValues. +func (m *MetricValues) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", m, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "endtime": + err = unpopulate(val, "EndTime", &m.EndTime) + delete(rawMsg, key) + case "interval": + err = unpopulate(val, "Interval", &m.Interval) + delete(rawMsg, key) + case "namespace": + err = unpopulate(val, "Namespace", &m.Namespace) + delete(rawMsg, key) + case "resourceid": + err = unpopulate(val, "ResourceID", &m.ResourceID) + delete(rawMsg, key) + case "resourceregion": + err = unpopulate(val, "ResourceRegion", &m.ResourceRegion) + delete(rawMsg, key) + case "starttime": + err = unpopulate(val, "StartTime", &m.StartTime) + delete(rawMsg, key) + case "value": + err = unpopulate(val, "Values", &m.Values) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", m, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type MetricValue. +func (m MetricValue) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "average", m.Average) + populate(objectMap, "count", m.Count) + populate(objectMap, "maximum", m.Maximum) + populate(objectMap, "minimum", m.Minimum) + populateDateTimeRFC3339(objectMap, "timeStamp", m.TimeStamp) + populate(objectMap, "total", m.Total) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type MetricValue. +func (m *MetricValue) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", m, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "average": + err = unpopulate(val, "Average", &m.Average) + delete(rawMsg, key) + case "count": + err = unpopulate(val, "Count", &m.Count) + delete(rawMsg, key) + case "maximum": + err = unpopulate(val, "Maximum", &m.Maximum) + delete(rawMsg, key) + case "minimum": + err = unpopulate(val, "Minimum", &m.Minimum) + delete(rawMsg, key) + case "timeStamp": + err = unpopulateDateTimeRFC3339(val, "TimeStamp", &m.TimeStamp) + delete(rawMsg, key) + case "total": + err = unpopulate(val, "Total", &m.Total) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", m, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type ResourceIDList. +func (r ResourceIDList) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "resourceids", r.ResourceIDs) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type ResourceIDList. +func (r *ResourceIDList) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", r, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "resourceids": + err = unpopulate(val, "ResourceIDs", &r.ResourceIDs) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", r, err) + } + } + return nil +} + +// MarshalJSON implements the json.Marshaller interface for type TimeSeriesElement. +func (t TimeSeriesElement) MarshalJSON() ([]byte, error) { + objectMap := make(map[string]any) + populate(objectMap, "data", t.Data) + populate(objectMap, "metadatavalues", t.MetadataValues) + return json.Marshal(objectMap) +} + +// UnmarshalJSON implements the json.Unmarshaller interface for type TimeSeriesElement. +func (t *TimeSeriesElement) UnmarshalJSON(data []byte) error { + var rawMsg map[string]json.RawMessage + if err := json.Unmarshal(data, &rawMsg); err != nil { + return fmt.Errorf("unmarshalling type %T: %v", t, err) + } + for key, val := range rawMsg { + var err error + switch key { + case "data": + err = unpopulate(val, "Data", &t.Data) + delete(rawMsg, key) + case "metadatavalues": + err = unpopulate(val, "MetadataValues", &t.MetadataValues) + delete(rawMsg, key) + } + if err != nil { + return fmt.Errorf("unmarshalling type %T: %v", t, err) + } + } + return nil +} + +func populate(m map[string]any, k string, v any) { + if v == nil { + return + } else if azcore.IsNullValue(v) { + m[k] = nil + } else if !reflect.ValueOf(v).IsNil() { + m[k] = v + } +} + +func unpopulate(data json.RawMessage, fn string, v any) error { + if data == nil { + return nil + } + if err := json.Unmarshal(data, v); err != nil { + return fmt.Errorf("struct field %s: %v", fn, err) + } + return nil +} diff --git a/sdk/monitor/query/azmetrics/options.go b/sdk/monitor/query/azmetrics/options.go new file mode 100644 index 000000000000..eed193195f66 --- /dev/null +++ b/sdk/monitor/query/azmetrics/options.go @@ -0,0 +1,55 @@ +//go:build go1.18 +// +build go1.18 + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. DO NOT EDIT. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +package azmetrics + +// ClientQueryResourcesOptions contains the optional parameters for the Client.QueryResources method. +type ClientQueryResourcesOptions struct { + // The list of aggregation types (comma separated) to retrieve.Examples: average, minimum, maximum + Aggregation *string + + // The end time of the query. It is a string in the format 'yyyy-MM-ddTHH:mm:ss.fffZ'. + EndTime *string + + // The filter is used to reduce the set of metric data returned. + // Example: + // Metric contains metadata A, B and C. + // - Return all time series of C where A = a1 and B = b1 or b2 + // filter=A eq ‘a1’ and B eq ‘b1’ or B eq ‘b2’ and C eq ‘’ + // - Invalid variant: + // filter=A eq ‘a1’ and B eq ‘b1’ and C eq ‘’ or B = ‘b2’ + // This is invalid because the logical or operator cannot separate two different metadata names. + // - Return all time series where A = a1, B = b1 and C = c1: + // filter=A eq ‘a1’ and B eq ‘b1’ and C eq ‘c1’ + // - Return all time series where A = a1 + // filter=A eq ‘a1’ and B eq ‘’ and C eq ‘’. + Filter *string + + // The interval (i.e. timegrain) of the query in ISO 8601 duration format. Defaults to PT1M. Special case for 'FULL' value + // that returns single datapoint for entire time span requested.Examples: PT15M, + // PT1H, P1D, FULL + Interval *string + + // The aggregation to use for sorting results and the direction of the sort. Only one order can be specified.Examples: sum + // asc + OrderBy *string + + // Dimension name(s) to rollup results by. For example if you only want to see metric values with a filter like 'City eq Seattle + // or City eq Tacoma' but don't want to see separate values for each city, + // you can specify 'RollUpBy=City' to see the results for Seattle and Tacoma rolled up into one timeseries. + RollUpBy *string + + // The start time of the query. It is a string in the format 'yyyy-MM-ddTHH:mm:ss.fffZ'. If you have specified the endtime + // parameter, then this parameter is required. If only starttime is specified, then + // endtime defaults to the current time. If no time interval is specified, the default is 1 hour. + StartTime *string + + // The maximum number of records to retrieve per resource ID in the request. Valid only if filter is specified. Defaults to + // 10. + Top *int32 +} diff --git a/sdk/monitor/query/azmetrics/response_types.go b/sdk/monitor/query/azmetrics/response_types.go new file mode 100644 index 000000000000..b6360c504e7c --- /dev/null +++ b/sdk/monitor/query/azmetrics/response_types.go @@ -0,0 +1,15 @@ +//go:build go1.18 +// +build go1.18 + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. DO NOT EDIT. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +package azmetrics + +// ClientQueryResourcesResponse contains the response from method Client.QueryResources. +type ClientQueryResourcesResponse struct { + // The metrics result for a resource. + MetricResults +} diff --git a/sdk/monitor/query/azmetrics/time_rfc3339.go b/sdk/monitor/query/azmetrics/time_rfc3339.go new file mode 100644 index 000000000000..e81767933a13 --- /dev/null +++ b/sdk/monitor/query/azmetrics/time_rfc3339.go @@ -0,0 +1,86 @@ +//go:build go1.18 +// +build go1.18 + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// Code generated by Microsoft (R) AutoRest Code Generator. DO NOT EDIT. +// Changes may cause incorrect behavior and will be lost if the code is regenerated. + +package azmetrics + +import ( + "encoding/json" + "fmt" + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "reflect" + "regexp" + "strings" + "time" +) + +// Azure reports time in UTC but it doesn't include the 'Z' time zone suffix in some cases. +var tzOffsetRegex = regexp.MustCompile(`(Z|z|\+|-)(\d+:\d+)*"*$`) + +const ( + utcDateTimeJSON = `"2006-01-02T15:04:05.999999999"` + utcDateTime = "2006-01-02T15:04:05.999999999" + dateTimeJSON = `"` + time.RFC3339Nano + `"` +) + +type dateTimeRFC3339 time.Time + +func (t dateTimeRFC3339) MarshalJSON() ([]byte, error) { + tt := time.Time(t) + return tt.MarshalJSON() +} + +func (t dateTimeRFC3339) MarshalText() ([]byte, error) { + tt := time.Time(t) + return tt.MarshalText() +} + +func (t *dateTimeRFC3339) UnmarshalJSON(data []byte) error { + layout := utcDateTimeJSON + if tzOffsetRegex.Match(data) { + layout = dateTimeJSON + } + return t.Parse(layout, string(data)) +} + +func (t *dateTimeRFC3339) UnmarshalText(data []byte) error { + layout := utcDateTime + if tzOffsetRegex.Match(data) { + layout = time.RFC3339Nano + } + return t.Parse(layout, string(data)) +} + +func (t *dateTimeRFC3339) Parse(layout, value string) error { + p, err := time.Parse(layout, strings.ToUpper(value)) + *t = dateTimeRFC3339(p) + return err +} + +func populateDateTimeRFC3339(m map[string]any, k string, t *time.Time) { + if t == nil { + return + } else if azcore.IsNullValue(t) { + m[k] = nil + return + } else if reflect.ValueOf(t).IsNil() { + return + } + m[k] = (*dateTimeRFC3339)(t) +} + +func unpopulateDateTimeRFC3339(data json.RawMessage, fn string, t **time.Time) error { + if data == nil || strings.EqualFold(string(data), "null") { + return nil + } + var aux dateTimeRFC3339 + if err := json.Unmarshal(data, &aux); err != nil { + return fmt.Errorf("struct field %s: %v", fn, err) + } + *t = (*time.Time)(&aux) + return nil +} From 2f90d077742b3b87a63647c2ac454746836bd981 Mon Sep 17 00:00:00 2001 From: gracewilcox Date: Fri, 26 Jan 2024 16:14:17 -0800 Subject: [PATCH 2/8] add files --- sdk/monitor/query/azmetrics/CHANGELOG.md | 11 +++ sdk/monitor/query/azmetrics/README.md | 79 ++++++++++++++++++++ sdk/monitor/query/azmetrics/ci.yml | 31 ++++++++ sdk/monitor/query/azmetrics/custom_client.go | 39 ++++++++++ sdk/monitor/query/azmetrics/version.go | 12 +++ 5 files changed, 172 insertions(+) create mode 100644 sdk/monitor/query/azmetrics/CHANGELOG.md create mode 100644 sdk/monitor/query/azmetrics/README.md create mode 100644 sdk/monitor/query/azmetrics/ci.yml create mode 100644 sdk/monitor/query/azmetrics/custom_client.go create mode 100644 sdk/monitor/query/azmetrics/version.go diff --git a/sdk/monitor/query/azmetrics/CHANGELOG.md b/sdk/monitor/query/azmetrics/CHANGELOG.md new file mode 100644 index 000000000000..bb825f2e6ba7 --- /dev/null +++ b/sdk/monitor/query/azmetrics/CHANGELOG.md @@ -0,0 +1,11 @@ +# Release History + +## 0.1.0 (Unreleased) + +### Features Added + +### Breaking Changes + +### Bugs Fixed + +### Other Changes \ No newline at end of file diff --git a/sdk/monitor/query/azmetrics/README.md b/sdk/monitor/query/azmetrics/README.md new file mode 100644 index 000000000000..07c75b699032 --- /dev/null +++ b/sdk/monitor/query/azmetrics/README.md @@ -0,0 +1,79 @@ +# Azure Monitor Metrics client module for Go + +Source code | Package (pkg.go.dev) | [REST API documentation][monitor_rest_docs] | [Product documentation][monitor_docs] | Samples + +## Getting started + +### Prerequisites + +* Go, version 1.18 or higher - [Install Go](https://go.dev/doc/install) +* Azure subscription - [Create a free account][azure_sub] +* The resource URI of an Azure resource (Storage Account, Key Vault, CosmosDB, etc.) that you plan to monitor + +### Install the packages + +Install the `azmetrics` and `azidentity` modules with `go get`: + +```bash +go get github.com/Azure/azure-sdk-for-go/sdk/monitor/query/azmetrics +go get github.com/Azure/azure-sdk-for-go/sdk/azidentity +``` + +The [azidentity][azure_identity] module is used for Azure Active Directory authentication during client construction. + +### Authentication + +An authenticated client object is required to execute a query. The examples demonstrate using [azidentity.NewDefaultAzureCredential][default_cred_ref] to authenticate; however, the client accepts any [azidentity][azure_identity] credential. See the [azidentity][azure_identity] documentation for more information about other credential types. + +The clients default to the Azure public cloud. For other cloud configurations, see the [cloud][cloud_documentation] package documentation. + +#### Create a client + +Example client + +## Key concepts + +### Metrics data structure + +Each set of metric values is a time series with the following characteristics: + +- The time the value was collected +- The resource associated with the value +- A namespace that acts like a category for the metric +- A metric name +- The value itself +- Some metrics may have multiple dimensions as described in [multi-dimensional metrics][multi-metrics]. Custom metrics can have up to 10 dimensions. + +## Examples + +Get started with our examples. + +## Contributing + +This project welcomes contributions and suggestions. Most contributions require you to agree to a [Contributor License Agreement (CLA)][cla] declaring that you have the right to, and actually do, grant us the rights to use your contribution. + +When you submit a pull request, a CLA-bot will automatically determine whether you need to provide a CLA and decorate +the PR appropriately (e.g., label, comment). Simply follow the instructions provided by the bot. You will only need to +do this once across all repos using our CLA. + +This project has adopted the [Microsoft Open Source Code of Conduct][coc]. For more information, see +the [Code of Conduct FAQ][coc_faq] or contact [opencode@microsoft.com][coc_contact] with any additional questions or +comments. + + +[azure_identity]: https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity +[azure_sub]: https://azure.microsoft.com/free/ +[azure_monitor_overview]: https://learn.microsoft.com/azure/azure-monitor/overview +[context]: https://pkg.go.dev/context +[cloud_documentation]: https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud +[default_cred_ref]: https://github.com/Azure/azure-sdk-for-go/tree/main/sdk/azidentity#defaultazurecredential +[go_samples]: (https://github.com/Azure-Samples/azure-sdk-for-go-samples) +[metrics_overview]: https://learn.microsoft.com/azure/azure-monitor/essentials/data-platform-metrics +[metric_namespaces]: https://learn.microsoft.com/azure/azure-monitor/reference/supported-metrics/metrics-index#metrics-by-resource-provider +[monitor_docs]: https://learn.microsoft.com/azure/azure-monitor/ +[monitor_rest_docs]: https://learn.microsoft.com/rest/api/monitor/ +[multi-metrics]: https://learn.microsoft.com/azure/azure-monitor/essentials/data-platform-metrics#multi-dimensional-metrics +[cla]: https://cla.microsoft.com +[coc]: https://opensource.microsoft.com/codeofconduct/ +[coc_faq]: https://opensource.microsoft.com/codeofconduct/faq/ +[coc_contact]: mailto:opencode@microsoft.com diff --git a/sdk/monitor/query/azmetrics/ci.yml b/sdk/monitor/query/azmetrics/ci.yml new file mode 100644 index 000000000000..bc141f160acd --- /dev/null +++ b/sdk/monitor/query/azmetrics/ci.yml @@ -0,0 +1,31 @@ +# NOTE: Please refer to https://aka.ms/azsdk/engsys/ci-yaml before editing this file. +trigger: + branches: + include: + - main + - feature/* + - hotfix/* + - release/* + paths: + include: + - sdk/monitor/query/azmetrics + +pr: + branches: + include: + - main + - feature/* + - hotfix/* + - release/* + paths: + include: + - sdk/monitor/query/azmetrics + + +stages: +- template: /eng/pipelines/templates/jobs/archetype-sdk-client.yml + parameters: + ServiceDirectory: 'monitor/query/azmetrics' + RunLiveTests: true + UsePipelineProxy: false + SupportedClouds: 'Public,UsGov,China' diff --git a/sdk/monitor/query/azmetrics/custom_client.go b/sdk/monitor/query/azmetrics/custom_client.go new file mode 100644 index 000000000000..b0c7fa0a9881 --- /dev/null +++ b/sdk/monitor/query/azmetrics/custom_client.go @@ -0,0 +1,39 @@ +//go:build go1.18 +// +build go1.18 + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +package azmetrics + +// this file contains handwritten additions to the generated code + +import ( + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime" +) + +// ClientOptions contains optional settings for Client. +type ClientOptions struct { + azcore.ClientOptions +} + +// NewClient creates a client that accesses Azure Monitor metrics data. +// Client should be used for performing metrics queries on multiple monitored resources in the same region. +// A credential with authorization at the subscription level is required when using this client. +// +// endpoint - The regional endpoint to use, for example https://eastus.metrics.monitor.azure.com. +// The region should match the region of the requested resources. For global resources, the region should be 'global'. +func NewClient(endpoint string, credential azcore.TokenCredential, options *ClientOptions) (*Client, error) { + if options == nil { + options = &ClientOptions{} + } + + authPolicy := runtime.NewBearerTokenPolicy(credential, []string{"https://metrics.monitor.azure.com" + "/.default"}, nil) + azcoreClient, err := azcore.NewClient(moduleName, version, runtime.PipelineOptions{PerRetry: []policy.Policy{authPolicy}}, &options.ClientOptions) + if err != nil { + return nil, err + } + return &Client{endpoint: endpoint, internal: azcoreClient}, nil +} diff --git a/sdk/monitor/query/azmetrics/version.go b/sdk/monitor/query/azmetrics/version.go new file mode 100644 index 000000000000..fb0485466981 --- /dev/null +++ b/sdk/monitor/query/azmetrics/version.go @@ -0,0 +1,12 @@ +//go:build go1.18 +// +build go1.18 + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +package azmetrics + +const ( + moduleName = "github.com/Azure/azure-sdk-for-go/sdk/monitor/query/azmetrics" + version = "v0.1.0" +) From bbc0a93ff178874865a4f40014b4b549779fba86 Mon Sep 17 00:00:00 2001 From: gracewilcox Date: Tue, 6 Feb 2024 09:36:14 -0800 Subject: [PATCH 3/8] cleanup code --- sdk/monitor/query/azmetrics/autorest.md | 16 ++++++++++- sdk/monitor/query/azmetrics/client.go | 28 +++++++++---------- sdk/monitor/query/azmetrics/options.go | 4 +-- sdk/monitor/query/azmetrics/response_types.go | 4 +-- 4 files changed, 33 insertions(+), 19 deletions(-) diff --git a/sdk/monitor/query/azmetrics/autorest.md b/sdk/monitor/query/azmetrics/autorest.md index dc8c98a4c0e4..452048e9860e 100644 --- a/sdk/monitor/query/azmetrics/autorest.md +++ b/sdk/monitor/query/azmetrics/autorest.md @@ -39,7 +39,13 @@ directive: where: $.definitions.MetricResults.properties.values.items transform: $["description"] = "Metric data values." - # fix casing, rename batch metric fields + # renaming or fixing the casing of struct fields and parameters + - from: swagger-document + where: $.parameters.MetricNamespaceParameter + transform: $["x-ms-client-name"] = "metricNamespace" + - from: swagger-document + where: $.parameters.MetricNamesParameter + transform: $["x-ms-client-name"] = "metricNames" - from: swagger-document where: $.parameters.StartTimeParameter transform: $["x-ms-client-name"] = "StartTime" @@ -73,4 +79,12 @@ directive: - from: swagger-document where: $.parameters.RollUpByParameter transform: $["x-ms-client-name"] = "RollUpBy" + + # delete client name prefix from method options and response types + - from: + - client.go + - options.go + - response_types.go + where: $ + transform: return $.replace(/Client(\w+)((?:Options|Response))/g, "$1$2"); ``` \ No newline at end of file diff --git a/sdk/monitor/query/azmetrics/client.go b/sdk/monitor/query/azmetrics/client.go index fe82c352a66e..39491b29e080 100644 --- a/sdk/monitor/query/azmetrics/client.go +++ b/sdk/monitor/query/azmetrics/client.go @@ -32,32 +32,32 @@ type Client struct { // // Generated from API version 2023-10-01 // - subscriptionID - The subscription identifier for the resources in this batch. -// - metricnamespace - Metric namespace that contains the requested metric names. -// - metricnames - The names of the metrics (comma separated) to retrieve. +// - metricNamespace - Metric namespace that contains the requested metric names. +// - metricNames - The names of the metrics (comma separated) to retrieve. // - batchRequest - Metrics batch body including the list of resource ids -// - options - ClientQueryResourcesOptions contains the optional parameters for the Client.QueryResources method. -func (client *Client) QueryResources(ctx context.Context, subscriptionID string, metricnamespace string, metricnames []string, batchRequest ResourceIDList, options *ClientQueryResourcesOptions) (ClientQueryResourcesResponse, error) { +// - options - QueryResourcesOptions contains the optional parameters for the Client.QueryResources method. +func (client *Client) QueryResources(ctx context.Context, subscriptionID string, metricNamespace string, metricNames []string, batchRequest ResourceIDList, options *QueryResourcesOptions) (QueryResourcesResponse, error) { var err error ctx, endSpan := runtime.StartSpan(ctx, "Client.QueryResources", client.internal.Tracer(), nil) defer func() { endSpan(err) }() - req, err := client.queryResourcesCreateRequest(ctx, subscriptionID, metricnamespace, metricnames, batchRequest, options) + req, err := client.queryResourcesCreateRequest(ctx, subscriptionID, metricNamespace, metricNames, batchRequest, options) if err != nil { - return ClientQueryResourcesResponse{}, err + return QueryResourcesResponse{}, err } httpResp, err := client.internal.Pipeline().Do(req) if err != nil { - return ClientQueryResourcesResponse{}, err + return QueryResourcesResponse{}, err } if !runtime.HasStatusCode(httpResp, http.StatusOK) { err = runtime.NewResponseError(httpResp) - return ClientQueryResourcesResponse{}, err + return QueryResourcesResponse{}, err } resp, err := client.queryResourcesHandleResponse(httpResp) return resp, err } // queryResourcesCreateRequest creates the QueryResources request. -func (client *Client) queryResourcesCreateRequest(ctx context.Context, subscriptionID string, metricnamespace string, metricnames []string, batchRequest ResourceIDList, options *ClientQueryResourcesOptions) (*policy.Request, error) { +func (client *Client) queryResourcesCreateRequest(ctx context.Context, subscriptionID string, metricNamespace string, metricNames []string, batchRequest ResourceIDList, options *QueryResourcesOptions) (*policy.Request, error) { urlPath := "/subscriptions/{subscriptionId}/metrics:getBatch" if subscriptionID == "" { return nil, errors.New("parameter subscriptionID cannot be empty") @@ -77,8 +77,8 @@ func (client *Client) queryResourcesCreateRequest(ctx context.Context, subscript if options != nil && options.Interval != nil { reqQP.Set("interval", *options.Interval) } - reqQP.Set("metricnamespace", metricnamespace) - reqQP.Set("metricnames", strings.Join(metricnames, ",")) + reqQP.Set("metricnamespace", metricNamespace) + reqQP.Set("metricnames", strings.Join(metricNames, ",")) if options != nil && options.Aggregation != nil { reqQP.Set("aggregation", *options.Aggregation) } @@ -104,10 +104,10 @@ func (client *Client) queryResourcesCreateRequest(ctx context.Context, subscript } // queryResourcesHandleResponse handles the QueryResources response. -func (client *Client) queryResourcesHandleResponse(resp *http.Response) (ClientQueryResourcesResponse, error) { - result := ClientQueryResourcesResponse{} +func (client *Client) queryResourcesHandleResponse(resp *http.Response) (QueryResourcesResponse, error) { + result := QueryResourcesResponse{} if err := runtime.UnmarshalAsJSON(resp, &result.MetricResults); err != nil { - return ClientQueryResourcesResponse{}, err + return QueryResourcesResponse{}, err } return result, nil } diff --git a/sdk/monitor/query/azmetrics/options.go b/sdk/monitor/query/azmetrics/options.go index eed193195f66..bb0bf4dbc6db 100644 --- a/sdk/monitor/query/azmetrics/options.go +++ b/sdk/monitor/query/azmetrics/options.go @@ -8,8 +8,8 @@ package azmetrics -// ClientQueryResourcesOptions contains the optional parameters for the Client.QueryResources method. -type ClientQueryResourcesOptions struct { +// QueryResourcesOptions contains the optional parameters for the Client.QueryResources method. +type QueryResourcesOptions struct { // The list of aggregation types (comma separated) to retrieve.Examples: average, minimum, maximum Aggregation *string diff --git a/sdk/monitor/query/azmetrics/response_types.go b/sdk/monitor/query/azmetrics/response_types.go index b6360c504e7c..4896f6185342 100644 --- a/sdk/monitor/query/azmetrics/response_types.go +++ b/sdk/monitor/query/azmetrics/response_types.go @@ -8,8 +8,8 @@ package azmetrics -// ClientQueryResourcesResponse contains the response from method Client.QueryResources. -type ClientQueryResourcesResponse struct { +// QueryResourcesResponse contains the response from method Client.QueryResources. +type QueryResourcesResponse struct { // The metrics result for a resource. MetricResults } From dc96efafd29d47cc7bbcb005c06d5d59252d027d Mon Sep 17 00:00:00 2001 From: gracewilcox Date: Fri, 9 Feb 2024 12:34:34 -0800 Subject: [PATCH 4/8] added tests --- sdk/monitor/query/azmetrics/assets.json | 6 + sdk/monitor/query/azmetrics/autorest.md | 6 + sdk/monitor/query/azmetrics/client.go | 10 +- sdk/monitor/query/azmetrics/client_test.go | 71 +++++++++ sdk/monitor/query/azmetrics/go.mod | 24 ++- sdk/monitor/query/azmetrics/go.sum | 45 +++++- sdk/monitor/query/azmetrics/models.go | 2 +- sdk/monitor/query/azmetrics/models_serde.go | 4 +- .../query/azmetrics/test-resources.bicep | 13 ++ sdk/monitor/query/azmetrics/utils_test.go | 149 ++++++++++++++++++ 10 files changed, 310 insertions(+), 20 deletions(-) create mode 100644 sdk/monitor/query/azmetrics/assets.json create mode 100644 sdk/monitor/query/azmetrics/client_test.go create mode 100644 sdk/monitor/query/azmetrics/test-resources.bicep create mode 100644 sdk/monitor/query/azmetrics/utils_test.go diff --git a/sdk/monitor/query/azmetrics/assets.json b/sdk/monitor/query/azmetrics/assets.json new file mode 100644 index 000000000000..56ae6d3b700c --- /dev/null +++ b/sdk/monitor/query/azmetrics/assets.json @@ -0,0 +1,6 @@ +{ + "AssetsRepo": "Azure/azure-sdk-assets", + "AssetsRepoPrefixPath": "go", + "TagPrefix": "go/monitor/query/azmetrics", + "Tag": "go/monitor/query/azmetrics_37251fcc33" + } \ No newline at end of file diff --git a/sdk/monitor/query/azmetrics/autorest.md b/sdk/monitor/query/azmetrics/autorest.md index 452048e9860e..5893d82e3ad2 100644 --- a/sdk/monitor/query/azmetrics/autorest.md +++ b/sdk/monitor/query/azmetrics/autorest.md @@ -40,6 +40,9 @@ directive: transform: $["description"] = "Metric data values." # renaming or fixing the casing of struct fields and parameters + - from: swagger-document + where: $.definitions.Metric.properties.timeseries + transform: $["x-ms-client-name"] = "TimeSeries" - from: swagger-document where: $.parameters.MetricNamespaceParameter transform: $["x-ms-client-name"] = "metricNamespace" @@ -79,6 +82,9 @@ directive: - from: swagger-document where: $.parameters.RollUpByParameter transform: $["x-ms-client-name"] = "RollUpBy" + - from: client.go + where: $ + transform: return $.replace(/batchRequest/g, "resourceIDs"); # delete client name prefix from method options and response types - from: diff --git a/sdk/monitor/query/azmetrics/client.go b/sdk/monitor/query/azmetrics/client.go index 39491b29e080..a510dd728920 100644 --- a/sdk/monitor/query/azmetrics/client.go +++ b/sdk/monitor/query/azmetrics/client.go @@ -34,13 +34,13 @@ type Client struct { // - subscriptionID - The subscription identifier for the resources in this batch. // - metricNamespace - Metric namespace that contains the requested metric names. // - metricNames - The names of the metrics (comma separated) to retrieve. -// - batchRequest - Metrics batch body including the list of resource ids +// - resourceIDs - Metrics batch body including the list of resource ids // - options - QueryResourcesOptions contains the optional parameters for the Client.QueryResources method. -func (client *Client) QueryResources(ctx context.Context, subscriptionID string, metricNamespace string, metricNames []string, batchRequest ResourceIDList, options *QueryResourcesOptions) (QueryResourcesResponse, error) { +func (client *Client) QueryResources(ctx context.Context, subscriptionID string, metricNamespace string, metricNames []string, resourceIDs ResourceIDList, options *QueryResourcesOptions) (QueryResourcesResponse, error) { var err error ctx, endSpan := runtime.StartSpan(ctx, "Client.QueryResources", client.internal.Tracer(), nil) defer func() { endSpan(err) }() - req, err := client.queryResourcesCreateRequest(ctx, subscriptionID, metricNamespace, metricNames, batchRequest, options) + req, err := client.queryResourcesCreateRequest(ctx, subscriptionID, metricNamespace, metricNames, resourceIDs, options) if err != nil { return QueryResourcesResponse{}, err } @@ -57,7 +57,7 @@ func (client *Client) QueryResources(ctx context.Context, subscriptionID string, } // queryResourcesCreateRequest creates the QueryResources request. -func (client *Client) queryResourcesCreateRequest(ctx context.Context, subscriptionID string, metricNamespace string, metricNames []string, batchRequest ResourceIDList, options *QueryResourcesOptions) (*policy.Request, error) { +func (client *Client) queryResourcesCreateRequest(ctx context.Context, subscriptionID string, metricNamespace string, metricNames []string, resourceIDs ResourceIDList, options *QueryResourcesOptions) (*policy.Request, error) { urlPath := "/subscriptions/{subscriptionId}/metrics:getBatch" if subscriptionID == "" { return nil, errors.New("parameter subscriptionID cannot be empty") @@ -97,7 +97,7 @@ func (client *Client) queryResourcesCreateRequest(ctx context.Context, subscript reqQP.Set("api-version", "2023-10-01") req.Raw().URL.RawQuery = reqQP.Encode() req.Raw().Header["Accept"] = []string{"application/json"} - if err := runtime.MarshalAsJSON(req, batchRequest); err != nil { + if err := runtime.MarshalAsJSON(req, resourceIDs); err != nil { return nil, err } return req, nil diff --git a/sdk/monitor/query/azmetrics/client_test.go b/sdk/monitor/query/azmetrics/client_test.go new file mode 100644 index 000000000000..1ff6690be45b --- /dev/null +++ b/sdk/monitor/query/azmetrics/client_test.go @@ -0,0 +1,71 @@ +//go:build go1.18 +// +build go1.18 + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +package azmetrics_test + +import ( + "context" + "testing" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" + "github.com/Azure/azure-sdk-for-go/sdk/monitor/query/azmetrics" + "github.com/stretchr/testify/require" +) + +func TestQueryResources_Pass(t *testing.T) { + client := startTest(t) + metricName := "HttpIncomingRequestCount" + resourceIDList := azmetrics.ResourceIDList{ResourceIDs: to.SliceOfPtrs(resourceURI)} + + res, err := client.QueryResources( + context.Background(), + subscriptionID, + "Microsoft.AppConfiguration/configurationStores", + []string{"HttpIncomingRequestCount"}, + resourceIDList, + &azmetrics.QueryResourcesOptions{ + Aggregation: to.Ptr("average"), + StartTime: to.Ptr("2023-11-15"), + EndTime: to.Ptr("2023-11-16"), + Interval: to.Ptr("PT1H"), + }, + ) + require.NoError(t, err) + require.NotNil(t, res) + + for _, resource := range res.Values { + for _, metric := range resource.Values { + require.Equal(t, metricName, *metric.Name.Value) + for _, timeSeriesElement := range metric.TimeSeries { + require.NotNil(t, timeSeriesElement) + } + } + } + + testSerde(t, &res) + testSerde(t, &resourceIDList) +} + +func TestQueryResources_Fail(t *testing.T) { + client := startTest(t) + + res, err := client.QueryResources( + context.Background(), + "fakesubscriptionID", + "Microsoft.AppConfiguration/configurationStores", + []string{"HttpIncomingRequestCount"}, + azmetrics.ResourceIDList{ResourceIDs: to.SliceOfPtrs(resourceURI)}, + nil, + ) + require.Error(t, err) + var httpErr *azcore.ResponseError + require.ErrorAs(t, err, &httpErr) + require.NotEqual(t, 200, httpErr.StatusCode) + require.Nil(t, res.Values) + + testSerde(t, &res) +} diff --git a/sdk/monitor/query/azmetrics/go.mod b/sdk/monitor/query/azmetrics/go.mod index eba916f8e6a6..8359c7344e58 100644 --- a/sdk/monitor/query/azmetrics/go.mod +++ b/sdk/monitor/query/azmetrics/go.mod @@ -2,10 +2,26 @@ module github.com/Azure/azure-sdk-for-go/sdk/monitor/query/azmetrics go 1.18 -require github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.0 +require ( + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1 + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1 + github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.1 + github.com/stretchr/testify v1.8.4 +) require ( - github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.0 // indirect - golang.org/x/net v0.17.0 // indirect - golang.org/x/text v0.13.0 // indirect + github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dnaeon/go-vcr v1.2.0 // indirect + github.com/golang-jwt/jwt/v5 v5.2.0 // indirect + github.com/google/uuid v1.5.0 // indirect + github.com/kylelemons/godebug v1.1.0 // indirect + github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + golang.org/x/crypto v0.17.0 // indirect + golang.org/x/net v0.19.0 // indirect + golang.org/x/sys v0.15.0 // indirect + golang.org/x/text v0.14.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/sdk/monitor/query/azmetrics/go.sum b/sdk/monitor/query/azmetrics/go.sum index 428cd71de305..77b218cc6b8e 100644 --- a/sdk/monitor/query/azmetrics/go.sum +++ b/sdk/monitor/query/azmetrics/go.sum @@ -1,12 +1,41 @@ -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.0 h1:fb8kj/Dh4CSwgsOzHeZY4Xh68cFVbzXx+ONXGMY//4w= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.0/go.mod h1:uReU2sSxZExRPBAg3qKzmAucSi51+SP1OhohieR821Q= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.0 h1:d81/ng9rET2YqdVkVwkb6EXeRrLJIwyGnJcAlAWKwhs= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.0/go.mod h1:s4kgfzA0covAXNicZHDMN58jExvcng2mC/DepXiF1EI= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1 h1:lGlwhPtrX6EVml1hO0ivjkUxsSyl4dsiw9qcA1k/3IQ= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1/go.mod h1:RKUqNu35KJYcVG/fqTRqmuXJZYNhYkBrnC/hX7yGbTA= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1 h1:sO0/P7g68FrryJzljemN+6GTssUXdANk6aJ7T1ZxnsQ= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1/go.mod h1:h8hyGFDsU5HMivxiS2iYFZsgDbU9OnnJ163x5UGVKYo= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.1 h1:6oNBlSdi1QqM1PNW7FPA6xOGA5UNsXnkaYZz9vdPGhA= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.1/go.mod h1:s4kgfzA0covAXNicZHDMN58jExvcng2mC/DepXiF1EI= +github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1 h1:DzHpqpoJVaCgOUdVHxE8QB52S6NiVdDQvGlny1qvPqA= +github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= +github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= +github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw= +github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= +github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= +golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/sdk/monitor/query/azmetrics/models.go b/sdk/monitor/query/azmetrics/models.go index b40e474985c6..919abc49ae63 100644 --- a/sdk/monitor/query/azmetrics/models.go +++ b/sdk/monitor/query/azmetrics/models.go @@ -37,7 +37,7 @@ type Metric struct { Name *LocalizableString // REQUIRED; The time series returned when a data query is performed. - Timeseries []*TimeSeriesElement + TimeSeries []*TimeSeriesElement // REQUIRED; The resource type of the metric resource. Type *string diff --git a/sdk/monitor/query/azmetrics/models_serde.go b/sdk/monitor/query/azmetrics/models_serde.go index ecad9d419bec..a283d6319a2b 100644 --- a/sdk/monitor/query/azmetrics/models_serde.go +++ b/sdk/monitor/query/azmetrics/models_serde.go @@ -85,7 +85,7 @@ func (m Metric) MarshalJSON() ([]byte, error) { populate(objectMap, "errorMessage", m.ErrorMessage) populate(objectMap, "id", m.ID) populate(objectMap, "name", m.Name) - populate(objectMap, "timeseries", m.Timeseries) + populate(objectMap, "timeseries", m.TimeSeries) populate(objectMap, "type", m.Type) populate(objectMap, "unit", m.Unit) return json.Marshal(objectMap) @@ -116,7 +116,7 @@ func (m *Metric) UnmarshalJSON(data []byte) error { err = unpopulate(val, "Name", &m.Name) delete(rawMsg, key) case "timeseries": - err = unpopulate(val, "Timeseries", &m.Timeseries) + err = unpopulate(val, "TimeSeries", &m.TimeSeries) delete(rawMsg, key) case "type": err = unpopulate(val, "Type", &m.Type) diff --git a/sdk/monitor/query/azmetrics/test-resources.bicep b/sdk/monitor/query/azmetrics/test-resources.bicep new file mode 100644 index 000000000000..f514455ce55b --- /dev/null +++ b/sdk/monitor/query/azmetrics/test-resources.bicep @@ -0,0 +1,13 @@ +param baseName string +param appSku string = 'standard' +param location string = resourceGroup().location + +resource app_config 'Microsoft.AppConfiguration/configurationStores@2022-05-01' = { + name: baseName + location: location + sku: { + name: appSku + } +} + +output RESOURCE_URI string = app_config.id diff --git a/sdk/monitor/query/azmetrics/utils_test.go b/sdk/monitor/query/azmetrics/utils_test.go new file mode 100644 index 000000000000..bbbbbb4dba23 --- /dev/null +++ b/sdk/monitor/query/azmetrics/utils_test.go @@ -0,0 +1,149 @@ +//go:build go1.18 +// +build go1.18 + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +package azmetrics_test + +import ( + "context" + "encoding/json" + "os" + "regexp" + "strings" + "testing" + "time" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" + "github.com/Azure/azure-sdk-for-go/sdk/azidentity" + "github.com/Azure/azure-sdk-for-go/sdk/internal/recording" + "github.com/Azure/azure-sdk-for-go/sdk/monitor/query/azmetrics" + "github.com/stretchr/testify/require" +) + +const recordingDirectory = "sdk/monitor/query/azmetrics/testdata" +const fakeResourceURI = "/subscriptions/faa080af-c1d8-40ad-9cce-e1a451va7b87/resourceGroups/rg-example/providers/Microsoft.AppConfiguration/configurationStores/example" +const fakeSubscrtiptionID = "faa080af-c1d8-40ad-9cce-e1a451va7b87" +const fakeRegion = "westus" + +var ( + credential azcore.TokenCredential + resourceURI string + subscriptionID string + region string +) + +func TestMain(m *testing.M) { + code := run(m) + os.Exit(code) +} + +func run(m *testing.M) int { + if recording.GetRecordMode() == recording.PlaybackMode || recording.GetRecordMode() == recording.RecordingMode { + proxy, err := recording.StartTestProxy(recordingDirectory, nil) + if err != nil { + panic(err) + } + defer func() { + err := recording.StopTestProxy(proxy) + if err != nil { + panic(err) + } + }() + } + + if recording.GetRecordMode() == recording.PlaybackMode { + credential = &FakeCredential{} + } else { + tenantID := getEnvVar("AZMETRICS_TENANT_ID", "") + clientID := getEnvVar("AZMETRICS_CLIENT_ID", "") + secret := getEnvVar("AZMETRICS_CLIENT_SECRET", "") + var err error + credential, err = azidentity.NewClientSecretCredential(tenantID, clientID, secret, nil) + if err != nil { + panic(err) + } + } + + resourceURI = getEnvVar("RESOURCE_URI", fakeResourceURI) + subscriptionID = getEnvVar("AZMETRICS_SUBSCRIPTION_ID", fakeSubscrtiptionID) + region = getEnvVar("AZMETRICS_LOCATION", fakeRegion) + + return m.Run() +} + +func startRecording(t *testing.T) { + err := recording.Start(t, recordingDirectory, nil) + require.NoError(t, err) + t.Cleanup(func() { + err := recording.Stop(t, nil) + require.NoError(t, err) + }) +} + +func startTest(t *testing.T) *azmetrics.Client { + startRecording(t) + transport, err := recording.NewRecordingHTTPClient(t, nil) + require.NoError(t, err) + opts := &azmetrics.ClientOptions{ClientOptions: azcore.ClientOptions{Transport: transport}} + endpoint := "https://" + region + ".metrics.monitor.azure.com" + client, err := azmetrics.NewClient(endpoint, credential, opts) + if err != nil { + panic(err) + } + return client +} + +func getEnvVar(envVar string, fakeValue string) string { + // get value + value := fakeValue + if recording.GetRecordMode() == recording.LiveMode || recording.GetRecordMode() == recording.RecordingMode { + value = os.Getenv(envVar) + if value == "" { + panic("no value for " + envVar) + } + } + + // sanitize value + if fakeValue != "" && recording.GetRecordMode() == recording.RecordingMode { + err := recording.AddGeneralRegexSanitizer(fakeValue, value, nil) + if err != nil { + panic(err) + } + } + + return value +} + +type FakeCredential struct{} + +func (f *FakeCredential) GetToken(ctx context.Context, options policy.TokenRequestOptions) (azcore.AccessToken, error) { + return azcore.AccessToken{Token: "faketoken", ExpiresOn: time.Now().Add(time.Hour).UTC()}, nil +} + +type serdeModel interface { + json.Marshaler + json.Unmarshaler +} + +func testSerde[T serdeModel](t *testing.T, model T) { + data, err := model.MarshalJSON() + require.NoError(t, err) + err = model.UnmarshalJSON(data) + require.NoError(t, err) + + // testing unmarshal error scenarios + var data2 []byte + err = model.UnmarshalJSON(data2) + require.Error(t, err) + + m := regexp.MustCompile(":.*$") + modifiedData := m.ReplaceAllString(string(data), ":false}") + if !strings.Contains(modifiedData, "render") && modifiedData != "{}" { + data3 := []byte(modifiedData) + err = model.UnmarshalJSON(data3) + require.Error(t, err) + } +} From 30550b4ac6179df900d36d22ba089a2bb34c65a1 Mon Sep 17 00:00:00 2001 From: gracewilcox Date: Fri, 9 Feb 2024 12:36:54 -0800 Subject: [PATCH 5/8] license --- sdk/monitor/query/azmetrics/LICENSE.txt | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 sdk/monitor/query/azmetrics/LICENSE.txt diff --git a/sdk/monitor/query/azmetrics/LICENSE.txt b/sdk/monitor/query/azmetrics/LICENSE.txt new file mode 100644 index 000000000000..4b3ba9df30d6 --- /dev/null +++ b/sdk/monitor/query/azmetrics/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) Microsoft Corporation. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE \ No newline at end of file From 9d5f9fabc201d029773e07f4ca64e607ecd66b84 Mon Sep 17 00:00:00 2001 From: gracewilcox Date: Mon, 12 Feb 2024 16:10:01 -0800 Subject: [PATCH 6/8] update code coverage --- eng/config.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/eng/config.json b/eng/config.json index fd4ae27babee..77083b68aba6 100644 --- a/eng/config.json +++ b/eng/config.json @@ -112,6 +112,10 @@ { "Name": "security/keyvault/azadmin", "CoverageGoal": 0.80 + }, + { + "Name": "monitor/query/azmetrics", + "CoverageGoal": 0.75 } ] } From 74e4dae57a78e07d67708085cad4d35a83e6c3ed Mon Sep 17 00:00:00 2001 From: gracewilcox Date: Tue, 13 Feb 2024 15:19:03 -0800 Subject: [PATCH 7/8] charles feedback --- sdk/monitor/query/azmetrics/CHANGELOG.md | 10 ++-------- sdk/monitor/query/azmetrics/README.md | 5 ++--- sdk/monitor/query/azmetrics/ci.yml | 2 +- sdk/monitor/query/azmetrics/custom_client.go | 2 +- sdk/monitor/query/azmetrics/utils_test.go | 14 +++++++------- 5 files changed, 13 insertions(+), 20 deletions(-) diff --git a/sdk/monitor/query/azmetrics/CHANGELOG.md b/sdk/monitor/query/azmetrics/CHANGELOG.md index bb825f2e6ba7..df342e801404 100644 --- a/sdk/monitor/query/azmetrics/CHANGELOG.md +++ b/sdk/monitor/query/azmetrics/CHANGELOG.md @@ -1,11 +1,5 @@ # Release History -## 0.1.0 (Unreleased) +## 0.1.0 (2024-02-13) -### Features Added - -### Breaking Changes - -### Bugs Fixed - -### Other Changes \ No newline at end of file +* This is the initial release of the `azmetrics` module \ No newline at end of file diff --git a/sdk/monitor/query/azmetrics/README.md b/sdk/monitor/query/azmetrics/README.md index 07c75b699032..aa6a66bcb7fd 100644 --- a/sdk/monitor/query/azmetrics/README.md +++ b/sdk/monitor/query/azmetrics/README.md @@ -6,7 +6,7 @@ Source code | Package (pkg.go.dev) | [REST API documentation][monitor_rest_docs] ### Prerequisites -* Go, version 1.18 or higher - [Install Go](https://go.dev/doc/install) +* Go version 1.18 or higher - [Install Go](https://go.dev/doc/install) * Azure subscription - [Create a free account][azure_sub] * The resource URI of an Azure resource (Storage Account, Key Vault, CosmosDB, etc.) that you plan to monitor @@ -19,7 +19,7 @@ go get github.com/Azure/azure-sdk-for-go/sdk/monitor/query/azmetrics go get github.com/Azure/azure-sdk-for-go/sdk/azidentity ``` -The [azidentity][azure_identity] module is used for Azure Active Directory authentication during client construction. +The [azidentity][azure_identity] module is used for client authentication. ### Authentication @@ -64,7 +64,6 @@ comments. [azure_identity]: https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity [azure_sub]: https://azure.microsoft.com/free/ [azure_monitor_overview]: https://learn.microsoft.com/azure/azure-monitor/overview -[context]: https://pkg.go.dev/context [cloud_documentation]: https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud [default_cred_ref]: https://github.com/Azure/azure-sdk-for-go/tree/main/sdk/azidentity#defaultazurecredential [go_samples]: (https://github.com/Azure-Samples/azure-sdk-for-go-samples) diff --git a/sdk/monitor/query/azmetrics/ci.yml b/sdk/monitor/query/azmetrics/ci.yml index bc141f160acd..78cdfcfc1465 100644 --- a/sdk/monitor/query/azmetrics/ci.yml +++ b/sdk/monitor/query/azmetrics/ci.yml @@ -28,4 +28,4 @@ stages: ServiceDirectory: 'monitor/query/azmetrics' RunLiveTests: true UsePipelineProxy: false - SupportedClouds: 'Public,UsGov,China' + SupportedClouds: 'Public' diff --git a/sdk/monitor/query/azmetrics/custom_client.go b/sdk/monitor/query/azmetrics/custom_client.go index b0c7fa0a9881..9227412e4580 100644 --- a/sdk/monitor/query/azmetrics/custom_client.go +++ b/sdk/monitor/query/azmetrics/custom_client.go @@ -30,7 +30,7 @@ func NewClient(endpoint string, credential azcore.TokenCredential, options *Clie options = &ClientOptions{} } - authPolicy := runtime.NewBearerTokenPolicy(credential, []string{"https://metrics.monitor.azure.com" + "/.default"}, nil) + authPolicy := runtime.NewBearerTokenPolicy(credential, []string{"https://metrics.monitor.azure.com/.default"}, nil) azcoreClient, err := azcore.NewClient(moduleName, version, runtime.PipelineOptions{PerRetry: []policy.Policy{authPolicy}}, &options.ClientOptions) if err != nil { return nil, err diff --git a/sdk/monitor/query/azmetrics/utils_test.go b/sdk/monitor/query/azmetrics/utils_test.go index bbbbbb4dba23..3b8ea71950c8 100644 --- a/sdk/monitor/query/azmetrics/utils_test.go +++ b/sdk/monitor/query/azmetrics/utils_test.go @@ -23,10 +23,12 @@ import ( "github.com/stretchr/testify/require" ) -const recordingDirectory = "sdk/monitor/query/azmetrics/testdata" -const fakeResourceURI = "/subscriptions/faa080af-c1d8-40ad-9cce-e1a451va7b87/resourceGroups/rg-example/providers/Microsoft.AppConfiguration/configurationStores/example" -const fakeSubscrtiptionID = "faa080af-c1d8-40ad-9cce-e1a451va7b87" -const fakeRegion = "westus" +const ( + recordingDirectory = "sdk/monitor/query/azmetrics/testdata" + fakeResourceURI = "/subscriptions/faa080af-c1d8-40ad-9cce-e1a451va7b87/resourceGroups/rg-example/providers/Microsoft.AppConfiguration/configurationStores/example" + fakeSubscrtiptionID = "faa080af-c1d8-40ad-9cce-e1a451va7b87" + fakeRegion = "westus" +) var ( credential azcore.TokenCredential @@ -90,9 +92,7 @@ func startTest(t *testing.T) *azmetrics.Client { opts := &azmetrics.ClientOptions{ClientOptions: azcore.ClientOptions{Transport: transport}} endpoint := "https://" + region + ".metrics.monitor.azure.com" client, err := azmetrics.NewClient(endpoint, credential, opts) - if err != nil { - panic(err) - } + require.NoError(t, err) return client } From 44be96d7592def555a5c9da7615dc5e1125a691c Mon Sep 17 00:00:00 2001 From: gracewilcox Date: Tue, 13 Feb 2024 19:20:12 -0800 Subject: [PATCH 8/8] charles feedback --- sdk/monitor/query/azmetrics/CHANGELOG.md | 2 +- sdk/monitor/query/azmetrics/README.md | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/sdk/monitor/query/azmetrics/CHANGELOG.md b/sdk/monitor/query/azmetrics/CHANGELOG.md index df342e801404..c785d006d045 100644 --- a/sdk/monitor/query/azmetrics/CHANGELOG.md +++ b/sdk/monitor/query/azmetrics/CHANGELOG.md @@ -1,5 +1,5 @@ # Release History -## 0.1.0 (2024-02-13) +## 0.1.0 (2024-02-14) * This is the initial release of the `azmetrics` module \ No newline at end of file diff --git a/sdk/monitor/query/azmetrics/README.md b/sdk/monitor/query/azmetrics/README.md index aa6a66bcb7fd..f22f0a4476dd 100644 --- a/sdk/monitor/query/azmetrics/README.md +++ b/sdk/monitor/query/azmetrics/README.md @@ -63,12 +63,8 @@ comments. [azure_identity]: https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity [azure_sub]: https://azure.microsoft.com/free/ -[azure_monitor_overview]: https://learn.microsoft.com/azure/azure-monitor/overview [cloud_documentation]: https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud [default_cred_ref]: https://github.com/Azure/azure-sdk-for-go/tree/main/sdk/azidentity#defaultazurecredential -[go_samples]: (https://github.com/Azure-Samples/azure-sdk-for-go-samples) -[metrics_overview]: https://learn.microsoft.com/azure/azure-monitor/essentials/data-platform-metrics -[metric_namespaces]: https://learn.microsoft.com/azure/azure-monitor/reference/supported-metrics/metrics-index#metrics-by-resource-provider [monitor_docs]: https://learn.microsoft.com/azure/azure-monitor/ [monitor_rest_docs]: https://learn.microsoft.com/rest/api/monitor/ [multi-metrics]: https://learn.microsoft.com/azure/azure-monitor/essentials/data-platform-metrics#multi-dimensional-metrics