pax_global_header00006660000000000000000000000064131513351650014515gustar00rootroot0000000000000052 comment=5432abe734f8d95c78340cd56712f912906e6514 go-autorest-8.3.1/000077500000000000000000000000001315133516500137775ustar00rootroot00000000000000go-autorest-8.3.1/.gitignore000066400000000000000000000006641315133516500157750ustar00rootroot00000000000000# The standard Go .gitignore file follows. (Sourced from: github.com/github/gitignore/master/Go.gitignore) # Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe *.test *.prof # go-autorest specific vendor/ autorest/azure/example/example go-autorest-8.3.1/.travis.yml000066400000000000000000000010321315133516500161040ustar00rootroot00000000000000sudo: false language: go go: - 1.8 - 1.7 - 1.6 install: - go get -u github.com/golang/lint/golint - go get -u github.com/Masterminds/glide - go get -u github.com/stretchr/testify - go get -u github.com/GoASTScanner/gas - glide install script: - test -z "$(gofmt -s -l -w ./autorest/. | tee /dev/stderr)" - test -z "$(golint ./autorest/... | tee /dev/stderr)" - go vet ./autorest/... - test -z "$(gas ./autorest/... | tee /dev/stderr | grep Error)" - go build -v ./autorest/... - go test -v ./autorest/... go-autorest-8.3.1/CHANGELOG.md000066400000000000000000000140161315133516500156120ustar00rootroot00000000000000# CHANGELOG ## v7.3.0 - Exposing new `RespondDecorator`, `ByDiscardingBody`. This allows operations to acknowledge that they do not need either the entire or a trailing portion of accepts response body. In doing so, Go's http library can reuse HTTP connections more readily. - Adding `PrepareDecorator` to target custom BaseURLs. - Adding ACR suffix to public cloud environment. - Updating Glide dependencies. ## v7.2.5 - Fixed the Active Directory endpoint for the China cloud. - Removes UTF-8 BOM if present in response payload. - Added telemetry. ## v7.2.3 - Fixing bug in calls to `DelayForBackoff` that caused doubling of delay duration. ## v7.2.2 - autorest/azure: added ASM and ARM VM DNS suffixes. ## v7.2.1 - fixed parsing of UTC times that are not RFC3339 conformant. ## v7.2.0 - autorest/validation: Reformat validation error for better error message. ## v7.1.0 - preparer: Added support for multipart formdata - WithMultiPartFormdata() - preparer: Added support for sending file in request body - WithFile - client: Added RetryDuration parameter. - autorest/validation: new package for validation code for Azure Go SDK. ## v7.0.7 - Add trailing / to endpoint - azure: add EnvironmentFromName ## v7.0.6 - Add retry logic for 408, 500, 502, 503 and 504 status codes. - Change url path and query encoding logic. - Fix DelayForBackoff for proper exponential delay. - Add CookieJar in Client. ## v7.0.5 - Add check to start polling only when status is in [200,201,202]. - Refactoring for unchecked errors. - azure/persist changes. - Fix 'file in use' issue in renewing token in deviceflow. - Store header RetryAfter for subsequent requests in polling. - Add attribute details in service error. ## v7.0.4 - Better error messages for long running operation failures ## v7.0.3 - Corrected DoPollForAsynchronous to properly handle the initial response ## v7.0.2 - Corrected DoPollForAsynchronous to continue using the polling method first discovered ## v7.0.1 - Fixed empty JSON input error in ByUnmarshallingJSON - Fixed polling support for GET calls - Changed format name from TimeRfc1123 to TimeRFC1123 ## v7.0.0 - Added ByCopying responder with supporting TeeReadCloser - Rewrote Azure asynchronous handling - Reverted to only unmarshalling JSON - Corrected handling of RFC3339 time strings and added support for Rfc1123 time format The `json.Decoder` does not catch bad data as thoroughly as `json.Unmarshal`. Since `encoding/json` successfully deserializes all core types, and extended types normally provide their custom JSON serialization handlers, the code has been reverted back to using `json.Unmarshal`. The original change to use `json.Decode` was made to reduce duplicate code; there is no loss of function, and there is a gain in accuracy, by reverting. Additionally, Azure services indicate requests to be polled by multiple means. The existing code only checked for one of those (that is, the presence of the `Azure-AsyncOperation` header). The new code correctly covers all cases and aligns with the other Azure SDKs. ## v6.1.0 - Introduced `date.ByUnmarshallingJSONDate` and `date.ByUnmarshallingJSONTime` to enable JSON encoded values. ## v6.0.0 - Completely reworked the handling of polled and asynchronous requests - Removed unnecessary routines - Reworked `mocks.Sender` to replay a series of `http.Response` objects - Added `PrepareDecorators` for primitive types (e.g., bool, int32) Handling polled and asynchronous requests is no longer part of `Client#Send`. Instead new `SendDecorators` implement different styles of polled behavior. See`autorest.DoPollForStatusCodes` and `azure.DoPollForAsynchronous` for examples. ## v5.0.0 - Added new RespondDecorators unmarshalling primitive types - Corrected application of inspection and authorization PrependDecorators ## v4.0.0 - Added support for Azure long-running operations. - Added cancelation support to all decorators and functions that may delay. - Breaking: `DelayForBackoff` now accepts a channel, which may be nil. ## v3.1.0 - Add support for OAuth Device Flow authorization. - Add support for ServicePrincipalTokens that are backed by an existing token, rather than other secret material. - Add helpers for persisting and restoring Tokens. - Increased code coverage in the github.com/Azure/autorest/azure package ## v3.0.0 - Breaking: `NewErrorWithError` no longer takes `statusCode int`. - Breaking: `NewErrorWithStatusCode` is replaced with `NewErrorWithResponse`. - Breaking: `Client#Send()` no longer takes `codes ...int` argument. - Add: XML unmarshaling support with `ByUnmarshallingXML()` - Stopped vending dependencies locally and switched to [Glide](https://github.com/Masterminds/glide). Applications using this library should either use Glide or vendor dependencies locally some other way. - Add: `azure.WithErrorUnlessStatusCode()` decorator to handle Azure errors. - Fix: use `net/http.DefaultClient` as base client. - Fix: Missing inspection for polling responses added. - Add: CopyAndDecode helpers. - Improved `./autorest/to` with `[]string` helpers. - Removed golint suppressions in .travis.yml. ## v2.1.0 - Added `StatusCode` to `Error` for more easily obtaining the HTTP Reponse StatusCode (if any) ## v2.0.0 - Changed `to.StringMapPtr` method signature to return a pointer - Changed `ServicePrincipalCertificateSecret` and `NewServicePrincipalTokenFromCertificate` to support generic certificate and private keys ## v1.0.0 - Added Logging inspectors to trace http.Request / Response - Added support for User-Agent header - Changed WithHeader PrepareDecorator to use set vs. add - Added JSON to error when unmarshalling fails - Added Client#Send method - Corrected case of "Azure" in package paths - Added "to" helpers, Azure helpers, and improved ease-of-use - Corrected golint issues ## v1.0.1 - Added CHANGELOG.md ## v1.1.0 - Added mechanism to retrieve a ServicePrincipalToken using a certificate-signed JWT - Added an example of creating a certificate-based ServicePrincipal and retrieving an OAuth token using the certificate ## v1.1.1 - Introduce godeps and vendor dependencies introduced in v1.1.1 go-autorest-8.3.1/LICENSE000066400000000000000000000250171315133516500150110ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS Copyright 2015 Microsoft Corporation 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. go-autorest-8.3.1/README.md000066400000000000000000000125251315133516500152630ustar00rootroot00000000000000# go-autorest [![GoDoc](https://godoc.org/github.com/Azure/go-autorest/autorest?status.png)](https://godoc.org/github.com/Azure/go-autorest/autorest) [![Build Status](https://travis-ci.org/Azure/go-autorest.svg?branch=master)](https://travis-ci.org/Azure/go-autorest) [![Go Report Card](https://goreportcard.com/badge/Azure/go-autorest)](https://goreportcard.com/report/Azure/go-autorest) ## Usage Package autorest implements an HTTP request pipeline suitable for use across multiple go-routines and provides the shared routines relied on by AutoRest (see https://github.com/Azure/autorest/) generated Go code. The package breaks sending and responding to HTTP requests into three phases: Preparing, Sending, and Responding. A typical pattern is: ```go req, err := Prepare(&http.Request{}, token.WithAuthorization()) resp, err := Send(req, WithLogging(logger), DoErrorIfStatusCode(http.StatusInternalServerError), DoCloseIfError(), DoRetryForAttempts(5, time.Second)) err = Respond(resp, ByDiscardingBody(), ByClosing()) ``` Each phase relies on decorators to modify and / or manage processing. Decorators may first modify and then pass the data along, pass the data first and then modify the result, or wrap themselves around passing the data (such as a logger might do). Decorators run in the order provided. For example, the following: ```go req, err := Prepare(&http.Request{}, WithBaseURL("https://microsoft.com/"), WithPath("a"), WithPath("b"), WithPath("c")) ``` will set the URL to: ``` https://microsoft.com/a/b/c ``` Preparers and Responders may be shared and re-used (assuming the underlying decorators support sharing and re-use). Performant use is obtained by creating one or more Preparers and Responders shared among multiple go-routines, and a single Sender shared among multiple sending go-routines, all bound together by means of input / output channels. Decorators hold their passed state within a closure (such as the path components in the example above). Be careful to share Preparers and Responders only in a context where such held state applies. For example, it may not make sense to share a Preparer that applies a query string from a fixed set of values. Similarly, sharing a Responder that reads the response body into a passed struct (e.g., `ByUnmarshallingJson`) is likely incorrect. Errors raised by autorest objects and methods will conform to the `autorest.Error` interface. See the included examples for more detail. For details on the suggested use of this package by generated clients, see the Client described below. ## Helpers ### Handling Swagger Dates The Swagger specification (https://swagger.io) that drives AutoRest (https://github.com/Azure/autorest/) precisely defines two date forms: date and date-time. The github.com/Azure/go-autorest/autorest/date package provides time.Time derivations to ensure correct parsing and formatting. ### Handling Empty Values In JSON, missing values have different semantics than empty values. This is especially true for services using the HTTP PATCH verb. The JSON submitted with a PATCH request generally contains only those values to modify. Missing values are to be left unchanged. Developers, then, require a means to both specify an empty value and to leave the value out of the submitted JSON. The Go JSON package (`encoding/json`) supports the `omitempty` tag. When specified, it omits empty values from the rendered JSON. Since Go defines default values for all base types (such as "" for string and 0 for int) and provides no means to mark a value as actually empty, the JSON package treats default values as meaning empty, omitting them from the rendered JSON. This means that, using the Go base types encoded through the default JSON package, it is not possible to create JSON to clear a value at the server. The workaround within the Go community is to use pointers to base types in lieu of base types within structures that map to JSON. For example, instead of a value of type `string`, the workaround uses `*string`. While this enables distinguishing empty values from those to be unchanged, creating pointers to a base type (notably constant, in-line values) requires additional variables. This, for example, ```go s := struct { S *string }{ S: &"foo" } ``` fails, while, this ```go v := "foo" s := struct { S *string }{ S: &v } ``` succeeds. To ease using pointers, the subpackage `to` contains helpers that convert to and from pointers for Go base types which have Swagger analogs. It also provides a helper that converts between `map[string]string` and `map[string]*string`, enabling the JSON to specify that the value associated with a key should be cleared. With the helpers, the previous example becomes ```go s := struct { S *string }{ S: to.StringPtr("foo") } ``` ## Install ```bash go get github.com/Azure/go-autorest/autorest go get github.com/Azure/go-autorest/autorest/azure go get github.com/Azure/go-autorest/autorest/date go get github.com/Azure/go-autorest/autorest/to ``` ## License See LICENSE file. ----- This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. go-autorest-8.3.1/autorest/000077500000000000000000000000001315133516500156455ustar00rootroot00000000000000go-autorest-8.3.1/autorest/adal/000077500000000000000000000000001315133516500165465ustar00rootroot00000000000000go-autorest-8.3.1/autorest/adal/README.md000066400000000000000000000151731315133516500200340ustar00rootroot00000000000000# Azure Active Directory library for Go This project provides a stand alone Azure Active Directory library for Go. The code was extracted from [go-autorest](https://github.com/Azure/go-autorest/) project, which is used as a base for [azure-sdk-for-go](https://github.com/Azure/azure-sdk-for-go). ## Installation ``` go get -u github.com/Azure/go-autorest/autorest/adal ``` ## Usage An Active Directory application is required in order to use this library. An application can be registered in the [Azure Portal](https://portal.azure.com/) follow these [guidelines](https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-integrating-applications) or using the [Azure CLI](https://github.com/Azure/azure-cli). ### Register an Azure AD Application with secret 1. Register a new application with a `secret` credential ``` az ad app create \ --display-name example-app \ --homepage https://example-app/home \ --identifier-uris https://example-app/app \ --password secret ``` 2. Create a service principal using the `Application ID` from previous step ``` az ad sp create --id "Application ID" ``` * Replace `Application ID` with `appId` from step 1. ### Register an Azure AD Application with certificate 1. Create a private key ``` openssl genrsa -out "example-app.key" 2048 ``` 2. Create the certificate ``` openssl req -new -key "example-app.key" -subj "/CN=example-app" -out "example-app.csr" openssl x509 -req -in "example-app.csr" -signkey "example-app.key" -out "example-app.crt" -days 10000 ``` 3. Create the PKCS12 version of the certificate containing also the private key ``` openssl pkcs12 -export -out "example-app.pfx" -inkey "example-app.key" -in "example-app.crt" -passout pass: ``` 4. Register a new application with the certificate content form `example-app.crt` ``` certificateContents="$(tail -n+2 "example-app.crt" | head -n-1)" az ad app create \ --display-name example-app \ --homepage https://example-app/home \ --identifier-uris https://example-app/app \ --key-usage Verify --end-date 2018-01-01 \ --key-value "${certificateContents}" ``` 5. Create a service principal using the `Application ID` from previous step ``` az ad sp create --id "APPLICATION_ID" ``` * Replace `APPLICATION_ID` with `appId` from step 4. ### Grant the necessary permissions Azure relies on a Role-Based Access Control (RBAC) model to manage the access to resources at a fine-grained level. There is a set of [pre-defined roles](https://docs.microsoft.com/en-us/azure/active-directory/role-based-access-built-in-roles) which can be assigned to a service principal of an Azure AD application depending of your needs. ``` az role assignment create --assigner "SERVICE_PRINCIPAL_ID" --role "ROLE_NAME" ``` * Replace the `SERVICE_PRINCIPAL_ID` with the `appId` from previous step. * Replace the `ROLE_NAME` with a role name of your choice. It is also possible to define custom role definitions. ``` az role definition create --role-definition role-definition.json ``` * Check [custom roles](https://docs.microsoft.com/en-us/azure/active-directory/role-based-access-control-custom-roles) for more details regarding the content of `role-definition.json` file. ### Acquire Access Token The common configuration used by all flows: ```Go const activeDirectoryEndpoint = "https://login.microsoftonline.com/" tenantID := "TENANT_ID" oauthConfig, err := adal.NewOAuthConfig(activeDirectoryEndpoint, tenantID) applicationID := "APPLICATION_ID" callback := func(token adal.Token) error { // This is called after the token is acquired } // The resource for which the token is acquired resource := "https://management.core.windows.net/" ``` * Replace the `TENANT_ID` with your tenant ID. * Replace the `APPLICATION_ID` with the value from previous section. #### Client Credentials ```Go applicationSecret := "APPLICATION_SECRET" spt, err := adal.NewServicePrincipalToken( oauthConfig, appliationID, applicationSecret, resource, callbacks...) if err != nil { return nil, err } // Acquire a new access token err = spt.Refresh() if (err == nil) { token := spt.Token } ``` * Replace the `APPLICATION_SECRET` with the `password` value from previous section. #### Client Certificate ```Go certificatePath := "./example-app.pfx" certData, err := ioutil.ReadFile(certificatePath) if err != nil { return nil, fmt.Errorf("failed to read the certificate file (%s): %v", certificatePath, err) } // Get the certificate and private key from pfx file certificate, rsaPrivateKey, err := decodePkcs12(certData, "") if err != nil { return nil, fmt.Errorf("failed to decode pkcs12 certificate while creating spt: %v", err) } spt, err := adal.NewServicePrincipalTokenFromCertificate( oauthConfig, applicationID, certificate, rsaPrivateKey, resource, callbacks...) // Acquire a new access token err = spt.Refresh() if (err == nil) { token := spt.Token } ``` * Update the certificate path to point to the example-app.pfx file which was created in previous section. #### Device Code ```Go oauthClient := &http.Client{} // Acquire the device code deviceCode, err := adal.InitiateDeviceAuth( oauthClient, oauthConfig, applicationID, resource) if err != nil { return nil, fmt.Errorf("Failed to start device auth flow: %s", err) } // Display the authentication message fmt.Println(*deviceCode.Message) // Wait here until the user is authenticated token, err := adal.WaitForUserCompletion(oauthClient, deviceCode) if err != nil { return nil, fmt.Errorf("Failed to finish device auth flow: %s", err) } spt, err := adal.NewServicePrincipalTokenFromManualToken( oauthConfig, applicationID, resource, *token, callbacks...) if (err == nil) { token := spt.Token } ``` ### Command Line Tool A command line tool is available in `cmd/adal.go` that can acquire a token for a given resource. It supports all flows mentioned above. ``` adal -h Usage of ./adal: -applicationId string application id -certificatePath string path to pk12/PFC application certificate -mode string authentication mode (device, secret, cert, refresh) (default "device") -resource string resource for which the token is requested -secret string application secret -tenantId string tenant id -tokenCachePath string location of oath token cache (default "/home/cgc/.adal/accessToken.json") ``` Example acquire a token for `https://management.core.windows.net/` using device code flow: ``` adal -mode device \ -applicationId "APPLICATION_ID" \ -tenantId "TENANT_ID" \ -resource https://management.core.windows.net/ ``` go-autorest-8.3.1/autorest/adal/cmd/000077500000000000000000000000001315133516500173115ustar00rootroot00000000000000go-autorest-8.3.1/autorest/adal/cmd/adal.go000066400000000000000000000154361315133516500205520ustar00rootroot00000000000000package main import ( "flag" "fmt" "log" "strings" "crypto/rsa" "crypto/x509" "io/ioutil" "net/http" "os/user" "github.com/Azure/go-autorest/autorest/adal" "golang.org/x/crypto/pkcs12" ) const ( deviceMode = "device" clientSecretMode = "secret" clientCertMode = "cert" refreshMode = "refresh" activeDirectoryEndpoint = "https://login.microsoftonline.com/" ) type option struct { name string value string } var ( mode string resource string tenantID string applicationID string applicationSecret string certificatePath string tokenCachePath string ) func checkMandatoryOptions(mode string, options ...option) { for _, option := range options { if strings.TrimSpace(option.value) == "" { log.Fatalf("Authentication mode '%s' requires mandatory option '%s'.", mode, option.name) } } } func defaultTokenCachePath() string { usr, err := user.Current() if err != nil { log.Fatal(err) } defaultTokenPath := usr.HomeDir + "/.adal/accessToken.json" return defaultTokenPath } func init() { flag.StringVar(&mode, "mode", "device", "authentication mode (device, secret, cert, refresh)") flag.StringVar(&resource, "resource", "", "resource for which the token is requested") flag.StringVar(&tenantID, "tenantId", "", "tenant id") flag.StringVar(&applicationID, "applicationId", "", "application id") flag.StringVar(&applicationSecret, "secret", "", "application secret") flag.StringVar(&certificatePath, "certificatePath", "", "path to pk12/PFC application certificate") flag.StringVar(&tokenCachePath, "tokenCachePath", defaultTokenCachePath(), "location of oath token cache") flag.Parse() switch mode = strings.TrimSpace(mode); mode { case clientSecretMode: checkMandatoryOptions(clientSecretMode, option{name: "resource", value: resource}, option{name: "tenantId", value: tenantID}, option{name: "applicationId", value: applicationID}, option{name: "secret", value: applicationSecret}, ) case clientCertMode: checkMandatoryOptions(clientCertMode, option{name: "resource", value: resource}, option{name: "tenantId", value: tenantID}, option{name: "applicationId", value: applicationID}, option{name: "certificatePath", value: certificatePath}, ) case deviceMode: checkMandatoryOptions(deviceMode, option{name: "resource", value: resource}, option{name: "tenantId", value: tenantID}, option{name: "applicationId", value: applicationID}, ) case refreshMode: checkMandatoryOptions(refreshMode, option{name: "resource", value: resource}, option{name: "tenantId", value: tenantID}, option{name: "applicationId", value: applicationID}, ) default: log.Fatalln("Authentication modes 'secret, 'cert', 'device' or 'refresh' are supported.") } } func acquireTokenClientSecretFlow(oauthConfig adal.OAuthConfig, appliationID string, applicationSecret string, resource string, callbacks ...adal.TokenRefreshCallback) (*adal.ServicePrincipalToken, error) { spt, err := adal.NewServicePrincipalToken( oauthConfig, appliationID, applicationSecret, resource, callbacks...) if err != nil { return nil, err } return spt, spt.Refresh() } func decodePkcs12(pkcs []byte, password string) (*x509.Certificate, *rsa.PrivateKey, error) { privateKey, certificate, err := pkcs12.Decode(pkcs, password) if err != nil { return nil, nil, err } rsaPrivateKey, isRsaKey := privateKey.(*rsa.PrivateKey) if !isRsaKey { return nil, nil, fmt.Errorf("PKCS#12 certificate must contain an RSA private key") } return certificate, rsaPrivateKey, nil } func acquireTokenClientCertFlow(oauthConfig adal.OAuthConfig, applicationID string, applicationCertPath string, resource string, callbacks ...adal.TokenRefreshCallback) (*adal.ServicePrincipalToken, error) { certData, err := ioutil.ReadFile(certificatePath) if err != nil { return nil, fmt.Errorf("failed to read the certificate file (%s): %v", certificatePath, err) } certificate, rsaPrivateKey, err := decodePkcs12(certData, "") if err != nil { return nil, fmt.Errorf("failed to decode pkcs12 certificate while creating spt: %v", err) } spt, err := adal.NewServicePrincipalTokenFromCertificate( oauthConfig, applicationID, certificate, rsaPrivateKey, resource, callbacks...) if err != nil { return nil, err } return spt, spt.Refresh() } func acquireTokenDeviceCodeFlow(oauthConfig adal.OAuthConfig, applicationID string, resource string, callbacks ...adal.TokenRefreshCallback) (*adal.ServicePrincipalToken, error) { oauthClient := &http.Client{} deviceCode, err := adal.InitiateDeviceAuth( oauthClient, oauthConfig, applicationID, resource) if err != nil { return nil, fmt.Errorf("Failed to start device auth flow: %s", err) } fmt.Println(*deviceCode.Message) token, err := adal.WaitForUserCompletion(oauthClient, deviceCode) if err != nil { return nil, fmt.Errorf("Failed to finish device auth flow: %s", err) } spt, err := adal.NewServicePrincipalTokenFromManualToken( oauthConfig, applicationID, resource, *token, callbacks...) return spt, err } func refreshToken(oauthConfig adal.OAuthConfig, applicationID string, resource string, tokenCachePath string, callbacks ...adal.TokenRefreshCallback) (*adal.ServicePrincipalToken, error) { token, err := adal.LoadToken(tokenCachePath) if err != nil { return nil, fmt.Errorf("failed to load token from cache: %v", err) } spt, err := adal.NewServicePrincipalTokenFromManualToken( oauthConfig, applicationID, resource, *token, callbacks...) if err != nil { return nil, err } return spt, spt.Refresh() } func saveToken(spt adal.Token) error { if tokenCachePath != "" { err := adal.SaveToken(tokenCachePath, 0600, spt) if err != nil { return err } log.Printf("Acquired token was saved in '%s' file\n", tokenCachePath) return nil } return fmt.Errorf("empty path for token cache") } func main() { oauthConfig, err := adal.NewOAuthConfig(activeDirectoryEndpoint, tenantID) if err != nil { panic(err) } callback := func(token adal.Token) error { return saveToken(token) } log.Printf("Authenticating with mode '%s'\n", mode) switch mode { case clientSecretMode: _, err = acquireTokenClientSecretFlow( *oauthConfig, applicationID, applicationSecret, resource, callback) case clientCertMode: _, err = acquireTokenClientCertFlow( *oauthConfig, applicationID, certificatePath, resource, callback) case deviceMode: var spt *adal.ServicePrincipalToken spt, err = acquireTokenDeviceCodeFlow( *oauthConfig, applicationID, resource, callback) if err == nil { err = saveToken(spt.Token) } case refreshMode: _, err = refreshToken( *oauthConfig, applicationID, resource, tokenCachePath, callback) } if err != nil { log.Fatalf("Failed to acquire a token for resource %s. Error: %v", resource, err) } } go-autorest-8.3.1/autorest/adal/config.go000066400000000000000000000025071315133516500203460ustar00rootroot00000000000000package adal import ( "fmt" "net/url" ) const ( activeDirectoryAPIVersion = "1.0" ) // OAuthConfig represents the endpoints needed // in OAuth operations type OAuthConfig struct { AuthorityEndpoint url.URL AuthorizeEndpoint url.URL TokenEndpoint url.URL DeviceCodeEndpoint url.URL } // NewOAuthConfig returns an OAuthConfig with tenant specific urls func NewOAuthConfig(activeDirectoryEndpoint, tenantID string) (*OAuthConfig, error) { const activeDirectoryEndpointTemplate = "%s/oauth2/%s?api-version=%s" u, err := url.Parse(activeDirectoryEndpoint) if err != nil { return nil, err } authorityURL, err := u.Parse(tenantID) if err != nil { return nil, err } authorizeURL, err := u.Parse(fmt.Sprintf(activeDirectoryEndpointTemplate, tenantID, "authorize", activeDirectoryAPIVersion)) if err != nil { return nil, err } tokenURL, err := u.Parse(fmt.Sprintf(activeDirectoryEndpointTemplate, tenantID, "token", activeDirectoryAPIVersion)) if err != nil { return nil, err } deviceCodeURL, err := u.Parse(fmt.Sprintf(activeDirectoryEndpointTemplate, tenantID, "devicecode", activeDirectoryAPIVersion)) if err != nil { return nil, err } return &OAuthConfig{ AuthorityEndpoint: *authorityURL, AuthorizeEndpoint: *authorizeURL, TokenEndpoint: *tokenURL, DeviceCodeEndpoint: *deviceCodeURL, }, nil } go-autorest-8.3.1/autorest/adal/config_test.go000066400000000000000000000023101315133516500213750ustar00rootroot00000000000000package adal import ( "testing" ) func TestNewOAuthConfig(t *testing.T) { const testActiveDirectoryEndpoint = "https://login.test.com" const testTenantID = "tenant-id-test" config, err := NewOAuthConfig(testActiveDirectoryEndpoint, testTenantID) if err != nil { t.Fatalf("autorest/adal: Unexpected error while creating oauth configuration for tenant: %v.", err) } expected := "https://login.test.com/tenant-id-test/oauth2/authorize?api-version=1.0" if config.AuthorizeEndpoint.String() != expected { t.Fatalf("autorest/adal: Incorrect authorize url for Tenant from Environment. expected(%s). actual(%v).", expected, config.AuthorizeEndpoint) } expected = "https://login.test.com/tenant-id-test/oauth2/token?api-version=1.0" if config.TokenEndpoint.String() != expected { t.Fatalf("autorest/adal: Incorrect authorize url for Tenant from Environment. expected(%s). actual(%v).", expected, config.TokenEndpoint) } expected = "https://login.test.com/tenant-id-test/oauth2/devicecode?api-version=1.0" if config.DeviceCodeEndpoint.String() != expected { t.Fatalf("autorest/adal Incorrect devicecode url for Tenant from Environment. expected(%s). actual(%v).", expected, config.DeviceCodeEndpoint) } } go-autorest-8.3.1/autorest/adal/devicetoken.go000066400000000000000000000175401315133516500214040ustar00rootroot00000000000000package adal /* This file is largely based on rjw57/oauth2device's code, with the follow differences: * scope -> resource, and only allow a single one * receive "Message" in the DeviceCode struct and show it to users as the prompt * azure-xplat-cli has the following behavior that this emulates: - does not send client_secret during the token exchange - sends resource again in the token exchange request */ import ( "encoding/json" "fmt" "io/ioutil" "net/http" "net/url" "strings" "time" ) const ( logPrefix = "autorest/adal/devicetoken:" ) var ( // ErrDeviceGeneric represents an unknown error from the token endpoint when using device flow ErrDeviceGeneric = fmt.Errorf("%s Error while retrieving OAuth token: Unknown Error", logPrefix) // ErrDeviceAccessDenied represents an access denied error from the token endpoint when using device flow ErrDeviceAccessDenied = fmt.Errorf("%s Error while retrieving OAuth token: Access Denied", logPrefix) // ErrDeviceAuthorizationPending represents the server waiting on the user to complete the device flow ErrDeviceAuthorizationPending = fmt.Errorf("%s Error while retrieving OAuth token: Authorization Pending", logPrefix) // ErrDeviceCodeExpired represents the server timing out and expiring the code during device flow ErrDeviceCodeExpired = fmt.Errorf("%s Error while retrieving OAuth token: Code Expired", logPrefix) // ErrDeviceSlowDown represents the service telling us we're polling too often during device flow ErrDeviceSlowDown = fmt.Errorf("%s Error while retrieving OAuth token: Slow Down", logPrefix) // ErrDeviceCodeEmpty represents an empty device code from the device endpoint while using device flow ErrDeviceCodeEmpty = fmt.Errorf("%s Error while retrieving device code: Device Code Empty", logPrefix) // ErrOAuthTokenEmpty represents an empty OAuth token from the token endpoint when using device flow ErrOAuthTokenEmpty = fmt.Errorf("%s Error while retrieving OAuth token: Token Empty", logPrefix) errCodeSendingFails = "Error occurred while sending request for Device Authorization Code" errCodeHandlingFails = "Error occurred while handling response from the Device Endpoint" errTokenSendingFails = "Error occurred while sending request with device code for a token" errTokenHandlingFails = "Error occurred while handling response from the Token Endpoint (during device flow)" errStatusNotOK = "Error HTTP status != 200" ) // DeviceCode is the object returned by the device auth endpoint // It contains information to instruct the user to complete the auth flow type DeviceCode struct { DeviceCode *string `json:"device_code,omitempty"` UserCode *string `json:"user_code,omitempty"` VerificationURL *string `json:"verification_url,omitempty"` ExpiresIn *int64 `json:"expires_in,string,omitempty"` Interval *int64 `json:"interval,string,omitempty"` Message *string `json:"message"` // Azure specific Resource string // store the following, stored when initiating, used when exchanging OAuthConfig OAuthConfig ClientID string } // TokenError is the object returned by the token exchange endpoint // when something is amiss type TokenError struct { Error *string `json:"error,omitempty"` ErrorCodes []int `json:"error_codes,omitempty"` ErrorDescription *string `json:"error_description,omitempty"` Timestamp *string `json:"timestamp,omitempty"` TraceID *string `json:"trace_id,omitempty"` } // DeviceToken is the object return by the token exchange endpoint // It can either look like a Token or an ErrorToken, so put both here // and check for presence of "Error" to know if we are in error state type deviceToken struct { Token TokenError } // InitiateDeviceAuth initiates a device auth flow. It returns a DeviceCode // that can be used with CheckForUserCompletion or WaitForUserCompletion. func InitiateDeviceAuth(sender Sender, oauthConfig OAuthConfig, clientID, resource string) (*DeviceCode, error) { v := url.Values{ "client_id": []string{clientID}, "resource": []string{resource}, } s := v.Encode() body := ioutil.NopCloser(strings.NewReader(s)) req, err := http.NewRequest(http.MethodPost, oauthConfig.DeviceCodeEndpoint.String(), body) if err != nil { return nil, fmt.Errorf("%s %s: %s", logPrefix, errCodeSendingFails, err.Error()) } req.ContentLength = int64(len(s)) req.Header.Set(contentType, mimeTypeFormPost) resp, err := sender.Do(req) if err != nil { return nil, fmt.Errorf("%s %s: %s", logPrefix, errCodeSendingFails, err.Error()) } defer resp.Body.Close() rb, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("%s %s: %s", logPrefix, errCodeHandlingFails, err.Error()) } if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("%s %s: %s", logPrefix, errCodeHandlingFails, errStatusNotOK) } if len(strings.Trim(string(rb), " ")) == 0 { return nil, ErrDeviceCodeEmpty } var code DeviceCode err = json.Unmarshal(rb, &code) if err != nil { return nil, fmt.Errorf("%s %s: %s", logPrefix, errCodeHandlingFails, err.Error()) } code.ClientID = clientID code.Resource = resource code.OAuthConfig = oauthConfig return &code, nil } // CheckForUserCompletion takes a DeviceCode and checks with the Azure AD OAuth endpoint // to see if the device flow has: been completed, timed out, or otherwise failed func CheckForUserCompletion(sender Sender, code *DeviceCode) (*Token, error) { v := url.Values{ "client_id": []string{code.ClientID}, "code": []string{*code.DeviceCode}, "grant_type": []string{OAuthGrantTypeDeviceCode}, "resource": []string{code.Resource}, } s := v.Encode() body := ioutil.NopCloser(strings.NewReader(s)) req, err := http.NewRequest(http.MethodPost, code.OAuthConfig.TokenEndpoint.String(), body) if err != nil { return nil, fmt.Errorf("%s %s: %s", logPrefix, errTokenSendingFails, err.Error()) } req.ContentLength = int64(len(s)) req.Header.Set(contentType, mimeTypeFormPost) resp, err := sender.Do(req) if err != nil { return nil, fmt.Errorf("%s %s: %s", logPrefix, errTokenSendingFails, err.Error()) } defer resp.Body.Close() rb, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("%s %s: %s", logPrefix, errTokenHandlingFails, err.Error()) } if resp.StatusCode != http.StatusOK && len(strings.Trim(string(rb), " ")) == 0 { return nil, fmt.Errorf("%s %s: %s", logPrefix, errTokenHandlingFails, errStatusNotOK) } if len(strings.Trim(string(rb), " ")) == 0 { return nil, ErrOAuthTokenEmpty } var token deviceToken err = json.Unmarshal(rb, &token) if err != nil { return nil, fmt.Errorf("%s %s: %s", logPrefix, errTokenHandlingFails, err.Error()) } if token.Error == nil { return &token.Token, nil } switch *token.Error { case "authorization_pending": return nil, ErrDeviceAuthorizationPending case "slow_down": return nil, ErrDeviceSlowDown case "access_denied": return nil, ErrDeviceAccessDenied case "code_expired": return nil, ErrDeviceCodeExpired default: return nil, ErrDeviceGeneric } } // WaitForUserCompletion calls CheckForUserCompletion repeatedly until a token is granted or an error state occurs. // This prevents the user from looping and checking against 'ErrDeviceAuthorizationPending'. func WaitForUserCompletion(sender Sender, code *DeviceCode) (*Token, error) { intervalDuration := time.Duration(*code.Interval) * time.Second waitDuration := intervalDuration for { token, err := CheckForUserCompletion(sender, code) if err == nil { return token, nil } switch err { case ErrDeviceSlowDown: waitDuration += waitDuration case ErrDeviceAuthorizationPending: // noop default: // everything else is "fatal" to us return nil, err } if waitDuration > (intervalDuration * 3) { return nil, fmt.Errorf("%s Error waiting for user to complete device flow. Server told us to slow_down too much", logPrefix) } time.Sleep(waitDuration) } } go-autorest-8.3.1/autorest/adal/devicetoken_test.go000066400000000000000000000233751315133516500224460ustar00rootroot00000000000000package adal import ( "encoding/json" "fmt" "net/http" "strings" "testing" "github.com/Azure/go-autorest/autorest/mocks" ) const ( TestResource = "SomeResource" TestClientID = "SomeClientID" TestTenantID = "SomeTenantID" TestActiveDirectoryEndpoint = "https://login.test.com/" ) var ( testOAuthConfig, _ = NewOAuthConfig(TestActiveDirectoryEndpoint, TestTenantID) TestOAuthConfig = *testOAuthConfig ) const MockDeviceCodeResponse = ` { "device_code": "10000-40-1234567890", "user_code": "ABCDEF", "verification_url": "http://aka.ms/deviceauth", "expires_in": "900", "interval": "0" } ` const MockDeviceTokenResponse = `{ "access_token": "accessToken", "refresh_token": "refreshToken", "expires_in": "1000", "expires_on": "2000", "not_before": "3000", "resource": "resource", "token_type": "type" } ` func TestDeviceCodeIncludesResource(t *testing.T) { sender := mocks.NewSender() sender.AppendResponse(mocks.NewResponseWithContent(MockDeviceCodeResponse)) code, err := InitiateDeviceAuth(sender, TestOAuthConfig, TestClientID, TestResource) if err != nil { t.Fatalf("adal: unexpected error initiating device auth") } if code.Resource != TestResource { t.Fatalf("adal: InitiateDeviceAuth failed to stash the resource in the DeviceCode struct") } } func TestDeviceCodeReturnsErrorIfSendingFails(t *testing.T) { sender := mocks.NewSender() sender.SetError(fmt.Errorf("this is an error")) _, err := InitiateDeviceAuth(sender, TestOAuthConfig, TestClientID, TestResource) if err == nil || !strings.Contains(err.Error(), errCodeSendingFails) { t.Fatalf("adal: failed to get correct error expected(%s) actual(%s)", errCodeSendingFails, err.Error()) } } func TestDeviceCodeReturnsErrorIfBadRequest(t *testing.T) { sender := mocks.NewSender() body := mocks.NewBody("doesn't matter") sender.AppendResponse(mocks.NewResponseWithBodyAndStatus(body, http.StatusBadRequest, "Bad Request")) _, err := InitiateDeviceAuth(sender, TestOAuthConfig, TestClientID, TestResource) if err == nil || !strings.Contains(err.Error(), errCodeHandlingFails) { t.Fatalf("adal: failed to get correct error expected(%s) actual(%s)", errCodeHandlingFails, err.Error()) } if body.IsOpen() { t.Fatalf("response body was left open!") } } func TestDeviceCodeReturnsErrorIfCannotDeserializeDeviceCode(t *testing.T) { gibberishJSON := strings.Replace(MockDeviceCodeResponse, "expires_in", "\":, :gibberish", -1) sender := mocks.NewSender() body := mocks.NewBody(gibberishJSON) sender.AppendResponse(mocks.NewResponseWithBodyAndStatus(body, http.StatusOK, "OK")) _, err := InitiateDeviceAuth(sender, TestOAuthConfig, TestClientID, TestResource) if err == nil || !strings.Contains(err.Error(), errCodeHandlingFails) { t.Fatalf("adal: failed to get correct error expected(%s) actual(%s)", errCodeHandlingFails, err.Error()) } if body.IsOpen() { t.Fatalf("response body was left open!") } } func TestDeviceCodeReturnsErrorIfEmptyDeviceCode(t *testing.T) { sender := mocks.NewSender() body := mocks.NewBody("") sender.AppendResponse(mocks.NewResponseWithBodyAndStatus(body, http.StatusOK, "OK")) _, err := InitiateDeviceAuth(sender, TestOAuthConfig, TestClientID, TestResource) if err != ErrDeviceCodeEmpty { t.Fatalf("adal: failed to get correct error expected(%s) actual(%s)", ErrDeviceCodeEmpty, err.Error()) } if body.IsOpen() { t.Fatalf("response body was left open!") } } func deviceCode() *DeviceCode { var deviceCode DeviceCode _ = json.Unmarshal([]byte(MockDeviceCodeResponse), &deviceCode) deviceCode.Resource = TestResource deviceCode.ClientID = TestClientID return &deviceCode } func TestDeviceTokenReturns(t *testing.T) { sender := mocks.NewSender() body := mocks.NewBody(MockDeviceTokenResponse) sender.AppendResponse(mocks.NewResponseWithBodyAndStatus(body, http.StatusOK, "OK")) _, err := WaitForUserCompletion(sender, deviceCode()) if err != nil { t.Fatalf("adal: got error unexpectedly") } if body.IsOpen() { t.Fatalf("response body was left open!") } } func TestDeviceTokenReturnsErrorIfSendingFails(t *testing.T) { sender := mocks.NewSender() sender.SetError(fmt.Errorf("this is an error")) _, err := WaitForUserCompletion(sender, deviceCode()) if err == nil || !strings.Contains(err.Error(), errTokenSendingFails) { t.Fatalf("adal: failed to get correct error expected(%s) actual(%s)", errTokenSendingFails, err.Error()) } } func TestDeviceTokenReturnsErrorIfServerError(t *testing.T) { sender := mocks.NewSender() body := mocks.NewBody("") sender.AppendResponse(mocks.NewResponseWithBodyAndStatus(body, http.StatusInternalServerError, "Internal Server Error")) _, err := WaitForUserCompletion(sender, deviceCode()) if err == nil || !strings.Contains(err.Error(), errTokenHandlingFails) { t.Fatalf("adal: failed to get correct error expected(%s) actual(%s)", errTokenHandlingFails, err.Error()) } if body.IsOpen() { t.Fatalf("response body was left open!") } } func TestDeviceTokenReturnsErrorIfCannotDeserializeDeviceToken(t *testing.T) { gibberishJSON := strings.Replace(MockDeviceTokenResponse, "expires_in", ";:\"gibberish", -1) sender := mocks.NewSender() body := mocks.NewBody(gibberishJSON) sender.AppendResponse(mocks.NewResponseWithBodyAndStatus(body, http.StatusOK, "OK")) _, err := WaitForUserCompletion(sender, deviceCode()) if err == nil || !strings.Contains(err.Error(), errTokenHandlingFails) { t.Fatalf("adal: failed to get correct error expected(%s) actual(%s)", errTokenHandlingFails, err.Error()) } if body.IsOpen() { t.Fatalf("response body was left open!") } } func errorDeviceTokenResponse(message string) string { return `{ "error": "` + message + `" }` } func TestDeviceTokenReturnsErrorIfAuthorizationPending(t *testing.T) { sender := mocks.NewSender() body := mocks.NewBody(errorDeviceTokenResponse("authorization_pending")) sender.AppendResponse(mocks.NewResponseWithBodyAndStatus(body, http.StatusBadRequest, "Bad Request")) _, err := CheckForUserCompletion(sender, deviceCode()) if err != ErrDeviceAuthorizationPending { t.Fatalf("!!!") } if body.IsOpen() { t.Fatalf("response body was left open!") } } func TestDeviceTokenReturnsErrorIfSlowDown(t *testing.T) { sender := mocks.NewSender() body := mocks.NewBody(errorDeviceTokenResponse("slow_down")) sender.AppendResponse(mocks.NewResponseWithBodyAndStatus(body, http.StatusBadRequest, "Bad Request")) _, err := CheckForUserCompletion(sender, deviceCode()) if err != ErrDeviceSlowDown { t.Fatalf("!!!") } if body.IsOpen() { t.Fatalf("response body was left open!") } } type deviceTokenSender struct { errorString string attempts int } func newDeviceTokenSender(deviceErrorString string) *deviceTokenSender { return &deviceTokenSender{errorString: deviceErrorString, attempts: 0} } func (s *deviceTokenSender) Do(req *http.Request) (*http.Response, error) { var resp *http.Response if s.attempts < 1 { s.attempts++ resp = mocks.NewResponseWithContent(errorDeviceTokenResponse(s.errorString)) } else { resp = mocks.NewResponseWithContent(MockDeviceTokenResponse) } return resp, nil } // since the above only exercise CheckForUserCompletion, we repeat the test here, // but with the intent of showing that WaitForUserCompletion loops properly. func TestDeviceTokenSucceedsWithIntermediateAuthPending(t *testing.T) { sender := newDeviceTokenSender("authorization_pending") _, err := WaitForUserCompletion(sender, deviceCode()) if err != nil { t.Fatalf("unexpected error occurred") } } // same as above but with SlowDown now func TestDeviceTokenSucceedsWithIntermediateSlowDown(t *testing.T) { sender := newDeviceTokenSender("slow_down") _, err := WaitForUserCompletion(sender, deviceCode()) if err != nil { t.Fatalf("unexpected error occurred") } } func TestDeviceTokenReturnsErrorIfAccessDenied(t *testing.T) { sender := mocks.NewSender() body := mocks.NewBody(errorDeviceTokenResponse("access_denied")) sender.AppendResponse(mocks.NewResponseWithBodyAndStatus(body, http.StatusBadRequest, "Bad Request")) _, err := WaitForUserCompletion(sender, deviceCode()) if err != ErrDeviceAccessDenied { t.Fatalf("adal: got wrong error expected(%s) actual(%s)", ErrDeviceAccessDenied.Error(), err.Error()) } if body.IsOpen() { t.Fatalf("response body was left open!") } } func TestDeviceTokenReturnsErrorIfCodeExpired(t *testing.T) { sender := mocks.NewSender() body := mocks.NewBody(errorDeviceTokenResponse("code_expired")) sender.AppendResponse(mocks.NewResponseWithBodyAndStatus(body, http.StatusBadRequest, "Bad Request")) _, err := WaitForUserCompletion(sender, deviceCode()) if err != ErrDeviceCodeExpired { t.Fatalf("adal: got wrong error expected(%s) actual(%s)", ErrDeviceCodeExpired.Error(), err.Error()) } if body.IsOpen() { t.Fatalf("response body was left open!") } } func TestDeviceTokenReturnsErrorForUnknownError(t *testing.T) { sender := mocks.NewSender() body := mocks.NewBody(errorDeviceTokenResponse("unknown_error")) sender.AppendResponse(mocks.NewResponseWithBodyAndStatus(body, http.StatusBadRequest, "Bad Request")) _, err := WaitForUserCompletion(sender, deviceCode()) if err == nil { t.Fatalf("failed to get error") } if err != ErrDeviceGeneric { t.Fatalf("adal: got wrong error expected(%s) actual(%s)", ErrDeviceGeneric.Error(), err.Error()) } if body.IsOpen() { t.Fatalf("response body was left open!") } } func TestDeviceTokenReturnsErrorIfTokenEmptyAndStatusOK(t *testing.T) { sender := mocks.NewSender() body := mocks.NewBody("") sender.AppendResponse(mocks.NewResponseWithBodyAndStatus(body, http.StatusOK, "OK")) _, err := WaitForUserCompletion(sender, deviceCode()) if err != ErrOAuthTokenEmpty { t.Fatalf("adal: got wrong error expected(%s) actual(%s)", ErrOAuthTokenEmpty.Error(), err.Error()) } if body.IsOpen() { t.Fatalf("response body was left open!") } } go-autorest-8.3.1/autorest/adal/persist.go000066400000000000000000000034731315133516500205750ustar00rootroot00000000000000package adal import ( "encoding/json" "fmt" "io/ioutil" "os" "path/filepath" ) // LoadToken restores a Token object from a file located at 'path'. func LoadToken(path string) (*Token, error) { file, err := os.Open(path) if err != nil { return nil, fmt.Errorf("failed to open file (%s) while loading token: %v", path, err) } defer file.Close() var token Token dec := json.NewDecoder(file) if err = dec.Decode(&token); err != nil { return nil, fmt.Errorf("failed to decode contents of file (%s) into Token representation: %v", path, err) } return &token, nil } // SaveToken persists an oauth token at the given location on disk. // It moves the new file into place so it can safely be used to replace an existing file // that maybe accessed by multiple processes. func SaveToken(path string, mode os.FileMode, token Token) error { dir := filepath.Dir(path) err := os.MkdirAll(dir, os.ModePerm) if err != nil { return fmt.Errorf("failed to create directory (%s) to store token in: %v", dir, err) } newFile, err := ioutil.TempFile(dir, "token") if err != nil { return fmt.Errorf("failed to create the temp file to write the token: %v", err) } tempPath := newFile.Name() if err := json.NewEncoder(newFile).Encode(token); err != nil { return fmt.Errorf("failed to encode token to file (%s) while saving token: %v", tempPath, err) } if err := newFile.Close(); err != nil { return fmt.Errorf("failed to close temp file %s: %v", tempPath, err) } // Atomic replace to avoid multi-writer file corruptions if err := os.Rename(tempPath, path); err != nil { return fmt.Errorf("failed to move temporary token to desired output location. src=%s dst=%s: %v", tempPath, path, err) } if err := os.Chmod(path, mode); err != nil { return fmt.Errorf("failed to chmod the token file %s: %v", path, err) } return nil } go-autorest-8.3.1/autorest/adal/persist_test.go000066400000000000000000000107001315133516500216230ustar00rootroot00000000000000package adal import ( "encoding/json" "io/ioutil" "os" "path" "reflect" "runtime" "strings" "testing" ) const MockTokenJSON string = `{ "access_token": "accessToken", "refresh_token": "refreshToken", "expires_in": "1000", "expires_on": "2000", "not_before": "3000", "resource": "resource", "token_type": "type" }` var TestToken = Token{ AccessToken: "accessToken", RefreshToken: "refreshToken", ExpiresIn: "1000", ExpiresOn: "2000", NotBefore: "3000", Resource: "resource", Type: "type", } func writeTestTokenFile(t *testing.T, suffix string, contents string) *os.File { f, err := ioutil.TempFile(os.TempDir(), suffix) if err != nil { t.Fatalf("azure: unexpected error when creating temp file: %v", err) } defer f.Close() _, err = f.Write([]byte(contents)) if err != nil { t.Fatalf("azure: unexpected error when writing temp test file: %v", err) } return f } func TestLoadToken(t *testing.T) { f := writeTestTokenFile(t, "testloadtoken", MockTokenJSON) defer os.Remove(f.Name()) expectedToken := TestToken actualToken, err := LoadToken(f.Name()) if err != nil { t.Fatalf("azure: unexpected error loading token from file: %v", err) } if *actualToken != expectedToken { t.Fatalf("azure: failed to decode properly expected(%v) actual(%v)", expectedToken, *actualToken) } // test that LoadToken closes the file properly err = SaveToken(f.Name(), 0600, *actualToken) if err != nil { t.Fatalf("azure: could not save token after LoadToken: %v", err) } } func TestLoadTokenFailsBadPath(t *testing.T) { _, err := LoadToken("/tmp/this_file_should_never_exist_really") expectedSubstring := "failed to open file" if err == nil || !strings.Contains(err.Error(), expectedSubstring) { t.Fatalf("azure: failed to get correct error expected(%s) actual(%s)", expectedSubstring, err.Error()) } } func TestLoadTokenFailsBadJson(t *testing.T) { gibberishJSON := strings.Replace(MockTokenJSON, "expires_on", ";:\"gibberish", -1) f := writeTestTokenFile(t, "testloadtokenfailsbadjson", gibberishJSON) defer os.Remove(f.Name()) _, err := LoadToken(f.Name()) expectedSubstring := "failed to decode contents of file" if err == nil || !strings.Contains(err.Error(), expectedSubstring) { t.Fatalf("azure: failed to get correct error expected(%s) actual(%s)", expectedSubstring, err.Error()) } } func token() *Token { var token Token json.Unmarshal([]byte(MockTokenJSON), &token) return &token } func TestSaveToken(t *testing.T) { f, err := ioutil.TempFile("", "testloadtoken") if err != nil { t.Fatalf("azure: unexpected error when creating temp file: %v", err) } defer os.Remove(f.Name()) f.Close() mode := os.ModePerm & 0642 err = SaveToken(f.Name(), mode, *token()) if err != nil { t.Fatalf("azure: unexpected error saving token to file: %v", err) } fi, err := os.Stat(f.Name()) // open a new stat as held ones are not fresh if err != nil { t.Fatalf("azure: stat failed: %v", err) } if runtime.GOOS != "windows" { // permissions don't work on Windows if perm := fi.Mode().Perm(); perm != mode { t.Fatalf("azure: wrong file perm. got:%s; expected:%s file :%s", perm, mode, f.Name()) } } var actualToken Token var expectedToken Token json.Unmarshal([]byte(MockTokenJSON), expectedToken) contents, err := ioutil.ReadFile(f.Name()) if err != nil { t.Fatal("!!") } json.Unmarshal(contents, actualToken) if !reflect.DeepEqual(actualToken, expectedToken) { t.Fatal("azure: token was not serialized correctly") } } func TestSaveTokenFailsNoPermission(t *testing.T) { pathWhereWeShouldntHavePermission := "/usr/thiswontwork/atall" if runtime.GOOS == "windows" { pathWhereWeShouldntHavePermission = path.Join(os.Getenv("windir"), "system32\\mytokendir\\mytoken") } err := SaveToken(pathWhereWeShouldntHavePermission, 0644, *token()) expectedSubstring := "failed to create directory" if err == nil || !strings.Contains(err.Error(), expectedSubstring) { t.Fatalf("azure: failed to get correct error expected(%s) actual(%v)", expectedSubstring, err) } } func TestSaveTokenFailsCantCreate(t *testing.T) { tokenPath := "/thiswontwork" if runtime.GOOS == "windows" { tokenPath = path.Join(os.Getenv("windir"), "system32") } err := SaveToken(tokenPath, 0644, *token()) expectedSubstring := "failed to create the temp file to write the token" if err == nil || !strings.Contains(err.Error(), expectedSubstring) { t.Fatalf("azure: failed to get correct error expected(%s) actual(%v)", expectedSubstring, err) } } go-autorest-8.3.1/autorest/adal/sender.go000066400000000000000000000030631315133516500203570ustar00rootroot00000000000000package adal import ( "net/http" ) const ( contentType = "Content-Type" mimeTypeFormPost = "application/x-www-form-urlencoded" ) // Sender is the interface that wraps the Do method to send HTTP requests. // // The standard http.Client conforms to this interface. type Sender interface { Do(*http.Request) (*http.Response, error) } // SenderFunc is a method that implements the Sender interface. type SenderFunc func(*http.Request) (*http.Response, error) // Do implements the Sender interface on SenderFunc. func (sf SenderFunc) Do(r *http.Request) (*http.Response, error) { return sf(r) } // SendDecorator takes and possibily decorates, by wrapping, a Sender. Decorators may affect the // http.Request and pass it along or, first, pass the http.Request along then react to the // http.Response result. type SendDecorator func(Sender) Sender // CreateSender creates, decorates, and returns, as a Sender, the default http.Client. func CreateSender(decorators ...SendDecorator) Sender { return DecorateSender(&http.Client{}, decorators...) } // DecorateSender accepts a Sender and a, possibly empty, set of SendDecorators, which is applies to // the Sender. Decorators are applied in the order received, but their affect upon the request // depends on whether they are a pre-decorator (change the http.Request and then pass it along) or a // post-decorator (pass the http.Request along and react to the results in http.Response). func DecorateSender(s Sender, decorators ...SendDecorator) Sender { for _, decorate := range decorators { s = decorate(s) } return s } go-autorest-8.3.1/autorest/adal/token.go000066400000000000000000000324161315133516500202230ustar00rootroot00000000000000package adal import ( "crypto/rand" "crypto/rsa" "crypto/sha1" "crypto/x509" "encoding/base64" "encoding/json" "fmt" "io/ioutil" "net/http" "net/url" "strconv" "strings" "time" "github.com/dgrijalva/jwt-go" ) const ( defaultRefresh = 5 * time.Minute tokenBaseDate = "1970-01-01T00:00:00Z" // OAuthGrantTypeDeviceCode is the "grant_type" identifier used in device flow OAuthGrantTypeDeviceCode = "device_code" // OAuthGrantTypeClientCredentials is the "grant_type" identifier used in credential flows OAuthGrantTypeClientCredentials = "client_credentials" // OAuthGrantTypeRefreshToken is the "grant_type" identifier used in refresh token flows OAuthGrantTypeRefreshToken = "refresh_token" // managedIdentitySettingsPath is the path to the MSI Extension settings file (to discover the endpoint) managedIdentitySettingsPath = "/var/lib/waagent/ManagedIdentity-Settings" // metadataHeader is the header required by MSI extension metadataHeader = "Metadata" ) var expirationBase time.Time func init() { expirationBase, _ = time.Parse(time.RFC3339, tokenBaseDate) } // OAuthTokenProvider is an interface which should be implemented by an access token retriever type OAuthTokenProvider interface { OAuthToken() string } // Refresher is an interface for token refresh functionality type Refresher interface { Refresh() error RefreshExchange(resource string) error EnsureFresh() error } // TokenRefreshCallback is the type representing callbacks that will be called after // a successful token refresh type TokenRefreshCallback func(Token) error // Token encapsulates the access token used to authorize Azure requests. type Token struct { AccessToken string `json:"access_token"` RefreshToken string `json:"refresh_token"` ExpiresIn string `json:"expires_in"` ExpiresOn string `json:"expires_on"` NotBefore string `json:"not_before"` Resource string `json:"resource"` Type string `json:"token_type"` } // Expires returns the time.Time when the Token expires. func (t Token) Expires() time.Time { s, err := strconv.Atoi(t.ExpiresOn) if err != nil { s = -3600 } return expirationBase.Add(time.Duration(s) * time.Second).UTC() } // IsExpired returns true if the Token is expired, false otherwise. func (t Token) IsExpired() bool { return t.WillExpireIn(0) } // WillExpireIn returns true if the Token will expire after the passed time.Duration interval // from now, false otherwise. func (t Token) WillExpireIn(d time.Duration) bool { return !t.Expires().After(time.Now().Add(d)) } //OAuthToken return the current access token func (t *Token) OAuthToken() string { return t.AccessToken } // ServicePrincipalNoSecret represents a secret type that contains no secret // meaning it is not valid for fetching a fresh token. This is used by Manual type ServicePrincipalNoSecret struct { } // SetAuthenticationValues is a method of the interface ServicePrincipalSecret // It only returns an error for the ServicePrincipalNoSecret type func (noSecret *ServicePrincipalNoSecret) SetAuthenticationValues(spt *ServicePrincipalToken, v *url.Values) error { return fmt.Errorf("Manually created ServicePrincipalToken does not contain secret material to retrieve a new access token") } // ServicePrincipalSecret is an interface that allows various secret mechanism to fill the form // that is submitted when acquiring an oAuth token. type ServicePrincipalSecret interface { SetAuthenticationValues(spt *ServicePrincipalToken, values *url.Values) error } // ServicePrincipalTokenSecret implements ServicePrincipalSecret for client_secret type authorization. type ServicePrincipalTokenSecret struct { ClientSecret string } // SetAuthenticationValues is a method of the interface ServicePrincipalSecret. // It will populate the form submitted during oAuth Token Acquisition using the client_secret. func (tokenSecret *ServicePrincipalTokenSecret) SetAuthenticationValues(spt *ServicePrincipalToken, v *url.Values) error { v.Set("client_secret", tokenSecret.ClientSecret) return nil } // ServicePrincipalCertificateSecret implements ServicePrincipalSecret for generic RSA cert auth with signed JWTs. type ServicePrincipalCertificateSecret struct { Certificate *x509.Certificate PrivateKey *rsa.PrivateKey } // ServicePrincipalMSISecret implements ServicePrincipalSecret for machines running the MSI Extension. type ServicePrincipalMSISecret struct { } // SetAuthenticationValues is a method of the interface ServicePrincipalSecret. // MSI extension requires the authority field to be set to the real tenant authority endpoint func (msiSecret *ServicePrincipalMSISecret) SetAuthenticationValues(spt *ServicePrincipalToken, v *url.Values) error { v.Set("authority", spt.oauthConfig.AuthorityEndpoint.String()) return nil } // SignJwt returns the JWT signed with the certificate's private key. func (secret *ServicePrincipalCertificateSecret) SignJwt(spt *ServicePrincipalToken) (string, error) { hasher := sha1.New() _, err := hasher.Write(secret.Certificate.Raw) if err != nil { return "", err } thumbprint := base64.URLEncoding.EncodeToString(hasher.Sum(nil)) // The jti (JWT ID) claim provides a unique identifier for the JWT. jti := make([]byte, 20) _, err = rand.Read(jti) if err != nil { return "", err } token := jwt.New(jwt.SigningMethodRS256) token.Header["x5t"] = thumbprint token.Claims = jwt.MapClaims{ "aud": spt.oauthConfig.TokenEndpoint.String(), "iss": spt.clientID, "sub": spt.clientID, "jti": base64.URLEncoding.EncodeToString(jti), "nbf": time.Now().Unix(), "exp": time.Now().Add(time.Hour * 24).Unix(), } signedString, err := token.SignedString(secret.PrivateKey) return signedString, err } // SetAuthenticationValues is a method of the interface ServicePrincipalSecret. // It will populate the form submitted during oAuth Token Acquisition using a JWT signed with a certificate. func (secret *ServicePrincipalCertificateSecret) SetAuthenticationValues(spt *ServicePrincipalToken, v *url.Values) error { jwt, err := secret.SignJwt(spt) if err != nil { return err } v.Set("client_assertion", jwt) v.Set("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer") return nil } // ServicePrincipalToken encapsulates a Token created for a Service Principal. type ServicePrincipalToken struct { Token secret ServicePrincipalSecret oauthConfig OAuthConfig clientID string resource string autoRefresh bool refreshWithin time.Duration sender Sender refreshCallbacks []TokenRefreshCallback } // NewServicePrincipalTokenWithSecret create a ServicePrincipalToken using the supplied ServicePrincipalSecret implementation. func NewServicePrincipalTokenWithSecret(oauthConfig OAuthConfig, id string, resource string, secret ServicePrincipalSecret, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) { spt := &ServicePrincipalToken{ oauthConfig: oauthConfig, secret: secret, clientID: id, resource: resource, autoRefresh: true, refreshWithin: defaultRefresh, sender: &http.Client{}, refreshCallbacks: callbacks, } return spt, nil } // NewServicePrincipalTokenFromManualToken creates a ServicePrincipalToken using the supplied token func NewServicePrincipalTokenFromManualToken(oauthConfig OAuthConfig, clientID string, resource string, token Token, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) { spt, err := NewServicePrincipalTokenWithSecret( oauthConfig, clientID, resource, &ServicePrincipalNoSecret{}, callbacks...) if err != nil { return nil, err } spt.Token = token return spt, nil } // NewServicePrincipalToken creates a ServicePrincipalToken from the supplied Service Principal // credentials scoped to the named resource. func NewServicePrincipalToken(oauthConfig OAuthConfig, clientID string, secret string, resource string, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) { return NewServicePrincipalTokenWithSecret( oauthConfig, clientID, resource, &ServicePrincipalTokenSecret{ ClientSecret: secret, }, callbacks..., ) } // NewServicePrincipalTokenFromCertificate create a ServicePrincipalToken from the supplied pkcs12 bytes. func NewServicePrincipalTokenFromCertificate(oauthConfig OAuthConfig, clientID string, certificate *x509.Certificate, privateKey *rsa.PrivateKey, resource string, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) { return NewServicePrincipalTokenWithSecret( oauthConfig, clientID, resource, &ServicePrincipalCertificateSecret{ PrivateKey: privateKey, Certificate: certificate, }, callbacks..., ) } // NewServicePrincipalTokenFromMSI creates a ServicePrincipalToken via the MSI VM Extension. func NewServicePrincipalTokenFromMSI(oauthConfig OAuthConfig, resource string, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) { return newServicePrincipalTokenFromMSI(oauthConfig, resource, managedIdentitySettingsPath, callbacks...) } func newServicePrincipalTokenFromMSI(oauthConfig OAuthConfig, resource, settingsPath string, callbacks ...TokenRefreshCallback) (*ServicePrincipalToken, error) { // Read MSI settings bytes, err := ioutil.ReadFile(settingsPath) if err != nil { return nil, err } msiSettings := struct { URL string `json:"url"` }{} err = json.Unmarshal(bytes, &msiSettings) if err != nil { return nil, err } // We set the oauth config token endpoint to be MSI's endpoint // We leave the authority as-is so MSI can POST it with the token request msiEndpointURL, err := url.Parse(msiSettings.URL) if err != nil { return nil, err } msiTokenEndpointURL, err := msiEndpointURL.Parse("/oauth2/token") if err != nil { return nil, err } oauthConfig.TokenEndpoint = *msiTokenEndpointURL spt := &ServicePrincipalToken{ oauthConfig: oauthConfig, secret: &ServicePrincipalMSISecret{}, resource: resource, autoRefresh: true, refreshWithin: defaultRefresh, sender: &http.Client{}, refreshCallbacks: callbacks, } return spt, nil } // EnsureFresh will refresh the token if it will expire within the refresh window (as set by // RefreshWithin) and autoRefresh flag is on. func (spt *ServicePrincipalToken) EnsureFresh() error { if spt.autoRefresh && spt.WillExpireIn(spt.refreshWithin) { return spt.Refresh() } return nil } // InvokeRefreshCallbacks calls any TokenRefreshCallbacks that were added to the SPT during initialization func (spt *ServicePrincipalToken) InvokeRefreshCallbacks(token Token) error { if spt.refreshCallbacks != nil { for _, callback := range spt.refreshCallbacks { err := callback(spt.Token) if err != nil { return fmt.Errorf("adal: TokenRefreshCallback handler failed. Error = '%v'", err) } } } return nil } // Refresh obtains a fresh token for the Service Principal. func (spt *ServicePrincipalToken) Refresh() error { return spt.refreshInternal(spt.resource) } // RefreshExchange refreshes the token, but for a different resource. func (spt *ServicePrincipalToken) RefreshExchange(resource string) error { return spt.refreshInternal(resource) } func (spt *ServicePrincipalToken) refreshInternal(resource string) error { v := url.Values{} v.Set("client_id", spt.clientID) v.Set("resource", resource) if spt.RefreshToken != "" { v.Set("grant_type", OAuthGrantTypeRefreshToken) v.Set("refresh_token", spt.RefreshToken) } else { v.Set("grant_type", OAuthGrantTypeClientCredentials) err := spt.secret.SetAuthenticationValues(spt, &v) if err != nil { return err } } s := v.Encode() body := ioutil.NopCloser(strings.NewReader(s)) req, err := http.NewRequest(http.MethodPost, spt.oauthConfig.TokenEndpoint.String(), body) if err != nil { return fmt.Errorf("adal: Failed to build the refresh request. Error = '%v'", err) } req.ContentLength = int64(len(s)) req.Header.Set(contentType, mimeTypeFormPost) if _, ok := spt.secret.(*ServicePrincipalMSISecret); ok { req.Header.Set(metadataHeader, "true") } resp, err := spt.sender.Do(req) if err != nil { return fmt.Errorf("adal: Failed to execute the refresh request. Error = '%v'", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return fmt.Errorf("adal: Refresh request failed. Status Code = '%d'", resp.StatusCode) } rb, err := ioutil.ReadAll(resp.Body) if err != nil { return fmt.Errorf("adal: Failed to read a new service principal token during refresh. Error = '%v'", err) } if len(strings.Trim(string(rb), " ")) == 0 { return fmt.Errorf("adal: Empty service principal token received during refresh") } var token Token err = json.Unmarshal(rb, &token) if err != nil { return fmt.Errorf("adal: Failed to unmarshal the service principal token during refresh. Error = '%v' JSON = '%s'", err, string(rb)) } spt.Token = token return spt.InvokeRefreshCallbacks(token) } // SetAutoRefresh enables or disables automatic refreshing of stale tokens. func (spt *ServicePrincipalToken) SetAutoRefresh(autoRefresh bool) { spt.autoRefresh = autoRefresh } // SetRefreshWithin sets the interval within which if the token will expire, EnsureFresh will // refresh the token. func (spt *ServicePrincipalToken) SetRefreshWithin(d time.Duration) { spt.refreshWithin = d return } // SetSender sets the http.Client used when obtaining the Service Principal token. An // undecorated http.Client is used by default. func (spt *ServicePrincipalToken) SetSender(s Sender) { spt.sender = s } go-autorest-8.3.1/autorest/adal/token_test.go000066400000000000000000000452321315133516500212620ustar00rootroot00000000000000package adal import ( "crypto/rand" "crypto/rsa" "crypto/x509" "crypto/x509/pkix" "fmt" "io/ioutil" "math/big" "net/http" "net/url" "os" "reflect" "strconv" "strings" "testing" "time" "github.com/Azure/go-autorest/autorest/mocks" ) const ( defaultFormData = "client_id=id&client_secret=secret&grant_type=client_credentials&resource=resource" defaultManualFormData = "client_id=id&grant_type=refresh_token&refresh_token=refreshtoken&resource=resource" ) func TestTokenExpires(t *testing.T) { tt := time.Now().Add(5 * time.Second) tk := newTokenExpiresAt(tt) if tk.Expires().Equal(tt) { t.Fatalf("adal: Token#Expires miscalculated expiration time -- received %v, expected %v", tk.Expires(), tt) } } func TestTokenIsExpired(t *testing.T) { tk := newTokenExpiresAt(time.Now().Add(-5 * time.Second)) if !tk.IsExpired() { t.Fatalf("adal: Token#IsExpired failed to mark a stale token as expired -- now %v, token expires at %v", time.Now().UTC(), tk.Expires()) } } func TestTokenIsExpiredUninitialized(t *testing.T) { tk := &Token{} if !tk.IsExpired() { t.Fatalf("adal: An uninitialized Token failed to mark itself as expired (expiration time %v)", tk.Expires()) } } func TestTokenIsNoExpired(t *testing.T) { tk := newTokenExpiresAt(time.Now().Add(1000 * time.Second)) if tk.IsExpired() { t.Fatalf("adal: Token marked a fresh token as expired -- now %v, token expires at %v", time.Now().UTC(), tk.Expires()) } } func TestTokenWillExpireIn(t *testing.T) { d := 5 * time.Second tk := newTokenExpiresIn(d) if !tk.WillExpireIn(d) { t.Fatal("adal: Token#WillExpireIn mismeasured expiration time") } } func TestServicePrincipalTokenSetAutoRefresh(t *testing.T) { spt := newServicePrincipalToken() if !spt.autoRefresh { t.Fatal("adal: ServicePrincipalToken did not default to automatic token refreshing") } spt.SetAutoRefresh(false) if spt.autoRefresh { t.Fatal("adal: ServicePrincipalToken#SetAutoRefresh did not disable automatic token refreshing") } } func TestServicePrincipalTokenSetRefreshWithin(t *testing.T) { spt := newServicePrincipalToken() if spt.refreshWithin != defaultRefresh { t.Fatal("adal: ServicePrincipalToken did not correctly set the default refresh interval") } spt.SetRefreshWithin(2 * defaultRefresh) if spt.refreshWithin != 2*defaultRefresh { t.Fatal("adal: ServicePrincipalToken#SetRefreshWithin did not set the refresh interval") } } func TestServicePrincipalTokenSetSender(t *testing.T) { spt := newServicePrincipalToken() c := &http.Client{} spt.SetSender(c) if !reflect.DeepEqual(c, spt.sender) { t.Fatal("adal: ServicePrincipalToken#SetSender did not set the sender") } } func TestServicePrincipalTokenRefreshUsesPOST(t *testing.T) { spt := newServicePrincipalToken() body := mocks.NewBody(newTokenJSON("test", "test")) resp := mocks.NewResponseWithBodyAndStatus(body, http.StatusOK, "OK") c := mocks.NewSender() s := DecorateSender(c, (func() SendDecorator { return func(s Sender) Sender { return SenderFunc(func(r *http.Request) (*http.Response, error) { if r.Method != "POST" { t.Fatalf("adal: ServicePrincipalToken#Refresh did not correctly set HTTP method -- expected %v, received %v", "POST", r.Method) } return resp, nil }) } })()) spt.SetSender(s) err := spt.Refresh() if err != nil { t.Fatalf("adal: ServicePrincipalToken#Refresh returned an unexpected error (%v)", err) } if body.IsOpen() { t.Fatalf("the response was not closed!") } } func TestServicePrincipalTokenFromMSIRefreshUsesPOST(t *testing.T) { resource := "https://resource" cb := func(token Token) error { return nil } tempSettingsFile, err := ioutil.TempFile("", "ManagedIdentity-Settings") if err != nil { t.Fatal("Couldn't write temp settings file") } defer os.Remove(tempSettingsFile.Name()) settingsContents := []byte(`{ "url": "http://msiendpoint/" }`) if _, err := tempSettingsFile.Write(settingsContents); err != nil { t.Fatal("Couldn't fill temp settings file") } oauthConfig, err := NewOAuthConfig("http://adendpoint", "1-2-3-4") if err != nil { t.Fatal("Failed to construct oauthconfig") } spt, err := newServicePrincipalTokenFromMSI( *oauthConfig, resource, tempSettingsFile.Name(), cb) if err != nil { t.Fatalf("Failed to get MSI SPT: %v", err) } body := mocks.NewBody(newTokenJSON("test", "test")) resp := mocks.NewResponseWithBodyAndStatus(body, http.StatusOK, "OK") c := mocks.NewSender() s := DecorateSender(c, (func() SendDecorator { return func(s Sender) Sender { return SenderFunc(func(r *http.Request) (*http.Response, error) { if r.Method != "POST" { t.Fatalf("adal: ServicePrincipalToken#Refresh did not correctly set HTTP method -- expected %v, received %v", "POST", r.Method) } if h := r.Header.Get("Metadata"); h != "true" { t.Fatalf("adal: ServicePrincipalToken#Refresh did not correctly set Metadata header for MSI") } return resp, nil }) } })()) spt.SetSender(s) err = spt.Refresh() if err != nil { t.Fatalf("adal: ServicePrincipalToken#Refresh returned an unexpected error (%v)", err) } if body.IsOpen() { t.Fatalf("the response was not closed!") } } func TestServicePrincipalTokenRefreshSetsMimeType(t *testing.T) { spt := newServicePrincipalToken() body := mocks.NewBody(newTokenJSON("test", "test")) resp := mocks.NewResponseWithBodyAndStatus(body, http.StatusOK, "OK") c := mocks.NewSender() s := DecorateSender(c, (func() SendDecorator { return func(s Sender) Sender { return SenderFunc(func(r *http.Request) (*http.Response, error) { if r.Header.Get(http.CanonicalHeaderKey("Content-Type")) != "application/x-www-form-urlencoded" { t.Fatalf("adal: ServicePrincipalToken#Refresh did not correctly set Content-Type -- expected %v, received %v", "application/x-form-urlencoded", r.Header.Get(http.CanonicalHeaderKey("Content-Type"))) } return resp, nil }) } })()) spt.SetSender(s) err := spt.Refresh() if err != nil { t.Fatalf("adal: ServicePrincipalToken#Refresh returned an unexpected error (%v)", err) } } func TestServicePrincipalTokenRefreshSetsURL(t *testing.T) { spt := newServicePrincipalToken() body := mocks.NewBody(newTokenJSON("test", "test")) resp := mocks.NewResponseWithBodyAndStatus(body, http.StatusOK, "OK") c := mocks.NewSender() s := DecorateSender(c, (func() SendDecorator { return func(s Sender) Sender { return SenderFunc(func(r *http.Request) (*http.Response, error) { if r.URL.String() != TestOAuthConfig.TokenEndpoint.String() { t.Fatalf("adal: ServicePrincipalToken#Refresh did not correctly set the URL -- expected %v, received %v", TestOAuthConfig.TokenEndpoint, r.URL) } return resp, nil }) } })()) spt.SetSender(s) err := spt.Refresh() if err != nil { t.Fatalf("adal: ServicePrincipalToken#Refresh returned an unexpected error (%v)", err) } } func testServicePrincipalTokenRefreshSetsBody(t *testing.T, spt *ServicePrincipalToken, f func(*testing.T, []byte)) { body := mocks.NewBody(newTokenJSON("test", "test")) resp := mocks.NewResponseWithBodyAndStatus(body, http.StatusOK, "OK") c := mocks.NewSender() s := DecorateSender(c, (func() SendDecorator { return func(s Sender) Sender { return SenderFunc(func(r *http.Request) (*http.Response, error) { b, err := ioutil.ReadAll(r.Body) if err != nil { t.Fatalf("adal: Failed to read body of Service Principal token request (%v)", err) } f(t, b) return resp, nil }) } })()) spt.SetSender(s) err := spt.Refresh() if err != nil { t.Fatalf("adal: ServicePrincipalToken#Refresh returned an unexpected error (%v)", err) } } func TestServicePrincipalTokenManualRefreshSetsBody(t *testing.T) { sptManual := newServicePrincipalTokenManual() testServicePrincipalTokenRefreshSetsBody(t, sptManual, func(t *testing.T, b []byte) { if string(b) != defaultManualFormData { t.Fatalf("adal: ServicePrincipalToken#Refresh did not correctly set the HTTP Request Body -- expected %v, received %v", defaultManualFormData, string(b)) } }) } func TestServicePrincipalTokenCertficateRefreshSetsBody(t *testing.T) { sptCert := newServicePrincipalTokenCertificate(t) testServicePrincipalTokenRefreshSetsBody(t, sptCert, func(t *testing.T, b []byte) { body := string(b) values, _ := url.ParseQuery(body) if values["client_assertion_type"][0] != "urn:ietf:params:oauth:client-assertion-type:jwt-bearer" || values["client_id"][0] != "id" || values["grant_type"][0] != "client_credentials" || values["resource"][0] != "resource" { t.Fatalf("adal: ServicePrincipalTokenCertificate#Refresh did not correctly set the HTTP Request Body.") } }) } func TestServicePrincipalTokenSecretRefreshSetsBody(t *testing.T) { spt := newServicePrincipalToken() testServicePrincipalTokenRefreshSetsBody(t, spt, func(t *testing.T, b []byte) { if string(b) != defaultFormData { t.Fatalf("adal: ServicePrincipalToken#Refresh did not correctly set the HTTP Request Body -- expected %v, received %v", defaultFormData, string(b)) } }) } func TestServicePrincipalTokenRefreshClosesRequestBody(t *testing.T) { spt := newServicePrincipalToken() body := mocks.NewBody(newTokenJSON("test", "test")) resp := mocks.NewResponseWithBodyAndStatus(body, http.StatusOK, "OK") c := mocks.NewSender() s := DecorateSender(c, (func() SendDecorator { return func(s Sender) Sender { return SenderFunc(func(r *http.Request) (*http.Response, error) { return resp, nil }) } })()) spt.SetSender(s) err := spt.Refresh() if err != nil { t.Fatalf("adal: ServicePrincipalToken#Refresh returned an unexpected error (%v)", err) } if resp.Body.(*mocks.Body).IsOpen() { t.Fatal("adal: ServicePrincipalToken#Refresh failed to close the HTTP Response Body") } } func TestServicePrincipalTokenRefreshRejectsResponsesWithStatusNotOK(t *testing.T) { spt := newServicePrincipalToken() body := mocks.NewBody(newTokenJSON("test", "test")) resp := mocks.NewResponseWithBodyAndStatus(body, http.StatusUnauthorized, "Unauthorized") c := mocks.NewSender() s := DecorateSender(c, (func() SendDecorator { return func(s Sender) Sender { return SenderFunc(func(r *http.Request) (*http.Response, error) { return resp, nil }) } })()) spt.SetSender(s) err := spt.Refresh() if err == nil { t.Fatalf("adal: ServicePrincipalToken#Refresh should reject a response with status != %d", http.StatusOK) } } func TestServicePrincipalTokenRefreshRejectsEmptyBody(t *testing.T) { spt := newServicePrincipalToken() c := mocks.NewSender() s := DecorateSender(c, (func() SendDecorator { return func(s Sender) Sender { return SenderFunc(func(r *http.Request) (*http.Response, error) { return mocks.NewResponse(), nil }) } })()) spt.SetSender(s) err := spt.Refresh() if err == nil { t.Fatal("adal: ServicePrincipalToken#Refresh should reject an empty token") } } func TestServicePrincipalTokenRefreshPropagatesErrors(t *testing.T) { spt := newServicePrincipalToken() c := mocks.NewSender() c.SetError(fmt.Errorf("Faux Error")) spt.SetSender(c) err := spt.Refresh() if err == nil { t.Fatal("adal: Failed to propagate the request error") } } func TestServicePrincipalTokenRefreshReturnsErrorIfNotOk(t *testing.T) { spt := newServicePrincipalToken() c := mocks.NewSender() c.AppendResponse(mocks.NewResponseWithStatus("401 NotAuthorized", http.StatusUnauthorized)) spt.SetSender(c) err := spt.Refresh() if err == nil { t.Fatalf("adal: Failed to return an when receiving a status code other than HTTP %d", http.StatusOK) } } func TestServicePrincipalTokenRefreshUnmarshals(t *testing.T) { spt := newServicePrincipalToken() expiresOn := strconv.Itoa(int(time.Now().Add(3600 * time.Second).Sub(expirationBase).Seconds())) j := newTokenJSON(expiresOn, "resource") resp := mocks.NewResponseWithContent(j) c := mocks.NewSender() s := DecorateSender(c, (func() SendDecorator { return func(s Sender) Sender { return SenderFunc(func(r *http.Request) (*http.Response, error) { return resp, nil }) } })()) spt.SetSender(s) err := spt.Refresh() if err != nil { t.Fatalf("adal: ServicePrincipalToken#Refresh returned an unexpected error (%v)", err) } else if spt.AccessToken != "accessToken" || spt.ExpiresIn != "3600" || spt.ExpiresOn != expiresOn || spt.NotBefore != expiresOn || spt.Resource != "resource" || spt.Type != "Bearer" { t.Fatalf("adal: ServicePrincipalToken#Refresh failed correctly unmarshal the JSON -- expected %v, received %v", j, *spt) } } func TestServicePrincipalTokenEnsureFreshRefreshes(t *testing.T) { spt := newServicePrincipalToken() expireToken(&spt.Token) body := mocks.NewBody(newTokenJSON("test", "test")) resp := mocks.NewResponseWithBodyAndStatus(body, http.StatusOK, "OK") f := false c := mocks.NewSender() s := DecorateSender(c, (func() SendDecorator { return func(s Sender) Sender { return SenderFunc(func(r *http.Request) (*http.Response, error) { f = true return resp, nil }) } })()) spt.SetSender(s) err := spt.EnsureFresh() if err != nil { t.Fatalf("adal: ServicePrincipalToken#EnsureFresh returned an unexpected error (%v)", err) } if !f { t.Fatal("adal: ServicePrincipalToken#EnsureFresh failed to call Refresh for stale token") } } func TestServicePrincipalTokenEnsureFreshSkipsIfFresh(t *testing.T) { spt := newServicePrincipalToken() setTokenToExpireIn(&spt.Token, 1000*time.Second) f := false c := mocks.NewSender() s := DecorateSender(c, (func() SendDecorator { return func(s Sender) Sender { return SenderFunc(func(r *http.Request) (*http.Response, error) { f = true return mocks.NewResponse(), nil }) } })()) spt.SetSender(s) err := spt.EnsureFresh() if err != nil { t.Fatalf("adal: ServicePrincipalToken#EnsureFresh returned an unexpected error (%v)", err) } if f { t.Fatal("adal: ServicePrincipalToken#EnsureFresh invoked Refresh for fresh token") } } func TestRefreshCallback(t *testing.T) { callbackTriggered := false spt := newServicePrincipalToken(func(Token) error { callbackTriggered = true return nil }) expiresOn := strconv.Itoa(int(time.Now().Add(3600 * time.Second).Sub(expirationBase).Seconds())) sender := mocks.NewSender() j := newTokenJSON(expiresOn, "resource") sender.AppendResponse(mocks.NewResponseWithContent(j)) spt.SetSender(sender) err := spt.Refresh() if err != nil { t.Fatalf("adal: ServicePrincipalToken#Refresh returned an unexpected error (%v)", err) } if !callbackTriggered { t.Fatalf("adal: RefreshCallback failed to trigger call callback") } } func TestRefreshCallbackErrorPropagates(t *testing.T) { errorText := "this is an error text" spt := newServicePrincipalToken(func(Token) error { return fmt.Errorf(errorText) }) expiresOn := strconv.Itoa(int(time.Now().Add(3600 * time.Second).Sub(expirationBase).Seconds())) sender := mocks.NewSender() j := newTokenJSON(expiresOn, "resource") sender.AppendResponse(mocks.NewResponseWithContent(j)) spt.SetSender(sender) err := spt.Refresh() if err == nil || !strings.Contains(err.Error(), errorText) { t.Fatalf("adal: RefreshCallback failed to propagate error") } } // This demonstrates the danger of manual token without a refresh token func TestServicePrincipalTokenManualRefreshFailsWithoutRefresh(t *testing.T) { spt := newServicePrincipalTokenManual() spt.RefreshToken = "" err := spt.Refresh() if err == nil { t.Fatalf("adal: ServicePrincipalToken#Refresh should have failed with a ManualTokenSecret without a refresh token") } } func TestNewServicePrincipalTokenFromMSI(t *testing.T) { resource := "https://resource" cb := func(token Token) error { return nil } tempSettingsFile, err := ioutil.TempFile("", "ManagedIdentity-Settings") if err != nil { t.Fatal("Couldn't write temp settings file") } defer os.Remove(tempSettingsFile.Name()) settingsContents := []byte(`{ "url": "http://msiendpoint/" }`) if _, err := tempSettingsFile.Write(settingsContents); err != nil { t.Fatal("Couldn't fill temp settings file") } oauthConfig, err := NewOAuthConfig("http://adendpoint", "1-2-3-4") if err != nil { t.Fatal("Failed to construct oauthconfig") } spt, err := newServicePrincipalTokenFromMSI( *oauthConfig, resource, tempSettingsFile.Name(), cb) if err != nil { t.Fatalf("Failed to get MSI SPT: %v", err) } // check some of the SPT fields if _, ok := spt.secret.(*ServicePrincipalMSISecret); !ok { t.Fatal("SPT secret was not of MSI type") } if spt.resource != resource { t.Fatal("SPT came back with incorrect resource") } if len(spt.refreshCallbacks) != 1 { t.Fatal("SPT had incorrect refresh callbacks.") } } func newToken() *Token { return &Token{ AccessToken: "ASECRETVALUE", Resource: "https://azure.microsoft.com/", Type: "Bearer", } } func newTokenJSON(expiresOn string, resource string) string { return fmt.Sprintf(`{ "access_token" : "accessToken", "expires_in" : "3600", "expires_on" : "%s", "not_before" : "%s", "resource" : "%s", "token_type" : "Bearer" }`, expiresOn, expiresOn, resource) } func newTokenExpiresIn(expireIn time.Duration) *Token { return setTokenToExpireIn(newToken(), expireIn) } func newTokenExpiresAt(expireAt time.Time) *Token { return setTokenToExpireAt(newToken(), expireAt) } func expireToken(t *Token) *Token { return setTokenToExpireIn(t, 0) } func setTokenToExpireAt(t *Token, expireAt time.Time) *Token { t.ExpiresIn = "3600" t.ExpiresOn = strconv.Itoa(int(expireAt.Sub(expirationBase).Seconds())) t.NotBefore = t.ExpiresOn return t } func setTokenToExpireIn(t *Token, expireIn time.Duration) *Token { return setTokenToExpireAt(t, time.Now().Add(expireIn)) } func newServicePrincipalToken(callbacks ...TokenRefreshCallback) *ServicePrincipalToken { spt, _ := NewServicePrincipalToken(TestOAuthConfig, "id", "secret", "resource", callbacks...) return spt } func newServicePrincipalTokenManual() *ServicePrincipalToken { token := newToken() token.RefreshToken = "refreshtoken" spt, _ := NewServicePrincipalTokenFromManualToken(TestOAuthConfig, "id", "resource", *token) return spt } func newServicePrincipalTokenCertificate(t *testing.T) *ServicePrincipalToken { template := x509.Certificate{ SerialNumber: big.NewInt(0), Subject: pkix.Name{CommonName: "test"}, BasicConstraintsValid: true, } privateKey, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { t.Fatal(err) } certificateBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &privateKey.PublicKey, privateKey) if err != nil { t.Fatal(err) } certificate, err := x509.ParseCertificate(certificateBytes) if err != nil { t.Fatal(err) } spt, _ := NewServicePrincipalTokenFromCertificate(TestOAuthConfig, "id", certificate, privateKey, "resource") return spt } go-autorest-8.3.1/autorest/authorization.go000066400000000000000000000121561315133516500211010ustar00rootroot00000000000000package autorest import ( "fmt" "net/http" "net/url" "strings" "github.com/Azure/go-autorest/autorest/adal" ) const ( bearerChallengeHeader = "Www-Authenticate" bearer = "Bearer" tenantID = "tenantID" ) // Authorizer is the interface that provides a PrepareDecorator used to supply request // authorization. Most often, the Authorizer decorator runs last so it has access to the full // state of the formed HTTP request. type Authorizer interface { WithAuthorization() PrepareDecorator } // NullAuthorizer implements a default, "do nothing" Authorizer. type NullAuthorizer struct{} // WithAuthorization returns a PrepareDecorator that does nothing. func (na NullAuthorizer) WithAuthorization() PrepareDecorator { return WithNothing() } // BearerAuthorizer implements the bearer authorization type BearerAuthorizer struct { tokenProvider adal.OAuthTokenProvider } // NewBearerAuthorizer crates a BearerAuthorizer using the given token provider func NewBearerAuthorizer(tp adal.OAuthTokenProvider) *BearerAuthorizer { return &BearerAuthorizer{tokenProvider: tp} } func (ba *BearerAuthorizer) withBearerAuthorization() PrepareDecorator { return WithHeader(headerAuthorization, fmt.Sprintf("Bearer %s", ba.tokenProvider.OAuthToken())) } // WithAuthorization returns a PrepareDecorator that adds an HTTP Authorization header whose // value is "Bearer " followed by the token. // // By default, the token will be automatically refreshed through the Refresher interface. func (ba *BearerAuthorizer) WithAuthorization() PrepareDecorator { return func(p Preparer) Preparer { return PreparerFunc(func(r *http.Request) (*http.Request, error) { refresher, ok := ba.tokenProvider.(adal.Refresher) if ok { err := refresher.EnsureFresh() if err != nil { return r, NewErrorWithError(err, "azure.BearerAuthorizer", "WithAuthorization", nil, "Failed to refresh the Token for request to %s", r.URL) } } return (ba.withBearerAuthorization()(p)).Prepare(r) }) } } // BearerAuthorizerCallbackFunc is the authentication callback signature. type BearerAuthorizerCallbackFunc func(tenantID, resource string) (*BearerAuthorizer, error) // BearerAuthorizerCallback implements bearer authorization via a callback. type BearerAuthorizerCallback struct { sender Sender callback BearerAuthorizerCallbackFunc } // NewBearerAuthorizerCallback creates a bearer authorization callback. The callback // is invoked when the HTTP request is submitted. func NewBearerAuthorizerCallback(sender Sender, callback BearerAuthorizerCallbackFunc) *BearerAuthorizerCallback { if sender == nil { sender = &http.Client{} } return &BearerAuthorizerCallback{sender: sender, callback: callback} } // WithAuthorization returns a PrepareDecorator that adds an HTTP Authorization header whose value // is "Bearer " followed by the token. The BearerAuthorizer is obtained via a user-supplied callback. // // By default, the token will be automatically refreshed through the Refresher interface. func (bacb *BearerAuthorizerCallback) WithAuthorization() PrepareDecorator { return func(p Preparer) Preparer { return PreparerFunc(func(r *http.Request) (*http.Request, error) { // make a copy of the request and remove the body as it's not // required and avoids us having to create a copy of it. rCopy := *r removeRequestBody(&rCopy) resp, err := bacb.sender.Do(&rCopy) if err == nil && resp.StatusCode == 401 { defer resp.Body.Close() if hasBearerChallenge(resp) { bc, err := newBearerChallenge(resp) if err != nil { return r, err } if bacb.callback != nil { ba, err := bacb.callback(bc.values[tenantID], bc.values["resource"]) if err != nil { return r, err } return ba.WithAuthorization()(p).Prepare(r) } } } return r, err }) } } // returns true if the HTTP response contains a bearer challenge func hasBearerChallenge(resp *http.Response) bool { authHeader := resp.Header.Get(bearerChallengeHeader) if len(authHeader) == 0 || strings.Index(authHeader, bearer) < 0 { return false } return true } type bearerChallenge struct { values map[string]string } func newBearerChallenge(resp *http.Response) (bc bearerChallenge, err error) { challenge := strings.TrimSpace(resp.Header.Get(bearerChallengeHeader)) trimmedChallenge := challenge[len(bearer)+1:] // challenge is a set of key=value pairs that are comma delimited pairs := strings.Split(trimmedChallenge, ",") if len(pairs) < 1 { err = fmt.Errorf("challenge '%s' contains no pairs", challenge) return bc, err } bc.values = make(map[string]string) for i := range pairs { trimmedPair := strings.TrimSpace(pairs[i]) pair := strings.Split(trimmedPair, "=") if len(pair) == 2 { // remove the enclosing quotes key := strings.Trim(pair[0], "\"") value := strings.Trim(pair[1], "\"") switch key { case "authorization", "authorization_uri": // strip the tenant ID from the authorization URL asURL, err := url.Parse(value) if err != nil { return bc, err } bc.values[tenantID] = asURL.Path[1:] default: bc.values[key] = value } } } return bc, err } go-autorest-8.3.1/autorest/authorization_test.go000066400000000000000000000134321315133516500221360ustar00rootroot00000000000000package autorest import ( "fmt" "net/http" "reflect" "testing" "github.com/Azure/go-autorest/autorest/adal" "github.com/Azure/go-autorest/autorest/mocks" ) const ( TestTenantID = "TestTenantID" TestActiveDirectoryEndpoint = "https://login/test.com/" ) func TestWithAuthorizer(t *testing.T) { r1 := mocks.NewRequest() na := &NullAuthorizer{} r2, err := Prepare(r1, na.WithAuthorization()) if err != nil { t.Fatalf("autorest: NullAuthorizer#WithAuthorization returned an unexpected error (%v)", err) } else if !reflect.DeepEqual(r1, r2) { t.Fatalf("autorest: NullAuthorizer#WithAuthorization modified the request -- received %v, expected %v", r2, r1) } } func TestTokenWithAuthorization(t *testing.T) { token := &adal.Token{ AccessToken: "TestToken", Resource: "https://azure.microsoft.com/", Type: "Bearer", } ba := NewBearerAuthorizer(token) req, err := Prepare(&http.Request{}, ba.WithAuthorization()) if err != nil { t.Fatalf("azure: BearerAuthorizer#WithAuthorization returned an error (%v)", err) } else if req.Header.Get(http.CanonicalHeaderKey("Authorization")) != fmt.Sprintf("Bearer %s", token.AccessToken) { t.Fatal("azure: BearerAuthorizer#WithAuthorization failed to set Authorization header") } } func TestServicePrincipalTokenWithAuthorizationNoRefresh(t *testing.T) { oauthConfig, err := adal.NewOAuthConfig(TestActiveDirectoryEndpoint, TestTenantID) if err != nil { t.Fatalf("azure: BearerAuthorizer#WithAuthorization returned an error (%v)", err) } spt, err := adal.NewServicePrincipalToken(*oauthConfig, "id", "secret", "resource", nil) if err != nil { t.Fatalf("azure: BearerAuthorizer#WithAuthorization returned an error (%v)", err) } spt.SetAutoRefresh(false) s := mocks.NewSender() spt.SetSender(s) ba := NewBearerAuthorizer(spt) req, err := Prepare(mocks.NewRequest(), ba.WithAuthorization()) if err != nil { t.Fatalf("azure: BearerAuthorizer#WithAuthorization returned an error (%v)", err) } else if req.Header.Get(http.CanonicalHeaderKey("Authorization")) != fmt.Sprintf("Bearer %s", spt.AccessToken) { t.Fatal("azure: BearerAuthorizer#WithAuthorization failed to set Authorization header") } } func TestServicePrincipalTokenWithAuthorizationRefresh(t *testing.T) { oauthConfig, err := adal.NewOAuthConfig(TestActiveDirectoryEndpoint, TestTenantID) if err != nil { t.Fatalf("azure: BearerAuthorizer#WithAuthorization returned an error (%v)", err) } refreshed := false spt, err := adal.NewServicePrincipalToken(*oauthConfig, "id", "secret", "resource", func(t adal.Token) error { refreshed = true return nil }) if err != nil { t.Fatalf("azure: BearerAuthorizer#WithAuthorization returned an error (%v)", err) } jwt := `{ "access_token" : "accessToken", "expires_in" : "3600", "expires_on" : "test", "not_before" : "test", "resource" : "test", "token_type" : "Bearer" }` body := mocks.NewBody(jwt) resp := mocks.NewResponseWithBodyAndStatus(body, http.StatusOK, "OK") c := mocks.NewSender() s := DecorateSender(c, (func() SendDecorator { return func(s Sender) Sender { return SenderFunc(func(r *http.Request) (*http.Response, error) { return resp, nil }) } })()) spt.SetSender(s) ba := NewBearerAuthorizer(spt) req, err := Prepare(mocks.NewRequest(), ba.WithAuthorization()) if err != nil { t.Fatalf("azure: BearerAuthorizer#WithAuthorization returned an error (%v)", err) } else if req.Header.Get(http.CanonicalHeaderKey("Authorization")) != fmt.Sprintf("Bearer %s", spt.AccessToken) { t.Fatal("azure: BearerAuthorizer#WithAuthorization failed to set Authorization header") } if !refreshed { t.Fatal("azure: BearerAuthorizer#WithAuthorization must refresh the token") } } func TestServicePrincipalTokenWithAuthorizationReturnsErrorIfConnotRefresh(t *testing.T) { oauthConfig, err := adal.NewOAuthConfig(TestActiveDirectoryEndpoint, TestTenantID) if err != nil { t.Fatalf("azure: BearerAuthorizer#WithAuthorization returned an error (%v)", err) } spt, err := adal.NewServicePrincipalToken(*oauthConfig, "id", "secret", "resource", nil) if err != nil { t.Fatalf("azure: BearerAuthorizer#WithAuthorization returned an error (%v)", err) } s := mocks.NewSender() s.AppendResponse(mocks.NewResponseWithStatus("400 Bad Request", http.StatusBadRequest)) spt.SetSender(s) ba := NewBearerAuthorizer(spt) _, err = Prepare(mocks.NewRequest(), ba.WithAuthorization()) if err == nil { t.Fatal("azure: BearerAuthorizer#WithAuthorization failed to return an error when refresh fails") } } func TestBearerAuthorizerCallback(t *testing.T) { tenantString := "123-tenantID-456" resourceString := "https://fake.resource.net" s := mocks.NewSender() resp := mocks.NewResponseWithStatus("401 Unauthorized", http.StatusUnauthorized) mocks.SetResponseHeader(resp, bearerChallengeHeader, bearer+" \"authorization\"=\"https://fake.net/"+tenantString+"\",\"resource\"=\""+resourceString+"\"") s.AppendResponse(resp) auth := NewBearerAuthorizerCallback(s, func(tenantID, resource string) (*BearerAuthorizer, error) { if tenantID != tenantString { t.Fatal("BearerAuthorizerCallback: bad tenant ID") } if resource != resourceString { t.Fatal("BearerAuthorizerCallback: bad resource") } oauthConfig, err := adal.NewOAuthConfig(TestActiveDirectoryEndpoint, tenantID) if err != nil { t.Fatalf("azure: NewOAuthConfig returned an error (%v)", err) } spt, err := adal.NewServicePrincipalToken(*oauthConfig, "id", "secret", resource) if err != nil { t.Fatalf("azure: NewServicePrincipalToken returned an error (%v)", err) } spt.SetSender(s) return NewBearerAuthorizer(spt), nil }) _, err := Prepare(mocks.NewRequest(), auth.WithAuthorization()) if err == nil { t.Fatal("azure: BearerAuthorizerCallback#WithAuthorization failed to return an error when refresh fails") } } go-autorest-8.3.1/autorest/autorest.go000066400000000000000000000102221315133516500200370ustar00rootroot00000000000000/* Package autorest implements an HTTP request pipeline suitable for use across multiple go-routines and provides the shared routines relied on by AutoRest (see https://github.com/Azure/autorest/) generated Go code. The package breaks sending and responding to HTTP requests into three phases: Preparing, Sending, and Responding. A typical pattern is: req, err := Prepare(&http.Request{}, token.WithAuthorization()) resp, err := Send(req, WithLogging(logger), DoErrorIfStatusCode(http.StatusInternalServerError), DoCloseIfError(), DoRetryForAttempts(5, time.Second)) err = Respond(resp, ByDiscardingBody(), ByClosing()) Each phase relies on decorators to modify and / or manage processing. Decorators may first modify and then pass the data along, pass the data first and then modify the result, or wrap themselves around passing the data (such as a logger might do). Decorators run in the order provided. For example, the following: req, err := Prepare(&http.Request{}, WithBaseURL("https://microsoft.com/"), WithPath("a"), WithPath("b"), WithPath("c")) will set the URL to: https://microsoft.com/a/b/c Preparers and Responders may be shared and re-used (assuming the underlying decorators support sharing and re-use). Performant use is obtained by creating one or more Preparers and Responders shared among multiple go-routines, and a single Sender shared among multiple sending go-routines, all bound together by means of input / output channels. Decorators hold their passed state within a closure (such as the path components in the example above). Be careful to share Preparers and Responders only in a context where such held state applies. For example, it may not make sense to share a Preparer that applies a query string from a fixed set of values. Similarly, sharing a Responder that reads the response body into a passed struct (e.g., ByUnmarshallingJson) is likely incorrect. Lastly, the Swagger specification (https://swagger.io) that drives AutoRest (https://github.com/Azure/autorest/) precisely defines two date forms: date and date-time. The github.com/Azure/go-autorest/autorest/date package provides time.Time derivations to ensure correct parsing and formatting. Errors raised by autorest objects and methods will conform to the autorest.Error interface. See the included examples for more detail. For details on the suggested use of this package by generated clients, see the Client described below. */ package autorest import ( "net/http" "time" ) const ( // HeaderLocation specifies the HTTP Location header. HeaderLocation = "Location" // HeaderRetryAfter specifies the HTTP Retry-After header. HeaderRetryAfter = "Retry-After" ) // ResponseHasStatusCode returns true if the status code in the HTTP Response is in the passed set // and false otherwise. func ResponseHasStatusCode(resp *http.Response, codes ...int) bool { return containsInt(codes, resp.StatusCode) } // GetLocation retrieves the URL from the Location header of the passed response. func GetLocation(resp *http.Response) string { return resp.Header.Get(HeaderLocation) } // GetRetryAfter extracts the retry delay from the Retry-After header of the passed response. If // the header is absent or is malformed, it will return the supplied default delay time.Duration. func GetRetryAfter(resp *http.Response, defaultDelay time.Duration) time.Duration { retry := resp.Header.Get(HeaderRetryAfter) if retry == "" { return defaultDelay } d, err := time.ParseDuration(retry + "s") if err != nil { return defaultDelay } return d } // NewPollingRequest allocates and returns a new http.Request to poll for the passed response. func NewPollingRequest(resp *http.Response, cancel <-chan struct{}) (*http.Request, error) { location := GetLocation(resp) if location == "" { return nil, NewErrorWithResponse("autorest", "NewPollingRequest", resp, "Location header missing from response that requires polling") } req, err := Prepare(&http.Request{Cancel: cancel}, AsGet(), WithBaseURL(location)) if err != nil { return nil, NewErrorWithError(err, "autorest", "NewPollingRequest", nil, "Failure creating poll request to %s", location) } return req, nil } go-autorest-8.3.1/autorest/autorest_test.go000066400000000000000000000106431315133516500211050ustar00rootroot00000000000000package autorest import ( "net/http" "testing" "github.com/Azure/go-autorest/autorest/mocks" ) func TestResponseHasStatusCode(t *testing.T) { codes := []int{http.StatusOK, http.StatusAccepted} resp := &http.Response{StatusCode: http.StatusAccepted} if !ResponseHasStatusCode(resp, codes...) { t.Fatalf("autorest: ResponseHasStatusCode failed to find %v in %v", resp.StatusCode, codes) } } func TestResponseHasStatusCodeNotPresent(t *testing.T) { codes := []int{http.StatusOK, http.StatusAccepted} resp := &http.Response{StatusCode: http.StatusInternalServerError} if ResponseHasStatusCode(resp, codes...) { t.Fatalf("autorest: ResponseHasStatusCode unexpectedly found %v in %v", resp.StatusCode, codes) } } func TestNewPollingRequestDoesNotReturnARequestWhenLocationHeaderIsMissing(t *testing.T) { resp := mocks.NewResponseWithStatus("500 InternalServerError", http.StatusInternalServerError) req, _ := NewPollingRequest(resp, nil) if req != nil { t.Fatal("autorest: NewPollingRequest returned an http.Request when the Location header was missing") } } func TestNewPollingRequestReturnsAnErrorWhenPrepareFails(t *testing.T) { resp := mocks.NewResponseWithStatus("202 Accepted", http.StatusAccepted) mocks.SetAcceptedHeaders(resp) resp.Header.Set(http.CanonicalHeaderKey(HeaderLocation), mocks.TestBadURL) _, err := NewPollingRequest(resp, nil) if err == nil { t.Fatal("autorest: NewPollingRequest failed to return an error when Prepare fails") } } func TestNewPollingRequestDoesNotReturnARequestWhenPrepareFails(t *testing.T) { resp := mocks.NewResponseWithStatus("202 Accepted", http.StatusAccepted) mocks.SetAcceptedHeaders(resp) resp.Header.Set(http.CanonicalHeaderKey(HeaderLocation), mocks.TestBadURL) req, _ := NewPollingRequest(resp, nil) if req != nil { t.Fatal("autorest: NewPollingRequest returned an http.Request when Prepare failed") } } func TestNewPollingRequestReturnsAGetRequest(t *testing.T) { resp := mocks.NewResponseWithStatus("202 Accepted", http.StatusAccepted) mocks.SetAcceptedHeaders(resp) req, _ := NewPollingRequest(resp, nil) if req.Method != "GET" { t.Fatalf("autorest: NewPollingRequest did not create an HTTP GET request -- actual method %v", req.Method) } } func TestNewPollingRequestProvidesTheURL(t *testing.T) { resp := mocks.NewResponseWithStatus("202 Accepted", http.StatusAccepted) mocks.SetAcceptedHeaders(resp) req, _ := NewPollingRequest(resp, nil) if req.URL.String() != mocks.TestURL { t.Fatalf("autorest: NewPollingRequest did not create an HTTP with the expected URL -- received %v, expected %v", req.URL, mocks.TestURL) } } func TestGetLocation(t *testing.T) { resp := mocks.NewResponseWithStatus("202 Accepted", http.StatusAccepted) mocks.SetAcceptedHeaders(resp) l := GetLocation(resp) if len(l) == 0 { t.Fatalf("autorest: GetLocation failed to return Location header -- expected %v, received %v", mocks.TestURL, l) } } func TestGetLocationReturnsEmptyStringForMissingLocation(t *testing.T) { resp := mocks.NewResponseWithStatus("202 Accepted", http.StatusAccepted) l := GetLocation(resp) if len(l) != 0 { t.Fatalf("autorest: GetLocation return a value without a Location header -- received %v", l) } } func TestGetRetryAfter(t *testing.T) { resp := mocks.NewResponseWithStatus("202 Accepted", http.StatusAccepted) mocks.SetAcceptedHeaders(resp) d := GetRetryAfter(resp, DefaultPollingDelay) if d != mocks.TestDelay { t.Fatalf("autorest: GetRetryAfter failed to returned the expected delay -- expected %v, received %v", mocks.TestDelay, d) } } func TestGetRetryAfterReturnsDefaultDelayIfRetryHeaderIsMissing(t *testing.T) { resp := mocks.NewResponseWithStatus("202 Accepted", http.StatusAccepted) d := GetRetryAfter(resp, DefaultPollingDelay) if d != DefaultPollingDelay { t.Fatalf("autorest: GetRetryAfter failed to returned the default delay for a missing Retry-After header -- expected %v, received %v", DefaultPollingDelay, d) } } func TestGetRetryAfterReturnsDefaultDelayIfRetryHeaderIsMalformed(t *testing.T) { resp := mocks.NewResponseWithStatus("202 Accepted", http.StatusAccepted) mocks.SetAcceptedHeaders(resp) resp.Header.Set(http.CanonicalHeaderKey(HeaderRetryAfter), "a very bad non-integer value") d := GetRetryAfter(resp, DefaultPollingDelay) if d != DefaultPollingDelay { t.Fatalf("autorest: GetRetryAfter failed to returned the default delay for a malformed Retry-After header -- expected %v, received %v", DefaultPollingDelay, d) } } go-autorest-8.3.1/autorest/azure/000077500000000000000000000000001315133516500167735ustar00rootroot00000000000000go-autorest-8.3.1/autorest/azure/async.go000066400000000000000000000206251315133516500204440ustar00rootroot00000000000000package azure import ( "bytes" "fmt" "io/ioutil" "net/http" "strings" "time" "github.com/Azure/go-autorest/autorest" "github.com/Azure/go-autorest/autorest/date" ) const ( headerAsyncOperation = "Azure-AsyncOperation" ) const ( operationInProgress string = "InProgress" operationCanceled string = "Canceled" operationFailed string = "Failed" operationSucceeded string = "Succeeded" ) // DoPollForAsynchronous returns a SendDecorator that polls if the http.Response is for an Azure // long-running operation. It will delay between requests for the duration specified in the // RetryAfter header or, if the header is absent, the passed delay. Polling may be canceled by // closing the optional channel on the http.Request. func DoPollForAsynchronous(delay time.Duration) autorest.SendDecorator { return func(s autorest.Sender) autorest.Sender { return autorest.SenderFunc(func(r *http.Request) (resp *http.Response, err error) { resp, err = s.Do(r) if err != nil { return resp, err } pollingCodes := []int{http.StatusAccepted, http.StatusCreated, http.StatusOK} if !autorest.ResponseHasStatusCode(resp, pollingCodes...) { return resp, nil } ps := pollingState{} for err == nil { err = updatePollingState(resp, &ps) if err != nil { break } if ps.hasTerminated() { if !ps.hasSucceeded() { err = ps } break } r, err = newPollingRequest(resp, ps) if err != nil { return resp, err } delay = autorest.GetRetryAfter(resp, delay) resp, err = autorest.SendWithSender(s, r, autorest.AfterDelay(delay)) } return resp, err }) } } func getAsyncOperation(resp *http.Response) string { return resp.Header.Get(http.CanonicalHeaderKey(headerAsyncOperation)) } func hasSucceeded(state string) bool { return state == operationSucceeded } func hasTerminated(state string) bool { switch state { case operationCanceled, operationFailed, operationSucceeded: return true default: return false } } func hasFailed(state string) bool { return state == operationFailed } type provisioningTracker interface { state() string hasSucceeded() bool hasTerminated() bool } type operationResource struct { // Note: // The specification states services should return the "id" field. However some return it as // "operationId". ID string `json:"id"` OperationID string `json:"operationId"` Name string `json:"name"` Status string `json:"status"` Properties map[string]interface{} `json:"properties"` OperationError ServiceError `json:"error"` StartTime date.Time `json:"startTime"` EndTime date.Time `json:"endTime"` PercentComplete float64 `json:"percentComplete"` } func (or operationResource) state() string { return or.Status } func (or operationResource) hasSucceeded() bool { return hasSucceeded(or.state()) } func (or operationResource) hasTerminated() bool { return hasTerminated(or.state()) } type provisioningProperties struct { ProvisioningState string `json:"provisioningState"` } type provisioningStatus struct { Properties provisioningProperties `json:"properties,omitempty"` ProvisioningError ServiceError `json:"error,omitempty"` } func (ps provisioningStatus) state() string { return ps.Properties.ProvisioningState } func (ps provisioningStatus) hasSucceeded() bool { return hasSucceeded(ps.state()) } func (ps provisioningStatus) hasTerminated() bool { return hasTerminated(ps.state()) } func (ps provisioningStatus) hasProvisioningError() bool { return ps.ProvisioningError != ServiceError{} } type pollingResponseFormat string const ( usesOperationResponse pollingResponseFormat = "OperationResponse" usesProvisioningStatus pollingResponseFormat = "ProvisioningStatus" formatIsUnknown pollingResponseFormat = "" ) type pollingState struct { responseFormat pollingResponseFormat uri string state string code string message string } func (ps pollingState) hasSucceeded() bool { return hasSucceeded(ps.state) } func (ps pollingState) hasTerminated() bool { return hasTerminated(ps.state) } func (ps pollingState) hasFailed() bool { return hasFailed(ps.state) } func (ps pollingState) Error() string { return fmt.Sprintf("Long running operation terminated with status '%s': Code=%q Message=%q", ps.state, ps.code, ps.message) } // updatePollingState maps the operation status -- retrieved from either a provisioningState // field, the status field of an OperationResource, or inferred from the HTTP status code -- // into a well-known states. Since the process begins from the initial request, the state // always comes from either a the provisioningState returned or is inferred from the HTTP // status code. Subsequent requests will read an Azure OperationResource object if the // service initially returned the Azure-AsyncOperation header. The responseFormat field notes // the expected response format. func updatePollingState(resp *http.Response, ps *pollingState) error { // Determine the response shape // -- The first response will always be a provisioningStatus response; only the polling requests, // depending on the header returned, may be something otherwise. var pt provisioningTracker if ps.responseFormat == usesOperationResponse { pt = &operationResource{} } else { pt = &provisioningStatus{} } // If this is the first request (that is, the polling response shape is unknown), determine how // to poll and what to expect if ps.responseFormat == formatIsUnknown { req := resp.Request if req == nil { return autorest.NewError("azure", "updatePollingState", "Azure Polling Error - Original HTTP request is missing") } // Prefer the Azure-AsyncOperation header ps.uri = getAsyncOperation(resp) if ps.uri != "" { ps.responseFormat = usesOperationResponse } else { ps.responseFormat = usesProvisioningStatus } // Else, use the Location header if ps.uri == "" { ps.uri = autorest.GetLocation(resp) } // Lastly, requests against an existing resource, use the last request URI if ps.uri == "" { m := strings.ToUpper(req.Method) if m == http.MethodPatch || m == http.MethodPut || m == http.MethodGet { ps.uri = req.URL.String() } } } // Read and interpret the response (saving the Body in case no polling is necessary) b := &bytes.Buffer{} err := autorest.Respond(resp, autorest.ByCopying(b), autorest.ByUnmarshallingJSON(pt), autorest.ByClosing()) resp.Body = ioutil.NopCloser(b) if err != nil { return err } // Interpret the results // -- Terminal states apply regardless // -- Unknown states are per-service inprogress states // -- Otherwise, infer state from HTTP status code if pt.hasTerminated() { ps.state = pt.state() } else if pt.state() != "" { ps.state = operationInProgress } else { switch resp.StatusCode { case http.StatusAccepted: ps.state = operationInProgress case http.StatusNoContent, http.StatusCreated, http.StatusOK: ps.state = operationSucceeded default: ps.state = operationFailed } } if ps.state == operationInProgress && ps.uri == "" { return autorest.NewError("azure", "updatePollingState", "Azure Polling Error - Unable to obtain polling URI for %s %s", resp.Request.Method, resp.Request.URL) } // For failed operation, check for error code and message in // -- Operation resource // -- Response // -- Otherwise, Unknown if ps.hasFailed() { if ps.responseFormat == usesOperationResponse { or := pt.(*operationResource) ps.code = or.OperationError.Code ps.message = or.OperationError.Message } else { p := pt.(*provisioningStatus) if p.hasProvisioningError() { ps.code = p.ProvisioningError.Code ps.message = p.ProvisioningError.Message } else { ps.code = "Unknown" ps.message = "None" } } } return nil } func newPollingRequest(resp *http.Response, ps pollingState) (*http.Request, error) { req := resp.Request if req == nil { return nil, autorest.NewError("azure", "newPollingRequest", "Azure Polling Error - Original HTTP request is missing") } reqPoll, err := autorest.Prepare(&http.Request{Cancel: req.Cancel}, autorest.AsGet(), autorest.WithBaseURL(ps.uri)) if err != nil { return nil, autorest.NewErrorWithError(err, "azure", "newPollingRequest", nil, "Failure creating poll request to %s", ps.uri) } return reqPoll, nil } go-autorest-8.3.1/autorest/azure/async_test.go000066400000000000000000001117241315133516500215040ustar00rootroot00000000000000package azure import ( "fmt" "io/ioutil" "net/http" "reflect" "strings" "sync" "testing" "time" "github.com/Azure/go-autorest/autorest" "github.com/Azure/go-autorest/autorest/mocks" ) func TestGetAsyncOperation_ReturnsAzureAsyncOperationHeader(t *testing.T) { r := newAsynchronousResponse() if getAsyncOperation(r) != mocks.TestAzureAsyncURL { t.Fatalf("azure: getAsyncOperation failed to extract the Azure-AsyncOperation header -- expected %v, received %v", mocks.TestURL, getAsyncOperation(r)) } } func TestGetAsyncOperation_ReturnsEmptyStringIfHeaderIsAbsent(t *testing.T) { r := mocks.NewResponse() if len(getAsyncOperation(r)) != 0 { t.Fatalf("azure: getAsyncOperation failed to return empty string when the Azure-AsyncOperation header is absent -- received %v", getAsyncOperation(r)) } } func TestHasSucceeded_ReturnsTrueForSuccess(t *testing.T) { if !hasSucceeded(operationSucceeded) { t.Fatal("azure: hasSucceeded failed to return true for success") } } func TestHasSucceeded_ReturnsFalseOtherwise(t *testing.T) { if hasSucceeded("not a success string") { t.Fatal("azure: hasSucceeded returned true for a non-success") } } func TestHasTerminated_ReturnsTrueForValidTerminationStates(t *testing.T) { for _, state := range []string{operationSucceeded, operationCanceled, operationFailed} { if !hasTerminated(state) { t.Fatalf("azure: hasTerminated failed to return true for the '%s' state", state) } } } func TestHasTerminated_ReturnsFalseForUnknownStates(t *testing.T) { if hasTerminated("not a known state") { t.Fatal("azure: hasTerminated returned true for an unknown state") } } func TestOperationError_ErrorReturnsAString(t *testing.T) { s := (ServiceError{Code: "server code", Message: "server error"}).Error() if s == "" { t.Fatalf("azure: operationError#Error failed to return an error") } if !strings.Contains(s, "server code") || !strings.Contains(s, "server error") { t.Fatalf("azure: operationError#Error returned a malformed error -- error='%v'", s) } } func TestOperationResource_StateReturnsState(t *testing.T) { if (operationResource{Status: "state"}).state() != "state" { t.Fatalf("azure: operationResource#state failed to return the correct state") } } func TestOperationResource_HasSucceededReturnsFalseIfNotSuccess(t *testing.T) { if (operationResource{Status: "not a success string"}).hasSucceeded() { t.Fatalf("azure: operationResource#hasSucceeded failed to return false for a canceled operation") } } func TestOperationResource_HasSucceededReturnsTrueIfSuccessful(t *testing.T) { if !(operationResource{Status: operationSucceeded}).hasSucceeded() { t.Fatalf("azure: operationResource#hasSucceeded failed to return true for a successful operation") } } func TestOperationResource_HasTerminatedReturnsTrueForKnownStates(t *testing.T) { for _, state := range []string{operationSucceeded, operationCanceled, operationFailed} { if !(operationResource{Status: state}).hasTerminated() { t.Fatalf("azure: operationResource#hasTerminated failed to return true for the '%s' state", state) } } } func TestOperationResource_HasTerminatedReturnsFalseForUnknownStates(t *testing.T) { if (operationResource{Status: "not a known state"}).hasTerminated() { t.Fatalf("azure: operationResource#hasTerminated returned true for a non-terminal operation") } } func TestProvisioningStatus_StateReturnsState(t *testing.T) { if (provisioningStatus{Properties: provisioningProperties{"state"}}).state() != "state" { t.Fatalf("azure: provisioningStatus#state failed to return the correct state") } } func TestProvisioningStatus_HasSucceededReturnsFalseIfNotSuccess(t *testing.T) { if (provisioningStatus{Properties: provisioningProperties{"not a success string"}}).hasSucceeded() { t.Fatalf("azure: provisioningStatus#hasSucceeded failed to return false for a canceled operation") } } func TestProvisioningStatus_HasSucceededReturnsTrueIfSuccessful(t *testing.T) { if !(provisioningStatus{Properties: provisioningProperties{operationSucceeded}}).hasSucceeded() { t.Fatalf("azure: provisioningStatus#hasSucceeded failed to return true for a successful operation") } } func TestProvisioningStatus_HasTerminatedReturnsTrueForKnownStates(t *testing.T) { for _, state := range []string{operationSucceeded, operationCanceled, operationFailed} { if !(provisioningStatus{Properties: provisioningProperties{state}}).hasTerminated() { t.Fatalf("azure: provisioningStatus#hasTerminated failed to return true for the '%s' state", state) } } } func TestProvisioningStatus_HasTerminatedReturnsFalseForUnknownStates(t *testing.T) { if (provisioningStatus{Properties: provisioningProperties{"not a known state"}}).hasTerminated() { t.Fatalf("azure: provisioningStatus#hasTerminated returned true for a non-terminal operation") } } func TestPollingState_HasSucceededReturnsFalseIfNotSuccess(t *testing.T) { if (pollingState{state: "not a success string"}).hasSucceeded() { t.Fatalf("azure: pollingState#hasSucceeded failed to return false for a canceled operation") } } func TestPollingState_HasSucceededReturnsTrueIfSuccessful(t *testing.T) { if !(pollingState{state: operationSucceeded}).hasSucceeded() { t.Fatalf("azure: pollingState#hasSucceeded failed to return true for a successful operation") } } func TestPollingState_HasTerminatedReturnsTrueForKnownStates(t *testing.T) { for _, state := range []string{operationSucceeded, operationCanceled, operationFailed} { if !(pollingState{state: state}).hasTerminated() { t.Fatalf("azure: pollingState#hasTerminated failed to return true for the '%s' state", state) } } } func TestPollingState_HasTerminatedReturnsFalseForUnknownStates(t *testing.T) { if (pollingState{state: "not a known state"}).hasTerminated() { t.Fatalf("azure: pollingState#hasTerminated returned true for a non-terminal operation") } } func TestUpdatePollingState_ReturnsAnErrorIfOneOccurs(t *testing.T) { resp := mocks.NewResponseWithContent(operationResourceIllegal) err := updatePollingState(resp, &pollingState{}) if err == nil { t.Fatalf("azure: updatePollingState failed to return an error after a JSON parsing error") } } func TestUpdatePollingState_ReturnsTerminatedForKnownProvisioningStates(t *testing.T) { for _, state := range []string{operationSucceeded, operationCanceled, operationFailed} { resp := mocks.NewResponseWithContent(fmt.Sprintf(pollingStateFormat, state)) resp.StatusCode = 42 ps := &pollingState{responseFormat: usesProvisioningStatus} updatePollingState(resp, ps) if !ps.hasTerminated() { t.Fatalf("azure: updatePollingState failed to return a terminating pollingState for the '%s' state", state) } } } func TestUpdatePollingState_ReturnsSuccessForSuccessfulProvisioningState(t *testing.T) { resp := mocks.NewResponseWithContent(fmt.Sprintf(pollingStateFormat, operationSucceeded)) resp.StatusCode = 42 ps := &pollingState{responseFormat: usesProvisioningStatus} updatePollingState(resp, ps) if !ps.hasSucceeded() { t.Fatalf("azure: updatePollingState failed to return a successful pollingState for the '%s' state", operationSucceeded) } } func TestUpdatePollingState_ReturnsInProgressForAllOtherProvisioningStates(t *testing.T) { s := "not a recognized state" resp := mocks.NewResponseWithContent(fmt.Sprintf(pollingStateFormat, s)) resp.StatusCode = 42 ps := &pollingState{responseFormat: usesProvisioningStatus} updatePollingState(resp, ps) if ps.hasTerminated() { t.Fatalf("azure: updatePollingState returned terminated for unknown state '%s'", s) } } func TestUpdatePollingState_ReturnsSuccessWhenProvisioningStateFieldIsAbsentForSuccessStatusCodes(t *testing.T) { for _, sc := range []int{http.StatusOK, http.StatusCreated, http.StatusNoContent} { resp := mocks.NewResponseWithContent(pollingStateEmpty) resp.StatusCode = sc ps := &pollingState{responseFormat: usesProvisioningStatus} updatePollingState(resp, ps) if !ps.hasSucceeded() { t.Fatalf("azure: updatePollingState failed to return success when the provisionState field is absent for Status Code %d", sc) } } } func TestUpdatePollingState_ReturnsInProgressWhenProvisioningStateFieldIsAbsentForAccepted(t *testing.T) { resp := mocks.NewResponseWithContent(pollingStateEmpty) resp.StatusCode = http.StatusAccepted ps := &pollingState{responseFormat: usesProvisioningStatus} updatePollingState(resp, ps) if ps.hasTerminated() { t.Fatalf("azure: updatePollingState returned terminated when the provisionState field is absent for Status Code Accepted") } } func TestUpdatePollingState_ReturnsFailedWhenProvisioningStateFieldIsAbsentForUnknownStatusCodes(t *testing.T) { resp := mocks.NewResponseWithContent(pollingStateEmpty) resp.StatusCode = 42 ps := &pollingState{responseFormat: usesProvisioningStatus} updatePollingState(resp, ps) if !ps.hasTerminated() || ps.hasSucceeded() { t.Fatalf("azure: updatePollingState did not return failed when the provisionState field is absent for an unknown Status Code") } } func TestUpdatePollingState_ReturnsTerminatedForKnownOperationResourceStates(t *testing.T) { for _, state := range []string{operationSucceeded, operationCanceled, operationFailed} { resp := mocks.NewResponseWithContent(fmt.Sprintf(operationResourceFormat, state)) resp.StatusCode = 42 ps := &pollingState{responseFormat: usesOperationResponse} updatePollingState(resp, ps) if !ps.hasTerminated() { t.Fatalf("azure: updatePollingState failed to return a terminating pollingState for the '%s' state", state) } } } func TestUpdatePollingState_ReturnsSuccessForSuccessfulOperationResourceState(t *testing.T) { resp := mocks.NewResponseWithContent(fmt.Sprintf(operationResourceFormat, operationSucceeded)) resp.StatusCode = 42 ps := &pollingState{responseFormat: usesOperationResponse} updatePollingState(resp, ps) if !ps.hasSucceeded() { t.Fatalf("azure: updatePollingState failed to return a successful pollingState for the '%s' state", operationSucceeded) } } func TestUpdatePollingState_ReturnsInProgressForAllOtherOperationResourceStates(t *testing.T) { s := "not a recognized state" resp := mocks.NewResponseWithContent(fmt.Sprintf(operationResourceFormat, s)) resp.StatusCode = 42 ps := &pollingState{responseFormat: usesOperationResponse} updatePollingState(resp, ps) if ps.hasTerminated() { t.Fatalf("azure: updatePollingState returned terminated for unknown state '%s'", s) } } func TestUpdatePollingState_CopiesTheResponseBody(t *testing.T) { s := fmt.Sprintf(pollingStateFormat, operationSucceeded) resp := mocks.NewResponseWithContent(s) resp.StatusCode = 42 ps := &pollingState{responseFormat: usesOperationResponse} updatePollingState(resp, ps) b, err := ioutil.ReadAll(resp.Body) if err != nil { t.Fatalf("azure: updatePollingState failed to replace the http.Response Body -- Error='%v'", err) } if string(b) != s { t.Fatalf("azure: updatePollingState failed to copy the http.Response Body -- Expected='%s' Received='%s'", s, string(b)) } } func TestUpdatePollingState_ClosesTheOriginalResponseBody(t *testing.T) { resp := mocks.NewResponse() b := resp.Body.(*mocks.Body) ps := &pollingState{responseFormat: usesProvisioningStatus} updatePollingState(resp, ps) if b.IsOpen() { t.Fatal("azure: updatePollingState failed to close the original http.Response Body") } } func TestUpdatePollingState_FailsWhenResponseLacksRequest(t *testing.T) { resp := newAsynchronousResponse() resp.Request = nil ps := pollingState{} err := updatePollingState(resp, &ps) if err == nil { t.Fatal("azure: updatePollingState failed to return an error when the http.Response lacked the original http.Request") } } func TestUpdatePollingState_SetsTheResponseFormatWhenUsingTheAzureAsyncOperationHeader(t *testing.T) { ps := pollingState{} updatePollingState(newAsynchronousResponse(), &ps) if ps.responseFormat != usesOperationResponse { t.Fatal("azure: updatePollingState failed to set the correct response format when using the Azure-AsyncOperation header") } } func TestUpdatePollingState_SetsTheResponseFormatWhenUsingTheAzureAsyncOperationHeaderIsMissing(t *testing.T) { resp := newAsynchronousResponse() resp.Header.Del(http.CanonicalHeaderKey(headerAsyncOperation)) ps := pollingState{} updatePollingState(resp, &ps) if ps.responseFormat != usesProvisioningStatus { t.Fatal("azure: updatePollingState failed to set the correct response format when the Azure-AsyncOperation header is absent") } } func TestUpdatePollingState_DoesNotChangeAnExistingReponseFormat(t *testing.T) { resp := newAsynchronousResponse() resp.Header.Del(http.CanonicalHeaderKey(headerAsyncOperation)) ps := pollingState{responseFormat: usesOperationResponse} updatePollingState(resp, &ps) if ps.responseFormat != usesOperationResponse { t.Fatal("azure: updatePollingState failed to leave an existing response format setting") } } func TestUpdatePollingState_PrefersTheAzureAsyncOperationHeader(t *testing.T) { resp := newAsynchronousResponse() ps := pollingState{} updatePollingState(resp, &ps) if ps.uri != mocks.TestAzureAsyncURL { t.Fatal("azure: updatePollingState failed to prefer the Azure-AsyncOperation header") } } func TestUpdatePollingState_PrefersLocationWhenTheAzureAsyncOperationHeaderMissing(t *testing.T) { resp := newAsynchronousResponse() resp.Header.Del(http.CanonicalHeaderKey(headerAsyncOperation)) ps := pollingState{} updatePollingState(resp, &ps) if ps.uri != mocks.TestLocationURL { t.Fatal("azure: updatePollingState failed to prefer the Location header when the Azure-AsyncOperation header is missing") } } func TestUpdatePollingState_UsesTheObjectLocationIfAsyncHeadersAreMissing(t *testing.T) { resp := newAsynchronousResponse() resp.Header.Del(http.CanonicalHeaderKey(headerAsyncOperation)) resp.Header.Del(http.CanonicalHeaderKey(autorest.HeaderLocation)) resp.Request.Method = http.MethodPatch ps := pollingState{} updatePollingState(resp, &ps) if ps.uri != mocks.TestURL { t.Fatal("azure: updatePollingState failed to use the Object URL when the asynchronous headers are missing") } } func TestUpdatePollingState_RecognizesLowerCaseHTTPVerbs(t *testing.T) { for _, m := range []string{strings.ToLower(http.MethodPatch), strings.ToLower(http.MethodPut), strings.ToLower(http.MethodGet)} { resp := newAsynchronousResponse() resp.Header.Del(http.CanonicalHeaderKey(headerAsyncOperation)) resp.Header.Del(http.CanonicalHeaderKey(autorest.HeaderLocation)) resp.Request.Method = m ps := pollingState{} updatePollingState(resp, &ps) if ps.uri != mocks.TestURL { t.Fatalf("azure: updatePollingState failed to recognize the lower-case HTTP verb '%s'", m) } } } func TestUpdatePollingState_ReturnsAnErrorIfAsyncHeadersAreMissingForANewOrDeletedObject(t *testing.T) { resp := newAsynchronousResponse() resp.Header.Del(http.CanonicalHeaderKey(headerAsyncOperation)) resp.Header.Del(http.CanonicalHeaderKey(autorest.HeaderLocation)) for _, m := range []string{http.MethodDelete, http.MethodPost} { resp.Request.Method = m err := updatePollingState(resp, &pollingState{}) if err == nil { t.Fatalf("azure: updatePollingState failed to return an error even though it could not determine the polling URL for Method '%s'", m) } } } func TestNewPollingRequest_FailsWhenResponseLacksRequest(t *testing.T) { resp := newAsynchronousResponse() resp.Request = nil _, err := newPollingRequest(resp, pollingState{}) if err == nil { t.Fatal("azure: newPollingRequest failed to return an error when the http.Response lacked the original http.Request") } } func TestNewPollingRequest_ReturnsAnErrorWhenPrepareFails(t *testing.T) { _, err := newPollingRequest(newAsynchronousResponse(), pollingState{responseFormat: usesOperationResponse, uri: mocks.TestBadURL}) if err == nil { t.Fatal("azure: newPollingRequest failed to return an error when Prepare fails") } } func TestNewPollingRequest_DoesNotReturnARequestWhenPrepareFails(t *testing.T) { req, _ := newPollingRequest(newAsynchronousResponse(), pollingState{responseFormat: usesOperationResponse, uri: mocks.TestBadURL}) if req != nil { t.Fatal("azure: newPollingRequest returned an http.Request when Prepare failed") } } func TestNewPollingRequest_ReturnsAGetRequest(t *testing.T) { req, _ := newPollingRequest(newAsynchronousResponse(), pollingState{responseFormat: usesOperationResponse, uri: mocks.TestAzureAsyncURL}) if req.Method != "GET" { t.Fatalf("azure: newPollingRequest did not create an HTTP GET request -- actual method %v", req.Method) } } func TestDoPollForAsynchronous_IgnoresUnspecifiedStatusCodes(t *testing.T) { client := mocks.NewSender() r, _ := autorest.SendWithSender(client, mocks.NewRequest(), DoPollForAsynchronous(time.Duration(0))) if client.Attempts() != 1 { t.Fatalf("azure: DoPollForAsynchronous polled for unspecified status code") } autorest.Respond(r, autorest.ByClosing()) } func TestDoPollForAsynchronous_PollsForSpecifiedStatusCodes(t *testing.T) { client := mocks.NewSender() client.AppendResponse(newAsynchronousResponse()) r, _ := autorest.SendWithSender(client, mocks.NewRequest(), DoPollForAsynchronous(time.Millisecond)) if client.Attempts() != 2 { t.Fatalf("azure: DoPollForAsynchronous failed to poll for specified status code") } autorest.Respond(r, autorest.ByClosing()) } func TestDoPollForAsynchronous_CanBeCanceled(t *testing.T) { cancel := make(chan struct{}) delay := 5 * time.Second r1 := newAsynchronousResponse() client := mocks.NewSender() client.AppendResponse(r1) client.AppendAndRepeatResponse(newOperationResourceResponse("Busy"), -1) var wg sync.WaitGroup wg.Add(1) start := time.Now() go func() { req := mocks.NewRequest() req.Cancel = cancel wg.Done() r, _ := autorest.SendWithSender(client, req, DoPollForAsynchronous(10*time.Second)) autorest.Respond(r, autorest.ByClosing()) }() wg.Wait() close(cancel) time.Sleep(5 * time.Millisecond) if time.Since(start) >= delay { t.Fatalf("azure: DoPollForAsynchronous failed to cancel") } } func TestDoPollForAsynchronous_ClosesAllNonreturnedResponseBodiesWhenPolling(t *testing.T) { r1 := newAsynchronousResponse() b1 := r1.Body.(*mocks.Body) r2 := newOperationResourceResponse("busy") b2 := r2.Body.(*mocks.Body) r3 := newOperationResourceResponse(operationSucceeded) b3 := r3.Body.(*mocks.Body) client := mocks.NewSender() client.AppendResponse(r1) client.AppendAndRepeatResponse(r2, 2) client.AppendResponse(r3) r, _ := autorest.SendWithSender(client, mocks.NewRequest(), DoPollForAsynchronous(time.Millisecond)) if b1.IsOpen() || b2.IsOpen() || b3.IsOpen() { t.Fatalf("azure: DoPollForAsynchronous did not close unreturned response bodies") } autorest.Respond(r, autorest.ByClosing()) } func TestDoPollForAsynchronous_LeavesLastResponseBodyOpen(t *testing.T) { r1 := newAsynchronousResponse() r2 := newOperationResourceResponse("busy") r3 := newOperationResourceResponse(operationSucceeded) client := mocks.NewSender() client.AppendResponse(r1) client.AppendAndRepeatResponse(r2, 2) client.AppendResponse(r3) r, _ := autorest.SendWithSender(client, mocks.NewRequest(), DoPollForAsynchronous(time.Millisecond)) b, err := ioutil.ReadAll(r.Body) if len(b) <= 0 || err != nil { t.Fatalf("azure: DoPollForAsynchronous did not leave open the body of the last response - Error='%v'", err) } autorest.Respond(r, autorest.ByClosing()) } func TestDoPollForAsynchronous_DoesNotPollIfOriginalRequestReturnedAnError(t *testing.T) { r1 := newAsynchronousResponse() r2 := newOperationResourceResponse("busy") client := mocks.NewSender() client.AppendResponse(r1) client.AppendResponse(r2) client.SetError(fmt.Errorf("Faux Error")) r, _ := autorest.SendWithSender(client, mocks.NewRequest(), DoPollForAsynchronous(time.Millisecond)) if client.Attempts() != 1 { t.Fatalf("azure: DoPollForAsynchronous tried to poll after receiving an error") } autorest.Respond(r, autorest.ByClosing()) } func TestDoPollForAsynchronous_DoesNotPollIfCreatingOperationRequestFails(t *testing.T) { r1 := newAsynchronousResponse() mocks.SetResponseHeader(r1, http.CanonicalHeaderKey(headerAsyncOperation), mocks.TestBadURL) r2 := newOperationResourceResponse("busy") client := mocks.NewSender() client.AppendResponse(r1) client.AppendAndRepeatResponse(r2, 2) r, _ := autorest.SendWithSender(client, mocks.NewRequest(), DoPollForAsynchronous(time.Millisecond)) if client.Attempts() > 1 { t.Fatalf("azure: DoPollForAsynchronous polled with an invalidly formed operation request") } autorest.Respond(r, autorest.ByClosing()) } func TestDoPollForAsynchronous_StopsPollingAfterAnError(t *testing.T) { r1 := newAsynchronousResponse() r2 := newOperationResourceResponse("busy") client := mocks.NewSender() client.AppendResponse(r1) client.AppendAndRepeatResponse(r2, 2) client.SetError(fmt.Errorf("Faux Error")) client.SetEmitErrorAfter(2) r, _ := autorest.SendWithSender(client, mocks.NewRequest(), DoPollForAsynchronous(time.Millisecond)) if client.Attempts() > 3 { t.Fatalf("azure: DoPollForAsynchronous failed to stop polling after receiving an error") } autorest.Respond(r, autorest.ByClosing()) } func TestDoPollForAsynchronous_ReturnsPollingError(t *testing.T) { client := mocks.NewSender() client.AppendAndRepeatResponse(newAsynchronousResponse(), 5) client.SetError(fmt.Errorf("Faux Error")) client.SetEmitErrorAfter(1) r, err := autorest.SendWithSender(client, mocks.NewRequest(), DoPollForAsynchronous(time.Millisecond)) if err == nil { t.Fatalf("azure: DoPollForAsynchronous failed to return error from polling") } autorest.Respond(r, autorest.ByClosing()) } func TestDoPollForAsynchronous_PollsForStatusAccepted(t *testing.T) { r1 := newAsynchronousResponse() r1.Status = "202 Accepted" r1.StatusCode = http.StatusAccepted r2 := newOperationResourceResponse("busy") r3 := newOperationResourceResponse(operationCanceled) client := mocks.NewSender() client.AppendResponse(r1) client.AppendAndRepeatResponse(r2, 2) client.AppendAndRepeatResponse(r3, 1) r, _ := autorest.SendWithSender(client, mocks.NewRequest(), DoPollForAsynchronous(time.Millisecond)) if client.Attempts() < 4 { t.Fatalf("azure: DoPollForAsynchronous stopped polling before receiving a terminated OperationResource") } autorest.Respond(r, autorest.ByClosing()) } func TestDoPollForAsynchronous_PollsForStatusCreated(t *testing.T) { r1 := newAsynchronousResponse() r1.Status = "201 Created" r1.StatusCode = http.StatusCreated r2 := newOperationResourceResponse("busy") r3 := newOperationResourceResponse(operationCanceled) client := mocks.NewSender() client.AppendResponse(r1) client.AppendAndRepeatResponse(r2, 2) client.AppendAndRepeatResponse(r3, 1) r, _ := autorest.SendWithSender(client, mocks.NewRequest(), DoPollForAsynchronous(time.Millisecond)) if client.Attempts() < 4 { t.Fatalf("azure: DoPollForAsynchronous stopped polling before receiving a terminated OperationResource") } autorest.Respond(r, autorest.ByClosing()) } func TestDoPollForAsynchronous_PollsUntilProvisioningStatusTerminates(t *testing.T) { r1 := newAsynchronousResponse() r1.Header.Del(http.CanonicalHeaderKey(headerAsyncOperation)) r2 := newProvisioningStatusResponse("busy") r2.Header.Del(http.CanonicalHeaderKey(headerAsyncOperation)) r3 := newProvisioningStatusResponse(operationCanceled) r3.Header.Del(http.CanonicalHeaderKey(headerAsyncOperation)) client := mocks.NewSender() client.AppendResponse(r1) client.AppendAndRepeatResponse(r2, 2) client.AppendAndRepeatResponse(r3, 1) r, _ := autorest.SendWithSender(client, mocks.NewRequest(), DoPollForAsynchronous(time.Millisecond)) if client.Attempts() < 4 { t.Fatalf("azure: DoPollForAsynchronous stopped polling before receiving a terminated OperationResource") } autorest.Respond(r, autorest.ByClosing()) } func TestDoPollForAsynchronous_PollsUntilProvisioningStatusSucceeds(t *testing.T) { r1 := newAsynchronousResponse() r1.Header.Del(http.CanonicalHeaderKey(headerAsyncOperation)) r2 := newProvisioningStatusResponse("busy") r2.Header.Del(http.CanonicalHeaderKey(headerAsyncOperation)) r3 := newProvisioningStatusResponse(operationSucceeded) r3.Header.Del(http.CanonicalHeaderKey(headerAsyncOperation)) client := mocks.NewSender() client.AppendResponse(r1) client.AppendAndRepeatResponse(r2, 2) client.AppendAndRepeatResponse(r3, 1) r, _ := autorest.SendWithSender(client, mocks.NewRequest(), DoPollForAsynchronous(time.Millisecond)) if client.Attempts() < 4 { t.Fatalf("azure: DoPollForAsynchronous stopped polling before receiving a terminated OperationResource") } autorest.Respond(r, autorest.ByClosing()) } func TestDoPollForAsynchronous_PollsUntilOperationResourceHasTerminated(t *testing.T) { r1 := newAsynchronousResponse() r2 := newOperationResourceResponse("busy") r3 := newOperationResourceResponse(operationCanceled) client := mocks.NewSender() client.AppendResponse(r1) client.AppendAndRepeatResponse(r2, 2) client.AppendAndRepeatResponse(r3, 1) r, _ := autorest.SendWithSender(client, mocks.NewRequest(), DoPollForAsynchronous(time.Millisecond)) if client.Attempts() < 4 { t.Fatalf("azure: DoPollForAsynchronous stopped polling before receiving a terminated OperationResource") } autorest.Respond(r, autorest.ByClosing()) } func TestDoPollForAsynchronous_PollsUntilOperationResourceHasSucceeded(t *testing.T) { r1 := newAsynchronousResponse() r2 := newOperationResourceResponse("busy") r3 := newOperationResourceResponse(operationSucceeded) client := mocks.NewSender() client.AppendResponse(r1) client.AppendAndRepeatResponse(r2, 2) client.AppendAndRepeatResponse(r3, 1) r, _ := autorest.SendWithSender(client, mocks.NewRequest(), DoPollForAsynchronous(time.Millisecond)) if client.Attempts() < 4 { t.Fatalf("azure: DoPollForAsynchronous stopped polling before receiving a terminated OperationResource") } autorest.Respond(r, autorest.ByClosing()) } func TestDoPollForAsynchronous_StopsPollingWhenOperationResourceHasTerminated(t *testing.T) { r1 := newAsynchronousResponse() r2 := newOperationResourceResponse("busy") r3 := newOperationResourceResponse(operationCanceled) client := mocks.NewSender() client.AppendResponse(r1) client.AppendAndRepeatResponse(r2, 2) client.AppendAndRepeatResponse(r3, 2) r, _ := autorest.SendWithSender(client, mocks.NewRequest(), DoPollForAsynchronous(time.Millisecond)) if client.Attempts() > 4 { t.Fatalf("azure: DoPollForAsynchronous failed to stop after receiving a terminated OperationResource") } autorest.Respond(r, autorest.ByClosing()) } func TestDoPollForAsynchronous_ReturnsAnErrorForCanceledOperations(t *testing.T) { r1 := newAsynchronousResponse() r2 := newOperationResourceResponse("busy") r3 := newOperationResourceErrorResponse(operationCanceled) client := mocks.NewSender() client.AppendResponse(r1) client.AppendAndRepeatResponse(r2, 2) client.AppendAndRepeatResponse(r3, 1) r, err := autorest.SendWithSender(client, mocks.NewRequest(), DoPollForAsynchronous(time.Millisecond)) if err == nil || !strings.Contains(fmt.Sprintf("%v", err), "Canceled") { t.Fatalf("azure: DoPollForAsynchronous failed to return an appropriate error for a canceled OperationResource") } autorest.Respond(r, autorest.ByClosing()) } func TestDoPollForAsynchronous_ReturnsAnErrorForFailedOperations(t *testing.T) { r1 := newAsynchronousResponse() r2 := newOperationResourceResponse("busy") r3 := newOperationResourceErrorResponse(operationFailed) client := mocks.NewSender() client.AppendResponse(r1) client.AppendAndRepeatResponse(r2, 2) client.AppendAndRepeatResponse(r3, 1) r, err := autorest.SendWithSender(client, mocks.NewRequest(), DoPollForAsynchronous(time.Millisecond)) if err == nil || !strings.Contains(fmt.Sprintf("%v", err), "Failed") { t.Fatalf("azure: DoPollForAsynchronous failed to return an appropriate error for a canceled OperationResource") } autorest.Respond(r, autorest.ByClosing()) } func TestDoPollForAsynchronous_WithNilURI(t *testing.T) { r1 := newAsynchronousResponse() r1.Header.Del(http.CanonicalHeaderKey(headerAsyncOperation)) r1.Header.Del(http.CanonicalHeaderKey(autorest.HeaderLocation)) r2 := newOperationResourceResponse("busy") r2.Header.Del(http.CanonicalHeaderKey(headerAsyncOperation)) r2.Header.Del(http.CanonicalHeaderKey(autorest.HeaderLocation)) client := mocks.NewSender() client.AppendResponse(r1) client.AppendResponse(r2) req, _ := http.NewRequest("POST", "https://microsoft.com/a/b/c/", mocks.NewBody("")) r, err := autorest.SendWithSender(client, req, DoPollForAsynchronous(time.Millisecond)) if err == nil { t.Fatalf("azure: DoPollForAsynchronous failed to return error for nil URI. got: nil; want: Azure Polling Error - Unable to obtain polling URI for POST") } autorest.Respond(r, autorest.ByClosing()) } func TestDoPollForAsynchronous_ReturnsAnUnknownErrorForFailedOperations(t *testing.T) { // Return unknown error if error not present in last response r1 := newAsynchronousResponse() r1.Header.Del(http.CanonicalHeaderKey(headerAsyncOperation)) r2 := newProvisioningStatusResponse("busy") r2.Header.Del(http.CanonicalHeaderKey(headerAsyncOperation)) r3 := newProvisioningStatusResponse(operationFailed) r3.Header.Del(http.CanonicalHeaderKey(headerAsyncOperation)) client := mocks.NewSender() client.AppendResponse(r1) client.AppendAndRepeatResponse(r2, 2) client.AppendAndRepeatResponse(r3, 1) r, err := autorest.SendWithSender(client, mocks.NewRequest(), DoPollForAsynchronous(time.Millisecond)) expected := makeLongRunningOperationErrorString("Unknown", "None") if err.Error() != expected { t.Fatalf("azure: DoPollForAsynchronous failed to return an appropriate error message for an unknown error. \n expected=%q \n got=%q", expected, err.Error()) } autorest.Respond(r, autorest.ByClosing()) } func TestDoPollForAsynchronous_ReturnsErrorForLastErrorResponse(t *testing.T) { // Return error code and message if error present in last response r1 := newAsynchronousResponse() r1.Header.Del(http.CanonicalHeaderKey(headerAsyncOperation)) r2 := newProvisioningStatusResponse("busy") r2.Header.Del(http.CanonicalHeaderKey(headerAsyncOperation)) r3 := newAsynchronousResponseWithError() r3.Header.Del(http.CanonicalHeaderKey(headerAsyncOperation)) client := mocks.NewSender() client.AppendResponse(r1) client.AppendAndRepeatResponse(r2, 2) client.AppendAndRepeatResponse(r3, 1) r, err := autorest.SendWithSender(client, mocks.NewRequest(), DoPollForAsynchronous(time.Millisecond)) expected := makeLongRunningOperationErrorString("InvalidParameter", "tom-service-DISCOVERY-server-base-v1.core.local' is not a valid captured VHD blob name prefix.") if err.Error() != expected { t.Fatalf("azure: DoPollForAsynchronous failed to return an appropriate error message for an unknown error. \n expected=%q \n got=%q", expected, err.Error()) } autorest.Respond(r, autorest.ByClosing()) } func TestDoPollForAsynchronous_ReturnsOperationResourceErrorForFailedOperations(t *testing.T) { // Return Operation resource response with error code and message in last operation resource response r1 := newAsynchronousResponse() r2 := newOperationResourceResponse("busy") r3 := newOperationResourceErrorResponse(operationFailed) client := mocks.NewSender() client.AppendResponse(r1) client.AppendAndRepeatResponse(r2, 2) client.AppendAndRepeatResponse(r3, 1) r, err := autorest.SendWithSender(client, mocks.NewRequest(), DoPollForAsynchronous(time.Millisecond)) expected := makeLongRunningOperationErrorString("BadArgument", "The provided database 'foo' has an invalid username.") if err.Error() != expected { t.Fatalf("azure: DoPollForAsynchronous failed to return an appropriate error message for a failed Operations. \n expected=%q \n got=%q", expected, err.Error()) } autorest.Respond(r, autorest.ByClosing()) } func TestDoPollForAsynchronous_ReturnsErrorForFirstPutRequest(t *testing.T) { // Return 400 bad response with error code and message in first put r1 := newAsynchronousResponseWithError() client := mocks.NewSender() client.AppendResponse(r1) res, err := autorest.SendWithSender(client, mocks.NewRequest(), DoPollForAsynchronous(time.Millisecond)) if err != nil { t.Fatalf("azure: DoPollForAsynchronous failed to return an appropriate error message for a failed Operations. \n expected=%q \n got=%q", errorResponse, err.Error()) } err = autorest.Respond(res, WithErrorUnlessStatusCode(http.StatusAccepted, http.StatusCreated, http.StatusOK), autorest.ByClosing()) reqError, ok := err.(*RequestError) if !ok { t.Fatalf("azure: returned error is not azure.RequestError: %T", err) } expected := &RequestError{ ServiceError: &ServiceError{ Code: "InvalidParameter", Message: "tom-service-DISCOVERY-server-base-v1.core.local' is not a valid captured VHD blob name prefix.", }, DetailedError: autorest.DetailedError{ StatusCode: 400, }, } if !reflect.DeepEqual(reqError, expected) { t.Fatalf("azure: wrong error. expected=%q\ngot=%q", expected, reqError) } defer res.Body.Close() b, err := ioutil.ReadAll(res.Body) if err != nil { t.Fatal(err) } if string(b) != errorResponse { t.Fatalf("azure: Response body is wrong. got=%q expected=%q", string(b), errorResponse) } } func TestDoPollForAsynchronous_ReturnsNoErrorForSuccessfulOperations(t *testing.T) { r1 := newAsynchronousResponse() r2 := newOperationResourceResponse("busy") r3 := newOperationResourceErrorResponse(operationSucceeded) client := mocks.NewSender() client.AppendResponse(r1) client.AppendAndRepeatResponse(r2, 2) client.AppendAndRepeatResponse(r3, 1) r, err := autorest.SendWithSender(client, mocks.NewRequest(), DoPollForAsynchronous(time.Millisecond)) if err != nil { t.Fatalf("azure: DoPollForAsynchronous returned an error for a successful OperationResource") } autorest.Respond(r, autorest.ByClosing()) } func TestDoPollForAsynchronous_StopsPollingIfItReceivesAnInvalidOperationResource(t *testing.T) { r1 := newAsynchronousResponse() r2 := newOperationResourceResponse("busy") r3 := newOperationResourceResponse("busy") r3.Body = mocks.NewBody(operationResourceIllegal) r4 := newOperationResourceResponse(operationSucceeded) client := mocks.NewSender() client.AppendResponse(r1) client.AppendAndRepeatResponse(r2, 2) client.AppendAndRepeatResponse(r3, 1) client.AppendAndRepeatResponse(r4, 1) r, err := autorest.SendWithSender(client, mocks.NewRequest(), DoPollForAsynchronous(time.Millisecond)) if client.Attempts() > 4 { t.Fatalf("azure: DoPollForAsynchronous failed to stop polling after receiving an invalid OperationResource") } if err == nil { t.Fatalf("azure: DoPollForAsynchronous failed to return an error after receving an invalid OperationResource") } autorest.Respond(r, autorest.ByClosing()) } const ( operationResourceIllegal = ` This is not JSON and should fail...badly. ` pollingStateFormat = ` { "unused" : { "somefield" : 42 }, "properties" : { "provisioningState": "%s" } } ` errorResponse = ` { "error" : { "code" : "InvalidParameter", "message" : "tom-service-DISCOVERY-server-base-v1.core.local' is not a valid captured VHD blob name prefix." } } ` pollingStateEmpty = ` { "unused" : { "somefield" : 42 }, "properties" : { } } ` operationResourceFormat = ` { "id": "/subscriptions/id/locations/westus/operationsStatus/sameguid", "name": "sameguid", "status" : "%s", "startTime" : "2006-01-02T15:04:05Z", "endTime" : "2006-01-02T16:04:05Z", "percentComplete" : 50.00, "properties" : {} } ` operationResourceErrorFormat = ` { "id": "/subscriptions/id/locations/westus/operationsStatus/sameguid", "name": "sameguid", "status" : "%s", "startTime" : "2006-01-02T15:04:05Z", "endTime" : "2006-01-02T16:04:05Z", "percentComplete" : 50.00, "properties" : {}, "error" : { "code" : "BadArgument", "message" : "The provided database 'foo' has an invalid username." } } ` ) func newAsynchronousResponse() *http.Response { r := mocks.NewResponseWithStatus("201 Created", http.StatusCreated) r.Body = mocks.NewBody(fmt.Sprintf(pollingStateFormat, operationInProgress)) mocks.SetResponseHeader(r, http.CanonicalHeaderKey(headerAsyncOperation), mocks.TestAzureAsyncURL) mocks.SetResponseHeader(r, http.CanonicalHeaderKey(autorest.HeaderLocation), mocks.TestLocationURL) mocks.SetRetryHeader(r, retryDelay) r.Request = mocks.NewRequestForURL(mocks.TestURL) return r } func newAsynchronousResponseWithError() *http.Response { r := mocks.NewResponseWithStatus("400 Bad Request", http.StatusBadRequest) mocks.SetRetryHeader(r, retryDelay) r.Request = mocks.NewRequestForURL(mocks.TestURL) r.Body = mocks.NewBody(errorResponse) return r } func newOperationResourceResponse(status string) *http.Response { r := newAsynchronousResponse() r.Body = mocks.NewBody(fmt.Sprintf(operationResourceFormat, status)) return r } func newOperationResourceErrorResponse(status string) *http.Response { r := newAsynchronousResponse() r.Body = mocks.NewBody(fmt.Sprintf(operationResourceErrorFormat, status)) return r } func newProvisioningStatusResponse(status string) *http.Response { r := newAsynchronousResponse() r.Body = mocks.NewBody(fmt.Sprintf(pollingStateFormat, status)) return r } func makeLongRunningOperationErrorString(code string, message string) string { return fmt.Sprintf("Long running operation terminated with status 'Failed': Code=%q Message=%q", code, message) } go-autorest-8.3.1/autorest/azure/azure.go000066400000000000000000000143611315133516500204550ustar00rootroot00000000000000/* Package azure provides Azure-specific implementations used with AutoRest. See the included examples for more detail. */ package azure import ( "encoding/json" "fmt" "io/ioutil" "net/http" "strconv" "github.com/Azure/go-autorest/autorest" ) const ( // HeaderClientID is the Azure extension header to set a user-specified request ID. HeaderClientID = "x-ms-client-request-id" // HeaderReturnClientID is the Azure extension header to set if the user-specified request ID // should be included in the response. HeaderReturnClientID = "x-ms-return-client-request-id" // HeaderRequestID is the Azure extension header of the service generated request ID returned // in the response. HeaderRequestID = "x-ms-request-id" ) // ServiceError encapsulates the error response from an Azure service. type ServiceError struct { Code string `json:"code"` Message string `json:"message"` Details *[]interface{} `json:"details"` } func (se ServiceError) Error() string { if se.Details != nil { d, err := json.Marshal(*(se.Details)) if err != nil { return fmt.Sprintf("Code=%q Message=%q Details=%v", se.Code, se.Message, *se.Details) } return fmt.Sprintf("Code=%q Message=%q Details=%v", se.Code, se.Message, string(d)) } return fmt.Sprintf("Code=%q Message=%q", se.Code, se.Message) } // RequestError describes an error response returned by Azure service. type RequestError struct { autorest.DetailedError // The error returned by the Azure service. ServiceError *ServiceError `json:"error"` // The request id (from the x-ms-request-id-header) of the request. RequestID string } // Error returns a human-friendly error message from service error. func (e RequestError) Error() string { return fmt.Sprintf("autorest/azure: Service returned an error. Status=%v %v", e.StatusCode, e.ServiceError) } // IsAzureError returns true if the passed error is an Azure Service error; false otherwise. func IsAzureError(e error) bool { _, ok := e.(*RequestError) return ok } // NewErrorWithError creates a new Error conforming object from the // passed packageType, method, statusCode of the given resp (UndefinedStatusCode // if resp is nil), message, and original error. message is treated as a format // string to which the optional args apply. func NewErrorWithError(original error, packageType string, method string, resp *http.Response, message string, args ...interface{}) RequestError { if v, ok := original.(*RequestError); ok { return *v } statusCode := autorest.UndefinedStatusCode if resp != nil { statusCode = resp.StatusCode } return RequestError{ DetailedError: autorest.DetailedError{ Original: original, PackageType: packageType, Method: method, StatusCode: statusCode, Message: fmt.Sprintf(message, args...), }, } } // WithReturningClientID returns a PrepareDecorator that adds an HTTP extension header of // x-ms-client-request-id whose value is the passed, undecorated UUID (e.g., // "0F39878C-5F76-4DB8-A25D-61D2C193C3CA"). It also sets the x-ms-return-client-request-id // header to true such that UUID accompanies the http.Response. func WithReturningClientID(uuid string) autorest.PrepareDecorator { preparer := autorest.CreatePreparer( WithClientID(uuid), WithReturnClientID(true)) return func(p autorest.Preparer) autorest.Preparer { return autorest.PreparerFunc(func(r *http.Request) (*http.Request, error) { r, err := p.Prepare(r) if err != nil { return r, err } return preparer.Prepare(r) }) } } // WithClientID returns a PrepareDecorator that adds an HTTP extension header of // x-ms-client-request-id whose value is passed, undecorated UUID (e.g., // "0F39878C-5F76-4DB8-A25D-61D2C193C3CA"). func WithClientID(uuid string) autorest.PrepareDecorator { return autorest.WithHeader(HeaderClientID, uuid) } // WithReturnClientID returns a PrepareDecorator that adds an HTTP extension header of // x-ms-return-client-request-id whose boolean value indicates if the value of the // x-ms-client-request-id header should be included in the http.Response. func WithReturnClientID(b bool) autorest.PrepareDecorator { return autorest.WithHeader(HeaderReturnClientID, strconv.FormatBool(b)) } // ExtractClientID extracts the client identifier from the x-ms-client-request-id header set on the // http.Request sent to the service (and returned in the http.Response) func ExtractClientID(resp *http.Response) string { return autorest.ExtractHeaderValue(HeaderClientID, resp) } // ExtractRequestID extracts the Azure server generated request identifier from the // x-ms-request-id header. func ExtractRequestID(resp *http.Response) string { return autorest.ExtractHeaderValue(HeaderRequestID, resp) } // WithErrorUnlessStatusCode returns a RespondDecorator that emits an // azure.RequestError by reading the response body unless the response HTTP status code // is among the set passed. // // If there is a chance service may return responses other than the Azure error // format and the response cannot be parsed into an error, a decoding error will // be returned containing the response body. In any case, the Responder will // return an error if the status code is not satisfied. // // If this Responder returns an error, the response body will be replaced with // an in-memory reader, which needs no further closing. func WithErrorUnlessStatusCode(codes ...int) autorest.RespondDecorator { return func(r autorest.Responder) autorest.Responder { return autorest.ResponderFunc(func(resp *http.Response) error { err := r.Respond(resp) if err == nil && !autorest.ResponseHasStatusCode(resp, codes...) { var e RequestError defer resp.Body.Close() // Copy and replace the Body in case it does not contain an error object. // This will leave the Body available to the caller. b, decodeErr := autorest.CopyAndDecode(autorest.EncodedAsJSON, resp.Body, &e) resp.Body = ioutil.NopCloser(&b) if decodeErr != nil { return fmt.Errorf("autorest/azure: error response cannot be parsed: %q error: %v", b.String(), decodeErr) } else if e.ServiceError == nil { e.ServiceError = &ServiceError{Code: "Unknown", Message: "Unknown service error"} } e.RequestID = ExtractRequestID(resp) if e.StatusCode == nil { e.StatusCode = resp.StatusCode } err = &e } return err }) } } go-autorest-8.3.1/autorest/azure/azure_test.go000066400000000000000000000320661315133516500215160ustar00rootroot00000000000000package azure import ( "encoding/json" "fmt" "io/ioutil" "net/http" "reflect" "strconv" "testing" "time" "github.com/Azure/go-autorest/autorest" "github.com/Azure/go-autorest/autorest/mocks" ) const ( headerAuthorization = "Authorization" longDelay = 5 * time.Second retryDelay = 10 * time.Millisecond testLogPrefix = "azure:" ) // Use a Client Inspector to set the request identifier. func ExampleWithClientID() { uuid := "71FDB9F4-5E49-4C12-B266-DE7B4FD999A6" req, _ := autorest.Prepare(&http.Request{}, autorest.AsGet(), autorest.WithBaseURL("https://microsoft.com/a/b/c/")) c := autorest.Client{Sender: mocks.NewSender()} c.RequestInspector = WithReturningClientID(uuid) autorest.SendWithSender(c, req) fmt.Printf("Inspector added the %s header with the value %s\n", HeaderClientID, req.Header.Get(HeaderClientID)) fmt.Printf("Inspector added the %s header with the value %s\n", HeaderReturnClientID, req.Header.Get(HeaderReturnClientID)) // Output: // Inspector added the x-ms-client-request-id header with the value 71FDB9F4-5E49-4C12-B266-DE7B4FD999A6 // Inspector added the x-ms-return-client-request-id header with the value true } func TestWithReturningClientIDReturnsError(t *testing.T) { var errIn error uuid := "71FDB9F4-5E49-4C12-B266-DE7B4FD999A6" _, errOut := autorest.Prepare(&http.Request{}, withErrorPrepareDecorator(&errIn), WithReturningClientID(uuid)) if errOut == nil || errIn != errOut { t.Fatalf("azure: WithReturningClientID failed to exit early when receiving an error -- expected (%v), received (%v)", errIn, errOut) } } func TestWithClientID(t *testing.T) { uuid := "71FDB9F4-5E49-4C12-B266-DE7B4FD999A6" req, _ := autorest.Prepare(&http.Request{}, WithClientID(uuid)) if req.Header.Get(HeaderClientID) != uuid { t.Fatalf("azure: WithClientID failed to set %s -- expected %s, received %s", HeaderClientID, uuid, req.Header.Get(HeaderClientID)) } } func TestWithReturnClientID(t *testing.T) { b := false req, _ := autorest.Prepare(&http.Request{}, WithReturnClientID(b)) if req.Header.Get(HeaderReturnClientID) != strconv.FormatBool(b) { t.Fatalf("azure: WithReturnClientID failed to set %s -- expected %s, received %s", HeaderClientID, strconv.FormatBool(b), req.Header.Get(HeaderClientID)) } } func TestExtractClientID(t *testing.T) { uuid := "71FDB9F4-5E49-4C12-B266-DE7B4FD999A6" resp := mocks.NewResponse() mocks.SetResponseHeader(resp, HeaderClientID, uuid) if ExtractClientID(resp) != uuid { t.Fatalf("azure: ExtractClientID failed to extract the %s -- expected %s, received %s", HeaderClientID, uuid, ExtractClientID(resp)) } } func TestExtractRequestID(t *testing.T) { uuid := "71FDB9F4-5E49-4C12-B266-DE7B4FD999A6" resp := mocks.NewResponse() mocks.SetResponseHeader(resp, HeaderRequestID, uuid) if ExtractRequestID(resp) != uuid { t.Fatalf("azure: ExtractRequestID failed to extract the %s -- expected %s, received %s", HeaderRequestID, uuid, ExtractRequestID(resp)) } } func TestIsAzureError_ReturnsTrueForAzureError(t *testing.T) { if !IsAzureError(&RequestError{}) { t.Fatalf("azure: IsAzureError failed to return true for an Azure Service error") } } func TestIsAzureError_ReturnsFalseForNonAzureError(t *testing.T) { if IsAzureError(fmt.Errorf("An Error")) { t.Fatalf("azure: IsAzureError return true for an non-Azure Service error") } } func TestNewErrorWithError_UsesReponseStatusCode(t *testing.T) { e := NewErrorWithError(fmt.Errorf("Error"), "packageType", "method", mocks.NewResponseWithStatus("Forbidden", http.StatusForbidden), "message") if e.StatusCode != http.StatusForbidden { t.Fatalf("azure: NewErrorWithError failed to use the Status Code of the passed Response -- expected %v, received %v", http.StatusForbidden, e.StatusCode) } } func TestNewErrorWithError_ReturnsUnwrappedError(t *testing.T) { e1 := RequestError{} e1.ServiceError = &ServiceError{Code: "42", Message: "A Message"} e1.StatusCode = 200 e1.RequestID = "A RequestID" e2 := NewErrorWithError(&e1, "packageType", "method", nil, "message") if !reflect.DeepEqual(e1, e2) { t.Fatalf("azure: NewErrorWithError wrapped an RequestError -- expected %T, received %T", e1, e2) } } func TestNewErrorWithError_WrapsAnError(t *testing.T) { e1 := fmt.Errorf("Inner Error") var e2 interface{} = NewErrorWithError(e1, "packageType", "method", nil, "message") if _, ok := e2.(RequestError); !ok { t.Fatalf("azure: NewErrorWithError failed to wrap a standard error -- received %T", e2) } } func TestWithErrorUnlessStatusCode_NotAnAzureError(t *testing.T) { body := ` IIS Error page Some non-JSON error page ` r := mocks.NewResponseWithContent(body) r.Request = mocks.NewRequest() r.StatusCode = http.StatusBadRequest r.Status = http.StatusText(r.StatusCode) err := autorest.Respond(r, WithErrorUnlessStatusCode(http.StatusOK), autorest.ByClosing()) ok, _ := err.(*RequestError) if ok != nil { t.Fatalf("azure: azure.RequestError returned from malformed response: %v", err) } // the error body should still be there defer r.Body.Close() b, err := ioutil.ReadAll(r.Body) if err != nil { t.Fatal(err) } if string(b) != body { t.Fatalf("response body is wrong. got=%q exptected=%q", string(b), body) } } func TestWithErrorUnlessStatusCode_FoundAzureErrorWithoutDetails(t *testing.T) { j := `{ "error": { "code": "InternalError", "message": "Azure is having trouble right now." } }` uuid := "71FDB9F4-5E49-4C12-B266-DE7B4FD999A6" r := mocks.NewResponseWithContent(j) mocks.SetResponseHeader(r, HeaderRequestID, uuid) r.Request = mocks.NewRequest() r.StatusCode = http.StatusInternalServerError r.Status = http.StatusText(r.StatusCode) err := autorest.Respond(r, WithErrorUnlessStatusCode(http.StatusOK), autorest.ByClosing()) if err == nil { t.Fatalf("azure: returned nil error for proper error response") } azErr, ok := err.(*RequestError) if !ok { t.Fatalf("azure: returned error is not azure.RequestError: %T", err) } expected := "autorest/azure: Service returned an error. Status=500 Code=\"InternalError\" Message=\"Azure is having trouble right now.\"" if !reflect.DeepEqual(expected, azErr.Error()) { t.Fatalf("azure: service error is not unmarshaled properly.\nexpected=%v\ngot=%v", expected, azErr.Error()) } if expected := http.StatusInternalServerError; azErr.StatusCode != expected { t.Fatalf("azure: got wrong StatusCode=%d Expected=%d", azErr.StatusCode, expected) } if expected := uuid; azErr.RequestID != expected { t.Fatalf("azure: wrong request ID in error. expected=%q; got=%q", expected, azErr.RequestID) } _ = azErr.Error() // the error body should still be there defer r.Body.Close() b, err := ioutil.ReadAll(r.Body) if err != nil { t.Fatal(err) } if string(b) != j { t.Fatalf("response body is wrong. got=%q expected=%q", string(b), j) } } func TestWithErrorUnlessStatusCode_FoundAzureErrorWithDetails(t *testing.T) { j := `{ "error": { "code": "InternalError", "message": "Azure is having trouble right now.", "details": [{"code": "conflict1", "message":"error message1"}, {"code": "conflict2", "message":"error message2"}] } }` uuid := "71FDB9F4-5E49-4C12-B266-DE7B4FD999A6" r := mocks.NewResponseWithContent(j) mocks.SetResponseHeader(r, HeaderRequestID, uuid) r.Request = mocks.NewRequest() r.StatusCode = http.StatusInternalServerError r.Status = http.StatusText(r.StatusCode) err := autorest.Respond(r, WithErrorUnlessStatusCode(http.StatusOK), autorest.ByClosing()) if err == nil { t.Fatalf("azure: returned nil error for proper error response") } azErr, ok := err.(*RequestError) if !ok { t.Fatalf("azure: returned error is not azure.RequestError: %T", err) } if expected := "InternalError"; azErr.ServiceError.Code != expected { t.Fatalf("azure: wrong error code. expected=%q; got=%q", expected, azErr.ServiceError.Code) } if azErr.ServiceError.Message == "" { t.Fatalf("azure: error message is not unmarshaled properly") } b, _ := json.Marshal(*azErr.ServiceError.Details) if string(b) != `[{"code":"conflict1","message":"error message1"},{"code":"conflict2","message":"error message2"}]` { t.Fatalf("azure: error details is not unmarshaled properly") } if expected := http.StatusInternalServerError; azErr.StatusCode != expected { t.Fatalf("azure: got wrong StatusCode=%v Expected=%d", azErr.StatusCode, expected) } if expected := uuid; azErr.RequestID != expected { t.Fatalf("azure: wrong request ID in error. expected=%q; got=%q", expected, azErr.RequestID) } _ = azErr.Error() // the error body should still be there defer r.Body.Close() b, err = ioutil.ReadAll(r.Body) if err != nil { t.Fatal(err) } if string(b) != j { t.Fatalf("response body is wrong. got=%q expected=%q", string(b), j) } } func TestWithErrorUnlessStatusCode_NoAzureError(t *testing.T) { j := `{ "Status":"NotFound" }` uuid := "71FDB9F4-5E49-4C12-B266-DE7B4FD999A6" r := mocks.NewResponseWithContent(j) mocks.SetResponseHeader(r, HeaderRequestID, uuid) r.Request = mocks.NewRequest() r.StatusCode = http.StatusInternalServerError r.Status = http.StatusText(r.StatusCode) err := autorest.Respond(r, WithErrorUnlessStatusCode(http.StatusOK), autorest.ByClosing()) if err == nil { t.Fatalf("azure: returned nil error for proper error response") } azErr, ok := err.(*RequestError) if !ok { t.Fatalf("azure: returned error is not azure.RequestError: %T", err) } expected := &ServiceError{ Code: "Unknown", Message: "Unknown service error", } if !reflect.DeepEqual(expected, azErr.ServiceError) { t.Fatalf("azure: service error is not unmarshaled properly. expected=%q\ngot=%q", expected, azErr.ServiceError) } if expected := http.StatusInternalServerError; azErr.StatusCode != expected { t.Fatalf("azure: got wrong StatusCode=%v Expected=%d", azErr.StatusCode, expected) } if expected := uuid; azErr.RequestID != expected { t.Fatalf("azure: wrong request ID in error. expected=%q; got=%q", expected, azErr.RequestID) } _ = azErr.Error() // the error body should still be there defer r.Body.Close() b, err := ioutil.ReadAll(r.Body) if err != nil { t.Fatal(err) } if string(b) != j { t.Fatalf("response body is wrong. got=%q expected=%q", string(b), j) } } func TestRequestErrorString_WithError(t *testing.T) { j := `{ "error": { "code": "InternalError", "message": "Conflict", "details": [{"code": "conflict1", "message":"error message1"}] } }` uuid := "71FDB9F4-5E49-4C12-B266-DE7B4FD999A6" r := mocks.NewResponseWithContent(j) mocks.SetResponseHeader(r, HeaderRequestID, uuid) r.Request = mocks.NewRequest() r.StatusCode = http.StatusInternalServerError r.Status = http.StatusText(r.StatusCode) err := autorest.Respond(r, WithErrorUnlessStatusCode(http.StatusOK), autorest.ByClosing()) if err == nil { t.Fatalf("azure: returned nil error for proper error response") } azErr, _ := err.(*RequestError) expected := "autorest/azure: Service returned an error. Status=500 Code=\"InternalError\" Message=\"Conflict\" Details=[{\"code\":\"conflict1\",\"message\":\"error message1\"}]" if expected != azErr.Error() { t.Fatalf("azure: send wrong RequestError.\nexpected=%v\ngot=%v", expected, azErr.Error()) } } func withErrorPrepareDecorator(e *error) autorest.PrepareDecorator { return func(p autorest.Preparer) autorest.Preparer { return autorest.PreparerFunc(func(r *http.Request) (*http.Request, error) { *e = fmt.Errorf("azure: Faux Prepare Error") return r, *e }) } } func withAsyncResponseDecorator(n int) autorest.SendDecorator { i := 0 return func(s autorest.Sender) autorest.Sender { return autorest.SenderFunc(func(r *http.Request) (*http.Response, error) { resp, err := s.Do(r) if err == nil { if i < n { resp.StatusCode = http.StatusCreated resp.Header = http.Header{} resp.Header.Add(http.CanonicalHeaderKey(headerAsyncOperation), mocks.TestURL) i++ } else { resp.StatusCode = http.StatusOK resp.Header.Del(http.CanonicalHeaderKey(headerAsyncOperation)) } } return resp, err }) } } type mockAuthorizer struct{} func (ma mockAuthorizer) WithAuthorization() autorest.PrepareDecorator { return autorest.WithHeader(headerAuthorization, mocks.TestAuthorizationHeader) } type mockFailingAuthorizer struct{} func (mfa mockFailingAuthorizer) WithAuthorization() autorest.PrepareDecorator { return func(p autorest.Preparer) autorest.Preparer { return autorest.PreparerFunc(func(r *http.Request) (*http.Request, error) { return r, fmt.Errorf("ERROR: mockFailingAuthorizer returned expected error") }) } } type mockInspector struct { wasInvoked bool } func (mi *mockInspector) WithInspection() autorest.PrepareDecorator { return func(p autorest.Preparer) autorest.Preparer { return autorest.PreparerFunc(func(r *http.Request) (*http.Request, error) { mi.wasInvoked = true return p.Prepare(r) }) } } func (mi *mockInspector) ByInspecting() autorest.RespondDecorator { return func(r autorest.Responder) autorest.Responder { return autorest.ResponderFunc(func(resp *http.Response) error { mi.wasInvoked = true return r.Respond(resp) }) } } go-autorest-8.3.1/autorest/azure/environments.go000066400000000000000000000144751315133516500220640ustar00rootroot00000000000000package azure import ( "fmt" "strings" ) var environments = map[string]Environment{ "AZURECHINACLOUD": ChinaCloud, "AZUREGERMANCLOUD": GermanCloud, "AZUREPUBLICCLOUD": PublicCloud, "AZUREUSGOVERNMENTCLOUD": USGovernmentCloud, } // Environment represents a set of endpoints for each of Azure's Clouds. type Environment struct { Name string `json:"name"` ManagementPortalURL string `json:"managementPortalURL"` PublishSettingsURL string `json:"publishSettingsURL"` ServiceManagementEndpoint string `json:"serviceManagementEndpoint"` ResourceManagerEndpoint string `json:"resourceManagerEndpoint"` ActiveDirectoryEndpoint string `json:"activeDirectoryEndpoint"` GalleryEndpoint string `json:"galleryEndpoint"` KeyVaultEndpoint string `json:"keyVaultEndpoint"` GraphEndpoint string `json:"graphEndpoint"` StorageEndpointSuffix string `json:"storageEndpointSuffix"` SQLDatabaseDNSSuffix string `json:"sqlDatabaseDNSSuffix"` TrafficManagerDNSSuffix string `json:"trafficManagerDNSSuffix"` KeyVaultDNSSuffix string `json:"keyVaultDNSSuffix"` ServiceBusEndpointSuffix string `json:"serviceBusEndpointSuffix"` ServiceManagementVMDNSSuffix string `json:"serviceManagementVMDNSSuffix"` ResourceManagerVMDNSSuffix string `json:"resourceManagerVMDNSSuffix"` ContainerRegistryDNSSuffix string `json:"containerRegistryDNSSuffix"` } var ( // PublicCloud is the default public Azure cloud environment PublicCloud = Environment{ Name: "AzurePublicCloud", ManagementPortalURL: "https://manage.windowsazure.com/", PublishSettingsURL: "https://manage.windowsazure.com/publishsettings/index", ServiceManagementEndpoint: "https://management.core.windows.net/", ResourceManagerEndpoint: "https://management.azure.com/", ActiveDirectoryEndpoint: "https://login.microsoftonline.com/", GalleryEndpoint: "https://gallery.azure.com/", KeyVaultEndpoint: "https://vault.azure.net/", GraphEndpoint: "https://graph.windows.net/", StorageEndpointSuffix: "core.windows.net", SQLDatabaseDNSSuffix: "database.windows.net", TrafficManagerDNSSuffix: "trafficmanager.net", KeyVaultDNSSuffix: "vault.azure.net", ServiceBusEndpointSuffix: "servicebus.azure.com", ServiceManagementVMDNSSuffix: "cloudapp.net", ResourceManagerVMDNSSuffix: "cloudapp.azure.com", ContainerRegistryDNSSuffix: "azurecr.io", } // USGovernmentCloud is the cloud environment for the US Government USGovernmentCloud = Environment{ Name: "AzureUSGovernmentCloud", ManagementPortalURL: "https://manage.windowsazure.us/", PublishSettingsURL: "https://manage.windowsazure.us/publishsettings/index", ServiceManagementEndpoint: "https://management.core.usgovcloudapi.net/", ResourceManagerEndpoint: "https://management.usgovcloudapi.net/", ActiveDirectoryEndpoint: "https://login.microsoftonline.com/", GalleryEndpoint: "https://gallery.usgovcloudapi.net/", KeyVaultEndpoint: "https://vault.usgovcloudapi.net/", GraphEndpoint: "https://graph.usgovcloudapi.net/", StorageEndpointSuffix: "core.usgovcloudapi.net", SQLDatabaseDNSSuffix: "database.usgovcloudapi.net", TrafficManagerDNSSuffix: "usgovtrafficmanager.net", KeyVaultDNSSuffix: "vault.usgovcloudapi.net", ServiceBusEndpointSuffix: "servicebus.usgovcloudapi.net", ServiceManagementVMDNSSuffix: "usgovcloudapp.net", ResourceManagerVMDNSSuffix: "cloudapp.windowsazure.us", ContainerRegistryDNSSuffix: "azurecr.io", } // ChinaCloud is the cloud environment operated in China ChinaCloud = Environment{ Name: "AzureChinaCloud", ManagementPortalURL: "https://manage.chinacloudapi.com/", PublishSettingsURL: "https://manage.chinacloudapi.com/publishsettings/index", ServiceManagementEndpoint: "https://management.core.chinacloudapi.cn/", ResourceManagerEndpoint: "https://management.chinacloudapi.cn/", ActiveDirectoryEndpoint: "https://login.chinacloudapi.cn/", GalleryEndpoint: "https://gallery.chinacloudapi.cn/", KeyVaultEndpoint: "https://vault.azure.cn/", GraphEndpoint: "https://graph.chinacloudapi.cn/", StorageEndpointSuffix: "core.chinacloudapi.cn", SQLDatabaseDNSSuffix: "database.chinacloudapi.cn", TrafficManagerDNSSuffix: "trafficmanager.cn", KeyVaultDNSSuffix: "vault.azure.cn", ServiceBusEndpointSuffix: "servicebus.chinacloudapi.net", ServiceManagementVMDNSSuffix: "chinacloudapp.cn", ResourceManagerVMDNSSuffix: "cloudapp.azure.cn", ContainerRegistryDNSSuffix: "azurecr.io", } // GermanCloud is the cloud environment operated in Germany GermanCloud = Environment{ Name: "AzureGermanCloud", ManagementPortalURL: "http://portal.microsoftazure.de/", PublishSettingsURL: "https://manage.microsoftazure.de/publishsettings/index", ServiceManagementEndpoint: "https://management.core.cloudapi.de/", ResourceManagerEndpoint: "https://management.microsoftazure.de/", ActiveDirectoryEndpoint: "https://login.microsoftonline.de/", GalleryEndpoint: "https://gallery.cloudapi.de/", KeyVaultEndpoint: "https://vault.microsoftazure.de/", GraphEndpoint: "https://graph.cloudapi.de/", StorageEndpointSuffix: "core.cloudapi.de", SQLDatabaseDNSSuffix: "database.cloudapi.de", TrafficManagerDNSSuffix: "azuretrafficmanager.de", KeyVaultDNSSuffix: "vault.microsoftazure.de", ServiceBusEndpointSuffix: "servicebus.cloudapi.de", ServiceManagementVMDNSSuffix: "azurecloudapp.de", ResourceManagerVMDNSSuffix: "cloudapp.microsoftazure.de", ContainerRegistryDNSSuffix: "azurecr.io", } ) // EnvironmentFromName returns an Environment based on the common name specified func EnvironmentFromName(name string) (Environment, error) { name = strings.ToUpper(name) env, ok := environments[name] if !ok { return env, fmt.Errorf("autorest/azure: There is no cloud environment matching the name %q", name) } return env, nil } go-autorest-8.3.1/autorest/azure/environments_test.go000066400000000000000000000243051315133516500231140ustar00rootroot00000000000000// test package azure import ( "encoding/json" "testing" ) func TestEnvironmentFromName(t *testing.T) { name := "azurechinacloud" if env, _ := EnvironmentFromName(name); env != ChinaCloud { t.Errorf("Expected to get ChinaCloud for %q", name) } name = "AzureChinaCloud" if env, _ := EnvironmentFromName(name); env != ChinaCloud { t.Errorf("Expected to get ChinaCloud for %q", name) } name = "azuregermancloud" if env, _ := EnvironmentFromName(name); env != GermanCloud { t.Errorf("Expected to get GermanCloud for %q", name) } name = "AzureGermanCloud" if env, _ := EnvironmentFromName(name); env != GermanCloud { t.Errorf("Expected to get GermanCloud for %q", name) } name = "azurepubliccloud" if env, _ := EnvironmentFromName(name); env != PublicCloud { t.Errorf("Expected to get PublicCloud for %q", name) } name = "AzurePublicCloud" if env, _ := EnvironmentFromName(name); env != PublicCloud { t.Errorf("Expected to get PublicCloud for %q", name) } name = "azureusgovernmentcloud" if env, _ := EnvironmentFromName(name); env != USGovernmentCloud { t.Errorf("Expected to get USGovernmentCloud for %q", name) } name = "AzureUSGovernmentCloud" if env, _ := EnvironmentFromName(name); env != USGovernmentCloud { t.Errorf("Expected to get USGovernmentCloud for %q", name) } name = "thisisnotarealcloudenv" if _, err := EnvironmentFromName(name); err == nil { t.Errorf("Expected to get an error for %q", name) } } func TestDeserializeEnvironment(t *testing.T) { env := `{ "name": "--name--", "ActiveDirectoryEndpoint": "--active-directory-endpoint--", "galleryEndpoint": "--gallery-endpoint--", "graphEndpoint": "--graph-endpoint--", "keyVaultDNSSuffix": "--key-vault-dns-suffix--", "keyVaultEndpoint": "--key-vault-endpoint--", "managementPortalURL": "--management-portal-url--", "publishSettingsURL": "--publish-settings-url--", "resourceManagerEndpoint": "--resource-manager-endpoint--", "serviceBusEndpointSuffix": "--service-bus-endpoint-suffix--", "serviceManagementEndpoint": "--service-management-endpoint--", "sqlDatabaseDNSSuffix": "--sql-database-dns-suffix--", "storageEndpointSuffix": "--storage-endpoint-suffix--", "trafficManagerDNSSuffix": "--traffic-manager-dns-suffix--", "serviceManagementVMDNSSuffix": "--asm-vm-dns-suffix--", "resourceManagerVMDNSSuffix": "--arm-vm-dns-suffix--", "containerRegistryDNSSuffix": "--container-registry-dns-suffix--" }` testSubject := Environment{} err := json.Unmarshal([]byte(env), &testSubject) if err != nil { t.Fatalf("failed to unmarshal: %s", err) } if "--name--" != testSubject.Name { t.Errorf("Expected Name to be \"--name--\", but got %q", testSubject.Name) } if "--management-portal-url--" != testSubject.ManagementPortalURL { t.Errorf("Expected ManagementPortalURL to be \"--management-portal-url--\", but got %q", testSubject.ManagementPortalURL) } if "--publish-settings-url--" != testSubject.PublishSettingsURL { t.Errorf("Expected PublishSettingsURL to be \"--publish-settings-url--\", but got %q", testSubject.PublishSettingsURL) } if "--service-management-endpoint--" != testSubject.ServiceManagementEndpoint { t.Errorf("Expected ServiceManagementEndpoint to be \"--service-management-endpoint--\", but got %q", testSubject.ServiceManagementEndpoint) } if "--resource-manager-endpoint--" != testSubject.ResourceManagerEndpoint { t.Errorf("Expected ResourceManagerEndpoint to be \"--resource-manager-endpoint--\", but got %q", testSubject.ResourceManagerEndpoint) } if "--active-directory-endpoint--" != testSubject.ActiveDirectoryEndpoint { t.Errorf("Expected ActiveDirectoryEndpoint to be \"--active-directory-endpoint--\", but got %q", testSubject.ActiveDirectoryEndpoint) } if "--gallery-endpoint--" != testSubject.GalleryEndpoint { t.Errorf("Expected GalleryEndpoint to be \"--gallery-endpoint--\", but got %q", testSubject.GalleryEndpoint) } if "--key-vault-endpoint--" != testSubject.KeyVaultEndpoint { t.Errorf("Expected KeyVaultEndpoint to be \"--key-vault-endpoint--\", but got %q", testSubject.KeyVaultEndpoint) } if "--graph-endpoint--" != testSubject.GraphEndpoint { t.Errorf("Expected GraphEndpoint to be \"--graph-endpoint--\", but got %q", testSubject.GraphEndpoint) } if "--storage-endpoint-suffix--" != testSubject.StorageEndpointSuffix { t.Errorf("Expected StorageEndpointSuffix to be \"--storage-endpoint-suffix--\", but got %q", testSubject.StorageEndpointSuffix) } if "--sql-database-dns-suffix--" != testSubject.SQLDatabaseDNSSuffix { t.Errorf("Expected sql-database-dns-suffix to be \"--sql-database-dns-suffix--\", but got %q", testSubject.SQLDatabaseDNSSuffix) } if "--key-vault-dns-suffix--" != testSubject.KeyVaultDNSSuffix { t.Errorf("Expected StorageEndpointSuffix to be \"--key-vault-dns-suffix--\", but got %q", testSubject.KeyVaultDNSSuffix) } if "--service-bus-endpoint-suffix--" != testSubject.ServiceBusEndpointSuffix { t.Errorf("Expected StorageEndpointSuffix to be \"--service-bus-endpoint-suffix--\", but got %q", testSubject.ServiceBusEndpointSuffix) } if "--asm-vm-dns-suffix--" != testSubject.ServiceManagementVMDNSSuffix { t.Errorf("Expected ServiceManagementVMDNSSuffix to be \"--asm-vm-dns-suffix--\", but got %q", testSubject.ServiceManagementVMDNSSuffix) } if "--arm-vm-dns-suffix--" != testSubject.ResourceManagerVMDNSSuffix { t.Errorf("Expected ResourceManagerVMDNSSuffix to be \"--arm-vm-dns-suffix--\", but got %q", testSubject.ResourceManagerVMDNSSuffix) } if "--container-registry-dns-suffix--" != testSubject.ContainerRegistryDNSSuffix { t.Errorf("Expected ContainerRegistryDNSSuffix to be \"--container-registry-dns-suffix--\", but got %q", testSubject.ContainerRegistryDNSSuffix) } } func TestRoundTripSerialization(t *testing.T) { env := Environment{ Name: "--unit-test--", ManagementPortalURL: "--management-portal-url", PublishSettingsURL: "--publish-settings-url--", ServiceManagementEndpoint: "--service-management-endpoint--", ResourceManagerEndpoint: "--resource-management-endpoint--", ActiveDirectoryEndpoint: "--active-directory-endpoint--", GalleryEndpoint: "--gallery-endpoint--", KeyVaultEndpoint: "--key-vault--endpoint--", GraphEndpoint: "--graph-endpoint--", StorageEndpointSuffix: "--storage-endpoint-suffix--", SQLDatabaseDNSSuffix: "--sql-database-dns-suffix--", TrafficManagerDNSSuffix: "--traffic-manager-dns-suffix--", KeyVaultDNSSuffix: "--key-vault-dns-suffix--", ServiceBusEndpointSuffix: "--service-bus-endpoint-suffix--", ServiceManagementVMDNSSuffix: "--asm-vm-dns-suffix--", ResourceManagerVMDNSSuffix: "--arm-vm-dns-suffix--", ContainerRegistryDNSSuffix: "--container-registry-dns-suffix--", } bytes, err := json.Marshal(env) if err != nil { t.Fatalf("failed to marshal: %s", err) } testSubject := Environment{} err = json.Unmarshal(bytes, &testSubject) if err != nil { t.Fatalf("failed to unmarshal: %s", err) } if env.Name != testSubject.Name { t.Errorf("Expected Name to be %q, but got %q", env.Name, testSubject.Name) } if env.ManagementPortalURL != testSubject.ManagementPortalURL { t.Errorf("Expected ManagementPortalURL to be %q, but got %q", env.ManagementPortalURL, testSubject.ManagementPortalURL) } if env.PublishSettingsURL != testSubject.PublishSettingsURL { t.Errorf("Expected PublishSettingsURL to be %q, but got %q", env.PublishSettingsURL, testSubject.PublishSettingsURL) } if env.ServiceManagementEndpoint != testSubject.ServiceManagementEndpoint { t.Errorf("Expected ServiceManagementEndpoint to be %q, but got %q", env.ServiceManagementEndpoint, testSubject.ServiceManagementEndpoint) } if env.ResourceManagerEndpoint != testSubject.ResourceManagerEndpoint { t.Errorf("Expected ResourceManagerEndpoint to be %q, but got %q", env.ResourceManagerEndpoint, testSubject.ResourceManagerEndpoint) } if env.ActiveDirectoryEndpoint != testSubject.ActiveDirectoryEndpoint { t.Errorf("Expected ActiveDirectoryEndpoint to be %q, but got %q", env.ActiveDirectoryEndpoint, testSubject.ActiveDirectoryEndpoint) } if env.GalleryEndpoint != testSubject.GalleryEndpoint { t.Errorf("Expected GalleryEndpoint to be %q, but got %q", env.GalleryEndpoint, testSubject.GalleryEndpoint) } if env.KeyVaultEndpoint != testSubject.KeyVaultEndpoint { t.Errorf("Expected KeyVaultEndpoint to be %q, but got %q", env.KeyVaultEndpoint, testSubject.KeyVaultEndpoint) } if env.GraphEndpoint != testSubject.GraphEndpoint { t.Errorf("Expected GraphEndpoint to be %q, but got %q", env.GraphEndpoint, testSubject.GraphEndpoint) } if env.StorageEndpointSuffix != testSubject.StorageEndpointSuffix { t.Errorf("Expected StorageEndpointSuffix to be %q, but got %q", env.StorageEndpointSuffix, testSubject.StorageEndpointSuffix) } if env.SQLDatabaseDNSSuffix != testSubject.SQLDatabaseDNSSuffix { t.Errorf("Expected SQLDatabaseDNSSuffix to be %q, but got %q", env.SQLDatabaseDNSSuffix, testSubject.SQLDatabaseDNSSuffix) } if env.TrafficManagerDNSSuffix != testSubject.TrafficManagerDNSSuffix { t.Errorf("Expected TrafficManagerDNSSuffix to be %q, but got %q", env.TrafficManagerDNSSuffix, testSubject.TrafficManagerDNSSuffix) } if env.KeyVaultDNSSuffix != testSubject.KeyVaultDNSSuffix { t.Errorf("Expected KeyVaultDNSSuffix to be %q, but got %q", env.KeyVaultDNSSuffix, testSubject.KeyVaultDNSSuffix) } if env.ServiceBusEndpointSuffix != testSubject.ServiceBusEndpointSuffix { t.Errorf("Expected ServiceBusEndpointSuffix to be %q, but got %q", env.ServiceBusEndpointSuffix, testSubject.ServiceBusEndpointSuffix) } if env.ServiceManagementVMDNSSuffix != testSubject.ServiceManagementVMDNSSuffix { t.Errorf("Expected ServiceManagementVMDNSSuffix to be %q, but got %q", env.ServiceManagementVMDNSSuffix, testSubject.ServiceManagementVMDNSSuffix) } if env.ResourceManagerVMDNSSuffix != testSubject.ResourceManagerVMDNSSuffix { t.Errorf("Expected ResourceManagerVMDNSSuffix to be %q, but got %q", env.ResourceManagerVMDNSSuffix, testSubject.ResourceManagerVMDNSSuffix) } if env.ContainerRegistryDNSSuffix != testSubject.ContainerRegistryDNSSuffix { t.Errorf("Expected ContainerRegistryDNSSuffix to be %q, but got %q", env.ContainerRegistryDNSSuffix, testSubject.ContainerRegistryDNSSuffix) } } go-autorest-8.3.1/autorest/azure/example/000077500000000000000000000000001315133516500204265ustar00rootroot00000000000000go-autorest-8.3.1/autorest/azure/example/README.md000066400000000000000000000073331315133516500217130ustar00rootroot00000000000000# autorest azure example ## Usage (device mode) This shows how to use the example for device auth. 1. Execute this. It will save your token to /tmp/azure-example-token: ``` ./example -tenantId "13de0a15-b5db-44b9-b682-b4ba82afbd29" -subscriptionId "aff271ee-e9be-4441-b9bb-42f5af4cbaeb" -mode "device" -tokenCachePath "/tmp/azure-example-token" ``` 2. Execute it again, it will load the token from cache and not prompt for auth again. ## Usage (certificate mode) This example covers how to make an authenticated call to the Azure Resource Manager APIs, using certificate-based authentication. 0. Export some required variables ``` export SUBSCRIPTION_ID="aff271ee-e9be-4441-b9bb-42f5af4cbaeb" export TENANT_ID="13de0a15-b5db-44b9-b682-b4ba82afbd29" export RESOURCE_GROUP="someresourcegroup" ``` * replace both values with your own 1. Create a private key ``` openssl genrsa -out "example.key" 2048 ``` 2. Create the certificate ``` openssl req -new -key "example.key" -subj "/CN=example" -out "example.csr" openssl x509 -req -in "example.csr" -signkey "example.key" -out "example.crt" -days 10000 ``` 3. Create the PKCS12 version of the certificate (with no password) ``` openssl pkcs12 -export -out "example.pfx" -inkey "example.key" -in "example.crt" -passout pass: ``` 4. Register a new Azure AD Application with the certificate contents ``` certificateContents="$(tail -n+2 "example.key" | head -n-1)" azure ad app create \ --name "example-azuread-app" \ --home-page="http://example-azuread-app/home" \ --identifier-uris "http://example-azuread-app/app" \ --key-usage "Verify" \ --end-date "2020-01-01" \ --key-value "${certificateContents}" ``` 5. Create a new service principal using the "Application Id" from the previous step ``` azure ad sp create "APPLICATION_ID" ``` * Replace APPLICATION_ID with the "Application Id" returned in step 4 6. Grant your service principal necessary permissions ``` azure role assignment create \ --resource-group "${RESOURCE_GROUP}" \ --roleName "Contributor" \ --subscription "${SUBSCRIPTION_ID}" \ --spn "http://example-azuread-app/app" ``` * Replace SUBSCRIPTION_ID with your subscription id * Replace RESOURCE_GROUP with the resource group for the assignment * Ensure that the `spn` parameter matches an `identifier-url` from Step 4 7. Run this example app to see your resource groups ``` go run main.go \ --tenantId="${TENANT_ID}" \ --subscriptionId="${SUBSCRIPTION_ID}" \ --applicationId="http://example-azuread-app/app" \ --certificatePath="certificate.pfx" ``` You should see something like this as output: ``` 2015/11/08 18:28:39 Using these settings: 2015/11/08 18:28:39 * certificatePath: certificate.pfx 2015/11/08 18:28:39 * applicationID: http://example-azuread-app/app 2015/11/08 18:28:39 * tenantID: 13de0a15-b5db-44b9-b682-b4ba82afbd29 2015/11/08 18:28:39 * subscriptionID: aff271ee-e9be-4441-b9bb-42f5af4cbaeb 2015/11/08 18:28:39 loading certificate... 2015/11/08 18:28:39 retrieve oauth token... 2015/11/08 18:28:39 querying the list of resource groups... 2015/11/08 18:28:50 2015/11/08 18:28:50 Groups: {"value":[{"id":"/subscriptions/aff271ee-e9be-4441-b9bb-42f5af4cbaeb/resourceGroups/kube-66f30810","name":"kube-66f30810","location":"westus","tags":{},"properties":{"provisioningState":"Succeeded"}}]} ``` ## Notes You may need to wait sometime between executing step 4, step 5 and step 6. If you issue those requests too quickly, you might hit an AD server that is not consistent with the server where the resource was created. go-autorest-8.3.1/autorest/azure/example/main.go000066400000000000000000000161741315133516500217120ustar00rootroot00000000000000package main import ( "crypto/rsa" "crypto/x509" "encoding/json" "flag" "fmt" "io/ioutil" "log" "net/http" "strings" "github.com/Azure/go-autorest/autorest" "github.com/Azure/go-autorest/autorest/adal" "github.com/Azure/go-autorest/autorest/azure" "golang.org/x/crypto/pkcs12" ) const ( resourceGroupURLTemplate = "https://management.azure.com" apiVersion = "2015-01-01" nativeAppClientID = "a87032a7-203c-4bf7-913c-44c50d23409a" resource = "https://management.core.windows.net/" ) var ( mode string tenantID string subscriptionID string applicationID string tokenCachePath string forceRefresh bool impatient bool certificatePath string ) func init() { flag.StringVar(&mode, "mode", "device", "mode of operation for SPT creation") flag.StringVar(&certificatePath, "certificatePath", "", "path to pk12/pfx certificate") flag.StringVar(&applicationID, "applicationId", "", "application id") flag.StringVar(&tenantID, "tenantId", "", "tenant id") flag.StringVar(&subscriptionID, "subscriptionId", "", "subscription id") flag.StringVar(&tokenCachePath, "tokenCachePath", "", "location of oauth token cache") flag.BoolVar(&forceRefresh, "forceRefresh", false, "pass true to force a token refresh") flag.Parse() log.Printf("mode(%s) certPath(%s) appID(%s) tenantID(%s), subID(%s)\n", mode, certificatePath, applicationID, tenantID, subscriptionID) if mode == "certificate" && (strings.TrimSpace(tenantID) == "" || strings.TrimSpace(subscriptionID) == "") { log.Fatalln("Bad usage. Using certificate mode. Please specify tenantID, subscriptionID") } if mode != "certificate" && mode != "device" { log.Fatalln("Bad usage. Mode must be one of 'certificate' or 'device'.") } if mode == "device" && strings.TrimSpace(applicationID) == "" { log.Println("Using device mode auth. Will use `azkube` clientID since none was specified on the comand line.") applicationID = nativeAppClientID } if mode == "certificate" && strings.TrimSpace(certificatePath) == "" { log.Fatalln("Bad usage. Mode 'certificate' requires the 'certificatePath' argument.") } if strings.TrimSpace(tenantID) == "" || strings.TrimSpace(subscriptionID) == "" || strings.TrimSpace(applicationID) == "" { log.Fatalln("Bad usage. Must specify the 'tenantId' and 'subscriptionId'") } } func getSptFromCachedToken(oauthConfig adal.OAuthConfig, clientID, resource string, callbacks ...adal.TokenRefreshCallback) (*adal.ServicePrincipalToken, error) { token, err := adal.LoadToken(tokenCachePath) if err != nil { return nil, fmt.Errorf("failed to load token from cache: %v", err) } spt, _ := adal.NewServicePrincipalTokenFromManualToken( oauthConfig, clientID, resource, *token, callbacks...) return spt, nil } func decodePkcs12(pkcs []byte, password string) (*x509.Certificate, *rsa.PrivateKey, error) { privateKey, certificate, err := pkcs12.Decode(pkcs, password) if err != nil { return nil, nil, err } rsaPrivateKey, isRsaKey := privateKey.(*rsa.PrivateKey) if !isRsaKey { return nil, nil, fmt.Errorf("PKCS#12 certificate must contain an RSA private key") } return certificate, rsaPrivateKey, nil } func getSptFromCertificate(oauthConfig adal.OAuthConfig, clientID, resource, certicatePath string, callbacks ...adal.TokenRefreshCallback) (*adal.ServicePrincipalToken, error) { certData, err := ioutil.ReadFile(certificatePath) if err != nil { return nil, fmt.Errorf("failed to read the certificate file (%s): %v", certificatePath, err) } certificate, rsaPrivateKey, err := decodePkcs12(certData, "") if err != nil { return nil, fmt.Errorf("failed to decode pkcs12 certificate while creating spt: %v", err) } spt, _ := adal.NewServicePrincipalTokenFromCertificate( oauthConfig, clientID, certificate, rsaPrivateKey, resource, callbacks...) return spt, nil } func getSptFromDeviceFlow(oauthConfig adal.OAuthConfig, clientID, resource string, callbacks ...adal.TokenRefreshCallback) (*adal.ServicePrincipalToken, error) { oauthClient := &autorest.Client{} deviceCode, err := adal.InitiateDeviceAuth(oauthClient, oauthConfig, clientID, resource) if err != nil { return nil, fmt.Errorf("failed to start device auth flow: %s", err) } fmt.Println(*deviceCode.Message) token, err := adal.WaitForUserCompletion(oauthClient, deviceCode) if err != nil { return nil, fmt.Errorf("failed to finish device auth flow: %s", err) } spt, err := adal.NewServicePrincipalTokenFromManualToken( oauthConfig, clientID, resource, *token, callbacks...) if err != nil { return nil, fmt.Errorf("failed to get oauth token from device flow: %v", err) } return spt, nil } func printResourceGroups(client *autorest.Client) error { p := map[string]interface{}{"subscription-id": subscriptionID} q := map[string]interface{}{"api-version": apiVersion} req, _ := autorest.Prepare(&http.Request{}, autorest.AsGet(), autorest.WithBaseURL(resourceGroupURLTemplate), autorest.WithPathParameters("/subscriptions/{subscription-id}/resourcegroups", p), autorest.WithQueryParameters(q)) resp, err := autorest.SendWithSender(client, req) if err != nil { return err } value := struct { ResourceGroups []struct { Name string `json:"name"` } `json:"value"` }{} defer resp.Body.Close() dec := json.NewDecoder(resp.Body) err = dec.Decode(&value) if err != nil { return err } var groupNames = make([]string, len(value.ResourceGroups)) for i, name := range value.ResourceGroups { groupNames[i] = name.Name } log.Println("Groups:", strings.Join(groupNames, ", ")) return err } func saveToken(spt adal.Token) { if tokenCachePath != "" { err := adal.SaveToken(tokenCachePath, 0600, spt) if err != nil { log.Println("error saving token", err) } else { log.Println("saved token to", tokenCachePath) } } } func main() { var spt *adal.ServicePrincipalToken var err error callback := func(t adal.Token) error { log.Println("refresh callback was called") saveToken(t) return nil } oauthConfig, err := adal.NewOAuthConfig(azure.PublicCloud.ActiveDirectoryEndpoint, tenantID) if err != nil { panic(err) } if tokenCachePath != "" { log.Println("tokenCachePath specified; attempting to load from", tokenCachePath) spt, err = getSptFromCachedToken(*oauthConfig, applicationID, resource, callback) if err != nil { spt = nil // just in case, this is the condition below log.Println("loading from cache failed:", err) } } if spt == nil { log.Println("authenticating via 'mode'", mode) switch mode { case "device": spt, err = getSptFromDeviceFlow(*oauthConfig, applicationID, resource, callback) case "certificate": spt, err = getSptFromCertificate(*oauthConfig, applicationID, resource, certificatePath, callback) } if err != nil { log.Fatalln("failed to retrieve token:", err) } // should save it as soon as you get it since Refresh won't be called for some time if tokenCachePath != "" { saveToken(spt.Token) } } client := &autorest.Client{} client.Authorizer = autorest.NewBearerAuthorizer(spt) printResourceGroups(client) if forceRefresh { err = spt.Refresh() if err != nil { panic(err) } printResourceGroups(client) } } go-autorest-8.3.1/autorest/client.go000066400000000000000000000165531315133516500174640ustar00rootroot00000000000000package autorest import ( "bytes" "fmt" "io" "io/ioutil" "log" "net/http" "net/http/cookiejar" "runtime" "time" ) const ( // DefaultPollingDelay is a reasonable delay between polling requests. DefaultPollingDelay = 60 * time.Second // DefaultPollingDuration is a reasonable total polling duration. DefaultPollingDuration = 15 * time.Minute // DefaultRetryAttempts is number of attempts for retry status codes (5xx). DefaultRetryAttempts = 3 ) var ( // defaultUserAgent builds a string containing the Go version, system archityecture and OS, // and the go-autorest version. defaultUserAgent = fmt.Sprintf("Go/%s (%s-%s) go-autorest/%s", runtime.Version(), runtime.GOARCH, runtime.GOOS, Version(), ) statusCodesForRetry = []int{ http.StatusRequestTimeout, // 408 http.StatusTooManyRequests, // 429 http.StatusInternalServerError, // 500 http.StatusBadGateway, // 502 http.StatusServiceUnavailable, // 503 http.StatusGatewayTimeout, // 504 } ) const ( requestFormat = `HTTP Request Begin =================================================== %s ===================================================== HTTP Request End ` responseFormat = `HTTP Response Begin =================================================== %s ===================================================== HTTP Response End ` ) // Response serves as the base for all responses from generated clients. It provides access to the // last http.Response. type Response struct { *http.Response `json:"-"` } // LoggingInspector implements request and response inspectors that log the full request and // response to a supplied log. type LoggingInspector struct { Logger *log.Logger } // WithInspection returns a PrepareDecorator that emits the http.Request to the supplied logger. The // body is restored after being emitted. // // Note: Since it reads the entire Body, this decorator should not be used where body streaming is // important. It is best used to trace JSON or similar body values. func (li LoggingInspector) WithInspection() PrepareDecorator { return func(p Preparer) Preparer { return PreparerFunc(func(r *http.Request) (*http.Request, error) { var body, b bytes.Buffer defer r.Body.Close() r.Body = ioutil.NopCloser(io.TeeReader(r.Body, &body)) if err := r.Write(&b); err != nil { return nil, fmt.Errorf("Failed to write response: %v", err) } li.Logger.Printf(requestFormat, b.String()) r.Body = ioutil.NopCloser(&body) return p.Prepare(r) }) } } // ByInspecting returns a RespondDecorator that emits the http.Response to the supplied logger. The // body is restored after being emitted. // // Note: Since it reads the entire Body, this decorator should not be used where body streaming is // important. It is best used to trace JSON or similar body values. func (li LoggingInspector) ByInspecting() RespondDecorator { return func(r Responder) Responder { return ResponderFunc(func(resp *http.Response) error { var body, b bytes.Buffer defer resp.Body.Close() resp.Body = ioutil.NopCloser(io.TeeReader(resp.Body, &body)) if err := resp.Write(&b); err != nil { return fmt.Errorf("Failed to write response: %v", err) } li.Logger.Printf(responseFormat, b.String()) resp.Body = ioutil.NopCloser(&body) return r.Respond(resp) }) } } // Client is the base for autorest generated clients. It provides default, "do nothing" // implementations of an Authorizer, RequestInspector, and ResponseInspector. It also returns the // standard, undecorated http.Client as a default Sender. // // Generated clients should also use Error (see NewError and NewErrorWithError) for errors and // return responses that compose with Response. // // Most customization of generated clients is best achieved by supplying a custom Authorizer, custom // RequestInspector, and / or custom ResponseInspector. Users may log requests, implement circuit // breakers (see https://msdn.microsoft.com/en-us/library/dn589784.aspx) or otherwise influence // sending the request by providing a decorated Sender. type Client struct { Authorizer Authorizer Sender Sender RequestInspector PrepareDecorator ResponseInspector RespondDecorator // PollingDelay sets the polling frequency used in absence of a Retry-After HTTP header PollingDelay time.Duration // PollingDuration sets the maximum polling time after which an error is returned. PollingDuration time.Duration // RetryAttempts sets the default number of retry attempts for client. RetryAttempts int // RetryDuration sets the delay duration for retries. RetryDuration time.Duration // UserAgent, if not empty, will be set as the HTTP User-Agent header on all requests sent // through the Do method. UserAgent string Jar http.CookieJar } // NewClientWithUserAgent returns an instance of a Client with the UserAgent set to the passed // string. func NewClientWithUserAgent(ua string) Client { c := Client{ PollingDelay: DefaultPollingDelay, PollingDuration: DefaultPollingDuration, RetryAttempts: DefaultRetryAttempts, RetryDuration: 30 * time.Second, UserAgent: defaultUserAgent, } c.AddToUserAgent(ua) return c } // AddToUserAgent adds an extension to the current user agent func (c *Client) AddToUserAgent(extension string) error { if extension != "" { c.UserAgent = fmt.Sprintf("%s %s", c.UserAgent, extension) return nil } return fmt.Errorf("Extension was empty, User Agent stayed as %s", c.UserAgent) } // Do implements the Sender interface by invoking the active Sender after applying authorization. // If Sender is not set, it uses a new instance of http.Client. In both cases it will, if UserAgent // is set, apply set the User-Agent header. func (c Client) Do(r *http.Request) (*http.Response, error) { if r.UserAgent() == "" { r, _ = Prepare(r, WithUserAgent(c.UserAgent)) } r, err := Prepare(r, c.WithInspection(), c.WithAuthorization()) if err != nil { return nil, NewErrorWithError(err, "autorest/Client", "Do", nil, "Preparing request failed") } resp, err := SendWithSender(c.sender(), r, DoRetryForStatusCodes(c.RetryAttempts, c.RetryDuration, statusCodesForRetry...)) Respond(resp, c.ByInspecting()) return resp, err } // sender returns the Sender to which to send requests. func (c Client) sender() Sender { if c.Sender == nil { j, _ := cookiejar.New(nil) return &http.Client{Jar: j} } return c.Sender } // WithAuthorization is a convenience method that returns the WithAuthorization PrepareDecorator // from the current Authorizer. If not Authorizer is set, it uses the NullAuthorizer. func (c Client) WithAuthorization() PrepareDecorator { return c.authorizer().WithAuthorization() } // authorizer returns the Authorizer to use. func (c Client) authorizer() Authorizer { if c.Authorizer == nil { return NullAuthorizer{} } return c.Authorizer } // WithInspection is a convenience method that passes the request to the supplied RequestInspector, // if present, or returns the WithNothing PrepareDecorator otherwise. func (c Client) WithInspection() PrepareDecorator { if c.RequestInspector == nil { return WithNothing() } return c.RequestInspector } // ByInspecting is a convenience method that passes the response to the supplied ResponseInspector, // if present, or returns the ByIgnoring RespondDecorator otherwise. func (c Client) ByInspecting() RespondDecorator { if c.ResponseInspector == nil { return ByIgnoring() } return c.ResponseInspector } go-autorest-8.3.1/autorest/client_test.go000066400000000000000000000207001315133516500205100ustar00rootroot00000000000000package autorest import ( "bytes" "fmt" "io/ioutil" "log" "math/rand" "net/http" "reflect" "testing" "time" "github.com/Azure/go-autorest/autorest/mocks" ) func TestLoggingInspectorWithInspection(t *testing.T) { b := bytes.Buffer{} c := Client{} li := LoggingInspector{Logger: log.New(&b, "", 0)} c.RequestInspector = li.WithInspection() Prepare(mocks.NewRequestWithContent("Content"), c.WithInspection()) if len(b.String()) <= 0 { t.Fatal("autorest: LoggingInspector#WithInspection did not record Request to the log") } } func TestLoggingInspectorWithInspectionEmitsErrors(t *testing.T) { b := bytes.Buffer{} c := Client{} r := mocks.NewRequestWithContent("Content") li := LoggingInspector{Logger: log.New(&b, "", 0)} c.RequestInspector = li.WithInspection() if _, err := Prepare(r, c.WithInspection()); err != nil { t.Error(err) } if len(b.String()) <= 0 { t.Fatal("autorest: LoggingInspector#WithInspection did not record Request to the log") } } func TestLoggingInspectorWithInspectionRestoresBody(t *testing.T) { b := bytes.Buffer{} c := Client{} r := mocks.NewRequestWithContent("Content") li := LoggingInspector{Logger: log.New(&b, "", 0)} c.RequestInspector = li.WithInspection() Prepare(r, c.WithInspection()) s, _ := ioutil.ReadAll(r.Body) if len(s) <= 0 { t.Fatal("autorest: LoggingInspector#WithInspection did not restore the Request body") } } func TestLoggingInspectorByInspecting(t *testing.T) { b := bytes.Buffer{} c := Client{} li := LoggingInspector{Logger: log.New(&b, "", 0)} c.ResponseInspector = li.ByInspecting() Respond(mocks.NewResponseWithContent("Content"), c.ByInspecting()) if len(b.String()) <= 0 { t.Fatal("autorest: LoggingInspector#ByInspection did not record Response to the log") } } func TestLoggingInspectorByInspectingEmitsErrors(t *testing.T) { b := bytes.Buffer{} c := Client{} r := mocks.NewResponseWithContent("Content") li := LoggingInspector{Logger: log.New(&b, "", 0)} c.ResponseInspector = li.ByInspecting() if err := Respond(r, c.ByInspecting()); err != nil { t.Fatal(err) } if len(b.String()) <= 0 { t.Fatal("autorest: LoggingInspector#ByInspection did not record Response to the log") } } func TestLoggingInspectorByInspectingRestoresBody(t *testing.T) { b := bytes.Buffer{} c := Client{} r := mocks.NewResponseWithContent("Content") li := LoggingInspector{Logger: log.New(&b, "", 0)} c.ResponseInspector = li.ByInspecting() Respond(r, c.ByInspecting()) s, _ := ioutil.ReadAll(r.Body) if len(s) <= 0 { t.Fatal("autorest: LoggingInspector#ByInspecting did not restore the Response body") } } func TestNewClientWithUserAgent(t *testing.T) { ua := "UserAgent" c := NewClientWithUserAgent(ua) completeUA := fmt.Sprintf("%s %s", defaultUserAgent, ua) if c.UserAgent != completeUA { t.Fatalf("autorest: NewClientWithUserAgent failed to set the UserAgent -- expected %s, received %s", completeUA, c.UserAgent) } } func TestAddToUserAgent(t *testing.T) { ua := "UserAgent" c := NewClientWithUserAgent(ua) ext := "extension" err := c.AddToUserAgent(ext) if err != nil { t.Fatalf("autorest: AddToUserAgent returned error -- expected nil, received %s", err) } completeUA := fmt.Sprintf("%s %s %s", defaultUserAgent, ua, ext) if c.UserAgent != completeUA { t.Fatalf("autorest: AddToUserAgent failed to add an extension to the UserAgent -- expected %s, received %s", completeUA, c.UserAgent) } err = c.AddToUserAgent("") if err == nil { t.Fatalf("autorest: AddToUserAgent didn't return error -- expected %s, received nil", fmt.Errorf("Extension was empty, User Agent stayed as %s", c.UserAgent)) } if c.UserAgent != completeUA { t.Fatalf("autorest: AddToUserAgent failed to not add an empty extension to the UserAgent -- expected %s, received %s", completeUA, c.UserAgent) } } func TestClientSenderReturnsHttpClientByDefault(t *testing.T) { c := Client{} if fmt.Sprintf("%T", c.sender()) != "*http.Client" { t.Fatal("autorest: Client#sender failed to return http.Client by default") } } func TestClientSenderReturnsSetSender(t *testing.T) { c := Client{} s := mocks.NewSender() c.Sender = s if c.sender() != s { t.Fatal("autorest: Client#sender failed to return set Sender") } } func TestClientDoInvokesSender(t *testing.T) { c := Client{} s := mocks.NewSender() c.Sender = s c.Do(&http.Request{}) if s.Attempts() != 1 { t.Fatal("autorest: Client#Do failed to invoke the Sender") } } func TestClientDoSetsUserAgent(t *testing.T) { ua := "UserAgent" c := Client{UserAgent: ua} r := mocks.NewRequest() s := mocks.NewSender() c.Sender = s c.Do(r) if r.UserAgent() != ua { t.Fatalf("autorest: Client#Do failed to correctly set User-Agent header: %s=%s", http.CanonicalHeaderKey(headerUserAgent), r.UserAgent()) } } func TestClientDoSetsAuthorization(t *testing.T) { r := mocks.NewRequest() s := mocks.NewSender() c := Client{Authorizer: mockAuthorizer{}, Sender: s} c.Do(r) if len(r.Header.Get(http.CanonicalHeaderKey(headerAuthorization))) <= 0 { t.Fatalf("autorest: Client#Send failed to set Authorization header -- %s=%s", http.CanonicalHeaderKey(headerAuthorization), r.Header.Get(http.CanonicalHeaderKey(headerAuthorization))) } } func TestClientDoInvokesRequestInspector(t *testing.T) { r := mocks.NewRequest() s := mocks.NewSender() i := &mockInspector{} c := Client{RequestInspector: i.WithInspection(), Sender: s} c.Do(r) if !i.wasInvoked { t.Fatal("autorest: Client#Send failed to invoke the RequestInspector") } } func TestClientDoInvokesResponseInspector(t *testing.T) { r := mocks.NewRequest() s := mocks.NewSender() i := &mockInspector{} c := Client{ResponseInspector: i.ByInspecting(), Sender: s} c.Do(r) if !i.wasInvoked { t.Fatal("autorest: Client#Send failed to invoke the ResponseInspector") } } func TestClientDoReturnsErrorIfPrepareFails(t *testing.T) { c := Client{} s := mocks.NewSender() c.Authorizer = mockFailingAuthorizer{} c.Sender = s _, err := c.Do(&http.Request{}) if err == nil { t.Fatalf("autorest: Client#Do failed to return an error when Prepare failed") } } func TestClientDoDoesNotSendIfPrepareFails(t *testing.T) { c := Client{} s := mocks.NewSender() c.Authorizer = mockFailingAuthorizer{} c.Sender = s c.Do(&http.Request{}) if s.Attempts() > 0 { t.Fatal("autorest: Client#Do failed to invoke the Sender") } } func TestClientAuthorizerReturnsNullAuthorizerByDefault(t *testing.T) { c := Client{} if fmt.Sprintf("%T", c.authorizer()) != "autorest.NullAuthorizer" { t.Fatal("autorest: Client#authorizer failed to return the NullAuthorizer by default") } } func TestClientAuthorizerReturnsSetAuthorizer(t *testing.T) { c := Client{} c.Authorizer = mockAuthorizer{} if fmt.Sprintf("%T", c.authorizer()) != "autorest.mockAuthorizer" { t.Fatal("autorest: Client#authorizer failed to return the set Authorizer") } } func TestClientWithAuthorizer(t *testing.T) { c := Client{} c.Authorizer = mockAuthorizer{} req, _ := Prepare(&http.Request{}, c.WithAuthorization()) if req.Header.Get(headerAuthorization) == "" { t.Fatal("autorest: Client#WithAuthorizer failed to return the WithAuthorizer from the active Authorizer") } } func TestClientWithInspection(t *testing.T) { c := Client{} r := &mockInspector{} c.RequestInspector = r.WithInspection() Prepare(&http.Request{}, c.WithInspection()) if !r.wasInvoked { t.Fatal("autorest: Client#WithInspection failed to invoke RequestInspector") } } func TestClientWithInspectionSetsDefault(t *testing.T) { c := Client{} r1 := &http.Request{} r2, _ := Prepare(r1, c.WithInspection()) if !reflect.DeepEqual(r1, r2) { t.Fatal("autorest: Client#WithInspection failed to provide a default RequestInspector") } } func TestClientByInspecting(t *testing.T) { c := Client{} r := &mockInspector{} c.ResponseInspector = r.ByInspecting() Respond(&http.Response{}, c.ByInspecting()) if !r.wasInvoked { t.Fatal("autorest: Client#ByInspecting failed to invoke ResponseInspector") } } func TestClientByInspectingSetsDefault(t *testing.T) { c := Client{} r := &http.Response{} Respond(r, c.ByInspecting()) if !reflect.DeepEqual(r, &http.Response{}) { t.Fatal("autorest: Client#ByInspecting failed to provide a default ResponseInspector") } } func randomString(n int) string { const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" r := rand.New(rand.NewSource(time.Now().UTC().UnixNano())) s := make([]byte, n) for i := range s { s[i] = chars[r.Intn(len(chars))] } return string(s) } go-autorest-8.3.1/autorest/date/000077500000000000000000000000001315133516500165625ustar00rootroot00000000000000go-autorest-8.3.1/autorest/date/date.go000066400000000000000000000046461315133516500200400ustar00rootroot00000000000000/* Package date provides time.Time derivatives that conform to the Swagger.io (https://swagger.io/) defined date formats: Date and DateTime. Both types may, in most cases, be used in lieu of time.Time types. And both convert to time.Time through a ToTime method. */ package date import ( "fmt" "time" ) const ( fullDate = "2006-01-02" fullDateJSON = `"2006-01-02"` dateFormat = "%04d-%02d-%02d" jsonFormat = `"%04d-%02d-%02d"` ) // Date defines a type similar to time.Time but assumes a layout of RFC3339 full-date (i.e., // 2006-01-02). type Date struct { time.Time } // ParseDate create a new Date from the passed string. func ParseDate(date string) (d Date, err error) { return parseDate(date, fullDate) } func parseDate(date string, format string) (Date, error) { d, err := time.Parse(format, date) return Date{Time: d}, err } // MarshalBinary preserves the Date as a byte array conforming to RFC3339 full-date (i.e., // 2006-01-02). func (d Date) MarshalBinary() ([]byte, error) { return d.MarshalText() } // UnmarshalBinary reconstitutes a Date saved as a byte array conforming to RFC3339 full-date (i.e., // 2006-01-02). func (d *Date) UnmarshalBinary(data []byte) error { return d.UnmarshalText(data) } // MarshalJSON preserves the Date as a JSON string conforming to RFC3339 full-date (i.e., // 2006-01-02). func (d Date) MarshalJSON() (json []byte, err error) { return []byte(fmt.Sprintf(jsonFormat, d.Year(), d.Month(), d.Day())), nil } // UnmarshalJSON reconstitutes the Date from a JSON string conforming to RFC3339 full-date (i.e., // 2006-01-02). func (d *Date) UnmarshalJSON(data []byte) (err error) { d.Time, err = time.Parse(fullDateJSON, string(data)) return err } // MarshalText preserves the Date as a byte array conforming to RFC3339 full-date (i.e., // 2006-01-02). func (d Date) MarshalText() (text []byte, err error) { return []byte(fmt.Sprintf(dateFormat, d.Year(), d.Month(), d.Day())), nil } // UnmarshalText reconstitutes a Date saved as a byte array conforming to RFC3339 full-date (i.e., // 2006-01-02). func (d *Date) UnmarshalText(data []byte) (err error) { d.Time, err = time.Parse(fullDate, string(data)) return err } // String returns the Date formatted as an RFC3339 full-date string (i.e., 2006-01-02). func (d Date) String() string { return fmt.Sprintf(dateFormat, d.Year(), d.Month(), d.Day()) } // ToTime returns a Date as a time.Time func (d Date) ToTime() time.Time { return d.Time } go-autorest-8.3.1/autorest/date/date_test.go000066400000000000000000000103711315133516500210670ustar00rootroot00000000000000package date import ( "encoding/json" "fmt" "reflect" "testing" "time" ) func ExampleParseDate() { d, err := ParseDate("2001-02-03") if err != nil { fmt.Println(err) } fmt.Println(d) // Output: 2001-02-03 } func ExampleDate() { d, err := ParseDate("2001-02-03") if err != nil { fmt.Println(err) } t, err := time.Parse(time.RFC3339, "2001-02-04T00:00:00Z") if err != nil { fmt.Println(err) } // Date acts as time.Time when the receiver if d.Before(t) { fmt.Printf("Before ") } else { fmt.Printf("After ") } // Convert Date when needing a time.Time if t.After(d.ToTime()) { fmt.Printf("After") } else { fmt.Printf("Before") } // Output: Before After } func ExampleDate_MarshalBinary() { d, err := ParseDate("2001-02-03") if err != nil { fmt.Println(err) } t, err := d.MarshalBinary() if err != nil { fmt.Println(err) } fmt.Println(string(t)) // Output: 2001-02-03 } func ExampleDate_UnmarshalBinary() { d := Date{} t := "2001-02-03" if err := d.UnmarshalBinary([]byte(t)); err != nil { fmt.Println(err) } fmt.Println(d) // Output: 2001-02-03 } func ExampleDate_MarshalJSON() { d, err := ParseDate("2001-02-03") if err != nil { fmt.Println(err) } j, err := json.Marshal(d) if err != nil { fmt.Println(err) } fmt.Println(string(j)) // Output: "2001-02-03" } func ExampleDate_UnmarshalJSON() { var d struct { Date Date `json:"date"` } j := `{"date" : "2001-02-03"}` if err := json.Unmarshal([]byte(j), &d); err != nil { fmt.Println(err) } fmt.Println(d.Date) // Output: 2001-02-03 } func ExampleDate_MarshalText() { d, err := ParseDate("2001-02-03") if err != nil { fmt.Println(err) } t, err := d.MarshalText() if err != nil { fmt.Println(err) } fmt.Println(string(t)) // Output: 2001-02-03 } func ExampleDate_UnmarshalText() { d := Date{} t := "2001-02-03" if err := d.UnmarshalText([]byte(t)); err != nil { fmt.Println(err) } fmt.Println(d) // Output: 2001-02-03 } func TestDateString(t *testing.T) { d, err := ParseDate("2001-02-03") if err != nil { t.Fatalf("date: String failed (%v)", err) } if d.String() != "2001-02-03" { t.Fatalf("date: String failed (%v)", d.String()) } } func TestDateBinaryRoundTrip(t *testing.T) { d1, err := ParseDate("2001-02-03") if err != nil { t.Fatalf("date: ParseDate failed (%v)", err) } t1, err := d1.MarshalBinary() if err != nil { t.Fatalf("date: MarshalBinary failed (%v)", err) } d2 := Date{} if err = d2.UnmarshalBinary(t1); err != nil { t.Fatalf("date: UnmarshalBinary failed (%v)", err) } if !reflect.DeepEqual(d1, d2) { t.Fatalf("date: Round-trip Binary failed (%v, %v)", d1, d2) } } func TestDateJSONRoundTrip(t *testing.T) { type s struct { Date Date `json:"date"` } var err error d1 := s{} d1.Date, err = ParseDate("2001-02-03") if err != nil { t.Fatalf("date: ParseDate failed (%v)", err) } j, err := json.Marshal(d1) if err != nil { t.Fatalf("date: MarshalJSON failed (%v)", err) } d2 := s{} if err = json.Unmarshal(j, &d2); err != nil { t.Fatalf("date: UnmarshalJSON failed (%v)", err) } if !reflect.DeepEqual(d1, d2) { t.Fatalf("date: Round-trip JSON failed (%v, %v)", d1, d2) } } func TestDateTextRoundTrip(t *testing.T) { d1, err := ParseDate("2001-02-03") if err != nil { t.Fatalf("date: ParseDate failed (%v)", err) } t1, err := d1.MarshalText() if err != nil { t.Fatalf("date: MarshalText failed (%v)", err) } d2 := Date{} if err = d2.UnmarshalText(t1); err != nil { t.Fatalf("date: UnmarshalText failed (%v)", err) } if !reflect.DeepEqual(d1, d2) { t.Fatalf("date: Round-trip Text failed (%v, %v)", d1, d2) } } func TestDateToTime(t *testing.T) { var d Date d, err := ParseDate("2001-02-03") if err != nil { t.Fatalf("date: ParseDate failed (%v)", err) } var _ time.Time = d.ToTime() } func TestDateUnmarshalJSONReturnsError(t *testing.T) { var d struct { Date Date `json:"date"` } j := `{"date" : "February 3, 2001"}` if err := json.Unmarshal([]byte(j), &d); err == nil { t.Fatal("date: Date failed to return error for malformed JSON date") } } func TestDateUnmarshalTextReturnsError(t *testing.T) { d := Date{} txt := "February 3, 2001" if err := d.UnmarshalText([]byte(txt)); err == nil { t.Fatal("date: Date failed to return error for malformed Text date") } } go-autorest-8.3.1/autorest/date/time.go000066400000000000000000000050171315133516500200520ustar00rootroot00000000000000package date import ( "regexp" "time" ) // Azure reports time in UTC but it doesn't include the 'Z' time zone suffix in some cases. const ( azureUtcFormatJSON = `"2006-01-02T15:04:05.999999999"` azureUtcFormat = "2006-01-02T15:04:05.999999999" rfc3339JSON = `"` + time.RFC3339Nano + `"` rfc3339 = time.RFC3339Nano tzOffsetRegex = `(Z|z|\+|-)(\d+:\d+)*"*$` ) // Time defines a type similar to time.Time but assumes a layout of RFC3339 date-time (i.e., // 2006-01-02T15:04:05Z). type Time struct { time.Time } // MarshalBinary preserves the Time as a byte array conforming to RFC3339 date-time (i.e., // 2006-01-02T15:04:05Z). func (t Time) MarshalBinary() ([]byte, error) { return t.Time.MarshalText() } // UnmarshalBinary reconstitutes a Time saved as a byte array conforming to RFC3339 date-time // (i.e., 2006-01-02T15:04:05Z). func (t *Time) UnmarshalBinary(data []byte) error { return t.UnmarshalText(data) } // MarshalJSON preserves the Time as a JSON string conforming to RFC3339 date-time (i.e., // 2006-01-02T15:04:05Z). func (t Time) MarshalJSON() (json []byte, err error) { return t.Time.MarshalJSON() } // UnmarshalJSON reconstitutes the Time from a JSON string conforming to RFC3339 date-time // (i.e., 2006-01-02T15:04:05Z). func (t *Time) UnmarshalJSON(data []byte) (err error) { timeFormat := azureUtcFormatJSON match, err := regexp.Match(tzOffsetRegex, data) if err != nil { return err } else if match { timeFormat = rfc3339JSON } t.Time, err = ParseTime(timeFormat, string(data)) return err } // MarshalText preserves the Time as a byte array conforming to RFC3339 date-time (i.e., // 2006-01-02T15:04:05Z). func (t Time) MarshalText() (text []byte, err error) { return t.Time.MarshalText() } // UnmarshalText reconstitutes a Time saved as a byte array conforming to RFC3339 date-time // (i.e., 2006-01-02T15:04:05Z). func (t *Time) UnmarshalText(data []byte) (err error) { timeFormat := azureUtcFormat match, err := regexp.Match(tzOffsetRegex, data) if err != nil { return err } else if match { timeFormat = rfc3339 } t.Time, err = ParseTime(timeFormat, string(data)) return err } // String returns the Time formatted as an RFC3339 date-time string (i.e., // 2006-01-02T15:04:05Z). func (t Time) String() string { // Note: time.Time.String does not return an RFC3339 compliant string, time.Time.MarshalText does. b, err := t.MarshalText() if err != nil { return "" } return string(b) } // ToTime returns a Time as a time.Time func (t Time) ToTime() time.Time { return t.Time } go-autorest-8.3.1/autorest/date/time_test.go000066400000000000000000000133201315133516500211050ustar00rootroot00000000000000package date import ( "encoding/json" "fmt" "reflect" "testing" "time" ) func ExampleParseTime() { d, _ := ParseTime(rfc3339, "2001-02-03T04:05:06Z") fmt.Println(d) // Output: 2001-02-03 04:05:06 +0000 UTC } func ExampleTime_MarshalBinary() { ti, err := ParseTime(rfc3339, "2001-02-03T04:05:06Z") if err != nil { fmt.Println(err) } d := Time{ti} t, err := d.MarshalBinary() if err != nil { fmt.Println(err) } fmt.Println(string(t)) // Output: 2001-02-03T04:05:06Z } func ExampleTime_UnmarshalBinary() { d := Time{} t := "2001-02-03T04:05:06Z" if err := d.UnmarshalBinary([]byte(t)); err != nil { fmt.Println(err) } fmt.Println(d) // Output: 2001-02-03T04:05:06Z } func ExampleTime_MarshalJSON() { d, err := ParseTime(rfc3339, "2001-02-03T04:05:06Z") if err != nil { fmt.Println(err) } j, err := json.Marshal(d) if err != nil { fmt.Println(err) } fmt.Println(string(j)) // Output: "2001-02-03T04:05:06Z" } func ExampleTime_UnmarshalJSON() { var d struct { Time Time `json:"datetime"` } j := `{"datetime" : "2001-02-03T04:05:06Z"}` if err := json.Unmarshal([]byte(j), &d); err != nil { fmt.Println(err) } fmt.Println(d.Time) // Output: 2001-02-03T04:05:06Z } func ExampleTime_MarshalText() { d, err := ParseTime(rfc3339, "2001-02-03T04:05:06Z") if err != nil { fmt.Println(err) } t, err := d.MarshalText() if err != nil { fmt.Println(err) } fmt.Println(string(t)) // Output: 2001-02-03T04:05:06Z } func ExampleTime_UnmarshalText() { d := Time{} t := "2001-02-03T04:05:06Z" if err := d.UnmarshalText([]byte(t)); err != nil { fmt.Println(err) } fmt.Println(d) // Output: 2001-02-03T04:05:06Z } func TestUnmarshalTextforInvalidDate(t *testing.T) { d := Time{} dt := "2001-02-03T04:05:06AAA" if err := d.UnmarshalText([]byte(dt)); err == nil { t.Fatalf("date: Time#Unmarshal was expecting error for invalid date") } } func TestUnmarshalJSONforInvalidDate(t *testing.T) { d := Time{} dt := `"2001-02-03T04:05:06AAA"` if err := d.UnmarshalJSON([]byte(dt)); err == nil { t.Fatalf("date: Time#Unmarshal was expecting error for invalid date") } } func TestTimeString(t *testing.T) { ti, err := ParseTime(rfc3339, "2001-02-03T04:05:06Z") if err != nil { fmt.Println(err) } d := Time{ti} if d.String() != "2001-02-03T04:05:06Z" { t.Fatalf("date: Time#String failed (%v)", d.String()) } } func TestTimeStringReturnsEmptyStringForError(t *testing.T) { d := Time{Time: time.Date(20000, 01, 01, 01, 01, 01, 01, time.UTC)} if d.String() != "" { t.Fatalf("date: Time#String failed empty string for an error") } } func TestTimeBinaryRoundTrip(t *testing.T) { ti, err := ParseTime(rfc3339, "2001-02-03T04:05:06Z") if err != nil { t.Fatalf("date: Time#ParseTime failed (%v)", err) } d1 := Time{ti} t1, err := d1.MarshalBinary() if err != nil { t.Fatalf("date: Time#MarshalBinary failed (%v)", err) } d2 := Time{} if err = d2.UnmarshalBinary(t1); err != nil { t.Fatalf("date: Time#UnmarshalBinary failed (%v)", err) } if !reflect.DeepEqual(d1, d2) { t.Fatalf("date:Round-trip Binary failed (%v, %v)", d1, d2) } } func TestTimeJSONRoundTrip(t *testing.T) { type s struct { Time Time `json:"datetime"` } ti, err := ParseTime(rfc3339, "2001-02-03T04:05:06Z") if err != nil { t.Fatalf("date: Time#ParseTime failed (%v)", err) } d1 := s{Time: Time{ti}} j, err := json.Marshal(d1) if err != nil { t.Fatalf("date: Time#MarshalJSON failed (%v)", err) } d2 := s{} if err = json.Unmarshal(j, &d2); err != nil { t.Fatalf("date: Time#UnmarshalJSON failed (%v)", err) } if !reflect.DeepEqual(d1, d2) { t.Fatalf("date: Round-trip JSON failed (%v, %v)", d1, d2) } } func TestTimeTextRoundTrip(t *testing.T) { ti, err := ParseTime(rfc3339, "2001-02-03T04:05:06Z") if err != nil { t.Fatalf("date: Time#ParseTime failed (%v)", err) } d1 := Time{Time: ti} t1, err := d1.MarshalText() if err != nil { t.Fatalf("date: Time#MarshalText failed (%v)", err) } d2 := Time{} if err = d2.UnmarshalText(t1); err != nil { t.Fatalf("date: Time#UnmarshalText failed (%v)", err) } if !reflect.DeepEqual(d1, d2) { t.Fatalf("date: Round-trip Text failed (%v, %v)", d1, d2) } } func TestTimeToTime(t *testing.T) { ti, err := ParseTime(rfc3339, "2001-02-03T04:05:06Z") d := Time{ti} if err != nil { t.Fatalf("date: Time#ParseTime failed (%v)", err) } var _ time.Time = d.ToTime() } func TestUnmarshalJSONNoOffset(t *testing.T) { var d struct { Time Time `json:"datetime"` } j := `{"datetime" : "2001-02-03T04:05:06.789"}` if err := json.Unmarshal([]byte(j), &d); err != nil { t.Fatalf("date: Time#Unmarshal failed (%v)", err) } } func TestUnmarshalJSONPosOffset(t *testing.T) { var d struct { Time Time `json:"datetime"` } j := `{"datetime" : "1980-01-02T00:11:35.01+01:00"}` if err := json.Unmarshal([]byte(j), &d); err != nil { t.Fatalf("date: Time#Unmarshal failed (%v)", err) } } func TestUnmarshalJSONNegOffset(t *testing.T) { var d struct { Time Time `json:"datetime"` } j := `{"datetime" : "1492-10-12T10:15:01.789-08:00"}` if err := json.Unmarshal([]byte(j), &d); err != nil { t.Fatalf("date: Time#Unmarshal failed (%v)", err) } } func TestUnmarshalTextNoOffset(t *testing.T) { d := Time{} t1 := "2001-02-03T04:05:06" if err := d.UnmarshalText([]byte(t1)); err != nil { t.Fatalf("date: Time#UnmarshalText failed (%v)", err) } } func TestUnmarshalTextPosOffset(t *testing.T) { d := Time{} t1 := "2001-02-03T04:05:06+00:30" if err := d.UnmarshalText([]byte(t1)); err != nil { t.Fatalf("date: Time#UnmarshalText failed (%v)", err) } } func TestUnmarshalTextNegOffset(t *testing.T) { d := Time{} t1 := "2001-02-03T04:05:06-11:00" if err := d.UnmarshalText([]byte(t1)); err != nil { t.Fatalf("date: Time#UnmarshalText failed (%v)", err) } } go-autorest-8.3.1/autorest/date/timerfc1123.go000066400000000000000000000046251315133516500210600ustar00rootroot00000000000000package date import ( "errors" "time" ) const ( rfc1123JSON = `"` + time.RFC1123 + `"` rfc1123 = time.RFC1123 ) // TimeRFC1123 defines a type similar to time.Time but assumes a layout of RFC1123 date-time (i.e., // Mon, 02 Jan 2006 15:04:05 MST). type TimeRFC1123 struct { time.Time } // UnmarshalJSON reconstitutes the Time from a JSON string conforming to RFC1123 date-time // (i.e., Mon, 02 Jan 2006 15:04:05 MST). func (t *TimeRFC1123) UnmarshalJSON(data []byte) (err error) { t.Time, err = ParseTime(rfc1123JSON, string(data)) if err != nil { return err } return nil } // MarshalJSON preserves the Time as a JSON string conforming to RFC1123 date-time (i.e., // Mon, 02 Jan 2006 15:04:05 MST). func (t TimeRFC1123) MarshalJSON() ([]byte, error) { if y := t.Year(); y < 0 || y >= 10000 { return nil, errors.New("Time.MarshalJSON: year outside of range [0,9999]") } b := []byte(t.Format(rfc1123JSON)) return b, nil } // MarshalText preserves the Time as a byte array conforming to RFC1123 date-time (i.e., // Mon, 02 Jan 2006 15:04:05 MST). func (t TimeRFC1123) MarshalText() ([]byte, error) { if y := t.Year(); y < 0 || y >= 10000 { return nil, errors.New("Time.MarshalText: year outside of range [0,9999]") } b := []byte(t.Format(rfc1123)) return b, nil } // UnmarshalText reconstitutes a Time saved as a byte array conforming to RFC1123 date-time // (i.e., Mon, 02 Jan 2006 15:04:05 MST). func (t *TimeRFC1123) UnmarshalText(data []byte) (err error) { t.Time, err = ParseTime(rfc1123, string(data)) if err != nil { return err } return nil } // MarshalBinary preserves the Time as a byte array conforming to RFC1123 date-time (i.e., // Mon, 02 Jan 2006 15:04:05 MST). func (t TimeRFC1123) MarshalBinary() ([]byte, error) { return t.MarshalText() } // UnmarshalBinary reconstitutes a Time saved as a byte array conforming to RFC1123 date-time // (i.e., Mon, 02 Jan 2006 15:04:05 MST). func (t *TimeRFC1123) UnmarshalBinary(data []byte) error { return t.UnmarshalText(data) } // ToTime returns a Time as a time.Time func (t TimeRFC1123) ToTime() time.Time { return t.Time } // String returns the Time formatted as an RFC1123 date-time string (i.e., // Mon, 02 Jan 2006 15:04:05 MST). func (t TimeRFC1123) String() string { // Note: time.Time.String does not return an RFC1123 compliant string, time.Time.MarshalText does. b, err := t.MarshalText() if err != nil { return "" } return string(b) } go-autorest-8.3.1/autorest/date/timerfc1123_test.go000066400000000000000000000121341315133516500221110ustar00rootroot00000000000000package date import ( "encoding/json" "fmt" "reflect" "testing" "time" ) func ExampleTimeRFC1123() { d, err := ParseTime(rfc1123, "Mon, 02 Jan 2006 15:04:05 MST") if err != nil { fmt.Println(err) } fmt.Println(d) // Output: 2006-01-02 15:04:05 +0000 MST } func ExampleTimeRFC1123_MarshalBinary() { ti, err := ParseTime(rfc1123, "Mon, 02 Jan 2006 15:04:05 MST") if err != nil { fmt.Println(err) } d := TimeRFC1123{ti} b, err := d.MarshalBinary() if err != nil { fmt.Println(err) } fmt.Println(string(b)) // Output: Mon, 02 Jan 2006 15:04:05 MST } func ExampleTimeRFC1123_UnmarshalBinary() { d := TimeRFC1123{} t := "Mon, 02 Jan 2006 15:04:05 MST" if err := d.UnmarshalBinary([]byte(t)); err != nil { fmt.Println(err) } fmt.Println(d) // Output: Mon, 02 Jan 2006 15:04:05 MST } func ExampleTimeRFC1123_MarshalJSON() { ti, err := ParseTime(rfc1123, "Mon, 02 Jan 2006 15:04:05 MST") if err != nil { fmt.Println(err) } d := TimeRFC1123{ti} j, err := json.Marshal(d) if err != nil { fmt.Println(err) } fmt.Println(string(j)) // Output: "Mon, 02 Jan 2006 15:04:05 MST" } func TestTimeRFC1123MarshalJSONInvalid(t *testing.T) { ti := time.Date(20000, 01, 01, 00, 00, 00, 00, time.UTC) d := TimeRFC1123{ti} if _, err := json.Marshal(d); err == nil { t.Fatalf("date: TimeRFC1123#Marshal failed for invalid date") } } func ExampleTimeRFC1123_UnmarshalJSON() { var d struct { Time TimeRFC1123 `json:"datetime"` } j := `{"datetime" : "Mon, 02 Jan 2006 15:04:05 MST"}` if err := json.Unmarshal([]byte(j), &d); err != nil { fmt.Println(err) } fmt.Println(d.Time) // Output: Mon, 02 Jan 2006 15:04:05 MST } func ExampleTimeRFC1123_MarshalText() { ti, err := ParseTime(rfc3339, "2001-02-03T04:05:06Z") if err != nil { fmt.Println(err) } d := TimeRFC1123{ti} t, err := d.MarshalText() if err != nil { fmt.Println(err) } fmt.Println(string(t)) // Output: Sat, 03 Feb 2001 04:05:06 UTC } func ExampleTimeRFC1123_UnmarshalText() { d := TimeRFC1123{} t := "Sat, 03 Feb 2001 04:05:06 UTC" if err := d.UnmarshalText([]byte(t)); err != nil { fmt.Println(err) } fmt.Println(d) // Output: Sat, 03 Feb 2001 04:05:06 UTC } func TestUnmarshalJSONforInvalidDateRfc1123(t *testing.T) { dt := `"Mon, 02 Jan 2000000 15:05 MST"` d := TimeRFC1123{} if err := d.UnmarshalJSON([]byte(dt)); err == nil { t.Fatalf("date: TimeRFC1123#Unmarshal failed for invalid date") } } func TestUnmarshalTextforInvalidDateRfc1123(t *testing.T) { dt := "Mon, 02 Jan 2000000 15:05 MST" d := TimeRFC1123{} if err := d.UnmarshalText([]byte(dt)); err == nil { t.Fatalf("date: TimeRFC1123#Unmarshal failed for invalid date") } } func TestTimeStringRfc1123(t *testing.T) { ti, err := ParseTime(rfc1123, "Mon, 02 Jan 2006 15:04:05 MST") if err != nil { fmt.Println(err) } d := TimeRFC1123{ti} if d.String() != "Mon, 02 Jan 2006 15:04:05 MST" { t.Fatalf("date: TimeRFC1123#String failed (%v)", d.String()) } } func TestTimeStringReturnsEmptyStringForErrorRfc1123(t *testing.T) { d := TimeRFC1123{Time: time.Date(20000, 01, 01, 01, 01, 01, 01, time.UTC)} if d.String() != "" { t.Fatalf("date: TimeRFC1123#String failed empty string for an error") } } func TestTimeBinaryRoundTripRfc1123(t *testing.T) { ti, err := ParseTime(rfc3339, "2001-02-03T04:05:06Z") if err != nil { t.Fatalf("date: TimeRFC1123#ParseTime failed (%v)", err) } d1 := TimeRFC1123{ti} t1, err := d1.MarshalBinary() if err != nil { t.Fatalf("date: TimeRFC1123#MarshalBinary failed (%v)", err) } d2 := TimeRFC1123{} if err = d2.UnmarshalBinary(t1); err != nil { t.Fatalf("date: TimeRFC1123#UnmarshalBinary failed (%v)", err) } if !reflect.DeepEqual(d1, d2) { t.Fatalf("date: Round-trip Binary failed (%v, %v)", d1, d2) } } func TestTimeJSONRoundTripRfc1123(t *testing.T) { type s struct { Time TimeRFC1123 `json:"datetime"` } var err error ti, err := ParseTime(rfc1123, "Mon, 02 Jan 2006 15:04:05 MST") if err != nil { t.Fatalf("date: TimeRFC1123#ParseTime failed (%v)", err) } d1 := s{Time: TimeRFC1123{ti}} j, err := json.Marshal(d1) if err != nil { t.Fatalf("date: TimeRFC1123#MarshalJSON failed (%v)", err) } d2 := s{} if err = json.Unmarshal(j, &d2); err != nil { t.Fatalf("date: TimeRFC1123#UnmarshalJSON failed (%v)", err) } if !reflect.DeepEqual(d1, d2) { t.Fatalf("date: Round-trip JSON failed (%v, %v)", d1, d2) } } func TestTimeTextRoundTripRfc1123(t *testing.T) { ti, err := ParseTime(rfc1123, "Mon, 02 Jan 2006 15:04:05 MST") if err != nil { t.Fatalf("date: TimeRFC1123#ParseTime failed (%v)", err) } d1 := TimeRFC1123{Time: ti} t1, err := d1.MarshalText() if err != nil { t.Fatalf("date: TimeRFC1123#MarshalText failed (%v)", err) } d2 := TimeRFC1123{} if err = d2.UnmarshalText(t1); err != nil { t.Fatalf("date: TimeRFC1123#UnmarshalText failed (%v)", err) } if !reflect.DeepEqual(d1, d2) { t.Fatalf("date: Round-trip Text failed (%v, %v)", d1, d2) } } func TestTimeToTimeRFC1123(t *testing.T) { ti, err := ParseTime(rfc1123, "Mon, 02 Jan 2006 15:04:05 MST") d := TimeRFC1123{ti} if err != nil { t.Fatalf("date: TimeRFC1123#ParseTime failed (%v)", err) } var _ time.Time = d.ToTime() } go-autorest-8.3.1/autorest/date/unixtime.go000066400000000000000000000065071315133516500207630ustar00rootroot00000000000000package date import ( "bytes" "encoding/binary" "encoding/json" "time" ) // unixEpoch is the moment in time that should be treated as timestamp 0. var unixEpoch = time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC) // UnixTime marshals and unmarshals a time that is represented as the number // of seconds (ignoring skip-seconds) since the Unix Epoch. type UnixTime time.Time // Duration returns the time as a Duration since the UnixEpoch. func (t UnixTime) Duration() time.Duration { return time.Time(t).Sub(unixEpoch) } // NewUnixTimeFromSeconds creates a UnixTime as a number of seconds from the UnixEpoch. func NewUnixTimeFromSeconds(seconds float64) UnixTime { return NewUnixTimeFromDuration(time.Duration(seconds * float64(time.Second))) } // NewUnixTimeFromNanoseconds creates a UnixTime as a number of nanoseconds from the UnixEpoch. func NewUnixTimeFromNanoseconds(nanoseconds int64) UnixTime { return NewUnixTimeFromDuration(time.Duration(nanoseconds)) } // NewUnixTimeFromDuration creates a UnixTime as a duration of time since the UnixEpoch. func NewUnixTimeFromDuration(dur time.Duration) UnixTime { return UnixTime(unixEpoch.Add(dur)) } // UnixEpoch retreives the moment considered the Unix Epoch. I.e. The time represented by '0' func UnixEpoch() time.Time { return unixEpoch } // MarshalJSON preserves the UnixTime as a JSON number conforming to Unix Timestamp requirements. // (i.e. the number of seconds since midnight January 1st, 1970 not considering leap seconds.) func (t UnixTime) MarshalJSON() ([]byte, error) { buffer := &bytes.Buffer{} enc := json.NewEncoder(buffer) err := enc.Encode(float64(time.Time(t).UnixNano()) / 1e9) if err != nil { return nil, err } return buffer.Bytes(), nil } // UnmarshalJSON reconstitures a UnixTime saved as a JSON number of the number of seconds since // midnight January 1st, 1970. func (t *UnixTime) UnmarshalJSON(text []byte) error { dec := json.NewDecoder(bytes.NewReader(text)) var secondsSinceEpoch float64 if err := dec.Decode(&secondsSinceEpoch); err != nil { return err } *t = NewUnixTimeFromSeconds(secondsSinceEpoch) return nil } // MarshalText stores the number of seconds since the Unix Epoch as a textual floating point number. func (t UnixTime) MarshalText() ([]byte, error) { cast := time.Time(t) return cast.MarshalText() } // UnmarshalText populates a UnixTime with a value stored textually as a floating point number of seconds since the Unix Epoch. func (t *UnixTime) UnmarshalText(raw []byte) error { var unmarshaled time.Time if err := unmarshaled.UnmarshalText(raw); err != nil { return err } *t = UnixTime(unmarshaled) return nil } // MarshalBinary converts a UnixTime into a binary.LittleEndian float64 of nanoseconds since the epoch. func (t UnixTime) MarshalBinary() ([]byte, error) { buf := &bytes.Buffer{} payload := int64(t.Duration()) if err := binary.Write(buf, binary.LittleEndian, &payload); err != nil { return nil, err } return buf.Bytes(), nil } // UnmarshalBinary converts a from a binary.LittleEndian float64 of nanoseconds since the epoch into a UnixTime. func (t *UnixTime) UnmarshalBinary(raw []byte) error { var nanosecondsSinceEpoch int64 if err := binary.Read(bytes.NewReader(raw), binary.LittleEndian, &nanosecondsSinceEpoch); err != nil { return err } *t = NewUnixTimeFromNanoseconds(nanosecondsSinceEpoch) return nil } go-autorest-8.3.1/autorest/date/unixtime_test.go000066400000000000000000000150711315133516500220160ustar00rootroot00000000000000// +build go1.7 package date import ( "bytes" "encoding/binary" "encoding/json" "fmt" "math" "testing" "time" ) func ExampleUnixTime_MarshalJSON() { epoch := UnixTime(UnixEpoch()) text, _ := json.Marshal(epoch) fmt.Print(string(text)) // Output: 0 } func ExampleUnixTime_UnmarshalJSON() { var myTime UnixTime json.Unmarshal([]byte("1.3e2"), &myTime) fmt.Printf("%v", time.Time(myTime)) // Output: 1970-01-01 00:02:10 +0000 UTC } func TestUnixTime_MarshalJSON(t *testing.T) { testCases := []time.Time{ UnixEpoch().Add(-1 * time.Second), // One second befote the Unix Epoch time.Date(2017, time.April, 14, 20, 27, 47, 0, time.UTC), // The time this test was written UnixEpoch(), time.Date(1800, 01, 01, 0, 0, 0, 0, time.UTC), time.Date(2200, 12, 29, 00, 01, 37, 82, time.UTC), } for _, tc := range testCases { t.Run(tc.String(), func(subT *testing.T) { var actual, expected float64 var marshaled []byte target := UnixTime(tc) expected = float64(target.Duration().Nanoseconds()) / 1e9 if temp, err := json.Marshal(target); err == nil { marshaled = temp } else { subT.Error(err) return } dec := json.NewDecoder(bytes.NewReader(marshaled)) if err := dec.Decode(&actual); err != nil { subT.Error(err) return } diff := math.Abs(actual - expected) subT.Logf("\ngot :\t%g\nwant:\t%g\ndiff:\t%g", actual, expected, diff) if diff > 1e-9 { //Must be within 1 nanosecond of one another subT.Fail() } }) } } func TestUnixTime_UnmarshalJSON(t *testing.T) { testCases := []struct { text string expected time.Time }{ {"1", UnixEpoch().Add(time.Second)}, {"0", UnixEpoch()}, {"1492203742", time.Date(2017, time.April, 14, 21, 02, 22, 0, time.UTC)}, // The time this test was written {"-1", time.Date(1969, time.December, 31, 23, 59, 59, 0, time.UTC)}, {"1.5", UnixEpoch().Add(1500 * time.Millisecond)}, {"0e1", UnixEpoch()}, // See http://json.org for 'number' format definition. {"1.3e+2", UnixEpoch().Add(130 * time.Second)}, {"1.6E-10", UnixEpoch()}, // This is so small, it should get truncated into the UnixEpoch {"2E-6", UnixEpoch().Add(2 * time.Microsecond)}, {"1.289345e9", UnixEpoch().Add(1289345000 * time.Second)}, {"1e-9", UnixEpoch().Add(time.Nanosecond)}, } for _, tc := range testCases { t.Run(tc.text, func(subT *testing.T) { var rehydrated UnixTime if err := json.Unmarshal([]byte(tc.text), &rehydrated); err != nil { subT.Error(err) return } if time.Time(rehydrated) != tc.expected { subT.Logf("\ngot: \t%v\nwant:\t%v\ndiff:\t%v", time.Time(rehydrated), tc.expected, time.Time(rehydrated).Sub(tc.expected)) subT.Fail() } }) } } func TestUnixTime_JSONRoundTrip(t *testing.T) { testCases := []time.Time{ UnixEpoch(), time.Date(2005, time.November, 5, 0, 0, 0, 0, time.UTC), // The day V for Vendetta (film) was released. UnixEpoch().Add(-6 * time.Second), UnixEpoch().Add(800 * time.Hour), UnixEpoch().Add(time.Nanosecond), time.Date(2015, time.September, 05, 4, 30, 12, 9992, time.UTC), } for _, tc := range testCases { t.Run(tc.String(), func(subT *testing.T) { subject := UnixTime(tc) var marshaled []byte if temp, err := json.Marshal(subject); err == nil { marshaled = temp } else { subT.Error(err) return } var unmarshaled UnixTime if err := json.Unmarshal(marshaled, &unmarshaled); err != nil { subT.Error(err) } actual := time.Time(unmarshaled) diff := actual.Sub(tc) subT.Logf("\ngot :\t%s\nwant:\t%s\ndiff:\t%s", actual.String(), tc.String(), diff.String()) if diff > time.Duration(100) { // We lose some precision be working in floats. We shouldn't lose more than 100 nanoseconds. subT.Fail() } }) } } func TestUnixTime_MarshalBinary(t *testing.T) { testCases := []struct { expected int64 subject time.Time }{ {0, UnixEpoch()}, {-15 * int64(time.Second), UnixEpoch().Add(-15 * time.Second)}, {54, UnixEpoch().Add(54 * time.Nanosecond)}, } for _, tc := range testCases { t.Run("", func(subT *testing.T) { var marshaled []byte if temp, err := UnixTime(tc.subject).MarshalBinary(); err == nil { marshaled = temp } else { subT.Error(err) return } var unmarshaled int64 if err := binary.Read(bytes.NewReader(marshaled), binary.LittleEndian, &unmarshaled); err != nil { subT.Error(err) return } if unmarshaled != tc.expected { subT.Logf("\ngot: \t%d\nwant:\t%d", unmarshaled, tc.expected) subT.Fail() } }) } } func TestUnixTime_BinaryRoundTrip(t *testing.T) { testCases := []time.Time{ UnixEpoch(), UnixEpoch().Add(800 * time.Minute), UnixEpoch().Add(7 * time.Hour), UnixEpoch().Add(-1 * time.Nanosecond), } for _, tc := range testCases { t.Run(tc.String(), func(subT *testing.T) { original := UnixTime(tc) var marshaled []byte if temp, err := original.MarshalBinary(); err == nil { marshaled = temp } else { subT.Error(err) return } var traveled UnixTime if err := traveled.UnmarshalBinary(marshaled); err != nil { subT.Error(err) return } if traveled != original { subT.Logf("\ngot: \t%s\nwant:\t%s", time.Time(original).String(), time.Time(traveled).String()) subT.Fail() } }) } } func TestUnixTime_MarshalText(t *testing.T) { testCases := []time.Time{ UnixEpoch(), UnixEpoch().Add(45 * time.Second), UnixEpoch().Add(time.Nanosecond), UnixEpoch().Add(-100000 * time.Second), } for _, tc := range testCases { expected, _ := tc.MarshalText() t.Run("", func(subT *testing.T) { var marshaled []byte if temp, err := UnixTime(tc).MarshalText(); err == nil { marshaled = temp } else { subT.Error(err) return } if string(marshaled) != string(expected) { subT.Logf("\ngot: \t%s\nwant:\t%s", string(marshaled), string(expected)) subT.Fail() } }) } } func TestUnixTime_TextRoundTrip(t *testing.T) { testCases := []time.Time{ UnixEpoch(), UnixEpoch().Add(-1 * time.Nanosecond), UnixEpoch().Add(1 * time.Nanosecond), time.Date(2017, time.April, 17, 21, 00, 00, 00, time.UTC), } for _, tc := range testCases { t.Run(tc.String(), func(subT *testing.T) { unixTC := UnixTime(tc) var marshaled []byte if temp, err := unixTC.MarshalText(); err == nil { marshaled = temp } else { subT.Error(err) return } var unmarshaled UnixTime if err := unmarshaled.UnmarshalText(marshaled); err != nil { subT.Error(err) return } if unmarshaled != unixTC { t.Logf("\ngot: \t%s\nwant:\t%s", time.Time(unmarshaled).String(), tc.String()) t.Fail() } }) } } go-autorest-8.3.1/autorest/date/utility.go000066400000000000000000000003301315133516500206100ustar00rootroot00000000000000package date import ( "strings" "time" ) // ParseTime to parse Time string to specified format. func ParseTime(format string, t string) (d time.Time, err error) { return time.Parse(format, strings.ToUpper(t)) } go-autorest-8.3.1/autorest/error.go000066400000000000000000000060061315133516500173270ustar00rootroot00000000000000package autorest import ( "fmt" "net/http" ) const ( // UndefinedStatusCode is used when HTTP status code is not available for an error. UndefinedStatusCode = 0 ) // DetailedError encloses a error with details of the package, method, and associated HTTP // status code (if any). type DetailedError struct { Original error // PackageType is the package type of the object emitting the error. For types, the value // matches that produced the the '%T' format specifier of the fmt package. For other elements, // such as functions, it is just the package name (e.g., "autorest"). PackageType string // Method is the name of the method raising the error. Method string // StatusCode is the HTTP Response StatusCode (if non-zero) that led to the error. StatusCode interface{} // Message is the error message. Message string // Service Error is the response body of failed API in bytes ServiceError []byte // Response is the response object that was returned during failure if applicable. Response *http.Response } // NewError creates a new Error conforming object from the passed packageType, method, and // message. message is treated as a format string to which the optional args apply. func NewError(packageType string, method string, message string, args ...interface{}) DetailedError { return NewErrorWithError(nil, packageType, method, nil, message, args...) } // NewErrorWithResponse creates a new Error conforming object from the passed // packageType, method, statusCode of the given resp (UndefinedStatusCode if // resp is nil), and message. message is treated as a format string to which the // optional args apply. func NewErrorWithResponse(packageType string, method string, resp *http.Response, message string, args ...interface{}) DetailedError { return NewErrorWithError(nil, packageType, method, resp, message, args...) } // NewErrorWithError creates a new Error conforming object from the // passed packageType, method, statusCode of the given resp (UndefinedStatusCode // if resp is nil), message, and original error. message is treated as a format // string to which the optional args apply. func NewErrorWithError(original error, packageType string, method string, resp *http.Response, message string, args ...interface{}) DetailedError { if v, ok := original.(DetailedError); ok { return v } statusCode := UndefinedStatusCode if resp != nil { statusCode = resp.StatusCode } return DetailedError{ Original: original, PackageType: packageType, Method: method, StatusCode: statusCode, Message: fmt.Sprintf(message, args...), Response: resp, } } // Error returns a formatted containing all available details (i.e., PackageType, Method, // StatusCode, Message, and original error (if any)). func (e DetailedError) Error() string { if e.Original == nil { return fmt.Sprintf("%s#%s: %s: StatusCode=%d", e.PackageType, e.Method, e.Message, e.StatusCode) } return fmt.Sprintf("%s#%s: %s: StatusCode=%d -- Original Error: %v", e.PackageType, e.Method, e.Message, e.StatusCode, e.Original) } go-autorest-8.3.1/autorest/error_test.go000066400000000000000000000154021315133516500203660ustar00rootroot00000000000000package autorest import ( "fmt" "net/http" "reflect" "regexp" "testing" ) func TestNewErrorWithError_AssignsPackageType(t *testing.T) { e := NewErrorWithError(fmt.Errorf("original"), "packageType", "method", nil, "message") if e.PackageType != "packageType" { t.Fatalf("autorest: Error failed to set package type -- expected %v, received %v", "packageType", e.PackageType) } } func TestNewErrorWithError_AssignsMethod(t *testing.T) { e := NewErrorWithError(fmt.Errorf("original"), "packageType", "method", nil, "message") if e.Method != "method" { t.Fatalf("autorest: Error failed to set method -- expected %v, received %v", "method", e.Method) } } func TestNewErrorWithError_AssignsMessage(t *testing.T) { e := NewErrorWithError(fmt.Errorf("original"), "packageType", "method", nil, "message") if e.Message != "message" { t.Fatalf("autorest: Error failed to set message -- expected %v, received %v", "message", e.Message) } } func TestNewErrorWithError_AssignsUndefinedStatusCodeIfRespNil(t *testing.T) { e := NewErrorWithError(nil, "packageType", "method", nil, "message") if e.StatusCode != UndefinedStatusCode { t.Fatalf("autorest: Error failed to set status code -- expected %v, received %v", UndefinedStatusCode, e.StatusCode) } } func TestNewErrorWithError_AssignsStatusCode(t *testing.T) { e := NewErrorWithError(fmt.Errorf("original"), "packageType", "method", &http.Response{ StatusCode: http.StatusBadRequest, Status: http.StatusText(http.StatusBadRequest)}, "message") if e.StatusCode != http.StatusBadRequest { t.Fatalf("autorest: Error failed to set status code -- expected %v, received %v", http.StatusBadRequest, e.StatusCode) } } func TestNewErrorWithError_AcceptsArgs(t *testing.T) { e := NewErrorWithError(fmt.Errorf("original"), "packageType", "method", nil, "message %s", "arg") if matched, _ := regexp.MatchString(`.*arg.*`, e.Message); !matched { t.Fatalf("autorest: Error failed to apply message arguments -- expected %v, received %v", `.*arg.*`, e.Message) } } func TestNewErrorWithError_AssignsError(t *testing.T) { err := fmt.Errorf("original") e := NewErrorWithError(err, "packageType", "method", nil, "message") if e.Original != err { t.Fatalf("autorest: Error failed to set error -- expected %v, received %v", err, e.Original) } } func TestNewErrorWithResponse_ContainsStatusCode(t *testing.T) { e := NewErrorWithResponse("packageType", "method", &http.Response{ StatusCode: http.StatusBadRequest, Status: http.StatusText(http.StatusBadRequest)}, "message") if e.StatusCode != http.StatusBadRequest { t.Fatalf("autorest: Error failed to set status code -- expected %v, received %v", http.StatusBadRequest, e.StatusCode) } } func TestNewErrorWithResponse_nilResponse_ReportsUndefinedStatusCode(t *testing.T) { e := NewErrorWithResponse("packageType", "method", nil, "message") if e.StatusCode != UndefinedStatusCode { t.Fatalf("autorest: Error failed to set status code -- expected %v, received %v", UndefinedStatusCode, e.StatusCode) } } func TestNewErrorWithResponse_Forwards(t *testing.T) { e1 := NewError("packageType", "method", "message %s", "arg") e2 := NewErrorWithResponse("packageType", "method", nil, "message %s", "arg") if !reflect.DeepEqual(e1, e2) { t.Fatal("autorest: NewError did not return an error equivelent to NewErrorWithError") } } func TestNewErrorWithError_Forwards(t *testing.T) { e1 := NewError("packageType", "method", "message %s", "arg") e2 := NewErrorWithError(nil, "packageType", "method", nil, "message %s", "arg") if !reflect.DeepEqual(e1, e2) { t.Fatal("autorest: NewError did not return an error equivelent to NewErrorWithError") } } func TestNewErrorWithError_DoesNotWrapADetailedError(t *testing.T) { e1 := NewError("packageType1", "method1", "message1 %s", "arg1") e2 := NewErrorWithError(e1, "packageType2", "method2", nil, "message2 %s", "arg2") if !reflect.DeepEqual(e1, e2) { t.Fatalf("autorest: NewErrorWithError incorrectly wrapped a DetailedError -- expected %v, received %v", e1, e2) } } func TestNewErrorWithError_WrapsAnError(t *testing.T) { e1 := fmt.Errorf("Inner Error") var e2 interface{} = NewErrorWithError(e1, "packageType", "method", nil, "message") if _, ok := e2.(DetailedError); !ok { t.Fatalf("autorest: NewErrorWithError failed to wrap a standard error -- received %T", e2) } } func TestDetailedError(t *testing.T) { err := fmt.Errorf("original") e := NewErrorWithError(err, "packageType", "method", nil, "message") if matched, _ := regexp.MatchString(`.*original.*`, e.Error()); !matched { t.Fatalf("autorest: Error#Error failed to return original error message -- expected %v, received %v", `.*original.*`, e.Error()) } } func TestDetailedErrorConstainsPackageType(t *testing.T) { e := NewErrorWithError(fmt.Errorf("original"), "packageType", "method", nil, "message") if matched, _ := regexp.MatchString(`.*packageType.*`, e.Error()); !matched { t.Fatalf("autorest: Error#String failed to include PackageType -- expected %v, received %v", `.*packageType.*`, e.Error()) } } func TestDetailedErrorConstainsMethod(t *testing.T) { e := NewErrorWithError(fmt.Errorf("original"), "packageType", "method", nil, "message") if matched, _ := regexp.MatchString(`.*method.*`, e.Error()); !matched { t.Fatalf("autorest: Error#String failed to include Method -- expected %v, received %v", `.*method.*`, e.Error()) } } func TestDetailedErrorConstainsMessage(t *testing.T) { e := NewErrorWithError(fmt.Errorf("original"), "packageType", "method", nil, "message") if matched, _ := regexp.MatchString(`.*message.*`, e.Error()); !matched { t.Fatalf("autorest: Error#String failed to include Message -- expected %v, received %v", `.*message.*`, e.Error()) } } func TestDetailedErrorConstainsStatusCode(t *testing.T) { e := NewErrorWithError(fmt.Errorf("original"), "packageType", "method", &http.Response{ StatusCode: http.StatusBadRequest, Status: http.StatusText(http.StatusBadRequest)}, "message") if matched, _ := regexp.MatchString(`.*400.*`, e.Error()); !matched { t.Fatalf("autorest: Error#String failed to include Status Code -- expected %v, received %v", `.*400.*`, e.Error()) } } func TestDetailedErrorConstainsOriginal(t *testing.T) { e := NewErrorWithError(fmt.Errorf("original"), "packageType", "method", nil, "message") if matched, _ := regexp.MatchString(`.*original.*`, e.Error()); !matched { t.Fatalf("autorest: Error#String failed to include Original error -- expected %v, received %v", `.*original.*`, e.Error()) } } func TestDetailedErrorSkipsOriginal(t *testing.T) { e := NewError("packageType", "method", "message") if matched, _ := regexp.MatchString(`.*Original.*`, e.Error()); matched { t.Fatalf("autorest: Error#String included missing Original error -- unexpected %v, received %v", `.*Original.*`, e.Error()) } } go-autorest-8.3.1/autorest/mocks/000077500000000000000000000000001315133516500167615ustar00rootroot00000000000000go-autorest-8.3.1/autorest/mocks/helpers.go000066400000000000000000000076261315133516500207650ustar00rootroot00000000000000package mocks import ( "fmt" "net/http" "time" ) const ( // TestAuthorizationHeader is a faux HTTP Authorization header value TestAuthorizationHeader = "BEARER SECRETTOKEN" // TestBadURL is a malformed URL TestBadURL = " " // TestDelay is the Retry-After delay used in tests. TestDelay = 0 * time.Second // TestHeader is the header used in tests. TestHeader = "x-test-header" // TestURL is the URL used in tests. TestURL = "https://microsoft.com/a/b/c/" // TestAzureAsyncURL is a URL used in Azure asynchronous tests TestAzureAsyncURL = "https://microsoft.com/a/b/c/async" // TestLocationURL is a URL used in Azure asynchronous tests TestLocationURL = "https://microsoft.com/a/b/c/location" ) const ( headerLocation = "Location" headerRetryAfter = "Retry-After" ) // NewRequest instantiates a new request. func NewRequest() *http.Request { return NewRequestWithContent("") } // NewRequestWithContent instantiates a new request using the passed string for the body content. func NewRequestWithContent(c string) *http.Request { r, _ := http.NewRequest("GET", "https://microsoft.com/a/b/c/", NewBody(c)) return r } // NewRequestWithCloseBody instantiates a new request. func NewRequestWithCloseBody() *http.Request { return NewRequestWithCloseBodyContent("request body") } // NewRequestWithCloseBodyContent instantiates a new request using the passed string for the body content. func NewRequestWithCloseBodyContent(c string) *http.Request { r, _ := http.NewRequest("GET", "https://microsoft.com/a/b/c/", NewBodyClose(c)) return r } // NewRequestForURL instantiates a new request using the passed URL. func NewRequestForURL(u string) *http.Request { r, err := http.NewRequest("GET", u, NewBody("")) if err != nil { panic(fmt.Sprintf("mocks: ERROR (%v) parsing testing URL %s", err, u)) } return r } // NewResponse instantiates a new response. func NewResponse() *http.Response { return NewResponseWithContent("") } // NewResponseWithContent instantiates a new response with the passed string as the body content. func NewResponseWithContent(c string) *http.Response { return &http.Response{ Status: "200 OK", StatusCode: 200, Proto: "HTTP/1.0", ProtoMajor: 1, ProtoMinor: 0, Body: NewBody(c), Request: NewRequest(), } } // NewResponseWithStatus instantiates a new response using the passed string and integer as the // status and status code. func NewResponseWithStatus(s string, c int) *http.Response { resp := NewResponse() resp.Status = s resp.StatusCode = c return resp } // NewResponseWithBodyAndStatus instantiates a new response using the specified mock body, // status and status code func NewResponseWithBodyAndStatus(body *Body, c int, s string) *http.Response { resp := NewResponse() resp.Body = body resp.Status = s resp.StatusCode = c return resp } // SetResponseHeader adds a header to the passed response. func SetResponseHeader(resp *http.Response, h string, v string) { if resp.Header == nil { resp.Header = make(http.Header) } resp.Header.Set(h, v) } // SetResponseHeaderValues adds a header containing all the passed string values. func SetResponseHeaderValues(resp *http.Response, h string, values []string) { if resp.Header == nil { resp.Header = make(http.Header) } for _, v := range values { resp.Header.Add(h, v) } } // SetAcceptedHeaders adds the headers usually associated with a 202 Accepted response. func SetAcceptedHeaders(resp *http.Response) { SetLocationHeader(resp, TestURL) SetRetryHeader(resp, TestDelay) } // SetLocationHeader adds the Location header. func SetLocationHeader(resp *http.Response, location string) { SetResponseHeader(resp, http.CanonicalHeaderKey(headerLocation), location) } // SetRetryHeader adds the Retry-After header. func SetRetryHeader(resp *http.Response, delay time.Duration) { SetResponseHeader(resp, http.CanonicalHeaderKey(headerRetryAfter), fmt.Sprintf("%v", delay.Seconds())) } go-autorest-8.3.1/autorest/mocks/helpers_test.go000066400000000000000000000000161315133516500220060ustar00rootroot00000000000000package mocks go-autorest-8.3.1/autorest/mocks/mocks.go000066400000000000000000000072011315133516500204240ustar00rootroot00000000000000/* Package mocks provides mocks and helpers used in testing. */ package mocks import ( "fmt" "io" "net/http" ) // Body implements acceptable body over a string. type Body struct { s string b []byte isOpen bool closeAttempts int } // NewBody creates a new instance of Body. func NewBody(s string) *Body { return (&Body{s: s}).reset() } // NewBodyClose creates a new instance of Body. func NewBodyClose(s string) *Body { return &Body{s: s} } // Read reads into the passed byte slice and returns the bytes read. func (body *Body) Read(b []byte) (n int, err error) { if !body.IsOpen() { return 0, fmt.Errorf("ERROR: Body has been closed") } if len(body.b) == 0 { return 0, io.EOF } n = copy(b, body.b) body.b = body.b[n:] return n, nil } // Close closes the body. func (body *Body) Close() error { if body.isOpen { body.isOpen = false body.closeAttempts++ } return nil } // CloseAttempts returns the number of times Close was called. func (body *Body) CloseAttempts() int { return body.closeAttempts } // IsOpen returns true if the Body has not been closed, false otherwise. func (body *Body) IsOpen() bool { return body.isOpen } func (body *Body) reset() *Body { body.isOpen = true body.b = []byte(body.s) return body } // Sender implements a simple null sender. type Sender struct { attempts int responses []*http.Response repeatResponse []int err error repeatError int emitErrorAfter int } // NewSender creates a new instance of Sender. func NewSender() *Sender { return &Sender{} } // Do accepts the passed request and, based on settings, emits a response and possible error. func (c *Sender) Do(r *http.Request) (resp *http.Response, err error) { c.attempts++ if len(c.responses) > 0 { resp = c.responses[0] if resp != nil { if b, ok := resp.Body.(*Body); ok { b.reset() } } c.repeatResponse[0]-- if c.repeatResponse[0] == 0 { c.responses = c.responses[1:] c.repeatResponse = c.repeatResponse[1:] } } else { resp = NewResponse() } if resp != nil { resp.Request = r } if c.emitErrorAfter > 0 { c.emitErrorAfter-- } else if c.err != nil { err = c.err c.repeatError-- if c.repeatError == 0 { c.err = nil } } return } // AppendResponse adds the passed http.Response to the response stack. func (c *Sender) AppendResponse(resp *http.Response) { c.AppendAndRepeatResponse(resp, 1) } // AppendAndRepeatResponse adds the passed http.Response to the response stack along with a // repeat count. A negative repeat count will return the response for all remaining calls to Do. func (c *Sender) AppendAndRepeatResponse(resp *http.Response, repeat int) { if c.responses == nil { c.responses = []*http.Response{resp} c.repeatResponse = []int{repeat} } else { c.responses = append(c.responses, resp) c.repeatResponse = append(c.repeatResponse, repeat) } } // Attempts returns the number of times Do was called. func (c *Sender) Attempts() int { return c.attempts } // SetError sets the error Do should return. func (c *Sender) SetError(err error) { c.SetAndRepeatError(err, 1) } // SetAndRepeatError sets the error Do should return and how many calls to Do will return the error. // A negative repeat value will return the error for all remaining calls to Do. func (c *Sender) SetAndRepeatError(err error, repeat int) { c.err = err c.repeatError = repeat } // SetEmitErrorAfter sets the number of attempts to be made before errors are emitted. func (c *Sender) SetEmitErrorAfter(ea int) { c.emitErrorAfter = ea } // T is a simple testing struct. type T struct { Name string `json:"name" xml:"Name"` Age int `json:"age" xml:"Age"` } go-autorest-8.3.1/autorest/mocks/mocks_test.go000066400000000000000000000000161315133516500214600ustar00rootroot00000000000000package mocks go-autorest-8.3.1/autorest/preparer.go000066400000000000000000000342671315133516500200300ustar00rootroot00000000000000package autorest import ( "bytes" "encoding/json" "fmt" "io" "io/ioutil" "mime/multipart" "net/http" "net/url" "strings" ) const ( mimeTypeJSON = "application/json" mimeTypeFormPost = "application/x-www-form-urlencoded" headerAuthorization = "Authorization" headerContentType = "Content-Type" headerUserAgent = "User-Agent" ) // Preparer is the interface that wraps the Prepare method. // // Prepare accepts and possibly modifies an http.Request (e.g., adding Headers). Implementations // must ensure to not share or hold per-invocation state since Preparers may be shared and re-used. type Preparer interface { Prepare(*http.Request) (*http.Request, error) } // PreparerFunc is a method that implements the Preparer interface. type PreparerFunc func(*http.Request) (*http.Request, error) // Prepare implements the Preparer interface on PreparerFunc. func (pf PreparerFunc) Prepare(r *http.Request) (*http.Request, error) { return pf(r) } // PrepareDecorator takes and possibly decorates, by wrapping, a Preparer. Decorators may affect the // http.Request and pass it along or, first, pass the http.Request along then affect the result. type PrepareDecorator func(Preparer) Preparer // CreatePreparer creates, decorates, and returns a Preparer. // Without decorators, the returned Preparer returns the passed http.Request unmodified. // Preparers are safe to share and re-use. func CreatePreparer(decorators ...PrepareDecorator) Preparer { return DecoratePreparer( Preparer(PreparerFunc(func(r *http.Request) (*http.Request, error) { return r, nil })), decorators...) } // DecoratePreparer accepts a Preparer and a, possibly empty, set of PrepareDecorators, which it // applies to the Preparer. Decorators are applied in the order received, but their affect upon the // request depends on whether they are a pre-decorator (change the http.Request and then pass it // along) or a post-decorator (pass the http.Request along and alter it on return). func DecoratePreparer(p Preparer, decorators ...PrepareDecorator) Preparer { for _, decorate := range decorators { p = decorate(p) } return p } // Prepare accepts an http.Request and a, possibly empty, set of PrepareDecorators. // It creates a Preparer from the decorators which it then applies to the passed http.Request. func Prepare(r *http.Request, decorators ...PrepareDecorator) (*http.Request, error) { if r == nil { return nil, NewError("autorest", "Prepare", "Invoked without an http.Request") } return CreatePreparer(decorators...).Prepare(r) } // WithNothing returns a "do nothing" PrepareDecorator that makes no changes to the passed // http.Request. func WithNothing() PrepareDecorator { return func(p Preparer) Preparer { return PreparerFunc(func(r *http.Request) (*http.Request, error) { return p.Prepare(r) }) } } // WithHeader returns a PrepareDecorator that sets the specified HTTP header of the http.Request to // the passed value. It canonicalizes the passed header name (via http.CanonicalHeaderKey) before // adding the header. func WithHeader(header string, value string) PrepareDecorator { return func(p Preparer) Preparer { return PreparerFunc(func(r *http.Request) (*http.Request, error) { r, err := p.Prepare(r) if err == nil { if r.Header == nil { r.Header = make(http.Header) } r.Header.Set(http.CanonicalHeaderKey(header), value) } return r, err }) } } // WithBearerAuthorization returns a PrepareDecorator that adds an HTTP Authorization header whose // value is "Bearer " followed by the supplied token. func WithBearerAuthorization(token string) PrepareDecorator { return WithHeader(headerAuthorization, fmt.Sprintf("Bearer %s", token)) } // AsContentType returns a PrepareDecorator that adds an HTTP Content-Type header whose value // is the passed contentType. func AsContentType(contentType string) PrepareDecorator { return WithHeader(headerContentType, contentType) } // WithUserAgent returns a PrepareDecorator that adds an HTTP User-Agent header whose value is the // passed string. func WithUserAgent(ua string) PrepareDecorator { return WithHeader(headerUserAgent, ua) } // AsFormURLEncoded returns a PrepareDecorator that adds an HTTP Content-Type header whose value is // "application/x-www-form-urlencoded". func AsFormURLEncoded() PrepareDecorator { return AsContentType(mimeTypeFormPost) } // AsJSON returns a PrepareDecorator that adds an HTTP Content-Type header whose value is // "application/json". func AsJSON() PrepareDecorator { return AsContentType(mimeTypeJSON) } // WithMethod returns a PrepareDecorator that sets the HTTP method of the passed request. The // decorator does not validate that the passed method string is a known HTTP method. func WithMethod(method string) PrepareDecorator { return func(p Preparer) Preparer { return PreparerFunc(func(r *http.Request) (*http.Request, error) { r.Method = method return p.Prepare(r) }) } } // AsDelete returns a PrepareDecorator that sets the HTTP method to DELETE. func AsDelete() PrepareDecorator { return WithMethod("DELETE") } // AsGet returns a PrepareDecorator that sets the HTTP method to GET. func AsGet() PrepareDecorator { return WithMethod("GET") } // AsHead returns a PrepareDecorator that sets the HTTP method to HEAD. func AsHead() PrepareDecorator { return WithMethod("HEAD") } // AsOptions returns a PrepareDecorator that sets the HTTP method to OPTIONS. func AsOptions() PrepareDecorator { return WithMethod("OPTIONS") } // AsPatch returns a PrepareDecorator that sets the HTTP method to PATCH. func AsPatch() PrepareDecorator { return WithMethod("PATCH") } // AsPost returns a PrepareDecorator that sets the HTTP method to POST. func AsPost() PrepareDecorator { return WithMethod("POST") } // AsPut returns a PrepareDecorator that sets the HTTP method to PUT. func AsPut() PrepareDecorator { return WithMethod("PUT") } // WithBaseURL returns a PrepareDecorator that populates the http.Request with a url.URL constructed // from the supplied baseUrl. func WithBaseURL(baseURL string) PrepareDecorator { return func(p Preparer) Preparer { return PreparerFunc(func(r *http.Request) (*http.Request, error) { r, err := p.Prepare(r) if err == nil { var u *url.URL if u, err = url.Parse(baseURL); err != nil { return r, err } if u.Scheme == "" { err = fmt.Errorf("autorest: No scheme detected in URL %s", baseURL) } if err == nil { r.URL = u } } return r, err }) } } // WithCustomBaseURL returns a PrepareDecorator that replaces brace-enclosed keys within the // request base URL (i.e., http.Request.URL) with the corresponding values from the passed map. func WithCustomBaseURL(baseURL string, urlParameters map[string]interface{}) PrepareDecorator { parameters := ensureValueStrings(urlParameters) for key, value := range parameters { baseURL = strings.Replace(baseURL, "{"+key+"}", value, -1) } return WithBaseURL(baseURL) } // WithFormData returns a PrepareDecoratore that "URL encodes" (e.g., bar=baz&foo=quux) into the // http.Request body. func WithFormData(v url.Values) PrepareDecorator { return func(p Preparer) Preparer { return PreparerFunc(func(r *http.Request) (*http.Request, error) { r, err := p.Prepare(r) if err == nil { s := v.Encode() r.ContentLength = int64(len(s)) r.Body = ioutil.NopCloser(strings.NewReader(s)) } return r, err }) } } // WithMultiPartFormData returns a PrepareDecoratore that "URL encodes" (e.g., bar=baz&foo=quux) form parameters // into the http.Request body. func WithMultiPartFormData(formDataParameters map[string]interface{}) PrepareDecorator { return func(p Preparer) Preparer { return PreparerFunc(func(r *http.Request) (*http.Request, error) { r, err := p.Prepare(r) if err == nil { var body bytes.Buffer writer := multipart.NewWriter(&body) for key, value := range formDataParameters { if rc, ok := value.(io.ReadCloser); ok { var fd io.Writer if fd, err = writer.CreateFormFile(key, key); err != nil { return r, err } if _, err = io.Copy(fd, rc); err != nil { return r, err } } else { if err = writer.WriteField(key, ensureValueString(value)); err != nil { return r, err } } } if err = writer.Close(); err != nil { return r, err } if r.Header == nil { r.Header = make(http.Header) } r.Header.Set(http.CanonicalHeaderKey(headerContentType), writer.FormDataContentType()) r.Body = ioutil.NopCloser(bytes.NewReader(body.Bytes())) r.ContentLength = int64(body.Len()) return r, err } return r, err }) } } // WithFile returns a PrepareDecorator that sends file in request body. func WithFile(f io.ReadCloser) PrepareDecorator { return func(p Preparer) Preparer { return PreparerFunc(func(r *http.Request) (*http.Request, error) { r, err := p.Prepare(r) if err == nil { b, err := ioutil.ReadAll(f) if err != nil { return r, err } r.Body = ioutil.NopCloser(bytes.NewReader(b)) r.ContentLength = int64(len(b)) } return r, err }) } } // WithBool returns a PrepareDecorator that encodes the passed bool into the body of the request // and sets the Content-Length header. func WithBool(v bool) PrepareDecorator { return WithString(fmt.Sprintf("%v", v)) } // WithFloat32 returns a PrepareDecorator that encodes the passed float32 into the body of the // request and sets the Content-Length header. func WithFloat32(v float32) PrepareDecorator { return WithString(fmt.Sprintf("%v", v)) } // WithFloat64 returns a PrepareDecorator that encodes the passed float64 into the body of the // request and sets the Content-Length header. func WithFloat64(v float64) PrepareDecorator { return WithString(fmt.Sprintf("%v", v)) } // WithInt32 returns a PrepareDecorator that encodes the passed int32 into the body of the request // and sets the Content-Length header. func WithInt32(v int32) PrepareDecorator { return WithString(fmt.Sprintf("%v", v)) } // WithInt64 returns a PrepareDecorator that encodes the passed int64 into the body of the request // and sets the Content-Length header. func WithInt64(v int64) PrepareDecorator { return WithString(fmt.Sprintf("%v", v)) } // WithString returns a PrepareDecorator that encodes the passed string into the body of the request // and sets the Content-Length header. func WithString(v string) PrepareDecorator { return func(p Preparer) Preparer { return PreparerFunc(func(r *http.Request) (*http.Request, error) { r, err := p.Prepare(r) if err == nil { r.ContentLength = int64(len(v)) r.Body = ioutil.NopCloser(strings.NewReader(v)) } return r, err }) } } // WithJSON returns a PrepareDecorator that encodes the data passed as JSON into the body of the // request and sets the Content-Length header. func WithJSON(v interface{}) PrepareDecorator { return func(p Preparer) Preparer { return PreparerFunc(func(r *http.Request) (*http.Request, error) { r, err := p.Prepare(r) if err == nil { b, err := json.Marshal(v) if err == nil { r.ContentLength = int64(len(b)) r.Body = ioutil.NopCloser(bytes.NewReader(b)) } } return r, err }) } } // WithPath returns a PrepareDecorator that adds the supplied path to the request URL. If the path // is absolute (that is, it begins with a "/"), it replaces the existing path. func WithPath(path string) PrepareDecorator { return func(p Preparer) Preparer { return PreparerFunc(func(r *http.Request) (*http.Request, error) { r, err := p.Prepare(r) if err == nil { if r.URL == nil { return r, NewError("autorest", "WithPath", "Invoked with a nil URL") } if r.URL, err = parseURL(r.URL, path); err != nil { return r, err } } return r, err }) } } // WithEscapedPathParameters returns a PrepareDecorator that replaces brace-enclosed keys within the // request path (i.e., http.Request.URL.Path) with the corresponding values from the passed map. The // values will be escaped (aka URL encoded) before insertion into the path. func WithEscapedPathParameters(path string, pathParameters map[string]interface{}) PrepareDecorator { parameters := escapeValueStrings(ensureValueStrings(pathParameters)) return func(p Preparer) Preparer { return PreparerFunc(func(r *http.Request) (*http.Request, error) { r, err := p.Prepare(r) if err == nil { if r.URL == nil { return r, NewError("autorest", "WithEscapedPathParameters", "Invoked with a nil URL") } for key, value := range parameters { path = strings.Replace(path, "{"+key+"}", value, -1) } if r.URL, err = parseURL(r.URL, path); err != nil { return r, err } } return r, err }) } } // WithPathParameters returns a PrepareDecorator that replaces brace-enclosed keys within the // request path (i.e., http.Request.URL.Path) with the corresponding values from the passed map. func WithPathParameters(path string, pathParameters map[string]interface{}) PrepareDecorator { parameters := ensureValueStrings(pathParameters) return func(p Preparer) Preparer { return PreparerFunc(func(r *http.Request) (*http.Request, error) { r, err := p.Prepare(r) if err == nil { if r.URL == nil { return r, NewError("autorest", "WithPathParameters", "Invoked with a nil URL") } for key, value := range parameters { path = strings.Replace(path, "{"+key+"}", value, -1) } if r.URL, err = parseURL(r.URL, path); err != nil { return r, err } } return r, err }) } } func parseURL(u *url.URL, path string) (*url.URL, error) { p := strings.TrimRight(u.String(), "/") if !strings.HasPrefix(path, "/") { path = "/" + path } return url.Parse(p + path) } // WithQueryParameters returns a PrepareDecorators that encodes and applies the query parameters // given in the supplied map (i.e., key=value). func WithQueryParameters(queryParameters map[string]interface{}) PrepareDecorator { parameters := ensureValueStrings(queryParameters) return func(p Preparer) Preparer { return PreparerFunc(func(r *http.Request) (*http.Request, error) { r, err := p.Prepare(r) if err == nil { if r.URL == nil { return r, NewError("autorest", "WithQueryParameters", "Invoked with a nil URL") } v := r.URL.Query() for key, value := range parameters { v.Add(key, value) } r.URL.RawQuery = createQuery(v) } return r, err }) } } go-autorest-8.3.1/autorest/preparer_test.go000066400000000000000000000510541315133516500210600ustar00rootroot00000000000000package autorest import ( "fmt" "io/ioutil" "net/http" "net/url" "reflect" "strconv" "strings" "testing" "github.com/Azure/go-autorest/autorest/mocks" ) // PrepareDecorators wrap and invoke a Preparer. Most often, the decorator invokes the passed // Preparer and decorates the response. func ExamplePrepareDecorator() { path := "a/b/c/" pd := func() PrepareDecorator { return func(p Preparer) Preparer { return PreparerFunc(func(r *http.Request) (*http.Request, error) { r, err := p.Prepare(r) if err == nil { if r.URL == nil { return r, fmt.Errorf("ERROR: URL is not set") } r.URL.Path += path } return r, err }) } } r, _ := Prepare(&http.Request{}, WithBaseURL("https://microsoft.com/"), pd()) fmt.Printf("Path is %s\n", r.URL) // Output: Path is https://microsoft.com/a/b/c/ } // PrepareDecorators may also modify and then invoke the Preparer. func ExamplePrepareDecorator_pre() { pd := func() PrepareDecorator { return func(p Preparer) Preparer { return PreparerFunc(func(r *http.Request) (*http.Request, error) { r.Header.Add(http.CanonicalHeaderKey("ContentType"), "application/json") return p.Prepare(r) }) } } r, _ := Prepare(&http.Request{Header: http.Header{}}, pd()) fmt.Printf("ContentType is %s\n", r.Header.Get("ContentType")) // Output: ContentType is application/json } // Create a sequence of three Preparers that build up the URL path. func ExampleCreatePreparer() { p := CreatePreparer( WithBaseURL("https://microsoft.com/"), WithPath("a"), WithPath("b"), WithPath("c")) r, err := p.Prepare(&http.Request{}) if err != nil { fmt.Printf("ERROR: %v\n", err) } else { fmt.Println(r.URL) } // Output: https://microsoft.com/a/b/c } // Create and apply separate Preparers func ExampleCreatePreparer_multiple() { params := map[string]interface{}{ "param1": "a", "param2": "c", } p1 := CreatePreparer(WithBaseURL("https://microsoft.com/")) p2 := CreatePreparer(WithPathParameters("/{param1}/b/{param2}/", params)) r, err := p1.Prepare(&http.Request{}) if err != nil { fmt.Printf("ERROR: %v\n", err) } r, err = p2.Prepare(r) if err != nil { fmt.Printf("ERROR: %v\n", err) } else { fmt.Println(r.URL) } // Output: https://microsoft.com/a/b/c/ } // Create and chain separate Preparers func ExampleCreatePreparer_chain() { params := map[string]interface{}{ "param1": "a", "param2": "c", } p := CreatePreparer(WithBaseURL("https://microsoft.com/")) p = DecoratePreparer(p, WithPathParameters("/{param1}/b/{param2}/", params)) r, err := p.Prepare(&http.Request{}) if err != nil { fmt.Printf("ERROR: %v\n", err) } else { fmt.Println(r.URL) } // Output: https://microsoft.com/a/b/c/ } // Create and prepare an http.Request in one call func ExamplePrepare() { r, err := Prepare(&http.Request{}, AsGet(), WithBaseURL("https://microsoft.com/"), WithPath("a/b/c/")) if err != nil { fmt.Printf("ERROR: %v\n", err) } else { fmt.Printf("%s %s", r.Method, r.URL) } // Output: GET https://microsoft.com/a/b/c/ } // Create a request for a supplied base URL and path func ExampleWithBaseURL() { r, err := Prepare(&http.Request{}, WithBaseURL("https://microsoft.com/a/b/c/")) if err != nil { fmt.Printf("ERROR: %v\n", err) } else { fmt.Println(r.URL) } // Output: https://microsoft.com/a/b/c/ } func ExampleWithBaseURL_second() { _, err := Prepare(&http.Request{}, WithBaseURL(":")) fmt.Println(err) // Output: parse :: missing protocol scheme } func ExampleWithCustomBaseURL() { r, err := Prepare(&http.Request{}, WithCustomBaseURL("https://{account}.{service}.core.windows.net/", map[string]interface{}{ "account": "myaccount", "service": "blob", })) if err != nil { fmt.Printf("ERROR: %v\n", err) } else { fmt.Println(r.URL) } // Output: https://myaccount.blob.core.windows.net/ } func ExampleWithCustomBaseURL_second() { _, err := Prepare(&http.Request{}, WithCustomBaseURL(":", map[string]interface{}{})) fmt.Println(err) // Output: parse :: missing protocol scheme } // Create a request with a custom HTTP header func ExampleWithHeader() { r, err := Prepare(&http.Request{}, WithBaseURL("https://microsoft.com/a/b/c/"), WithHeader("x-foo", "bar")) if err != nil { fmt.Printf("ERROR: %v\n", err) } else { fmt.Printf("Header %s=%s\n", "x-foo", r.Header.Get("x-foo")) } // Output: Header x-foo=bar } // Create a request whose Body is the JSON encoding of a structure func ExampleWithFormData() { v := url.Values{} v.Add("name", "Rob Pike") v.Add("age", "42") r, err := Prepare(&http.Request{}, WithFormData(v)) if err != nil { fmt.Printf("ERROR: %v\n", err) } b, err := ioutil.ReadAll(r.Body) if err != nil { fmt.Printf("ERROR: %v\n", err) } else { fmt.Printf("Request Body contains %s\n", string(b)) } // Output: Request Body contains age=42&name=Rob+Pike } // Create a request whose Body is the JSON encoding of a structure func ExampleWithJSON() { t := mocks.T{Name: "Rob Pike", Age: 42} r, err := Prepare(&http.Request{}, WithJSON(&t)) if err != nil { fmt.Printf("ERROR: %v\n", err) } b, err := ioutil.ReadAll(r.Body) if err != nil { fmt.Printf("ERROR: %v\n", err) } else { fmt.Printf("Request Body contains %s\n", string(b)) } // Output: Request Body contains {"name":"Rob Pike","age":42} } // Create a request from a path with escaped parameters func ExampleWithEscapedPathParameters() { params := map[string]interface{}{ "param1": "a b c", "param2": "d e f", } r, err := Prepare(&http.Request{}, WithBaseURL("https://microsoft.com/"), WithEscapedPathParameters("/{param1}/b/{param2}/", params)) if err != nil { fmt.Printf("ERROR: %v\n", err) } else { fmt.Println(r.URL) } // Output: https://microsoft.com/a+b+c/b/d+e+f/ } // Create a request from a path with parameters func ExampleWithPathParameters() { params := map[string]interface{}{ "param1": "a", "param2": "c", } r, err := Prepare(&http.Request{}, WithBaseURL("https://microsoft.com/"), WithPathParameters("/{param1}/b/{param2}/", params)) if err != nil { fmt.Printf("ERROR: %v\n", err) } else { fmt.Println(r.URL) } // Output: https://microsoft.com/a/b/c/ } // Create a request with query parameters func ExampleWithQueryParameters() { params := map[string]interface{}{ "q1": "value1", "q2": "value2", } r, err := Prepare(&http.Request{}, WithBaseURL("https://microsoft.com/"), WithPath("/a/b/c/"), WithQueryParameters(params)) if err != nil { fmt.Printf("ERROR: %v\n", err) } else { fmt.Println(r.URL) } // Output: https://microsoft.com/a/b/c/?q1=value1&q2=value2 } func TestWithCustomBaseURL(t *testing.T) { r, err := Prepare(&http.Request{}, WithCustomBaseURL("https://{account}.{service}.core.windows.net/", map[string]interface{}{ "account": "myaccount", "service": "blob", })) if err != nil { t.Fatalf("autorest: WithCustomBaseURL should not fail") } if r.URL.String() != "https://myaccount.blob.core.windows.net/" { t.Fatalf("autorest: WithCustomBaseURL expected https://myaccount.blob.core.windows.net/, got %s", r.URL) } } func TestWithCustomBaseURLwithInvalidURL(t *testing.T) { _, err := Prepare(&http.Request{}, WithCustomBaseURL("hello/{account}.{service}.core.windows.net/", map[string]interface{}{ "account": "myaccount", "service": "blob", })) if err == nil { t.Fatalf("autorest: WithCustomBaseURL should fail fo URL parse error") } } func TestWithPathWithInvalidPath(t *testing.T) { p := "path%2*end" if _, err := Prepare(&http.Request{}, WithBaseURL("https://microsoft.com/"), WithPath(p)); err == nil { t.Fatalf("autorest: WithPath should fail for invalid URL escape error for path '%v' ", p) } } func TestWithPathParametersWithInvalidPath(t *testing.T) { p := "path%2*end" m := map[string]interface{}{ "path1": p, } if _, err := Prepare(&http.Request{}, WithBaseURL("https://microsoft.com/"), WithPathParameters("/{path1}/", m)); err == nil { t.Fatalf("autorest: WithPath should fail for invalid URL escape for path '%v' ", p) } } func TestCreatePreparerDoesNotModify(t *testing.T) { r1 := &http.Request{} p := CreatePreparer() r2, err := p.Prepare(r1) if err != nil { t.Fatalf("autorest: CreatePreparer failed (%v)", err) } if !reflect.DeepEqual(r1, r2) { t.Fatalf("autorest: CreatePreparer without decorators modified the request") } } func TestCreatePreparerRunsDecoratorsInOrder(t *testing.T) { p := CreatePreparer(WithBaseURL("https://microsoft.com/"), WithPath("1"), WithPath("2"), WithPath("3")) r, err := p.Prepare(&http.Request{}) if err != nil { t.Fatalf("autorest: CreatePreparer failed (%v)", err) } if r.URL.String() != "https:/1/2/3" && r.URL.Host != "microsoft.com" { t.Fatalf("autorest: CreatePreparer failed to run decorators in order") } } func TestAsContentType(t *testing.T) { r, err := Prepare(mocks.NewRequest(), AsContentType("application/text")) if err != nil { fmt.Printf("ERROR: %v", err) } if r.Header.Get(headerContentType) != "application/text" { t.Fatalf("autorest: AsContentType failed to add header (%s=%s)", headerContentType, r.Header.Get(headerContentType)) } } func TestAsFormURLEncoded(t *testing.T) { r, err := Prepare(mocks.NewRequest(), AsFormURLEncoded()) if err != nil { fmt.Printf("ERROR: %v", err) } if r.Header.Get(headerContentType) != mimeTypeFormPost { t.Fatalf("autorest: AsFormURLEncoded failed to add header (%s=%s)", headerContentType, r.Header.Get(headerContentType)) } } func TestAsJSON(t *testing.T) { r, err := Prepare(mocks.NewRequest(), AsJSON()) if err != nil { fmt.Printf("ERROR: %v", err) } if r.Header.Get(headerContentType) != mimeTypeJSON { t.Fatalf("autorest: AsJSON failed to add header (%s=%s)", headerContentType, r.Header.Get(headerContentType)) } } func TestWithNothing(t *testing.T) { r1 := mocks.NewRequest() r2, err := Prepare(r1, WithNothing()) if err != nil { t.Fatalf("autorest: WithNothing returned an unexpected error (%v)", err) } if !reflect.DeepEqual(r1, r2) { t.Fatal("azure: WithNothing modified the passed HTTP Request") } } func TestWithBearerAuthorization(t *testing.T) { r, err := Prepare(mocks.NewRequest(), WithBearerAuthorization("SOME-TOKEN")) if err != nil { fmt.Printf("ERROR: %v", err) } if r.Header.Get(headerAuthorization) != "Bearer SOME-TOKEN" { t.Fatalf("autorest: WithBearerAuthorization failed to add header (%s=%s)", headerAuthorization, r.Header.Get(headerAuthorization)) } } func TestWithUserAgent(t *testing.T) { ua := "User Agent Go" r, err := Prepare(mocks.NewRequest(), WithUserAgent(ua)) if err != nil { fmt.Printf("ERROR: %v", err) } if r.UserAgent() != ua || r.Header.Get(headerUserAgent) != ua { t.Fatalf("autorest: WithUserAgent failed to add header (%s=%s)", headerUserAgent, r.Header.Get(headerUserAgent)) } } func TestWithMethod(t *testing.T) { r, _ := Prepare(mocks.NewRequest(), WithMethod("HEAD")) if r.Method != "HEAD" { t.Fatal("autorest: WithMethod failed to set HTTP method header") } } func TestAsDelete(t *testing.T) { r, _ := Prepare(mocks.NewRequest(), AsDelete()) if r.Method != "DELETE" { t.Fatal("autorest: AsDelete failed to set HTTP method header to DELETE") } } func TestAsGet(t *testing.T) { r, _ := Prepare(mocks.NewRequest(), AsGet()) if r.Method != "GET" { t.Fatal("autorest: AsGet failed to set HTTP method header to GET") } } func TestAsHead(t *testing.T) { r, _ := Prepare(mocks.NewRequest(), AsHead()) if r.Method != "HEAD" { t.Fatal("autorest: AsHead failed to set HTTP method header to HEAD") } } func TestAsOptions(t *testing.T) { r, _ := Prepare(mocks.NewRequest(), AsOptions()) if r.Method != "OPTIONS" { t.Fatal("autorest: AsOptions failed to set HTTP method header to OPTIONS") } } func TestAsPatch(t *testing.T) { r, _ := Prepare(mocks.NewRequest(), AsPatch()) if r.Method != "PATCH" { t.Fatal("autorest: AsPatch failed to set HTTP method header to PATCH") } } func TestAsPost(t *testing.T) { r, _ := Prepare(mocks.NewRequest(), AsPost()) if r.Method != "POST" { t.Fatal("autorest: AsPost failed to set HTTP method header to POST") } } func TestAsPut(t *testing.T) { r, _ := Prepare(mocks.NewRequest(), AsPut()) if r.Method != "PUT" { t.Fatal("autorest: AsPut failed to set HTTP method header to PUT") } } func TestPrepareWithNullRequest(t *testing.T) { _, err := Prepare(nil) if err == nil { t.Fatal("autorest: Prepare failed to return an error when given a null http.Request") } } func TestWithFormDataSetsContentLength(t *testing.T) { v := url.Values{} v.Add("name", "Rob Pike") v.Add("age", "42") r, err := Prepare(&http.Request{}, WithFormData(v)) if err != nil { t.Fatalf("autorest: WithFormData failed with error (%v)", err) } b, err := ioutil.ReadAll(r.Body) if err != nil { t.Fatalf("autorest: WithFormData failed with error (%v)", err) } expected := "name=Rob+Pike&age=42" if !(string(b) == "name=Rob+Pike&age=42" || string(b) == "age=42&name=Rob+Pike") { t.Fatalf("autorest:WithFormData failed to return correct string got (%v), expected (%v)", string(b), expected) } if r.ContentLength != int64(len(b)) { t.Fatalf("autorest:WithFormData set Content-Length to %v, expected %v", r.ContentLength, len(b)) } } func TestWithMultiPartFormDataSetsContentLength(t *testing.T) { v := map[string]interface{}{ "file": ioutil.NopCloser(strings.NewReader("Hello Gopher")), "age": "42", } r, err := Prepare(&http.Request{}, WithMultiPartFormData(v)) if err != nil { t.Fatalf("autorest: WithMultiPartFormData failed with error (%v)", err) } b, err := ioutil.ReadAll(r.Body) if err != nil { t.Fatalf("autorest: WithMultiPartFormData failed with error (%v)", err) } if r.ContentLength != int64(len(b)) { t.Fatalf("autorest:WithMultiPartFormData set Content-Length to %v, expected %v", r.ContentLength, len(b)) } } func TestWithMultiPartFormDataWithNoFile(t *testing.T) { v := map[string]interface{}{ "file": "no file", "age": "42", } r, err := Prepare(&http.Request{}, WithMultiPartFormData(v)) if err != nil { t.Fatalf("autorest: WithMultiPartFormData failed with error (%v)", err) } b, err := ioutil.ReadAll(r.Body) if err != nil { t.Fatalf("autorest: WithMultiPartFormData failed with error (%v)", err) } if r.ContentLength != int64(len(b)) { t.Fatalf("autorest:WithMultiPartFormData set Content-Length to %v, expected %v", r.ContentLength, len(b)) } } func TestWithFile(t *testing.T) { r, err := Prepare(&http.Request{}, WithFile(ioutil.NopCloser(strings.NewReader("Hello Gopher")))) if err != nil { t.Fatalf("autorest: WithFile failed with error (%v)", err) } b, err := ioutil.ReadAll(r.Body) if err != nil { t.Fatalf("autorest: WithFile failed with error (%v)", err) } if r.ContentLength != int64(len(b)) { t.Fatalf("autorest:WithFile set Content-Length to %v, expected %v", r.ContentLength, len(b)) } } func TestWithBool_SetsTheBody(t *testing.T) { r, err := Prepare(&http.Request{}, WithBool(false)) if err != nil { t.Fatalf("autorest: WithBool failed with error (%v)", err) } s, err := ioutil.ReadAll(r.Body) if err != nil { t.Fatalf("autorest: WithBool failed with error (%v)", err) } if r.ContentLength != int64(len(fmt.Sprintf("%v", false))) { t.Fatalf("autorest: WithBool set Content-Length to %v, expected %v", r.ContentLength, int64(len(fmt.Sprintf("%v", false)))) } v, err := strconv.ParseBool(string(s)) if err != nil || v { t.Fatalf("autorest: WithBool incorrectly encoded the boolean as %v", s) } } func TestWithFloat32_SetsTheBody(t *testing.T) { r, err := Prepare(&http.Request{}, WithFloat32(42.0)) if err != nil { t.Fatalf("autorest: WithFloat32 failed with error (%v)", err) } s, err := ioutil.ReadAll(r.Body) if err != nil { t.Fatalf("autorest: WithFloat32 failed with error (%v)", err) } if r.ContentLength != int64(len(fmt.Sprintf("%v", 42.0))) { t.Fatalf("autorest: WithFloat32 set Content-Length to %v, expected %v", r.ContentLength, int64(len(fmt.Sprintf("%v", 42.0)))) } v, err := strconv.ParseFloat(string(s), 32) if err != nil || float32(v) != float32(42.0) { t.Fatalf("autorest: WithFloat32 incorrectly encoded the boolean as %v", s) } } func TestWithFloat64_SetsTheBody(t *testing.T) { r, err := Prepare(&http.Request{}, WithFloat64(42.0)) if err != nil { t.Fatalf("autorest: WithFloat64 failed with error (%v)", err) } s, err := ioutil.ReadAll(r.Body) if err != nil { t.Fatalf("autorest: WithFloat64 failed with error (%v)", err) } if r.ContentLength != int64(len(fmt.Sprintf("%v", 42.0))) { t.Fatalf("autorest: WithFloat64 set Content-Length to %v, expected %v", r.ContentLength, int64(len(fmt.Sprintf("%v", 42.0)))) } v, err := strconv.ParseFloat(string(s), 64) if err != nil || v != float64(42.0) { t.Fatalf("autorest: WithFloat64 incorrectly encoded the boolean as %v", s) } } func TestWithInt32_SetsTheBody(t *testing.T) { r, err := Prepare(&http.Request{}, WithInt32(42)) if err != nil { t.Fatalf("autorest: WithInt32 failed with error (%v)", err) } s, err := ioutil.ReadAll(r.Body) if err != nil { t.Fatalf("autorest: WithInt32 failed with error (%v)", err) } if r.ContentLength != int64(len(fmt.Sprintf("%v", 42))) { t.Fatalf("autorest: WithInt32 set Content-Length to %v, expected %v", r.ContentLength, int64(len(fmt.Sprintf("%v", 42)))) } v, err := strconv.ParseInt(string(s), 10, 32) if err != nil || int32(v) != int32(42) { t.Fatalf("autorest: WithInt32 incorrectly encoded the boolean as %v", s) } } func TestWithInt64_SetsTheBody(t *testing.T) { r, err := Prepare(&http.Request{}, WithInt64(42)) if err != nil { t.Fatalf("autorest: WithInt64 failed with error (%v)", err) } s, err := ioutil.ReadAll(r.Body) if err != nil { t.Fatalf("autorest: WithInt64 failed with error (%v)", err) } if r.ContentLength != int64(len(fmt.Sprintf("%v", 42))) { t.Fatalf("autorest: WithInt64 set Content-Length to %v, expected %v", r.ContentLength, int64(len(fmt.Sprintf("%v", 42)))) } v, err := strconv.ParseInt(string(s), 10, 64) if err != nil || v != int64(42) { t.Fatalf("autorest: WithInt64 incorrectly encoded the boolean as %v", s) } } func TestWithString_SetsTheBody(t *testing.T) { r, err := Prepare(&http.Request{}, WithString("value")) if err != nil { t.Fatalf("autorest: WithString failed with error (%v)", err) } s, err := ioutil.ReadAll(r.Body) if err != nil { t.Fatalf("autorest: WithString failed with error (%v)", err) } if r.ContentLength != int64(len("value")) { t.Fatalf("autorest: WithString set Content-Length to %v, expected %v", r.ContentLength, int64(len("value"))) } if string(s) != "value" { t.Fatalf("autorest: WithString incorrectly encoded the string as %v", s) } } func TestWithJSONSetsContentLength(t *testing.T) { r, err := Prepare(&http.Request{}, WithJSON(&mocks.T{Name: "Rob Pike", Age: 42})) if err != nil { t.Fatalf("autorest: WithJSON failed with error (%v)", err) } b, err := ioutil.ReadAll(r.Body) if err != nil { t.Fatalf("autorest: WithJSON failed with error (%v)", err) } if r.ContentLength != int64(len(b)) { t.Fatalf("autorest:WithJSON set Content-Length to %v, expected %v", r.ContentLength, len(b)) } } func TestWithHeaderAllocatesHeaders(t *testing.T) { r, err := Prepare(mocks.NewRequest(), WithHeader("x-foo", "bar")) if err != nil { t.Fatalf("autorest: WithHeader failed (%v)", err) } if r.Header.Get("x-foo") != "bar" { t.Fatalf("autorest: WithHeader failed to add header (%s=%s)", "x-foo", r.Header.Get("x-foo")) } } func TestWithPathCatchesNilURL(t *testing.T) { _, err := Prepare(&http.Request{}, WithPath("a")) if err == nil { t.Fatalf("autorest: WithPath failed to catch a nil URL") } } func TestWithEscapedPathParametersCatchesNilURL(t *testing.T) { _, err := Prepare(&http.Request{}, WithEscapedPathParameters("", map[string]interface{}{"foo": "bar"})) if err == nil { t.Fatalf("autorest: WithEscapedPathParameters failed to catch a nil URL") } } func TestWithPathParametersCatchesNilURL(t *testing.T) { _, err := Prepare(&http.Request{}, WithPathParameters("", map[string]interface{}{"foo": "bar"})) if err == nil { t.Fatalf("autorest: WithPathParameters failed to catch a nil URL") } } func TestWithQueryParametersCatchesNilURL(t *testing.T) { _, err := Prepare(&http.Request{}, WithQueryParameters(map[string]interface{}{"foo": "bar"})) if err == nil { t.Fatalf("autorest: WithQueryParameters failed to catch a nil URL") } } func TestModifyingExistingRequest(t *testing.T) { r, err := Prepare(mocks.NewRequestForURL("https://bing.com"), WithPath("search"), WithQueryParameters(map[string]interface{}{"q": "golang"})) if err != nil { t.Fatalf("autorest: Preparing an existing request returned an error (%v)", err) } if r.URL.String() != "https:/search?q=golang" && r.URL.Host != "bing.com" { t.Fatalf("autorest: Preparing an existing request failed (%s)", r.URL) } } go-autorest-8.3.1/autorest/responder.go000066400000000000000000000204421315133516500201770ustar00rootroot00000000000000package autorest import ( "bytes" "encoding/json" "encoding/xml" "fmt" "io" "io/ioutil" "net/http" "strings" ) // Responder is the interface that wraps the Respond method. // // Respond accepts and reacts to an http.Response. Implementations must ensure to not share or hold // state since Responders may be shared and re-used. type Responder interface { Respond(*http.Response) error } // ResponderFunc is a method that implements the Responder interface. type ResponderFunc func(*http.Response) error // Respond implements the Responder interface on ResponderFunc. func (rf ResponderFunc) Respond(r *http.Response) error { return rf(r) } // RespondDecorator takes and possibly decorates, by wrapping, a Responder. Decorators may react to // the http.Response and pass it along or, first, pass the http.Response along then react. type RespondDecorator func(Responder) Responder // CreateResponder creates, decorates, and returns a Responder. Without decorators, the returned // Responder returns the passed http.Response unmodified. Responders may or may not be safe to share // and re-used: It depends on the applied decorators. For example, a standard decorator that closes // the response body is fine to share whereas a decorator that reads the body into a passed struct // is not. // // To prevent memory leaks, ensure that at least one Responder closes the response body. func CreateResponder(decorators ...RespondDecorator) Responder { return DecorateResponder( Responder(ResponderFunc(func(r *http.Response) error { return nil })), decorators...) } // DecorateResponder accepts a Responder and a, possibly empty, set of RespondDecorators, which it // applies to the Responder. Decorators are applied in the order received, but their affect upon the // request depends on whether they are a pre-decorator (react to the http.Response and then pass it // along) or a post-decorator (pass the http.Response along and then react). func DecorateResponder(r Responder, decorators ...RespondDecorator) Responder { for _, decorate := range decorators { r = decorate(r) } return r } // Respond accepts an http.Response and a, possibly empty, set of RespondDecorators. // It creates a Responder from the decorators it then applies to the passed http.Response. func Respond(r *http.Response, decorators ...RespondDecorator) error { if r == nil { return nil } return CreateResponder(decorators...).Respond(r) } // ByIgnoring returns a RespondDecorator that ignores the passed http.Response passing it unexamined // to the next RespondDecorator. func ByIgnoring() RespondDecorator { return func(r Responder) Responder { return ResponderFunc(func(resp *http.Response) error { return r.Respond(resp) }) } } // ByCopying copies the contents of the http.Response Body into the passed bytes.Buffer as // the Body is read. func ByCopying(b *bytes.Buffer) RespondDecorator { return func(r Responder) Responder { return ResponderFunc(func(resp *http.Response) error { err := r.Respond(resp) if err == nil && resp != nil && resp.Body != nil { resp.Body = TeeReadCloser(resp.Body, b) } return err }) } } // ByDiscardingBody returns a RespondDecorator that first invokes the passed Responder after which // it copies the remaining bytes (if any) in the response body to ioutil.Discard. Since the passed // Responder is invoked prior to discarding the response body, the decorator may occur anywhere // within the set. func ByDiscardingBody() RespondDecorator { return func(r Responder) Responder { return ResponderFunc(func(resp *http.Response) error { err := r.Respond(resp) if err == nil && resp != nil && resp.Body != nil { if _, err := io.Copy(ioutil.Discard, resp.Body); err != nil { return fmt.Errorf("Error discarding the response body: %v", err) } } return err }) } } // ByClosing returns a RespondDecorator that first invokes the passed Responder after which it // closes the response body. Since the passed Responder is invoked prior to closing the response // body, the decorator may occur anywhere within the set. func ByClosing() RespondDecorator { return func(r Responder) Responder { return ResponderFunc(func(resp *http.Response) error { err := r.Respond(resp) if resp != nil && resp.Body != nil { if err := resp.Body.Close(); err != nil { return fmt.Errorf("Error closing the response body: %v", err) } } return err }) } } // ByClosingIfError returns a RespondDecorator that first invokes the passed Responder after which // it closes the response if the passed Responder returns an error and the response body exists. func ByClosingIfError() RespondDecorator { return func(r Responder) Responder { return ResponderFunc(func(resp *http.Response) error { err := r.Respond(resp) if err != nil && resp != nil && resp.Body != nil { if err := resp.Body.Close(); err != nil { return fmt.Errorf("Error closing the response body: %v", err) } } return err }) } } // ByUnmarshallingJSON returns a RespondDecorator that decodes a JSON document returned in the // response Body into the value pointed to by v. func ByUnmarshallingJSON(v interface{}) RespondDecorator { return func(r Responder) Responder { return ResponderFunc(func(resp *http.Response) error { err := r.Respond(resp) if err == nil { b, errInner := ioutil.ReadAll(resp.Body) // Some responses might include a BOM, remove for successful unmarshalling b = bytes.TrimPrefix(b, []byte("\xef\xbb\xbf")) if errInner != nil { err = fmt.Errorf("Error occurred reading http.Response#Body - Error = '%v'", errInner) } else if len(strings.Trim(string(b), " ")) > 0 { errInner = json.Unmarshal(b, v) if errInner != nil { err = fmt.Errorf("Error occurred unmarshalling JSON - Error = '%v' JSON = '%s'", errInner, string(b)) } } } return err }) } } // ByUnmarshallingXML returns a RespondDecorator that decodes a XML document returned in the // response Body into the value pointed to by v. func ByUnmarshallingXML(v interface{}) RespondDecorator { return func(r Responder) Responder { return ResponderFunc(func(resp *http.Response) error { err := r.Respond(resp) if err == nil { b, errInner := ioutil.ReadAll(resp.Body) if errInner != nil { err = fmt.Errorf("Error occurred reading http.Response#Body - Error = '%v'", errInner) } else { errInner = xml.Unmarshal(b, v) if errInner != nil { err = fmt.Errorf("Error occurred unmarshalling Xml - Error = '%v' Xml = '%s'", errInner, string(b)) } } } return err }) } } // WithErrorUnlessStatusCode returns a RespondDecorator that emits an error unless the response // StatusCode is among the set passed. On error, response body is fully read into a buffer and // presented in the returned error, as well as in the response body. func WithErrorUnlessStatusCode(codes ...int) RespondDecorator { return func(r Responder) Responder { return ResponderFunc(func(resp *http.Response) error { err := r.Respond(resp) if err == nil && !ResponseHasStatusCode(resp, codes...) { derr := NewErrorWithResponse("autorest", "WithErrorUnlessStatusCode", resp, "%v %v failed with %s", resp.Request.Method, resp.Request.URL, resp.Status) if resp.Body != nil { defer resp.Body.Close() b, _ := ioutil.ReadAll(resp.Body) derr.ServiceError = b resp.Body = ioutil.NopCloser(bytes.NewReader(b)) } err = derr } return err }) } } // WithErrorUnlessOK returns a RespondDecorator that emits an error if the response StatusCode is // anything other than HTTP 200. func WithErrorUnlessOK() RespondDecorator { return WithErrorUnlessStatusCode(http.StatusOK) } // ExtractHeader extracts all values of the specified header from the http.Response. It returns an // empty string slice if the passed http.Response is nil or the header does not exist. func ExtractHeader(header string, resp *http.Response) []string { if resp != nil && resp.Header != nil { return resp.Header[http.CanonicalHeaderKey(header)] } return nil } // ExtractHeaderValue extracts the first value of the specified header from the http.Response. It // returns an empty string if the passed http.Response is nil or the header does not exist. func ExtractHeaderValue(header string, resp *http.Response) string { h := ExtractHeader(header, resp) if len(h) > 0 { return h[0] } return "" } go-autorest-8.3.1/autorest/responder_test.go000066400000000000000000000374701315133516500212470ustar00rootroot00000000000000package autorest import ( "bytes" "encoding/json" "fmt" "io/ioutil" "net/http" "reflect" "strings" "testing" "github.com/Azure/go-autorest/autorest/mocks" ) func ExampleWithErrorUnlessOK() { r := mocks.NewResponse() r.Request = mocks.NewRequest() // Respond and leave the response body open (for a subsequent responder to close) err := Respond(r, WithErrorUnlessOK(), ByDiscardingBody(), ByClosingIfError()) if err == nil { fmt.Printf("%s of %s returned HTTP 200", r.Request.Method, r.Request.URL) // Complete handling the response and close the body Respond(r, ByDiscardingBody(), ByClosing()) } // Output: GET of https://microsoft.com/a/b/c/ returned HTTP 200 } func ExampleByUnmarshallingJSON() { c := ` { "name" : "Rob Pike", "age" : 42 } ` type V struct { Name string `json:"name"` Age int `json:"age"` } v := &V{} Respond(mocks.NewResponseWithContent(c), ByUnmarshallingJSON(v), ByClosing()) fmt.Printf("%s is %d years old\n", v.Name, v.Age) // Output: Rob Pike is 42 years old } func ExampleByUnmarshallingXML() { c := ` Rob Pike 42 ` type V struct { Name string `xml:"Name"` Age int `xml:"Age"` } v := &V{} Respond(mocks.NewResponseWithContent(c), ByUnmarshallingXML(v), ByClosing()) fmt.Printf("%s is %d years old\n", v.Name, v.Age) // Output: Rob Pike is 42 years old } func TestCreateResponderDoesNotModify(t *testing.T) { r1 := mocks.NewResponse() r2 := mocks.NewResponse() p := CreateResponder() err := p.Respond(r1) if err != nil { t.Fatalf("autorest: CreateResponder failed (%v)", err) } if !reflect.DeepEqual(r1, r2) { t.Fatalf("autorest: CreateResponder without decorators modified the response") } } func TestCreateResponderRunsDecoratorsInOrder(t *testing.T) { s := "" d := func(n int) RespondDecorator { return func(r Responder) Responder { return ResponderFunc(func(resp *http.Response) error { err := r.Respond(resp) if err == nil { s += fmt.Sprintf("%d", n) } return err }) } } p := CreateResponder(d(1), d(2), d(3)) err := p.Respond(&http.Response{}) if err != nil { t.Fatalf("autorest: Respond failed (%v)", err) } if s != "123" { t.Fatalf("autorest: CreateResponder invoked decorators in an incorrect order; expected '123', received '%s'", s) } } func TestByIgnoring(t *testing.T) { r := mocks.NewResponse() Respond(r, (func() RespondDecorator { return func(r Responder) Responder { return ResponderFunc(func(r2 *http.Response) error { r1 := mocks.NewResponse() if !reflect.DeepEqual(r1, r2) { t.Fatalf("autorest: ByIgnoring modified the HTTP Response -- received %v, expected %v", r2, r1) } return nil }) } })(), ByIgnoring(), ByClosing()) } func TestByCopying_Copies(t *testing.T) { r := mocks.NewResponseWithContent(jsonT) b := &bytes.Buffer{} err := Respond(r, ByCopying(b), ByUnmarshallingJSON(&mocks.T{}), ByClosing()) if err != nil { t.Fatalf("autorest: ByCopying returned an unexpected error -- %v", err) } if b.String() != jsonT { t.Fatalf("autorest: ByCopying failed to copy the bytes read") } } func TestByCopying_ReturnsNestedErrors(t *testing.T) { r := mocks.NewResponseWithContent(jsonT) r.Body.Close() err := Respond(r, ByCopying(&bytes.Buffer{}), ByUnmarshallingJSON(&mocks.T{}), ByClosing()) if err == nil { t.Fatalf("autorest: ByCopying failed to return the expected error") } } func TestByCopying_AcceptsNilReponse(t *testing.T) { r := mocks.NewResponse() Respond(r, (func() RespondDecorator { return func(r Responder) Responder { return ResponderFunc(func(resp *http.Response) error { resp.Body.Close() r.Respond(nil) return nil }) } })(), ByCopying(&bytes.Buffer{})) } func TestByCopying_AcceptsNilBody(t *testing.T) { r := mocks.NewResponse() Respond(r, (func() RespondDecorator { return func(r Responder) Responder { return ResponderFunc(func(resp *http.Response) error { resp.Body.Close() resp.Body = nil r.Respond(resp) return nil }) } })(), ByCopying(&bytes.Buffer{})) } func TestByClosing(t *testing.T) { r := mocks.NewResponse() err := Respond(r, ByClosing()) if err != nil { t.Fatalf("autorest: ByClosing failed (%v)", err) } if r.Body.(*mocks.Body).IsOpen() { t.Fatalf("autorest: ByClosing did not close the response body") } } func TestByClosingAcceptsNilResponse(t *testing.T) { r := mocks.NewResponse() Respond(r, (func() RespondDecorator { return func(r Responder) Responder { return ResponderFunc(func(resp *http.Response) error { resp.Body.Close() r.Respond(nil) return nil }) } })(), ByClosing()) } func TestByClosingAcceptsNilBody(t *testing.T) { r := mocks.NewResponse() Respond(r, (func() RespondDecorator { return func(r Responder) Responder { return ResponderFunc(func(resp *http.Response) error { resp.Body.Close() resp.Body = nil r.Respond(resp) return nil }) } })(), ByClosing()) } func TestByClosingClosesEvenAfterErrors(t *testing.T) { var e error r := mocks.NewResponse() Respond(r, withErrorRespondDecorator(&e), ByClosing()) if r.Body.(*mocks.Body).IsOpen() { t.Fatalf("autorest: ByClosing did not close the response body after an error occurred") } } func TestByClosingClosesReturnsNestedErrors(t *testing.T) { var e error r := mocks.NewResponse() err := Respond(r, withErrorRespondDecorator(&e), ByClosing()) if err == nil || !reflect.DeepEqual(e, err) { t.Fatalf("autorest: ByClosing failed to return a nested error") } } func TestByClosingIfErrorAcceptsNilResponse(t *testing.T) { var e error r := mocks.NewResponse() Respond(r, withErrorRespondDecorator(&e), (func() RespondDecorator { return func(r Responder) Responder { return ResponderFunc(func(resp *http.Response) error { resp.Body.Close() r.Respond(nil) return nil }) } })(), ByClosingIfError()) } func TestByClosingIfErrorAcceptsNilBody(t *testing.T) { var e error r := mocks.NewResponse() Respond(r, withErrorRespondDecorator(&e), (func() RespondDecorator { return func(r Responder) Responder { return ResponderFunc(func(resp *http.Response) error { resp.Body.Close() resp.Body = nil r.Respond(resp) return nil }) } })(), ByClosingIfError()) } func TestByClosingIfErrorClosesIfAnErrorOccurs(t *testing.T) { var e error r := mocks.NewResponse() Respond(r, withErrorRespondDecorator(&e), ByClosingIfError()) if r.Body.(*mocks.Body).IsOpen() { t.Fatalf("autorest: ByClosingIfError did not close the response body after an error occurred") } } func TestByClosingIfErrorDoesNotClosesIfNoErrorOccurs(t *testing.T) { r := mocks.NewResponse() Respond(r, ByClosingIfError()) if !r.Body.(*mocks.Body).IsOpen() { t.Fatalf("autorest: ByClosingIfError closed the response body even though no error occurred") } } func TestByDiscardingBody(t *testing.T) { r := mocks.NewResponse() err := Respond(r, ByDiscardingBody()) if err != nil { t.Fatalf("autorest: ByDiscardingBody failed (%v)", err) } buf, err := ioutil.ReadAll(r.Body) if err != nil { t.Fatalf("autorest: Reading result of ByDiscardingBody failed (%v)", err) } if len(buf) != 0 { t.Logf("autorest: Body was not empty after calling ByDiscardingBody.") t.Fail() } } func TestByDiscardingBodyAcceptsNilResponse(t *testing.T) { var e error r := mocks.NewResponse() Respond(r, withErrorRespondDecorator(&e), (func() RespondDecorator { return func(r Responder) Responder { return ResponderFunc(func(resp *http.Response) error { resp.Body.Close() r.Respond(nil) return nil }) } })(), ByDiscardingBody()) } func TestByDiscardingBodyAcceptsNilBody(t *testing.T) { var e error r := mocks.NewResponse() Respond(r, withErrorRespondDecorator(&e), (func() RespondDecorator { return func(r Responder) Responder { return ResponderFunc(func(resp *http.Response) error { resp.Body.Close() resp.Body = nil r.Respond(resp) return nil }) } })(), ByDiscardingBody()) } func TestByUnmarshallingJSON(t *testing.T) { v := &mocks.T{} r := mocks.NewResponseWithContent(jsonT) err := Respond(r, ByUnmarshallingJSON(v), ByClosing()) if err != nil { t.Fatalf("autorest: ByUnmarshallingJSON failed (%v)", err) } if v.Name != "Rob Pike" || v.Age != 42 { t.Fatalf("autorest: ByUnmarshallingJSON failed to properly unmarshal") } } func TestByUnmarshallingJSON_HandlesReadErrors(t *testing.T) { v := &mocks.T{} r := mocks.NewResponseWithContent(jsonT) r.Body.(*mocks.Body).Close() err := Respond(r, ByUnmarshallingJSON(v), ByClosing()) if err == nil { t.Fatalf("autorest: ByUnmarshallingJSON failed to receive / respond to read error") } } func TestByUnmarshallingJSONIncludesJSONInErrors(t *testing.T) { v := &mocks.T{} j := jsonT[0 : len(jsonT)-2] r := mocks.NewResponseWithContent(j) err := Respond(r, ByUnmarshallingJSON(v), ByClosing()) if err == nil || !strings.Contains(err.Error(), j) { t.Fatalf("autorest: ByUnmarshallingJSON failed to return JSON in error (%v)", err) } } func TestByUnmarshallingJSONEmptyInput(t *testing.T) { v := &mocks.T{} r := mocks.NewResponseWithContent(``) err := Respond(r, ByUnmarshallingJSON(v), ByClosing()) if err != nil { t.Fatalf("autorest: ByUnmarshallingJSON failed to return nil in case of empty JSON (%v)", err) } } func TestByUnmarshallingXML(t *testing.T) { v := &mocks.T{} r := mocks.NewResponseWithContent(xmlT) err := Respond(r, ByUnmarshallingXML(v), ByClosing()) if err != nil { t.Fatalf("autorest: ByUnmarshallingXML failed (%v)", err) } if v.Name != "Rob Pike" || v.Age != 42 { t.Fatalf("autorest: ByUnmarshallingXML failed to properly unmarshal") } } func TestByUnmarshallingXML_HandlesReadErrors(t *testing.T) { v := &mocks.T{} r := mocks.NewResponseWithContent(xmlT) r.Body.(*mocks.Body).Close() err := Respond(r, ByUnmarshallingXML(v), ByClosing()) if err == nil { t.Fatalf("autorest: ByUnmarshallingXML failed to receive / respond to read error") } } func TestByUnmarshallingXMLIncludesXMLInErrors(t *testing.T) { v := &mocks.T{} x := xmlT[0 : len(xmlT)-2] r := mocks.NewResponseWithContent(x) err := Respond(r, ByUnmarshallingXML(v), ByClosing()) if err == nil || !strings.Contains(err.Error(), x) { t.Fatalf("autorest: ByUnmarshallingXML failed to return XML in error (%v)", err) } } func TestRespondAcceptsNullResponse(t *testing.T) { err := Respond(nil) if err != nil { t.Fatalf("autorest: Respond returned an unexpected error when given a null Response (%v)", err) } } func TestWithErrorUnlessStatusCodeOKResponse(t *testing.T) { v := &mocks.T{} r := mocks.NewResponseWithContent(jsonT) err := Respond(r, WithErrorUnlessStatusCode(http.StatusOK), ByUnmarshallingJSON(v), ByClosing()) if err != nil { t.Fatalf("autorest: WithErrorUnlessStatusCode(http.StatusOK) failed on okay response. (%v)", err) } if v.Name != "Rob Pike" || v.Age != 42 { t.Fatalf("autorest: WithErrorUnlessStatusCode(http.StatusOK) corrupted the response body of okay response.") } } func TesWithErrorUnlessStatusCodeErrorResponse(t *testing.T) { v := &mocks.T{} e := &mocks.T{} r := mocks.NewResponseWithContent(jsonT) r.Status = "400 BadRequest" r.StatusCode = http.StatusBadRequest err := Respond(r, WithErrorUnlessStatusCode(http.StatusOK), ByUnmarshallingJSON(v), ByClosing()) if err == nil { t.Fatal("autorest: WithErrorUnlessStatusCode(http.StatusOK) did not return error, on a response to a bad request.") } var errorRespBody []byte if derr, ok := err.(DetailedError); !ok { t.Fatalf("autorest: WithErrorUnlessStatusCode(http.StatusOK) got wrong error type : %T, expected: DetailedError, on a response to a bad request.", err) } else { errorRespBody = derr.ServiceError } if errorRespBody == nil { t.Fatalf("autorest: WithErrorUnlessStatusCode(http.StatusOK) ServiceError not returned in DetailedError on a response to a bad request.") } err = json.Unmarshal(errorRespBody, e) if err != nil { t.Fatalf("autorest: WithErrorUnlessStatusCode(http.StatusOK) cannot parse error returned in ServiceError into json. %v", err) } expected := &mocks.T{Name: "Rob Pike", Age: 42} if e != expected { t.Fatalf("autorest: WithErrorUnlessStatusCode(http.StatusOK wrong value from parsed ServiceError: got=%#v expected=%#v", e, expected) } } func TestWithErrorUnlessStatusCode(t *testing.T) { r := mocks.NewResponse() r.Request = mocks.NewRequest() r.Status = "400 BadRequest" r.StatusCode = http.StatusBadRequest err := Respond(r, WithErrorUnlessStatusCode(http.StatusBadRequest, http.StatusUnauthorized, http.StatusInternalServerError), ByClosingIfError()) if err != nil { t.Fatalf("autorest: WithErrorUnlessStatusCode returned an error (%v) for an acceptable status code (%s)", err, r.Status) } } func TestWithErrorUnlessStatusCodeEmitsErrorForUnacceptableStatusCode(t *testing.T) { r := mocks.NewResponse() r.Request = mocks.NewRequest() r.Status = "400 BadRequest" r.StatusCode = http.StatusBadRequest err := Respond(r, WithErrorUnlessStatusCode(http.StatusOK, http.StatusUnauthorized, http.StatusInternalServerError), ByClosingIfError()) if err == nil { t.Fatalf("autorest: WithErrorUnlessStatusCode failed to return an error for an unacceptable status code (%s)", r.Status) } } func TestWithErrorUnlessOK(t *testing.T) { r := mocks.NewResponse() r.Request = mocks.NewRequest() err := Respond(r, WithErrorUnlessOK(), ByClosingIfError()) if err != nil { t.Fatalf("autorest: WithErrorUnlessOK returned an error for OK status code (%v)", err) } } func TestWithErrorUnlessOKEmitsErrorIfNotOK(t *testing.T) { r := mocks.NewResponse() r.Request = mocks.NewRequest() r.Status = "400 BadRequest" r.StatusCode = http.StatusBadRequest err := Respond(r, WithErrorUnlessOK(), ByClosingIfError()) if err == nil { t.Fatalf("autorest: WithErrorUnlessOK failed to return an error for a non-OK status code (%v)", err) } } func TestExtractHeader(t *testing.T) { r := mocks.NewResponse() v := []string{"v1", "v2", "v3"} mocks.SetResponseHeaderValues(r, mocks.TestHeader, v) if !reflect.DeepEqual(ExtractHeader(mocks.TestHeader, r), v) { t.Fatalf("autorest: ExtractHeader failed to retrieve the expected header -- expected [%s]%v, received [%s]%v", mocks.TestHeader, v, mocks.TestHeader, ExtractHeader(mocks.TestHeader, r)) } } func TestExtractHeaderHandlesMissingHeader(t *testing.T) { var v []string r := mocks.NewResponse() if !reflect.DeepEqual(ExtractHeader(mocks.TestHeader, r), v) { t.Fatalf("autorest: ExtractHeader failed to handle a missing header -- expected %v, received %v", v, ExtractHeader(mocks.TestHeader, r)) } } func TestExtractHeaderValue(t *testing.T) { r := mocks.NewResponse() v := "v1" mocks.SetResponseHeader(r, mocks.TestHeader, v) if ExtractHeaderValue(mocks.TestHeader, r) != v { t.Fatalf("autorest: ExtractHeader failed to retrieve the expected header -- expected [%s]%v, received [%s]%v", mocks.TestHeader, v, mocks.TestHeader, ExtractHeaderValue(mocks.TestHeader, r)) } } func TestExtractHeaderValueHandlesMissingHeader(t *testing.T) { r := mocks.NewResponse() v := "" if ExtractHeaderValue(mocks.TestHeader, r) != v { t.Fatalf("autorest: ExtractHeader failed to retrieve the expected header -- expected [%s]%v, received [%s]%v", mocks.TestHeader, v, mocks.TestHeader, ExtractHeaderValue(mocks.TestHeader, r)) } } func TestExtractHeaderValueRetrievesFirstValue(t *testing.T) { r := mocks.NewResponse() v := []string{"v1", "v2", "v3"} mocks.SetResponseHeaderValues(r, mocks.TestHeader, v) if ExtractHeaderValue(mocks.TestHeader, r) != v[0] { t.Fatalf("autorest: ExtractHeader failed to retrieve the expected header -- expected [%s]%v, received [%s]%v", mocks.TestHeader, v[0], mocks.TestHeader, ExtractHeaderValue(mocks.TestHeader, r)) } } go-autorest-8.3.1/autorest/retriablerequest.go000066400000000000000000000014741315133516500215640ustar00rootroot00000000000000package autorest import ( "bytes" "io" "io/ioutil" "net/http" ) // NewRetriableRequest returns a wrapper around an HTTP request that support retry logic. func NewRetriableRequest(req *http.Request) *RetriableRequest { return &RetriableRequest{req: req} } // Request returns the wrapped HTTP request. func (rr *RetriableRequest) Request() *http.Request { return rr.req } func (rr *RetriableRequest) prepareFromByteReader() (err error) { // fall back to making a copy (only do this once) b := []byte{} if rr.req.ContentLength > 0 { b = make([]byte, rr.req.ContentLength) _, err = io.ReadFull(rr.req.Body, b) if err != nil { return err } } else { b, err = ioutil.ReadAll(rr.req.Body) if err != nil { return err } } rr.br = bytes.NewReader(b) rr.req.Body = ioutil.NopCloser(rr.br) return err } go-autorest-8.3.1/autorest/retriablerequest_1.7.go000066400000000000000000000016431315133516500221470ustar00rootroot00000000000000// +build !go1.8 package autorest import ( "bytes" "net/http" ) // RetriableRequest provides facilities for retrying an HTTP request. type RetriableRequest struct { req *http.Request br *bytes.Reader reset bool } // Prepare signals that the request is about to be sent. func (rr *RetriableRequest) Prepare() (err error) { // preserve the request body; this is to support retry logic as // the underlying transport will always close the reqeust body if rr.req.Body != nil { if rr.reset { if rr.br != nil { _, err = rr.br.Seek(0, 0 /*io.SeekStart*/) } rr.reset = false if err != nil { return err } } if rr.br == nil { // fall back to making a copy (only do this once) err = rr.prepareFromByteReader() } // indicates that the request body needs to be reset rr.reset = true } return err } func removeRequestBody(req *http.Request) { req.Body = nil req.ContentLength = 0 } go-autorest-8.3.1/autorest/retriablerequest_1.8.go000066400000000000000000000023501315133516500221440ustar00rootroot00000000000000// +build go1.8 package autorest import ( "bytes" "io" "net/http" ) // RetriableRequest provides facilities for retrying an HTTP request. type RetriableRequest struct { req *http.Request rc io.ReadCloser br *bytes.Reader reset bool } // Prepare signals that the request is about to be sent. func (rr *RetriableRequest) Prepare() (err error) { // preserve the request body; this is to support retry logic as // the underlying transport will always close the reqeust body if rr.req.Body != nil { if rr.reset { if rr.rc != nil { rr.req.Body = rr.rc } else if rr.br != nil { _, err = rr.br.Seek(0, io.SeekStart) } rr.reset = false if err != nil { return err } } if rr.req.GetBody != nil { // this will allow us to preserve the body without having to // make a copy. note we need to do this on each iteration rr.rc, err = rr.req.GetBody() if err != nil { return err } } else if rr.br == nil { // fall back to making a copy (only do this once) err = rr.prepareFromByteReader() } // indicates that the request body needs to be reset rr.reset = true } return err } func removeRequestBody(req *http.Request) { req.Body = nil req.GetBody = nil req.ContentLength = 0 } go-autorest-8.3.1/autorest/sender.go000066400000000000000000000250161315133516500174600ustar00rootroot00000000000000package autorest import ( "fmt" "log" "math" "net/http" "strconv" "time" ) // Sender is the interface that wraps the Do method to send HTTP requests. // // The standard http.Client conforms to this interface. type Sender interface { Do(*http.Request) (*http.Response, error) } // SenderFunc is a method that implements the Sender interface. type SenderFunc func(*http.Request) (*http.Response, error) // Do implements the Sender interface on SenderFunc. func (sf SenderFunc) Do(r *http.Request) (*http.Response, error) { return sf(r) } // SendDecorator takes and possibily decorates, by wrapping, a Sender. Decorators may affect the // http.Request and pass it along or, first, pass the http.Request along then react to the // http.Response result. type SendDecorator func(Sender) Sender // CreateSender creates, decorates, and returns, as a Sender, the default http.Client. func CreateSender(decorators ...SendDecorator) Sender { return DecorateSender(&http.Client{}, decorators...) } // DecorateSender accepts a Sender and a, possibly empty, set of SendDecorators, which is applies to // the Sender. Decorators are applied in the order received, but their affect upon the request // depends on whether they are a pre-decorator (change the http.Request and then pass it along) or a // post-decorator (pass the http.Request along and react to the results in http.Response). func DecorateSender(s Sender, decorators ...SendDecorator) Sender { for _, decorate := range decorators { s = decorate(s) } return s } // Send sends, by means of the default http.Client, the passed http.Request, returning the // http.Response and possible error. It also accepts a, possibly empty, set of SendDecorators which // it will apply the http.Client before invoking the Do method. // // Send is a convenience method and not recommended for production. Advanced users should use // SendWithSender, passing and sharing their own Sender (e.g., instance of http.Client). // // Send will not poll or retry requests. func Send(r *http.Request, decorators ...SendDecorator) (*http.Response, error) { return SendWithSender(&http.Client{}, r, decorators...) } // SendWithSender sends the passed http.Request, through the provided Sender, returning the // http.Response and possible error. It also accepts a, possibly empty, set of SendDecorators which // it will apply the http.Client before invoking the Do method. // // SendWithSender will not poll or retry requests. func SendWithSender(s Sender, r *http.Request, decorators ...SendDecorator) (*http.Response, error) { return DecorateSender(s, decorators...).Do(r) } // AfterDelay returns a SendDecorator that delays for the passed time.Duration before // invoking the Sender. The delay may be terminated by closing the optional channel on the // http.Request. If canceled, no further Senders are invoked. func AfterDelay(d time.Duration) SendDecorator { return func(s Sender) Sender { return SenderFunc(func(r *http.Request) (*http.Response, error) { if !DelayForBackoff(d, 0, r.Cancel) { return nil, fmt.Errorf("autorest: AfterDelay canceled before full delay") } return s.Do(r) }) } } // AsIs returns a SendDecorator that invokes the passed Sender without modifying the http.Request. func AsIs() SendDecorator { return func(s Sender) Sender { return SenderFunc(func(r *http.Request) (*http.Response, error) { return s.Do(r) }) } } // DoCloseIfError returns a SendDecorator that first invokes the passed Sender after which // it closes the response if the passed Sender returns an error and the response body exists. func DoCloseIfError() SendDecorator { return func(s Sender) Sender { return SenderFunc(func(r *http.Request) (*http.Response, error) { resp, err := s.Do(r) if err != nil { Respond(resp, ByDiscardingBody(), ByClosing()) } return resp, err }) } } // DoErrorIfStatusCode returns a SendDecorator that emits an error if the response StatusCode is // among the set passed. Since these are artificial errors, the response body may still require // closing. func DoErrorIfStatusCode(codes ...int) SendDecorator { return func(s Sender) Sender { return SenderFunc(func(r *http.Request) (*http.Response, error) { resp, err := s.Do(r) if err == nil && ResponseHasStatusCode(resp, codes...) { err = NewErrorWithResponse("autorest", "DoErrorIfStatusCode", resp, "%v %v failed with %s", resp.Request.Method, resp.Request.URL, resp.Status) } return resp, err }) } } // DoErrorUnlessStatusCode returns a SendDecorator that emits an error unless the response // StatusCode is among the set passed. Since these are artificial errors, the response body // may still require closing. func DoErrorUnlessStatusCode(codes ...int) SendDecorator { return func(s Sender) Sender { return SenderFunc(func(r *http.Request) (*http.Response, error) { resp, err := s.Do(r) if err == nil && !ResponseHasStatusCode(resp, codes...) { err = NewErrorWithResponse("autorest", "DoErrorUnlessStatusCode", resp, "%v %v failed with %s", resp.Request.Method, resp.Request.URL, resp.Status) } return resp, err }) } } // DoPollForStatusCodes returns a SendDecorator that polls if the http.Response contains one of the // passed status codes. It expects the http.Response to contain a Location header providing the // URL at which to poll (using GET) and will poll until the time passed is equal to or greater than // the supplied duration. It will delay between requests for the duration specified in the // RetryAfter header or, if the header is absent, the passed delay. Polling may be canceled by // closing the optional channel on the http.Request. func DoPollForStatusCodes(duration time.Duration, delay time.Duration, codes ...int) SendDecorator { return func(s Sender) Sender { return SenderFunc(func(r *http.Request) (resp *http.Response, err error) { resp, err = s.Do(r) if err == nil && ResponseHasStatusCode(resp, codes...) { r, err = NewPollingRequest(resp, r.Cancel) for err == nil && ResponseHasStatusCode(resp, codes...) { Respond(resp, ByDiscardingBody(), ByClosing()) resp, err = SendWithSender(s, r, AfterDelay(GetRetryAfter(resp, delay))) } } return resp, err }) } } // DoRetryForAttempts returns a SendDecorator that retries a failed request for up to the specified // number of attempts, exponentially backing off between requests using the supplied backoff // time.Duration (which may be zero). Retrying may be canceled by closing the optional channel on // the http.Request. func DoRetryForAttempts(attempts int, backoff time.Duration) SendDecorator { return func(s Sender) Sender { return SenderFunc(func(r *http.Request) (resp *http.Response, err error) { rr := NewRetriableRequest(r) for attempt := 0; attempt < attempts; attempt++ { err = rr.Prepare() if err != nil { return resp, err } resp, err = s.Do(rr.Request()) if err == nil { return resp, err } DelayForBackoff(backoff, attempt, r.Cancel) } return resp, err }) } } // DoRetryForStatusCodes returns a SendDecorator that retries for specified statusCodes for up to the specified // number of attempts, exponentially backing off between requests using the supplied backoff // time.Duration (which may be zero). Retrying may be canceled by closing the optional channel on // the http.Request. func DoRetryForStatusCodes(attempts int, backoff time.Duration, codes ...int) SendDecorator { return func(s Sender) Sender { return SenderFunc(func(r *http.Request) (resp *http.Response, err error) { rr := NewRetriableRequest(r) // Increment to add the first call (attempts denotes number of retries) attempts++ for attempt := 0; attempt < attempts; attempt++ { err = rr.Prepare() if err != nil { return resp, err } resp, err = s.Do(rr.Request()) if err != nil || !ResponseHasStatusCode(resp, codes...) { return resp, err } delayed := DelayWithRetryAfter(resp, r.Cancel) if !delayed { DelayForBackoff(backoff, attempt, r.Cancel) } } return resp, err }) } } // DelayWithRetryAfter invokes time.After for the duration specified in the "Retry-After" header in // responses with status code 429 func DelayWithRetryAfter(resp *http.Response, cancel <-chan struct{}) bool { retryAfter, _ := strconv.Atoi(resp.Header.Get("Retry-After")) if resp.StatusCode == http.StatusTooManyRequests && retryAfter > 0 { select { case <-time.After(time.Duration(retryAfter) * time.Second): return true case <-cancel: return false } } return false } // DoRetryForDuration returns a SendDecorator that retries the request until the total time is equal // to or greater than the specified duration, exponentially backing off between requests using the // supplied backoff time.Duration (which may be zero). Retrying may be canceled by closing the // optional channel on the http.Request. func DoRetryForDuration(d time.Duration, backoff time.Duration) SendDecorator { return func(s Sender) Sender { return SenderFunc(func(r *http.Request) (resp *http.Response, err error) { rr := NewRetriableRequest(r) end := time.Now().Add(d) for attempt := 0; time.Now().Before(end); attempt++ { err = rr.Prepare() if err != nil { return resp, err } resp, err = s.Do(rr.Request()) if err == nil { return resp, err } DelayForBackoff(backoff, attempt, r.Cancel) } return resp, err }) } } // WithLogging returns a SendDecorator that implements simple before and after logging of the // request. func WithLogging(logger *log.Logger) SendDecorator { return func(s Sender) Sender { return SenderFunc(func(r *http.Request) (*http.Response, error) { logger.Printf("Sending %s %s", r.Method, r.URL) resp, err := s.Do(r) if err != nil { logger.Printf("%s %s received error '%v'", r.Method, r.URL, err) } else { logger.Printf("%s %s received %s", r.Method, r.URL, resp.Status) } return resp, err }) } } // DelayForBackoff invokes time.After for the supplied backoff duration raised to the power of // passed attempt (i.e., an exponential backoff delay). Backoff duration is in seconds and can set // to zero for no delay. The delay may be canceled by closing the passed channel. If terminated early, // returns false. // Note: Passing attempt 1 will result in doubling "backoff" duration. Treat this as a zero-based attempt // count. func DelayForBackoff(backoff time.Duration, attempt int, cancel <-chan struct{}) bool { select { case <-time.After(time.Duration(backoff.Seconds()*math.Pow(2, float64(attempt))) * time.Second): return true case <-cancel: return false } } go-autorest-8.3.1/autorest/sender_test.go000066400000000000000000000514021315133516500205150ustar00rootroot00000000000000package autorest import ( "bytes" "fmt" "log" "net/http" "os" "reflect" "sync" "testing" "time" "github.com/Azure/go-autorest/autorest/mocks" ) func ExampleSendWithSender() { r := mocks.NewResponseWithStatus("202 Accepted", http.StatusAccepted) mocks.SetAcceptedHeaders(r) client := mocks.NewSender() client.AppendAndRepeatResponse(r, 10) logger := log.New(os.Stdout, "autorest: ", 0) na := NullAuthorizer{} req, _ := Prepare(&http.Request{}, AsGet(), WithBaseURL("https://microsoft.com/a/b/c/"), na.WithAuthorization()) r, _ = SendWithSender(client, req, WithLogging(logger), DoErrorIfStatusCode(http.StatusAccepted), DoCloseIfError(), DoRetryForAttempts(5, time.Duration(0))) Respond(r, ByDiscardingBody(), ByClosing()) // Output: // autorest: Sending GET https://microsoft.com/a/b/c/ // autorest: GET https://microsoft.com/a/b/c/ received 202 Accepted // autorest: Sending GET https://microsoft.com/a/b/c/ // autorest: GET https://microsoft.com/a/b/c/ received 202 Accepted // autorest: Sending GET https://microsoft.com/a/b/c/ // autorest: GET https://microsoft.com/a/b/c/ received 202 Accepted // autorest: Sending GET https://microsoft.com/a/b/c/ // autorest: GET https://microsoft.com/a/b/c/ received 202 Accepted // autorest: Sending GET https://microsoft.com/a/b/c/ // autorest: GET https://microsoft.com/a/b/c/ received 202 Accepted } func ExampleDoRetryForAttempts() { client := mocks.NewSender() client.SetAndRepeatError(fmt.Errorf("Faux Error"), 10) // Retry with backoff -- ensure returned Bodies are closed r, _ := SendWithSender(client, mocks.NewRequest(), DoCloseIfError(), DoRetryForAttempts(5, time.Duration(0))) Respond(r, ByDiscardingBody(), ByClosing()) fmt.Printf("Retry stopped after %d attempts", client.Attempts()) // Output: Retry stopped after 5 attempts } func ExampleDoErrorIfStatusCode() { client := mocks.NewSender() client.AppendAndRepeatResponse(mocks.NewResponseWithStatus("204 NoContent", http.StatusNoContent), 10) // Chain decorators to retry the request, up to five times, if the status code is 204 r, _ := SendWithSender(client, mocks.NewRequest(), DoErrorIfStatusCode(http.StatusNoContent), DoCloseIfError(), DoRetryForAttempts(5, time.Duration(0))) Respond(r, ByDiscardingBody(), ByClosing()) fmt.Printf("Retry stopped after %d attempts with code %s", client.Attempts(), r.Status) // Output: Retry stopped after 5 attempts with code 204 NoContent } func TestSendWithSenderRunsDecoratorsInOrder(t *testing.T) { client := mocks.NewSender() s := "" r, err := SendWithSender(client, mocks.NewRequest(), withMessage(&s, "a"), withMessage(&s, "b"), withMessage(&s, "c")) if err != nil { t.Fatalf("autorest: SendWithSender returned an error (%v)", err) } Respond(r, ByDiscardingBody(), ByClosing()) if s != "abc" { t.Fatalf("autorest: SendWithSender invoke decorators out of order; expected 'abc', received '%s'", s) } } func TestCreateSender(t *testing.T) { f := false s := CreateSender( (func() SendDecorator { return func(s Sender) Sender { return SenderFunc(func(r *http.Request) (*http.Response, error) { f = true return nil, nil }) } })()) s.Do(&http.Request{}) if !f { t.Fatal("autorest: CreateSender failed to apply supplied decorator") } } func TestSend(t *testing.T) { f := false Send(&http.Request{}, (func() SendDecorator { return func(s Sender) Sender { return SenderFunc(func(r *http.Request) (*http.Response, error) { f = true return nil, nil }) } })()) if !f { t.Fatal("autorest: Send failed to apply supplied decorator") } } func TestAfterDelayWaits(t *testing.T) { client := mocks.NewSender() d := 2 * time.Second tt := time.Now() r, _ := SendWithSender(client, mocks.NewRequest(), AfterDelay(d)) s := time.Since(tt) if s < d { t.Fatal("autorest: AfterDelay failed to wait for at least the specified duration") } Respond(r, ByDiscardingBody(), ByClosing()) } func TestAfterDelay_Cancels(t *testing.T) { client := mocks.NewSender() cancel := make(chan struct{}) delay := 5 * time.Second var wg sync.WaitGroup wg.Add(1) tt := time.Now() go func() { req := mocks.NewRequest() req.Cancel = cancel wg.Done() SendWithSender(client, req, AfterDelay(delay)) }() wg.Wait() close(cancel) time.Sleep(5 * time.Millisecond) if time.Since(tt) >= delay { t.Fatal("autorest: AfterDelay failed to cancel") } } func TestAfterDelayDoesNotWaitTooLong(t *testing.T) { client := mocks.NewSender() d := 5 * time.Millisecond start := time.Now() r, _ := SendWithSender(client, mocks.NewRequest(), AfterDelay(d)) if time.Since(start) > (5 * d) { t.Fatal("autorest: AfterDelay waited too long (exceeded 5 times specified duration)") } Respond(r, ByDiscardingBody(), ByClosing()) } func TestAsIs(t *testing.T) { client := mocks.NewSender() r1 := mocks.NewResponse() client.AppendResponse(r1) r2, err := SendWithSender(client, mocks.NewRequest(), AsIs()) if err != nil { t.Fatalf("autorest: AsIs returned an unexpected error (%v)", err) } else if !reflect.DeepEqual(r1, r2) { t.Fatalf("autorest: AsIs modified the response -- received %v, expected %v", r2, r1) } Respond(r1, ByDiscardingBody(), ByClosing()) Respond(r2, ByDiscardingBody(), ByClosing()) } func TestDoCloseIfError(t *testing.T) { client := mocks.NewSender() client.AppendResponse(mocks.NewResponseWithStatus("400 BadRequest", http.StatusBadRequest)) r, _ := SendWithSender(client, mocks.NewRequest(), DoErrorIfStatusCode(http.StatusBadRequest), DoCloseIfError()) if r.Body.(*mocks.Body).IsOpen() { t.Fatal("autorest: Expected DoCloseIfError to close response body -- it was left open") } Respond(r, ByDiscardingBody(), ByClosing()) } func TestDoCloseIfErrorAcceptsNilResponse(t *testing.T) { client := mocks.NewSender() SendWithSender(client, mocks.NewRequest(), (func() SendDecorator { return func(s Sender) Sender { return SenderFunc(func(r *http.Request) (*http.Response, error) { resp, err := s.Do(r) if err != nil { resp.Body.Close() } return nil, fmt.Errorf("Faux Error") }) } })(), DoCloseIfError()) } func TestDoCloseIfErrorAcceptsNilBody(t *testing.T) { client := mocks.NewSender() SendWithSender(client, mocks.NewRequest(), (func() SendDecorator { return func(s Sender) Sender { return SenderFunc(func(r *http.Request) (*http.Response, error) { resp, err := s.Do(r) if err != nil { resp.Body.Close() } resp.Body = nil return resp, fmt.Errorf("Faux Error") }) } })(), DoCloseIfError()) } func TestDoErrorIfStatusCode(t *testing.T) { client := mocks.NewSender() client.AppendResponse(mocks.NewResponseWithStatus("400 BadRequest", http.StatusBadRequest)) r, err := SendWithSender(client, mocks.NewRequest(), DoErrorIfStatusCode(http.StatusBadRequest), DoCloseIfError()) if err == nil { t.Fatal("autorest: DoErrorIfStatusCode failed to emit an error for passed code") } Respond(r, ByDiscardingBody(), ByClosing()) } func TestDoErrorIfStatusCodeIgnoresStatusCodes(t *testing.T) { client := mocks.NewSender() client.AppendResponse(newAcceptedResponse()) r, err := SendWithSender(client, mocks.NewRequest(), DoErrorIfStatusCode(http.StatusBadRequest), DoCloseIfError()) if err != nil { t.Fatal("autorest: DoErrorIfStatusCode failed to ignore a status code") } Respond(r, ByDiscardingBody(), ByClosing()) } func TestDoErrorUnlessStatusCode(t *testing.T) { client := mocks.NewSender() client.AppendResponse(mocks.NewResponseWithStatus("400 BadRequest", http.StatusBadRequest)) r, err := SendWithSender(client, mocks.NewRequest(), DoErrorUnlessStatusCode(http.StatusAccepted), DoCloseIfError()) if err == nil { t.Fatal("autorest: DoErrorUnlessStatusCode failed to emit an error for an unknown status code") } Respond(r, ByDiscardingBody(), ByClosing()) } func TestDoErrorUnlessStatusCodeIgnoresStatusCodes(t *testing.T) { client := mocks.NewSender() client.AppendResponse(newAcceptedResponse()) r, err := SendWithSender(client, mocks.NewRequest(), DoErrorUnlessStatusCode(http.StatusAccepted), DoCloseIfError()) if err != nil { t.Fatal("autorest: DoErrorUnlessStatusCode emitted an error for a knonwn status code") } Respond(r, ByDiscardingBody(), ByClosing()) } func TestDoRetryForAttemptsStopsAfterSuccess(t *testing.T) { client := mocks.NewSender() r, err := SendWithSender(client, mocks.NewRequest(), DoRetryForAttempts(5, time.Duration(0))) if client.Attempts() != 1 { t.Fatalf("autorest: DoRetryForAttempts failed to stop after success -- expected attempts %v, actual %v", 1, client.Attempts()) } if err != nil { t.Fatalf("autorest: DoRetryForAttempts returned an unexpected error (%v)", err) } Respond(r, ByDiscardingBody(), ByClosing()) } func TestDoRetryForAttemptsStopsAfterAttempts(t *testing.T) { client := mocks.NewSender() client.SetAndRepeatError(fmt.Errorf("Faux Error"), 10) r, err := SendWithSender(client, mocks.NewRequest(), DoRetryForAttempts(5, time.Duration(0)), DoCloseIfError()) if err == nil { t.Fatal("autorest: Mock client failed to emit errors") } Respond(r, ByDiscardingBody(), ByClosing()) if client.Attempts() != 5 { t.Fatal("autorest: DoRetryForAttempts failed to stop after specified number of attempts") } } func TestDoRetryForAttemptsReturnsResponse(t *testing.T) { client := mocks.NewSender() client.SetError(fmt.Errorf("Faux Error")) r, err := SendWithSender(client, mocks.NewRequest(), DoRetryForAttempts(1, time.Duration(0))) if err == nil { t.Fatal("autorest: Mock client failed to emit errors") } if r == nil { t.Fatal("autorest: DoRetryForAttempts failed to return the underlying response") } Respond(r, ByDiscardingBody(), ByClosing()) } func TestDoRetryForDurationStopsAfterSuccess(t *testing.T) { client := mocks.NewSender() r, err := SendWithSender(client, mocks.NewRequest(), DoRetryForDuration(10*time.Millisecond, time.Duration(0))) if client.Attempts() != 1 { t.Fatalf("autorest: DoRetryForDuration failed to stop after success -- expected attempts %v, actual %v", 1, client.Attempts()) } if err != nil { t.Fatalf("autorest: DoRetryForDuration returned an unexpected error (%v)", err) } Respond(r, ByDiscardingBody(), ByClosing()) } func TestDoRetryForDurationStopsAfterDuration(t *testing.T) { client := mocks.NewSender() client.SetAndRepeatError(fmt.Errorf("Faux Error"), -1) d := 5 * time.Millisecond start := time.Now() r, err := SendWithSender(client, mocks.NewRequest(), DoRetryForDuration(d, time.Duration(0)), DoCloseIfError()) if err == nil { t.Fatal("autorest: Mock client failed to emit errors") } if time.Since(start) < d { t.Fatal("autorest: DoRetryForDuration failed stopped too soon") } Respond(r, ByDiscardingBody(), ByClosing()) } func TestDoRetryForDurationStopsWithinReason(t *testing.T) { client := mocks.NewSender() client.SetAndRepeatError(fmt.Errorf("Faux Error"), -1) d := 5 * time.Second start := time.Now() r, err := SendWithSender(client, mocks.NewRequest(), DoRetryForDuration(d, time.Duration(0)), DoCloseIfError()) if err == nil { t.Fatal("autorest: Mock client failed to emit errors") } if time.Since(start) > (5 * d) { t.Fatal("autorest: DoRetryForDuration failed stopped soon enough (exceeded 5 times specified duration)") } Respond(r, ByDiscardingBody(), ByClosing()) } func TestDoRetryForDurationReturnsResponse(t *testing.T) { client := mocks.NewSender() client.SetAndRepeatError(fmt.Errorf("Faux Error"), -1) r, err := SendWithSender(client, mocks.NewRequest(), DoRetryForDuration(10*time.Millisecond, time.Duration(0)), DoCloseIfError()) if err == nil { t.Fatal("autorest: Mock client failed to emit errors") } if r == nil { t.Fatal("autorest: DoRetryForDuration failed to return the underlying response") } Respond(r, ByDiscardingBody(), ByClosing()) } func TestDelayForBackoff(t *testing.T) { d := 2 * time.Second start := time.Now() DelayForBackoff(d, 0, nil) if time.Since(start) < d { t.Fatal("autorest: DelayForBackoff did not delay as long as expected") } } func TestDelayForBackoff_Cancels(t *testing.T) { cancel := make(chan struct{}) delay := 5 * time.Second var wg sync.WaitGroup wg.Add(1) start := time.Now() go func() { wg.Done() DelayForBackoff(delay, 0, cancel) }() wg.Wait() close(cancel) time.Sleep(5 * time.Millisecond) if time.Since(start) >= delay { t.Fatal("autorest: DelayForBackoff failed to cancel") } } func TestDelayForBackoffWithinReason(t *testing.T) { d := 5 * time.Second maxCoefficient := 2 start := time.Now() DelayForBackoff(d, 0, nil) if time.Since(start) > (time.Duration(maxCoefficient) * d) { t.Fatalf("autorest: DelayForBackoff delayed too long (exceeded %d times the specified duration)", maxCoefficient) } } func TestDoPollForStatusCodes_IgnoresUnspecifiedStatusCodes(t *testing.T) { client := mocks.NewSender() r, _ := SendWithSender(client, mocks.NewRequest(), DoPollForStatusCodes(time.Duration(0), time.Duration(0))) if client.Attempts() != 1 { t.Fatalf("autorest: Sender#DoPollForStatusCodes polled for unspecified status code") } Respond(r, ByDiscardingBody(), ByClosing()) } func TestDoPollForStatusCodes_PollsForSpecifiedStatusCodes(t *testing.T) { client := mocks.NewSender() client.AppendResponse(newAcceptedResponse()) r, _ := SendWithSender(client, mocks.NewRequest(), DoPollForStatusCodes(time.Millisecond, time.Millisecond, http.StatusAccepted)) if client.Attempts() != 2 { t.Fatalf("autorest: Sender#DoPollForStatusCodes failed to poll for specified status code") } Respond(r, ByDiscardingBody(), ByClosing()) } func TestDoPollForStatusCodes_CanBeCanceled(t *testing.T) { cancel := make(chan struct{}) delay := 5 * time.Second r := mocks.NewResponse() mocks.SetAcceptedHeaders(r) client := mocks.NewSender() client.AppendAndRepeatResponse(r, 100) var wg sync.WaitGroup wg.Add(1) start := time.Now() go func() { wg.Done() r, _ := SendWithSender(client, mocks.NewRequest(), DoPollForStatusCodes(time.Millisecond, time.Millisecond, http.StatusAccepted)) Respond(r, ByDiscardingBody(), ByClosing()) }() wg.Wait() close(cancel) time.Sleep(5 * time.Millisecond) if time.Since(start) >= delay { t.Fatalf("autorest: Sender#DoPollForStatusCodes failed to cancel") } } func TestDoPollForStatusCodes_ClosesAllNonreturnedResponseBodiesWhenPolling(t *testing.T) { resp := newAcceptedResponse() client := mocks.NewSender() client.AppendAndRepeatResponse(resp, 2) r, _ := SendWithSender(client, mocks.NewRequest(), DoPollForStatusCodes(time.Millisecond, time.Millisecond, http.StatusAccepted)) if resp.Body.(*mocks.Body).IsOpen() || resp.Body.(*mocks.Body).CloseAttempts() < 2 { t.Fatalf("autorest: Sender#DoPollForStatusCodes did not close unreturned response bodies") } Respond(r, ByDiscardingBody(), ByClosing()) } func TestDoPollForStatusCodes_LeavesLastResponseBodyOpen(t *testing.T) { client := mocks.NewSender() client.AppendResponse(newAcceptedResponse()) r, _ := SendWithSender(client, mocks.NewRequest(), DoPollForStatusCodes(time.Millisecond, time.Millisecond, http.StatusAccepted)) if !r.Body.(*mocks.Body).IsOpen() { t.Fatalf("autorest: Sender#DoPollForStatusCodes did not leave open the body of the last response") } Respond(r, ByDiscardingBody(), ByClosing()) } func TestDoPollForStatusCodes_StopsPollingAfterAnError(t *testing.T) { client := mocks.NewSender() client.AppendAndRepeatResponse(newAcceptedResponse(), 5) client.SetError(fmt.Errorf("Faux Error")) client.SetEmitErrorAfter(1) r, _ := SendWithSender(client, mocks.NewRequest(), DoPollForStatusCodes(time.Millisecond, time.Millisecond, http.StatusAccepted)) if client.Attempts() > 2 { t.Fatalf("autorest: Sender#DoPollForStatusCodes failed to stop polling after receiving an error") } Respond(r, ByDiscardingBody(), ByClosing()) } func TestDoPollForStatusCodes_ReturnsPollingError(t *testing.T) { client := mocks.NewSender() client.AppendAndRepeatResponse(newAcceptedResponse(), 5) client.SetError(fmt.Errorf("Faux Error")) client.SetEmitErrorAfter(1) r, err := SendWithSender(client, mocks.NewRequest(), DoPollForStatusCodes(time.Millisecond, time.Millisecond, http.StatusAccepted)) if err == nil { t.Fatalf("autorest: Sender#DoPollForStatusCodes failed to return error from polling") } Respond(r, ByDiscardingBody(), ByClosing()) } func TestWithLogging_Logs(t *testing.T) { buf := &bytes.Buffer{} logger := log.New(buf, "autorest: ", 0) client := mocks.NewSender() r, _ := SendWithSender(client, &http.Request{}, WithLogging(logger)) if buf.String() == "" { t.Fatal("autorest: Sender#WithLogging failed to log the request") } Respond(r, ByDiscardingBody(), ByClosing()) } func TestWithLogging_HandlesMissingResponse(t *testing.T) { buf := &bytes.Buffer{} logger := log.New(buf, "autorest: ", 0) client := mocks.NewSender() client.AppendResponse(nil) client.SetError(fmt.Errorf("Faux Error")) r, err := SendWithSender(client, &http.Request{}, WithLogging(logger)) if r != nil || err == nil { t.Fatal("autorest: Sender#WithLogging returned a valid response -- expecting nil") } if buf.String() == "" { t.Fatal("autorest: Sender#WithLogging failed to log the request for a nil response") } Respond(r, ByDiscardingBody(), ByClosing()) } func TestDoRetryForStatusCodesWithSuccess(t *testing.T) { client := mocks.NewSender() client.AppendAndRepeatResponse(mocks.NewResponseWithStatus("408 Request Timeout", http.StatusRequestTimeout), 2) client.AppendResponse(mocks.NewResponseWithStatus("200 OK", http.StatusOK)) r, _ := SendWithSender(client, mocks.NewRequest(), DoRetryForStatusCodes(5, time.Duration(2*time.Second), http.StatusRequestTimeout), ) Respond(r, ByDiscardingBody(), ByClosing()) if client.Attempts() != 3 { t.Fatalf("autorest: Sender#DoRetryForStatusCodes -- Got: StatusCode %v in %v attempts; Want: StatusCode 200 OK in 2 attempts -- ", r.Status, client.Attempts()-1) } } func TestDoRetryForStatusCodesWithNoSuccess(t *testing.T) { client := mocks.NewSender() client.AppendAndRepeatResponse(mocks.NewResponseWithStatus("504 Gateway Timeout", http.StatusGatewayTimeout), 5) r, _ := SendWithSender(client, mocks.NewRequest(), DoRetryForStatusCodes(2, time.Duration(2*time.Second), http.StatusGatewayTimeout), ) Respond(r, ByDiscardingBody(), ByClosing()) if client.Attempts() != 3 { t.Fatalf("autorest: Sender#DoRetryForStatusCodes -- Got: failed stop after %v retry attempts; Want: Stop after 2 retry attempts", client.Attempts()-1) } } func TestDoRetryForStatusCodes_CodeNotInRetryList(t *testing.T) { client := mocks.NewSender() client.AppendAndRepeatResponse(mocks.NewResponseWithStatus("204 No Content", http.StatusNoContent), 1) r, _ := SendWithSender(client, mocks.NewRequest(), DoRetryForStatusCodes(6, time.Duration(2*time.Second), http.StatusGatewayTimeout), ) Respond(r, ByDiscardingBody(), ByClosing()) if client.Attempts() != 1 || r.Status != "204 No Content" { t.Fatalf("autorest: Sender#DoRetryForStatusCodes -- Got: Retry attempts %v for StatusCode %v; Want: 0 attempts for StatusCode 204", client.Attempts(), r.Status) } } func TestDoRetryForStatusCodes_RequestBodyReadError(t *testing.T) { client := mocks.NewSender() client.AppendAndRepeatResponse(mocks.NewResponseWithStatus("204 No Content", http.StatusNoContent), 2) r, err := SendWithSender(client, mocks.NewRequestWithCloseBody(), DoRetryForStatusCodes(6, time.Duration(2*time.Second), http.StatusGatewayTimeout), ) Respond(r, ByDiscardingBody(), ByClosing()) if err == nil || client.Attempts() != 0 { t.Fatalf("autorest: Sender#DoRetryForStatusCodes -- Got: Not failed for request body read error; Want: Failed for body read error - %v", err) } } func newAcceptedResponse() *http.Response { resp := mocks.NewResponseWithStatus("202 Accepted", http.StatusAccepted) mocks.SetAcceptedHeaders(resp) return resp } func TestDelayWithRetryAfterWithSuccess(t *testing.T) { after, retries := 5, 2 totalSecs := after * retries client := mocks.NewSender() resp := mocks.NewResponseWithStatus("429 Too many requests", http.StatusTooManyRequests) mocks.SetResponseHeader(resp, "Retry-After", fmt.Sprintf("%v", after)) client.AppendAndRepeatResponse(resp, retries) client.AppendResponse(mocks.NewResponseWithStatus("200 OK", http.StatusOK)) d := time.Second * time.Duration(totalSecs) start := time.Now() r, _ := SendWithSender(client, mocks.NewRequest(), DoRetryForStatusCodes(5, time.Duration(time.Second), http.StatusTooManyRequests), ) if time.Since(start) < d { t.Fatal("autorest: DelayWithRetryAfter failed stopped too soon") } Respond(r, ByDiscardingBody(), ByClosing()) if client.Attempts() != 3 { t.Fatalf("autorest: Sender#DelayWithRetryAfter -- Got: StatusCode %v in %v attempts; Want: StatusCode 200 OK in 2 attempts -- ", r.Status, client.Attempts()-1) } } go-autorest-8.3.1/autorest/to/000077500000000000000000000000001315133516500162675ustar00rootroot00000000000000go-autorest-8.3.1/autorest/to/convert.go000066400000000000000000000055771315133516500203140ustar00rootroot00000000000000/* Package to provides helpers to ease working with pointer values of marshalled structures. */ package to // String returns a string value for the passed string pointer. It returns the empty string if the // pointer is nil. func String(s *string) string { if s != nil { return *s } return "" } // StringPtr returns a pointer to the passed string. func StringPtr(s string) *string { return &s } // StringSlice returns a string slice value for the passed string slice pointer. It returns a nil // slice if the pointer is nil. func StringSlice(s *[]string) []string { if s != nil { return *s } return nil } // StringSlicePtr returns a pointer to the passed string slice. func StringSlicePtr(s []string) *[]string { return &s } // StringMap returns a map of strings built from the map of string pointers. The empty string is // used for nil pointers. func StringMap(msp map[string]*string) map[string]string { ms := make(map[string]string, len(msp)) for k, sp := range msp { if sp != nil { ms[k] = *sp } else { ms[k] = "" } } return ms } // StringMapPtr returns a pointer to a map of string pointers built from the passed map of strings. func StringMapPtr(ms map[string]string) *map[string]*string { msp := make(map[string]*string, len(ms)) for k, s := range ms { msp[k] = StringPtr(s) } return &msp } // Bool returns a bool value for the passed bool pointer. It returns false if the pointer is nil. func Bool(b *bool) bool { if b != nil { return *b } return false } // BoolPtr returns a pointer to the passed bool. func BoolPtr(b bool) *bool { return &b } // Int returns an int value for the passed int pointer. It returns 0 if the pointer is nil. func Int(i *int) int { if i != nil { return *i } return 0 } // IntPtr returns a pointer to the passed int. func IntPtr(i int) *int { return &i } // Int32 returns an int value for the passed int pointer. It returns 0 if the pointer is nil. func Int32(i *int32) int32 { if i != nil { return *i } return 0 } // Int32Ptr returns a pointer to the passed int32. func Int32Ptr(i int32) *int32 { return &i } // Int64 returns an int value for the passed int pointer. It returns 0 if the pointer is nil. func Int64(i *int64) int64 { if i != nil { return *i } return 0 } // Int64Ptr returns a pointer to the passed int64. func Int64Ptr(i int64) *int64 { return &i } // Float32 returns an int value for the passed int pointer. It returns 0.0 if the pointer is nil. func Float32(i *float32) float32 { if i != nil { return *i } return 0.0 } // Float32Ptr returns a pointer to the passed float32. func Float32Ptr(i float32) *float32 { return &i } // Float64 returns an int value for the passed int pointer. It returns 0.0 if the pointer is nil. func Float64(i *float64) float64 { if i != nil { return *i } return 0.0 } // Float64Ptr returns a pointer to the passed float64. func Float64Ptr(i float64) *float64 { return &i } go-autorest-8.3.1/autorest/to/convert_test.go000066400000000000000000000125341315133516500213420ustar00rootroot00000000000000package to import ( "reflect" "testing" ) func TestString(t *testing.T) { v := "" if String(&v) != v { t.Fatalf("to: String failed to return the correct string -- expected %v, received %v", v, String(&v)) } } func TestStringHandlesNil(t *testing.T) { if String(nil) != "" { t.Fatalf("to: String failed to correctly convert nil -- expected %v, received %v", "", String(nil)) } } func TestStringPtr(t *testing.T) { v := "" if *StringPtr(v) != v { t.Fatalf("to: StringPtr failed to return the correct string -- expected %v, received %v", v, *StringPtr(v)) } } func TestStringSlice(t *testing.T) { v := []string{} if out := StringSlice(&v); !reflect.DeepEqual(out, v) { t.Fatalf("to: StringSlice failed to return the correct slice -- expected %v, received %v", v, out) } } func TestStringSliceHandlesNil(t *testing.T) { if out := StringSlice(nil); out != nil { t.Fatalf("to: StringSlice failed to correctly convert nil -- expected %v, received %v", nil, out) } } func TestStringSlicePtr(t *testing.T) { v := []string{"a", "b"} if out := StringSlicePtr(v); !reflect.DeepEqual(*out, v) { t.Fatalf("to: StringSlicePtr failed to return the correct slice -- expected %v, received %v", v, *out) } } func TestStringMap(t *testing.T) { msp := map[string]*string{"foo": StringPtr("foo"), "bar": StringPtr("bar"), "baz": StringPtr("baz")} for k, v := range StringMap(msp) { if *msp[k] != v { t.Fatalf("to: StringMap incorrectly converted an entry -- expected [%s]%v, received[%s]%v", k, v, k, *msp[k]) } } } func TestStringMapHandlesNil(t *testing.T) { msp := map[string]*string{"foo": StringPtr("foo"), "bar": nil, "baz": StringPtr("baz")} for k, v := range StringMap(msp) { if msp[k] == nil && v != "" { t.Fatalf("to: StringMap incorrectly converted a nil entry -- expected [%s]%v, received[%s]%v", k, v, k, *msp[k]) } } } func TestStringMapPtr(t *testing.T) { ms := map[string]string{"foo": "foo", "bar": "bar", "baz": "baz"} for k, msp := range *StringMapPtr(ms) { if ms[k] != *msp { t.Fatalf("to: StringMapPtr incorrectly converted an entry -- expected [%s]%v, received[%s]%v", k, ms[k], k, *msp) } } } func TestBool(t *testing.T) { v := false if Bool(&v) != v { t.Fatalf("to: Bool failed to return the correct string -- expected %v, received %v", v, Bool(&v)) } } func TestBoolHandlesNil(t *testing.T) { if Bool(nil) != false { t.Fatalf("to: Bool failed to correctly convert nil -- expected %v, received %v", false, Bool(nil)) } } func TestBoolPtr(t *testing.T) { v := false if *BoolPtr(v) != v { t.Fatalf("to: BoolPtr failed to return the correct string -- expected %v, received %v", v, *BoolPtr(v)) } } func TestInt(t *testing.T) { v := 0 if Int(&v) != v { t.Fatalf("to: Int failed to return the correct string -- expected %v, received %v", v, Int(&v)) } } func TestIntHandlesNil(t *testing.T) { if Int(nil) != 0 { t.Fatalf("to: Int failed to correctly convert nil -- expected %v, received %v", 0, Int(nil)) } } func TestIntPtr(t *testing.T) { v := 0 if *IntPtr(v) != v { t.Fatalf("to: IntPtr failed to return the correct string -- expected %v, received %v", v, *IntPtr(v)) } } func TestInt32(t *testing.T) { v := int32(0) if Int32(&v) != v { t.Fatalf("to: Int32 failed to return the correct string -- expected %v, received %v", v, Int32(&v)) } } func TestInt32HandlesNil(t *testing.T) { if Int32(nil) != int32(0) { t.Fatalf("to: Int32 failed to correctly convert nil -- expected %v, received %v", 0, Int32(nil)) } } func TestInt32Ptr(t *testing.T) { v := int32(0) if *Int32Ptr(v) != v { t.Fatalf("to: Int32Ptr failed to return the correct string -- expected %v, received %v", v, *Int32Ptr(v)) } } func TestInt64(t *testing.T) { v := int64(0) if Int64(&v) != v { t.Fatalf("to: Int64 failed to return the correct string -- expected %v, received %v", v, Int64(&v)) } } func TestInt64HandlesNil(t *testing.T) { if Int64(nil) != int64(0) { t.Fatalf("to: Int64 failed to correctly convert nil -- expected %v, received %v", 0, Int64(nil)) } } func TestInt64Ptr(t *testing.T) { v := int64(0) if *Int64Ptr(v) != v { t.Fatalf("to: Int64Ptr failed to return the correct string -- expected %v, received %v", v, *Int64Ptr(v)) } } func TestFloat32(t *testing.T) { v := float32(0) if Float32(&v) != v { t.Fatalf("to: Float32 failed to return the correct string -- expected %v, received %v", v, Float32(&v)) } } func TestFloat32HandlesNil(t *testing.T) { if Float32(nil) != float32(0) { t.Fatalf("to: Float32 failed to correctly convert nil -- expected %v, received %v", 0, Float32(nil)) } } func TestFloat32Ptr(t *testing.T) { v := float32(0) if *Float32Ptr(v) != v { t.Fatalf("to: Float32Ptr failed to return the correct string -- expected %v, received %v", v, *Float32Ptr(v)) } } func TestFloat64(t *testing.T) { v := float64(0) if Float64(&v) != v { t.Fatalf("to: Float64 failed to return the correct string -- expected %v, received %v", v, Float64(&v)) } } func TestFloat64HandlesNil(t *testing.T) { if Float64(nil) != float64(0) { t.Fatalf("to: Float64 failed to correctly convert nil -- expected %v, received %v", 0, Float64(nil)) } } func TestFloat64Ptr(t *testing.T) { v := float64(0) if *Float64Ptr(v) != v { t.Fatalf("to: Float64Ptr failed to return the correct string -- expected %v, received %v", v, *Float64Ptr(v)) } } go-autorest-8.3.1/autorest/utility.go000066400000000000000000000105161315133516500177020ustar00rootroot00000000000000package autorest import ( "bytes" "encoding/json" "encoding/xml" "fmt" "io" "net/url" "reflect" "sort" "strings" ) // EncodedAs is a series of constants specifying various data encodings type EncodedAs string const ( // EncodedAsJSON states that data is encoded as JSON EncodedAsJSON EncodedAs = "JSON" // EncodedAsXML states that data is encoded as Xml EncodedAsXML EncodedAs = "XML" ) // Decoder defines the decoding method json.Decoder and xml.Decoder share type Decoder interface { Decode(v interface{}) error } // NewDecoder creates a new decoder appropriate to the passed encoding. // encodedAs specifies the type of encoding and r supplies the io.Reader containing the // encoded data. func NewDecoder(encodedAs EncodedAs, r io.Reader) Decoder { if encodedAs == EncodedAsJSON { return json.NewDecoder(r) } else if encodedAs == EncodedAsXML { return xml.NewDecoder(r) } return nil } // CopyAndDecode decodes the data from the passed io.Reader while making a copy. Having a copy // is especially useful if there is a chance the data will fail to decode. // encodedAs specifies the expected encoding, r provides the io.Reader to the data, and v // is the decoding destination. func CopyAndDecode(encodedAs EncodedAs, r io.Reader, v interface{}) (bytes.Buffer, error) { b := bytes.Buffer{} return b, NewDecoder(encodedAs, io.TeeReader(r, &b)).Decode(v) } // TeeReadCloser returns a ReadCloser that writes to w what it reads from rc. // It utilizes io.TeeReader to copy the data read and has the same behavior when reading. // Further, when it is closed, it ensures that rc is closed as well. func TeeReadCloser(rc io.ReadCloser, w io.Writer) io.ReadCloser { return &teeReadCloser{rc, io.TeeReader(rc, w)} } type teeReadCloser struct { rc io.ReadCloser r io.Reader } func (t *teeReadCloser) Read(p []byte) (int, error) { return t.r.Read(p) } func (t *teeReadCloser) Close() error { return t.rc.Close() } func containsInt(ints []int, n int) bool { for _, i := range ints { if i == n { return true } } return false } func escapeValueStrings(m map[string]string) map[string]string { for key, value := range m { m[key] = url.QueryEscape(value) } return m } func ensureValueStrings(mapOfInterface map[string]interface{}) map[string]string { mapOfStrings := make(map[string]string) for key, value := range mapOfInterface { mapOfStrings[key] = ensureValueString(value) } return mapOfStrings } func ensureValueString(value interface{}) string { if value == nil { return "" } switch v := value.(type) { case string: return v case []byte: return string(v) default: return fmt.Sprintf("%v", v) } } // MapToValues method converts map[string]interface{} to url.Values. func MapToValues(m map[string]interface{}) url.Values { v := url.Values{} for key, value := range m { x := reflect.ValueOf(value) if x.Kind() == reflect.Array || x.Kind() == reflect.Slice { for i := 0; i < x.Len(); i++ { v.Add(key, ensureValueString(x.Index(i))) } } else { v.Add(key, ensureValueString(value)) } } return v } // String method converts interface v to string. If interface is a list, it // joins list elements using separator. func String(v interface{}, sep ...string) string { if len(sep) > 0 { return ensureValueString(strings.Join(v.([]string), sep[0])) } return ensureValueString(v) } // Encode method encodes url path and query parameters. func Encode(location string, v interface{}, sep ...string) string { s := String(v, sep...) switch strings.ToLower(location) { case "path": return pathEscape(s) case "query": return queryEscape(s) default: return s } } func pathEscape(s string) string { return strings.Replace(url.QueryEscape(s), "+", "%20", -1) } func queryEscape(s string) string { return url.QueryEscape(s) } // This method is same as Encode() method of "net/url" go package, // except it does not encode the query parameters because they // already come encoded. It formats values map in query format (bar=foo&a=b). func createQuery(v url.Values) string { var buf bytes.Buffer keys := make([]string, 0, len(v)) for k := range v { keys = append(keys, k) } sort.Strings(keys) for _, k := range keys { vs := v[k] prefix := url.QueryEscape(k) + "=" for _, v := range vs { if buf.Len() > 0 { buf.WriteByte('&') } buf.WriteString(prefix) buf.WriteString(v) } } return buf.String() } go-autorest-8.3.1/autorest/utility_test.go000066400000000000000000000214571315133516500207470ustar00rootroot00000000000000package autorest import ( "bytes" "encoding/json" "encoding/xml" "fmt" "net/http" "net/url" "reflect" "sort" "strings" "testing" "github.com/Azure/go-autorest/autorest/mocks" ) const ( jsonT = ` { "name":"Rob Pike", "age":42 }` xmlT = ` Rob Pike 42 ` ) func TestNewDecoderCreatesJSONDecoder(t *testing.T) { d := NewDecoder(EncodedAsJSON, strings.NewReader(jsonT)) _, ok := d.(*json.Decoder) if d == nil || !ok { t.Fatal("autorest: NewDecoder failed to create a JSON decoder when requested") } } func TestNewDecoderCreatesXMLDecoder(t *testing.T) { d := NewDecoder(EncodedAsXML, strings.NewReader(xmlT)) _, ok := d.(*xml.Decoder) if d == nil || !ok { t.Fatal("autorest: NewDecoder failed to create an XML decoder when requested") } } func TestNewDecoderReturnsNilForUnknownEncoding(t *testing.T) { d := NewDecoder("unknown", strings.NewReader(xmlT)) if d != nil { t.Fatal("autorest: NewDecoder created a decoder for an unknown encoding") } } func TestCopyAndDecodeDecodesJSON(t *testing.T) { _, err := CopyAndDecode(EncodedAsJSON, strings.NewReader(jsonT), &mocks.T{}) if err != nil { t.Fatalf("autorest: CopyAndDecode returned an error with valid JSON - %v", err) } } func TestCopyAndDecodeDecodesXML(t *testing.T) { _, err := CopyAndDecode(EncodedAsXML, strings.NewReader(xmlT), &mocks.T{}) if err != nil { t.Fatalf("autorest: CopyAndDecode returned an error with valid XML - %v", err) } } func TestCopyAndDecodeReturnsJSONDecodingErrors(t *testing.T) { _, err := CopyAndDecode(EncodedAsJSON, strings.NewReader(jsonT[0:len(jsonT)-2]), &mocks.T{}) if err == nil { t.Fatalf("autorest: CopyAndDecode failed to return an error with invalid JSON") } } func TestCopyAndDecodeReturnsXMLDecodingErrors(t *testing.T) { _, err := CopyAndDecode(EncodedAsXML, strings.NewReader(xmlT[0:len(xmlT)-2]), &mocks.T{}) if err == nil { t.Fatalf("autorest: CopyAndDecode failed to return an error with invalid XML") } } func TestCopyAndDecodeAlwaysReturnsACopy(t *testing.T) { b, _ := CopyAndDecode(EncodedAsJSON, strings.NewReader(jsonT), &mocks.T{}) if b.String() != jsonT { t.Fatalf("autorest: CopyAndDecode failed to return a valid copy of the data - %v", b.String()) } } func TestTeeReadCloser_Copies(t *testing.T) { v := &mocks.T{} r := mocks.NewResponseWithContent(jsonT) b := &bytes.Buffer{} r.Body = TeeReadCloser(r.Body, b) err := Respond(r, ByUnmarshallingJSON(v), ByClosing()) if err != nil { t.Fatalf("autorest: TeeReadCloser returned an unexpected error -- %v", err) } if b.String() != jsonT { t.Fatalf("autorest: TeeReadCloser failed to copy the bytes read") } } func TestTeeReadCloser_PassesReadErrors(t *testing.T) { v := &mocks.T{} r := mocks.NewResponseWithContent(jsonT) r.Body.(*mocks.Body).Close() r.Body = TeeReadCloser(r.Body, &bytes.Buffer{}) err := Respond(r, ByUnmarshallingJSON(v), ByClosing()) if err == nil { t.Fatalf("autorest: TeeReadCloser failed to return the expected error") } } func TestTeeReadCloser_ClosesWrappedReader(t *testing.T) { v := &mocks.T{} r := mocks.NewResponseWithContent(jsonT) b := r.Body.(*mocks.Body) r.Body = TeeReadCloser(r.Body, &bytes.Buffer{}) err := Respond(r, ByUnmarshallingJSON(v), ByClosing()) if err != nil { t.Fatalf("autorest: TeeReadCloser returned an unexpected error -- %v", err) } if b.IsOpen() { t.Fatalf("autorest: TeeReadCloser failed to close the nested io.ReadCloser") } } func TestContainsIntFindsValue(t *testing.T) { ints := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} v := 5 if !containsInt(ints, v) { t.Fatalf("autorest: containsInt failed to find %v in %v", v, ints) } } func TestContainsIntDoesNotFindValue(t *testing.T) { ints := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} v := 42 if containsInt(ints, v) { t.Fatalf("autorest: containsInt unexpectedly found %v in %v", v, ints) } } func TestContainsIntAcceptsEmptyList(t *testing.T) { ints := make([]int, 10) if containsInt(ints, 42) { t.Fatalf("autorest: containsInt failed to handle an empty list") } } func TestContainsIntAcceptsNilList(t *testing.T) { var ints []int if containsInt(ints, 42) { t.Fatalf("autorest: containsInt failed to handle an nil list") } } func TestEscapeStrings(t *testing.T) { m := map[string]string{ "string": "a long string with = odd characters", "int": "42", "nil": "", } r := map[string]string{ "string": "a+long+string+with+%3D+odd+characters", "int": "42", "nil": "", } v := escapeValueStrings(m) if !reflect.DeepEqual(v, r) { t.Fatalf("autorest: ensureValueStrings returned %v\n", v) } } func TestEnsureStrings(t *testing.T) { m := map[string]interface{}{ "string": "string", "int": 42, "nil": nil, "bytes": []byte{255, 254, 253}, } r := map[string]string{ "string": "string", "int": "42", "nil": "", "bytes": string([]byte{255, 254, 253}), } v := ensureValueStrings(m) if !reflect.DeepEqual(v, r) { t.Fatalf("autorest: ensureValueStrings returned %v\n", v) } } func ExampleString() { m := []string{ "string1", "string2", "string3", } fmt.Println(String(m, ",")) // Output: string1,string2,string3 } func TestStringWithValidString(t *testing.T) { i := 123 if String(i) != "123" { t.Fatal("autorest: String method failed to convert integer 123 to string") } } func TestEncodeWithValidPath(t *testing.T) { s := Encode("Path", "Hello Gopher") if s != "Hello%20Gopher" { t.Fatalf("autorest: Encode method failed for valid path encoding. Got: %v; Want: %v", s, "Hello%20Gopher") } } func TestEncodeWithValidQuery(t *testing.T) { s := Encode("Query", "Hello Gopher") if s != "Hello+Gopher" { t.Fatalf("autorest: Encode method failed for valid query encoding. Got: '%v'; Want: 'Hello+Gopher'", s) } } func TestEncodeWithValidNotPathQuery(t *testing.T) { s := Encode("Host", "Hello Gopher") if s != "Hello Gopher" { t.Fatalf("autorest: Encode method failed for parameter not query or path. Got: '%v'; Want: 'Hello Gopher'", s) } } func TestMapToValues(t *testing.T) { m := map[string]interface{}{ "a": "a", "b": 2, } v := url.Values{} v.Add("a", "a") v.Add("b", "2") if !isEqual(v, MapToValues(m)) { t.Fatalf("autorest: MapToValues method failed to return correct values - expected(%v) got(%v)", v, MapToValues(m)) } } func TestMapToValuesWithArrayValues(t *testing.T) { m := map[string]interface{}{ "a": []string{"a", "b"}, "b": 2, "c": []int{3, 4}, } v := url.Values{} v.Add("a", "a") v.Add("a", "b") v.Add("b", "2") v.Add("c", "3") v.Add("c", "4") if !isEqual(v, MapToValues(m)) { t.Fatalf("autorest: MapToValues method failed to return correct values - expected(%v) got(%v)", v, MapToValues(m)) } } func isEqual(v, u url.Values) bool { for key, value := range v { if len(u[key]) == 0 { return false } sort.Strings(value) sort.Strings(u[key]) for i := range value { if value[i] != u[key][i] { return false } } u.Del(key) } if len(u) > 0 { return false } return true } func doEnsureBodyClosed(t *testing.T) SendDecorator { return func(s Sender) Sender { return SenderFunc(func(r *http.Request) (*http.Response, error) { resp, err := s.Do(r) if resp != nil && resp.Body != nil && resp.Body.(*mocks.Body).IsOpen() { t.Fatal("autorest: Expected Body to be closed -- it was left open") } return resp, err }) } } type mockAuthorizer struct{} func (ma mockAuthorizer) WithAuthorization() PrepareDecorator { return WithHeader(headerAuthorization, mocks.TestAuthorizationHeader) } type mockFailingAuthorizer struct{} func (mfa mockFailingAuthorizer) WithAuthorization() PrepareDecorator { return func(p Preparer) Preparer { return PreparerFunc(func(r *http.Request) (*http.Request, error) { return r, fmt.Errorf("ERROR: mockFailingAuthorizer returned expected error") }) } } type mockInspector struct { wasInvoked bool } func (mi *mockInspector) WithInspection() PrepareDecorator { return func(p Preparer) Preparer { return PreparerFunc(func(r *http.Request) (*http.Request, error) { mi.wasInvoked = true return p.Prepare(r) }) } } func (mi *mockInspector) ByInspecting() RespondDecorator { return func(r Responder) Responder { return ResponderFunc(func(resp *http.Response) error { mi.wasInvoked = true return r.Respond(resp) }) } } func withMessage(output *string, msg string) SendDecorator { return func(s Sender) Sender { return SenderFunc(func(r *http.Request) (*http.Response, error) { resp, err := s.Do(r) if err == nil { *output += msg } return resp, err }) } } func withErrorRespondDecorator(e *error) RespondDecorator { return func(r Responder) Responder { return ResponderFunc(func(resp *http.Response) error { err := r.Respond(resp) if err != nil { return err } *e = fmt.Errorf("autorest: Faux Respond Error") return *e }) } } go-autorest-8.3.1/autorest/utils/000077500000000000000000000000001315133516500170055ustar00rootroot00000000000000go-autorest-8.3.1/autorest/utils/auth.go000066400000000000000000000022641315133516500203010ustar00rootroot00000000000000package utils import ( "fmt" "os" "github.com/Azure/go-autorest/autorest" "github.com/Azure/go-autorest/autorest/adal" "github.com/Azure/go-autorest/autorest/azure" ) // GetAuthorizer gets an Azure Service Principal authorizer. // This func assumes "AZURE_TENANT_ID", "AZURE_CLIENT_ID", // "AZURE_CLIENT_SECRET" are set as environment variables. func GetAuthorizer(env azure.Environment) (*autorest.BearerAuthorizer, error) { tenantID := GetEnvVarOrExit("AZURE_TENANT_ID") oauthConfig, err := adal.NewOAuthConfig(env.ActiveDirectoryEndpoint, tenantID) if err != nil { return nil, err } clientID := GetEnvVarOrExit("AZURE_CLIENT_ID") clientSecret := GetEnvVarOrExit("AZURE_CLIENT_SECRET") spToken, err := adal.NewServicePrincipalToken(*oauthConfig, clientID, clientSecret, env.ResourceManagerEndpoint) if err != nil { return nil, err } return autorest.NewBearerAuthorizer(spToken), nil } // GetEnvVarOrExit returns the value of specified environment variable or terminates if it's not defined. func GetEnvVarOrExit(varName string) string { value := os.Getenv(varName) if value == "" { fmt.Printf("Missing environment variable %s\n", varName) os.Exit(1) } return value } go-autorest-8.3.1/autorest/utils/commit.go000066400000000000000000000004351315133516500206260ustar00rootroot00000000000000package utils import ( "bytes" "os/exec" ) // GetCommit returns git HEAD (short) func GetCommit() string { cmd := exec.Command("git", "rev-parse", "HEAD") var out bytes.Buffer cmd.Stdout = &out err := cmd.Run() if err != nil { return "" } return string(out.Bytes()[:7]) } go-autorest-8.3.1/autorest/validation/000077500000000000000000000000001315133516500177775ustar00rootroot00000000000000go-autorest-8.3.1/autorest/validation/validation.go000066400000000000000000000243221315133516500224630ustar00rootroot00000000000000/* Package validation provides methods for validating parameter value using reflection. */ package validation import ( "fmt" "reflect" "regexp" "strings" ) // Constraint stores constraint name, target field name // Rule and chain validations. type Constraint struct { // Target field name for validation. Target string // Constraint name e.g. minLength, MaxLength, Pattern, etc. Name string // Rule for constraint e.g. greater than 10, less than 5 etc. Rule interface{} // Chain Validations for struct type Chain []Constraint } // Validation stores parameter-wise validation. type Validation struct { TargetValue interface{} Constraints []Constraint } // Constraint list const ( Empty = "Empty" Null = "Null" ReadOnly = "ReadOnly" Pattern = "Pattern" MaxLength = "MaxLength" MinLength = "MinLength" MaxItems = "MaxItems" MinItems = "MinItems" MultipleOf = "MultipleOf" UniqueItems = "UniqueItems" InclusiveMaximum = "InclusiveMaximum" ExclusiveMaximum = "ExclusiveMaximum" ExclusiveMinimum = "ExclusiveMinimum" InclusiveMinimum = "InclusiveMinimum" ) // Validate method validates constraints on parameter // passed in validation array. func Validate(m []Validation) error { for _, item := range m { v := reflect.ValueOf(item.TargetValue) for _, constraint := range item.Constraints { var err error switch v.Kind() { case reflect.Ptr: err = validatePtr(v, constraint) case reflect.String: err = validateString(v, constraint) case reflect.Struct: err = validateStruct(v, constraint) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: err = validateInt(v, constraint) case reflect.Float32, reflect.Float64: err = validateFloat(v, constraint) case reflect.Array, reflect.Slice, reflect.Map: err = validateArrayMap(v, constraint) default: err = createError(v, constraint, fmt.Sprintf("unknown type %v", v.Kind())) } if err != nil { return err } } } return nil } func validateStruct(x reflect.Value, v Constraint, name ...string) error { //Get field name from target name which is in format a.b.c s := strings.Split(v.Target, ".") f := x.FieldByName(s[len(s)-1]) if isZero(f) { return createError(x, v, fmt.Sprintf("field %q doesn't exist", v.Target)) } if err := Validate([]Validation{ { TargetValue: getInterfaceValue(f), Constraints: []Constraint{v}, }, }); err != nil { return err } return nil } func validatePtr(x reflect.Value, v Constraint) error { if v.Name == ReadOnly { if !x.IsNil() { return createError(x.Elem(), v, "readonly parameter; must send as nil or empty in request") } return nil } if x.IsNil() { return checkNil(x, v) } if v.Chain != nil { return Validate([]Validation{ { TargetValue: getInterfaceValue(x.Elem()), Constraints: v.Chain, }, }) } return nil } func validateInt(x reflect.Value, v Constraint) error { i := x.Int() r, ok := v.Rule.(int) if !ok { return createError(x, v, fmt.Sprintf("rule must be integer value for %v constraint; got: %v", v.Name, v.Rule)) } switch v.Name { case MultipleOf: if i%int64(r) != 0 { return createError(x, v, fmt.Sprintf("value must be a multiple of %v", r)) } case ExclusiveMinimum: if i <= int64(r) { return createError(x, v, fmt.Sprintf("value must be greater than %v", r)) } case ExclusiveMaximum: if i >= int64(r) { return createError(x, v, fmt.Sprintf("value must be less than %v", r)) } case InclusiveMinimum: if i < int64(r) { return createError(x, v, fmt.Sprintf("value must be greater than or equal to %v", r)) } case InclusiveMaximum: if i > int64(r) { return createError(x, v, fmt.Sprintf("value must be less than or equal to %v", r)) } default: return createError(x, v, fmt.Sprintf("constraint %v is not applicable for type integer", v.Name)) } return nil } func validateFloat(x reflect.Value, v Constraint) error { f := x.Float() r, ok := v.Rule.(float64) if !ok { return createError(x, v, fmt.Sprintf("rule must be float value for %v constraint; got: %v", v.Name, v.Rule)) } switch v.Name { case ExclusiveMinimum: if f <= r { return createError(x, v, fmt.Sprintf("value must be greater than %v", r)) } case ExclusiveMaximum: if f >= r { return createError(x, v, fmt.Sprintf("value must be less than %v", r)) } case InclusiveMinimum: if f < r { return createError(x, v, fmt.Sprintf("value must be greater than or equal to %v", r)) } case InclusiveMaximum: if f > r { return createError(x, v, fmt.Sprintf("value must be less than or equal to %v", r)) } default: return createError(x, v, fmt.Sprintf("constraint %s is not applicable for type float", v.Name)) } return nil } func validateString(x reflect.Value, v Constraint) error { s := x.String() switch v.Name { case Empty: if len(s) == 0 { return checkEmpty(x, v) } case Pattern: reg, err := regexp.Compile(v.Rule.(string)) if err != nil { return createError(x, v, err.Error()) } if !reg.MatchString(s) { return createError(x, v, fmt.Sprintf("value doesn't match pattern %v", v.Rule)) } case MaxLength: if _, ok := v.Rule.(int); !ok { return createError(x, v, fmt.Sprintf("rule must be integer value for %v constraint; got: %v", v.Name, v.Rule)) } if len(s) > v.Rule.(int) { return createError(x, v, fmt.Sprintf("value length must be less than or equal to %v", v.Rule)) } case MinLength: if _, ok := v.Rule.(int); !ok { return createError(x, v, fmt.Sprintf("rule must be integer value for %v constraint; got: %v", v.Name, v.Rule)) } if len(s) < v.Rule.(int) { return createError(x, v, fmt.Sprintf("value length must be greater than or equal to %v", v.Rule)) } case ReadOnly: if len(s) > 0 { return createError(reflect.ValueOf(s), v, "readonly parameter; must send as nil or empty in request") } default: return createError(x, v, fmt.Sprintf("constraint %s is not applicable to string type", v.Name)) } if v.Chain != nil { return Validate([]Validation{ { TargetValue: getInterfaceValue(x), Constraints: v.Chain, }, }) } return nil } func validateArrayMap(x reflect.Value, v Constraint) error { switch v.Name { case Null: if x.IsNil() { return checkNil(x, v) } case Empty: if x.IsNil() || x.Len() == 0 { return checkEmpty(x, v) } case MaxItems: if _, ok := v.Rule.(int); !ok { return createError(x, v, fmt.Sprintf("rule must be integer for %v constraint; got: %v", v.Name, v.Rule)) } if x.Len() > v.Rule.(int) { return createError(x, v, fmt.Sprintf("maximum item limit is %v; got: %v", v.Rule, x.Len())) } case MinItems: if _, ok := v.Rule.(int); !ok { return createError(x, v, fmt.Sprintf("rule must be integer for %v constraint; got: %v", v.Name, v.Rule)) } if x.Len() < v.Rule.(int) { return createError(x, v, fmt.Sprintf("minimum item limit is %v; got: %v", v.Rule, x.Len())) } case UniqueItems: if x.Kind() == reflect.Array || x.Kind() == reflect.Slice { if !checkForUniqueInArray(x) { return createError(x, v, fmt.Sprintf("all items in parameter %q must be unique; got:%v", v.Target, x)) } } else if x.Kind() == reflect.Map { if !checkForUniqueInMap(x) { return createError(x, v, fmt.Sprintf("all items in parameter %q must be unique; got:%v", v.Target, x)) } } else { return createError(x, v, fmt.Sprintf("type must be array, slice or map for constraint %v; got: %v", v.Name, x.Kind())) } case ReadOnly: if x.Len() != 0 { return createError(x, v, "readonly parameter; must send as nil or empty in request") } case Pattern: reg, err := regexp.Compile(v.Rule.(string)) if err != nil { return createError(x, v, err.Error()) } keys := x.MapKeys() for _, k := range keys { if !reg.MatchString(k.String()) { return createError(k, v, fmt.Sprintf("map key doesn't match pattern %v", v.Rule)) } } default: return createError(x, v, fmt.Sprintf("constraint %v is not applicable to array, slice and map type", v.Name)) } if v.Chain != nil { return Validate([]Validation{ { TargetValue: getInterfaceValue(x), Constraints: v.Chain, }, }) } return nil } func checkNil(x reflect.Value, v Constraint) error { if _, ok := v.Rule.(bool); !ok { return createError(x, v, fmt.Sprintf("rule must be bool value for %v constraint; got: %v", v.Name, v.Rule)) } if v.Rule.(bool) { return createError(x, v, "value can not be null; required parameter") } return nil } func checkEmpty(x reflect.Value, v Constraint) error { if _, ok := v.Rule.(bool); !ok { return createError(x, v, fmt.Sprintf("rule must be bool value for %v constraint; got: %v", v.Name, v.Rule)) } if v.Rule.(bool) { return createError(x, v, "value can not be null or empty; required parameter") } return nil } func checkForUniqueInArray(x reflect.Value) bool { if x == reflect.Zero(reflect.TypeOf(x)) || x.Len() == 0 { return false } arrOfInterface := make([]interface{}, x.Len()) for i := 0; i < x.Len(); i++ { arrOfInterface[i] = x.Index(i).Interface() } m := make(map[interface{}]bool) for _, val := range arrOfInterface { if m[val] { return false } m[val] = true } return true } func checkForUniqueInMap(x reflect.Value) bool { if x == reflect.Zero(reflect.TypeOf(x)) || x.Len() == 0 { return false } mapOfInterface := make(map[interface{}]interface{}, x.Len()) keys := x.MapKeys() for _, k := range keys { mapOfInterface[k.Interface()] = x.MapIndex(k).Interface() } m := make(map[interface{}]bool) for _, val := range mapOfInterface { if m[val] { return false } m[val] = true } return true } func getInterfaceValue(x reflect.Value) interface{} { if x.Kind() == reflect.Invalid { return nil } return x.Interface() } func isZero(x interface{}) bool { return x == reflect.Zero(reflect.TypeOf(x)).Interface() } func createError(x reflect.Value, v Constraint, err string) error { return fmt.Errorf("autorest/validation: validation failed: parameter=%s constraint=%s value=%#v details: %s", v.Target, v.Name, getInterfaceValue(x), err) } // NewErrorWithValidationError appends package type and method name in // validation error. func NewErrorWithValidationError(err error, packageType, method string) error { return fmt.Errorf("%s#%s: Invalid input: %v", packageType, method, err) } go-autorest-8.3.1/autorest/validation/validation_test.go000066400000000000000000001650651315133516500235340ustar00rootroot00000000000000package validation import ( "fmt" "reflect" "strings" "testing" "github.com/stretchr/testify/require" ) func TestCheckForUniqueInArrayTrue(t *testing.T) { require.Equal(t, checkForUniqueInArray(reflect.ValueOf([]int{1, 2, 3})), true) } func TestCheckForUniqueInArrayFalse(t *testing.T) { require.Equal(t, checkForUniqueInArray(reflect.ValueOf([]int{1, 2, 3, 3})), false) } func TestCheckForUniqueInArrayEmpty(t *testing.T) { require.Equal(t, checkForUniqueInArray(reflect.ValueOf([]int{})), false) } func TestCheckForUniqueInMapTrue(t *testing.T) { require.Equal(t, checkForUniqueInMap(reflect.ValueOf(map[string]int{"one": 1, "two": 2})), true) } func TestCheckForUniqueInMapFalse(t *testing.T) { require.Equal(t, checkForUniqueInMap(reflect.ValueOf(map[int]string{1: "one", 2: "one"})), false) } func TestCheckForUniqueInMapEmpty(t *testing.T) { require.Equal(t, checkForUniqueInMap(reflect.ValueOf(map[int]string{})), false) } func TestCheckEmpty_WithValueEmptyRuleTrue(t *testing.T) { var x interface{} v := Constraint{ Target: "str", Name: Empty, Rule: true, Chain: nil, } expected := createError(reflect.ValueOf(x), v, "value can not be null or empty; required parameter") require.Equal(t, checkEmpty(reflect.ValueOf(x), v).Error(), expected.Error()) } func TestCheckEmpty_WithEmptyStringRuleFalse(t *testing.T) { var x interface{} v := Constraint{ Target: "str", Name: Empty, Rule: false, Chain: nil, } require.Nil(t, checkEmpty(reflect.ValueOf(x), v)) } func TestCheckEmpty_IncorrectRule(t *testing.T) { var x interface{} v := Constraint{ Target: "str", Name: Empty, Rule: 10, Chain: nil, } expected := createError(reflect.ValueOf(x), v, fmt.Sprintf("rule must be bool value for %v constraint; got: %v", v.Name, v.Rule)) require.Equal(t, checkEmpty(reflect.ValueOf(x), v).Error(), expected.Error()) } func TestCheckEmpty_WithErrorArray(t *testing.T) { var x interface{} = []string{} v := Constraint{ Target: "str", Name: Empty, Rule: true, Chain: nil, } expected := createError(reflect.ValueOf(x), v, "value can not be null or empty; required parameter") require.Equal(t, checkEmpty(reflect.ValueOf(x), v).Error(), expected.Error()) } func TestCheckNil_WithNilValueRuleTrue(t *testing.T) { var x interface{} v := Constraint{ Target: "x", Name: Null, Rule: true, Chain: []Constraint{ {"x", MaxItems, 4, nil}, }, } expected := createError(reflect.ValueOf(x), v, "value can not be null; required parameter") require.Equal(t, checkNil(reflect.ValueOf(x), v).Error(), expected.Error()) } func TestCheckNil_WithNilValueRuleFalse(t *testing.T) { var x interface{} v := Constraint{ Target: "x", Name: Null, Rule: false, Chain: []Constraint{ {"x", MaxItems, 4, nil}, }, } require.Nil(t, checkNil(reflect.ValueOf(x), v)) } func TestCheckNil_IncorrectRule(t *testing.T) { var x interface{} c := Constraint{ Target: "str", Name: Null, Rule: 10, Chain: nil, } expected := createError(reflect.ValueOf(x), c, fmt.Sprintf("rule must be bool value for %v constraint; got: %v", c.Name, c.Rule)) require.Equal(t, checkNil(reflect.ValueOf(x), c).Error(), expected.Error()) } func TestValidateArrayMap_WithNilValueRuleTrue(t *testing.T) { var a []string var x interface{} = a c := Constraint{ Target: "arr", Name: Null, Rule: true, Chain: nil, } expected := createError(reflect.ValueOf(x), c, "value can not be null; required parameter") require.Equal(t, validateArrayMap(reflect.ValueOf(x), c), expected) } func TestValidateArrayMap_WithNilValueRuleFalse(t *testing.T) { var x interface{} = []string{} c := Constraint{ Target: "arr", Name: Null, Rule: false, Chain: nil, } require.Nil(t, validateArrayMap(reflect.ValueOf(x), c)) } func TestValidateArrayMap_WithValueRuleNullTrue(t *testing.T) { var x interface{} = []string{"1", "2"} c := Constraint{ Target: "arr", Name: Null, Rule: false, Chain: nil, } require.Nil(t, validateArrayMap(reflect.ValueOf(x), c)) } func TestValidateArrayMap_WithEmptyValueRuleTrue(t *testing.T) { var x interface{} = []string{} c := Constraint{ Target: "arr", Name: Empty, Rule: true, Chain: nil, } expected := createError(reflect.ValueOf(x), c, "value can not be null or empty; required parameter") require.Equal(t, validateArrayMap(reflect.ValueOf(x), c), expected) } func TestValidateArrayMap_WithEmptyValueRuleFalse(t *testing.T) { var x interface{} = []string{} c := Constraint{ Target: "arr", Name: Empty, Rule: false, Chain: nil, } require.Nil(t, validateArrayMap(reflect.ValueOf(x), c)) } func TestValidateArrayMap_WithEmptyRuleEmptyTrue(t *testing.T) { var x interface{} = []string{"1", "2"} c := Constraint{ Target: "arr", Name: Empty, Rule: false, Chain: nil, } require.Nil(t, validateArrayMap(reflect.ValueOf(x), c)) } func TestValidateArrayMap_MaxItemsIncorrectRule(t *testing.T) { var x interface{} = []string{"1", "2"} c := Constraint{ Target: "arr", Name: MaxItems, Rule: false, Chain: nil, } expected := createError(reflect.ValueOf(x), c, fmt.Sprintf("rule must be integer for %v constraint; got: %v", c.Name, c.Rule)) require.Equal(t, validateArrayMap(reflect.ValueOf(x), c).Error(), expected.Error()) } func TestValidateArrayMap_MaxItemsNoError(t *testing.T) { var x interface{} = []string{"1", "2"} c := Constraint{ Target: "arr", Name: MaxItems, Rule: 2, Chain: nil, } require.Nil(t, validateArrayMap(reflect.ValueOf(x), c)) } func TestValidateArrayMap_MaxItemsWithError(t *testing.T) { var x interface{} = []string{"1", "2", "3"} c := Constraint{ Target: "arr", Name: MaxItems, Rule: 2, Chain: nil, } expected := createError(reflect.ValueOf(x), c, fmt.Sprintf("maximum item limit is %v; got: 3", c.Rule)) require.Equal(t, validateArrayMap(reflect.ValueOf(x), c).Error(), expected.Error()) } func TestValidateArrayMap_MaxItemsWithEmpty(t *testing.T) { var x interface{} = []string{} c := Constraint{ Target: "arr", Name: MaxItems, Rule: 2, Chain: nil, } require.Nil(t, validateArrayMap(reflect.ValueOf(x), c)) } func TestValidateArrayMap_MinItemsIncorrectRule(t *testing.T) { var x interface{} = []int{1, 2} c := Constraint{ Target: "arr", Name: MinItems, Rule: false, Chain: nil, } expected := createError(reflect.ValueOf(x), c, fmt.Sprintf("rule must be integer for %v constraint; got: %v", c.Name, c.Rule)) require.Equal(t, validateArrayMap(reflect.ValueOf(x), c).Error(), expected.Error()) } func TestValidateArrayMap_MinItemsNoError1(t *testing.T) { c := Constraint{ Target: "arr", Name: MinItems, Rule: 2, Chain: nil, } require.Nil(t, validateArrayMap(reflect.ValueOf([]int{1, 2}), c)) } func TestValidateArrayMap_MinItemsNoError2(t *testing.T) { c := Constraint{ Target: "arr", Name: MinItems, Rule: 2, Chain: nil, } require.Nil(t, validateArrayMap(reflect.ValueOf([]int{1, 2, 3}), c)) } func TestValidateArrayMap_MinItemsWithError(t *testing.T) { var x interface{} = []int{1} c := Constraint{ Target: "arr", Name: MinItems, Rule: 2, Chain: nil, } expected := createError(reflect.ValueOf(x), c, fmt.Sprintf("minimum item limit is %v; got: 1", c.Rule)) require.Equal(t, validateArrayMap(reflect.ValueOf(x), c).Error(), expected.Error()) } func TestValidateArrayMap_MinItemsWithEmpty(t *testing.T) { var x interface{} = []int{} c := Constraint{ Target: "arr", Name: MinItems, Rule: 2, Chain: nil, } expected := createError(reflect.ValueOf(x), c, fmt.Sprintf("minimum item limit is %v; got: 0", c.Rule)) require.Equal(t, validateArrayMap(reflect.ValueOf(x), c).Error(), expected.Error()) } func TestValidateArrayMap_Map_MaxItemsIncorrectRule(t *testing.T) { var x interface{} = map[int]string{1: "1", 2: "2"} c := Constraint{ Target: "arr", Name: MaxItems, Rule: false, Chain: nil, } require.Equal(t, strings.Contains(validateArrayMap(reflect.ValueOf(x), c).Error(), fmt.Sprintf("rule must be integer for %v constraint; got: %v", c.Name, c.Rule)), true) } func TestValidateArrayMap_Map_MaxItemsNoError(t *testing.T) { var x interface{} = map[int]string{1: "1", 2: "2"} c := Constraint{ Target: "arr", Name: MaxItems, Rule: 2, Chain: nil, } require.Nil(t, validateArrayMap(reflect.ValueOf(x), c)) } func TestValidateArrayMap_Map_MaxItemsWithError(t *testing.T) { a := map[int]string{1: "1", 2: "2", 3: "3"} var x interface{} = a c := Constraint{ Target: "arr", Name: MaxItems, Rule: 2, Chain: nil, } require.Equal(t, strings.Contains(validateArrayMap(reflect.ValueOf(x), c).Error(), fmt.Sprintf("maximum item limit is %v; got: %v", c.Rule, len(a))), true) } func TestValidateArrayMap_Map_MaxItemsWithEmpty(t *testing.T) { a := map[int]string{} var x interface{} = a c := Constraint{ Target: "arr", Name: MaxItems, Rule: 2, Chain: nil, } require.Nil(t, validateArrayMap(reflect.ValueOf(x), c)) } func TestValidateArrayMap_Map_MinItemsIncorrectRule(t *testing.T) { var x interface{} = map[int]string{1: "1", 2: "2"} c := Constraint{ Target: "arr", Name: MinItems, Rule: false, Chain: nil, } require.Equal(t, strings.Contains(validateArrayMap(reflect.ValueOf(x), c).Error(), fmt.Sprintf("rule must be integer for %v constraint; got: %v", c.Name, c.Rule)), true) } func TestValidateArrayMap_Map_MinItemsNoError1(t *testing.T) { var x interface{} = map[int]string{1: "1", 2: "2"} require.Nil(t, validateArrayMap(reflect.ValueOf(x), Constraint{ Target: "arr", Name: MinItems, Rule: 2, Chain: nil, })) } func TestValidateArrayMap_Map_MinItemsNoError2(t *testing.T) { var x interface{} = map[int]string{1: "1", 2: "2", 3: "3"} c := Constraint{ Target: "arr", Name: MinItems, Rule: 2, Chain: nil, } require.Nil(t, validateArrayMap(reflect.ValueOf(x), c)) } func TestValidateArrayMap_Map_MinItemsWithError(t *testing.T) { a := map[int]string{1: "1"} var x interface{} = a c := Constraint{ Target: "arr", Name: MinItems, Rule: 2, Chain: nil, } expected := createError(reflect.ValueOf(x), c, fmt.Sprintf("minimum item limit is %v; got: %v", c.Rule, len(a))) require.Equal(t, validateArrayMap(reflect.ValueOf(x), c).Error(), expected.Error()) } func TestValidateArrayMap_Map_MinItemsWithEmpty(t *testing.T) { a := map[int]string{} var x interface{} = a c := Constraint{ Target: "arr", Name: MinItems, Rule: 2, Chain: nil, } expected := createError(reflect.ValueOf(x), c, fmt.Sprintf("minimum item limit is %v; got: %v", c.Rule, len(a))) require.Equal(t, validateArrayMap(reflect.ValueOf(x), c).Error(), expected.Error()) } // func TestValidateArrayMap_Map_MinItemsNil(t *testing.T) { // var a map[int]float64 // var x interface{} = a // c := Constraint{ // Target: "str", // Name: MinItems, // Rule: true, // Chain: nil, // } // expected := createError(reflect.Value(x), c, fmt.Sprintf("all items in parameter %v must be unique; got:%v", c.Target, x)) // if z := validateArrayMap(reflect.ValueOf(x), c); strings.Contains(z.Error(), "all items in parameter str must be unique;") { // t.Fatalf("autorest/validation: valiateArrayMap failed to return error \nexpect: %v;\ngot: %v", expected, z) // } // } func TestValidateArrayMap_Map_UniqueItemsTrue(t *testing.T) { var x interface{} = map[float64]int{1.2: 1, 1.4: 2} c := Constraint{ Target: "str", Name: UniqueItems, Rule: true, Chain: nil, } require.Nil(t, validateArrayMap(reflect.ValueOf(x), c)) } func TestValidateArrayMap_Map_UniqueItemsFalse(t *testing.T) { var x interface{} = map[string]string{"1": "1", "2": "2", "3": "1"} c := Constraint{ Target: "str", Name: UniqueItems, Rule: true, Chain: nil, } z := validateArrayMap(reflect.ValueOf(x), c) require.Equal(t, strings.Contains(z.Error(), fmt.Sprintf("all items in parameter %q must be unique", c.Target)), true) } func TestValidateArrayMap_Map_UniqueItemsEmpty(t *testing.T) { // Consider Empty map as not unique returns false var x interface{} = map[int]float64{} c := Constraint{ Target: "str", Name: UniqueItems, Rule: true, Chain: nil, } z := validateArrayMap(reflect.ValueOf(x), c) require.Equal(t, strings.Contains(z.Error(), fmt.Sprintf("all items in parameter %q must be unique", c.Target)), true) } func TestValidateArrayMap_Map_UniqueItemsNil(t *testing.T) { var a map[int]float64 var x interface{} = a c := Constraint{ Target: "str", Name: UniqueItems, Rule: true, Chain: nil, } z := validateArrayMap(reflect.ValueOf(x), c) require.Equal(t, strings.Contains(z.Error(), fmt.Sprintf("all items in parameter %q must be unique; got:%v", c.Target, x)), true) } func TestValidateArrayMap_Array_UniqueItemsTrue(t *testing.T) { var x interface{} = []int{1, 2} c := Constraint{ Target: "str", Name: UniqueItems, Rule: true, Chain: nil, } require.Nil(t, validateArrayMap(reflect.ValueOf(x), c)) } func TestValidateArrayMap_Array_UniqueItemsFalse(t *testing.T) { var x interface{} = []string{"1", "2", "1"} c := Constraint{ Target: "str", Name: UniqueItems, Rule: true, Chain: nil, } z := validateArrayMap(reflect.ValueOf(x), c) require.Equal(t, strings.Contains(z.Error(), fmt.Sprintf("all items in parameter %q must be unique; got:%v", c.Target, x)), true) } func TestValidateArrayMap_Array_UniqueItemsEmpty(t *testing.T) { // Consider Empty array as not unique returns false var x interface{} = []float64{} c := Constraint{ Target: "str", Name: UniqueItems, Rule: true, Chain: nil, } z := validateArrayMap(reflect.ValueOf(x), c) require.Equal(t, strings.Contains(z.Error(), fmt.Sprintf("all items in parameter %q must be unique; got:%v", c.Target, x)), true) } func TestValidateArrayMap_Array_UniqueItemsNil(t *testing.T) { // Consider nil array as not unique returns false var a []float64 var x interface{} = a c := Constraint{ Target: "str", Name: UniqueItems, Rule: true, Chain: nil, } z := validateArrayMap(reflect.ValueOf(x), c) require.Equal(t, strings.Contains(z.Error(), fmt.Sprintf("all items in parameter %q must be unique; got:%v", c.Target, x)), true) } func TestValidateArrayMap_Array_UniqueItemsInvalidType(t *testing.T) { var x interface{} = "hello" c := Constraint{ Target: "str", Name: UniqueItems, Rule: true, Chain: nil, } z := validateArrayMap(reflect.ValueOf(x), c) require.Equal(t, strings.Contains(z.Error(), fmt.Sprintf("type must be array, slice or map for constraint %v; got: %v", c.Name, reflect.ValueOf(x).Kind())), true) } func TestValidateArrayMap_Array_UniqueItemsInvalidConstraint(t *testing.T) { var x interface{} = "hello" c := Constraint{ Target: "str", Name: "sdad", Rule: true, Chain: nil, } z := validateArrayMap(reflect.ValueOf(x), c) require.Equal(t, strings.Contains(z.Error(), fmt.Sprintf("constraint %v is not applicable to array, slice and map type", c.Name)), true) } func TestValidateArrayMap_ValidateChainConstraint1(t *testing.T) { a := []int{1, 2, 3, 4} var x interface{} = a c := Constraint{ Target: "str", Name: Null, Rule: true, Chain: []Constraint{ {"str", MaxItems, 3, nil}, }, } z := validateArrayMap(reflect.ValueOf(x), c) require.Equal(t, strings.Contains(z.Error(), fmt.Sprintf("maximum item limit is %v; got: %v", (c.Chain)[0].Rule, len(a))), true) } func TestValidateArrayMap_ValidateChainConstraint2(t *testing.T) { a := []int{1, 2, 3, 4} var x interface{} = a c := Constraint{ Target: "str", Name: Empty, Rule: true, Chain: []Constraint{ {"str", MaxItems, 3, nil}, }, } z := validateArrayMap(reflect.ValueOf(x), c) require.Equal(t, strings.Contains(z.Error(), fmt.Sprintf("maximum item limit is %v; got: %v", (c.Chain)[0].Rule, len(a))), true) } func TestValidateArrayMap_ValidateChainConstraint3(t *testing.T) { var a []string var x interface{} = a c := Constraint{ Target: "str", Name: Null, Rule: true, Chain: []Constraint{ {"str", MaxItems, 3, nil}, }, } z := validateArrayMap(reflect.ValueOf(x), c) require.Equal(t, strings.Contains(z.Error(), fmt.Sprintf("value can not be null; required parameter")), true) } func TestValidateArrayMap_ValidateChainConstraint4(t *testing.T) { var x interface{} = []int{} c := Constraint{ Target: "str", Name: Empty, Rule: true, Chain: []Constraint{ {"str", MaxItems, 3, nil}, }, } z := validateArrayMap(reflect.ValueOf(x), c) require.Equal(t, strings.Contains(z.Error(), fmt.Sprintf("value can not be null or empty; required parameter")), true) } func TestValidateArrayMap_ValidateChainConstraintNilNotRequired(t *testing.T) { var a []int var x interface{} = a c := Constraint{ Target: "str", Name: Null, Rule: false, Chain: []Constraint{ {"str", MaxItems, 3, nil}, }, } require.Nil(t, validateArrayMap(reflect.ValueOf(x), c)) } func TestValidateArrayMap_ValidateChainConstraintEmptyNotRequired(t *testing.T) { var x interface{} = map[string]int{} c := Constraint{ Target: "str", Name: Empty, Rule: false, Chain: []Constraint{ {"str", MaxItems, 3, nil}, }, } require.Nil(t, validateArrayMap(reflect.ValueOf(x), c)) } func TestValidateArrayMap_ReadOnlyWithError(t *testing.T) { var x interface{} = []int{1, 2} c := Constraint{ Target: "str", Name: ReadOnly, Rule: true, Chain: []Constraint{ {"str", MaxItems, 3, nil}, }, } z := validateArrayMap(reflect.ValueOf(x), c) require.Equal(t, strings.Contains(z.Error(), fmt.Sprintf("readonly parameter; must send as nil or empty in request")), true) } func TestValidateArrayMap_ReadOnlyWithoutError(t *testing.T) { var x interface{} = []int{} c := Constraint{ Target: "str", Name: ReadOnly, Rule: true, Chain: nil, } require.Nil(t, validateArrayMap(reflect.ValueOf(x), c)) } func TestValidateString_ReadOnly(t *testing.T) { var x interface{} = "Hello Gopher" c := Constraint{ Target: "str", Name: ReadOnly, Rule: true, Chain: nil, } require.Equal(t, strings.Contains(validateString(reflect.ValueOf(x), c).Error(), fmt.Sprintf("readonly parameter; must send as nil or empty in request")), true) } func TestValidateString_EmptyTrue(t *testing.T) { // Empty true means parameter is required but Empty returns error c := Constraint{ Target: "str", Name: Empty, Rule: true, Chain: nil, } require.Equal(t, strings.Contains(validateString(reflect.ValueOf(""), c).Error(), fmt.Sprintf("value can not be null or empty; required parameter")), true) } func TestValidateString_EmptyFalse(t *testing.T) { // Empty false means parameter is not required and Empty return nil var x interface{} c := Constraint{ Target: "str", Name: Empty, Rule: false, Chain: nil, } require.Nil(t, validateString(reflect.ValueOf(x), c)) } func TestValidateString_MaxLengthInvalid(t *testing.T) { // Empty true means parameter is required but Empty returns error var x interface{} = "Hello" c := Constraint{ Target: "str", Name: MaxLength, Rule: 4, Chain: nil, } require.Equal(t, strings.Contains(validateString(reflect.ValueOf(x), c).Error(), fmt.Sprintf("value length must be less than or equal to %v", c.Rule)), true) } func TestValidateString_MaxLengthValid(t *testing.T) { // Empty false means parameter is not required and Empty return nil c := Constraint{ Target: "str", Name: MaxLength, Rule: 7, Chain: nil, } require.Nil(t, validateString(reflect.ValueOf("Hello"), c)) } func TestValidateString_MaxLengthRuleInvalid(t *testing.T) { var x interface{} = "Hello" c := Constraint{ Target: "str", Name: MaxLength, Rule: true, // must be int for maxLength Chain: nil, } require.Equal(t, strings.Contains(validateString(reflect.ValueOf(x), c).Error(), fmt.Sprintf("rule must be integer value for %v constraint; got: %v", c.Name, c.Rule)), true) } func TestValidateString_MinLengthInvalid(t *testing.T) { var x interface{} = "Hello" c := Constraint{ Target: "str", Name: MinLength, Rule: 10, Chain: nil, } require.Equal(t, strings.Contains(validateString(reflect.ValueOf(x), c).Error(), fmt.Sprintf("value length must be greater than or equal to %v", c.Rule)), true) } func TestValidateString_MinLengthValid(t *testing.T) { c := Constraint{ Target: "str", Name: MinLength, Rule: 2, Chain: nil, } require.Nil(t, validateString(reflect.ValueOf("Hello"), c)) } func TestValidateString_MinLengthRuleInvalid(t *testing.T) { var x interface{} = "Hello" c := Constraint{ Target: "str", Name: MinLength, Rule: true, // must be int for minLength Chain: nil, } require.Equal(t, strings.Contains(validateString(reflect.ValueOf(x), c).Error(), fmt.Sprintf("rule must be integer value for %v constraint; got: %v", c.Name, c.Rule)), true) } func TestValidateString_PatternInvalidPattern(t *testing.T) { var x interface{} = "Hello" c := Constraint{ Target: "str", Name: Pattern, Rule: `^[[:alnum:$`, Chain: nil, } require.Equal(t, strings.Contains(validateString(reflect.ValueOf(x), c).Error(), "error parsing regexp: missing closing ]"), true) } func TestValidateString_PatternMatch1(t *testing.T) { c := Constraint{ Target: "str", Name: Pattern, Rule: `^http://\w+$`, Chain: nil, } require.Nil(t, validateString(reflect.ValueOf("http://masd"), c)) } func TestValidateString_PatternMatch2(t *testing.T) { c := Constraint{ Target: "str", Name: Pattern, Rule: `^[a-zA-Z0-9]+$`, Chain: nil, } require.Nil(t, validateString(reflect.ValueOf("asdadad2323sad"), c)) } func TestValidateString_PatternNotMatch(t *testing.T) { var x interface{} = "asdad@@ad2323sad" c := Constraint{ Target: "str", Name: Pattern, Rule: `^[a-zA-Z0-9]+$`, Chain: nil, } require.Equal(t, strings.Contains(validateString(reflect.ValueOf(x), c).Error(), fmt.Sprintf("value doesn't match pattern %v", c.Rule)), true) } func TestValidateString_InvalidConstraint(t *testing.T) { var x interface{} = "asdad@@ad2323sad" c := Constraint{ Target: "str", Name: UniqueItems, Rule: "^[a-zA-Z0-9]+$", Chain: nil, } require.Equal(t, strings.Contains(validateString(reflect.ValueOf(x), c).Error(), fmt.Sprintf("constraint %s is not applicable to string type", c.Name)), true) } func TestValidateFloat_InvalidConstraint(t *testing.T) { var x interface{} = 1.4 c := Constraint{ Target: "str", Name: UniqueItems, Rule: 3.0, Chain: nil, } require.Equal(t, strings.Contains(validateFloat(reflect.ValueOf(x), c).Error(), fmt.Sprintf("constraint %v is not applicable for type float", c.Name)), true) } func TestValidateFloat_InvalidRuleValue(t *testing.T) { var x interface{} = 1.4 c := Constraint{ Target: "str", Name: ExclusiveMinimum, Rule: 3, Chain: nil, } require.Equal(t, strings.Contains(validateFloat(reflect.ValueOf(x), c).Error(), fmt.Sprintf("rule must be float value for %v constraint; got: %v", c.Name, c.Rule)), true) } func TestValidateFloat_ExclusiveMinimumConstraintValid(t *testing.T) { c := Constraint{ Target: "str", Name: ExclusiveMinimum, Rule: 1.0, Chain: nil, } require.Nil(t, validateFloat(reflect.ValueOf(1.42), c)) } func TestValidateFloat_ExclusiveMinimumConstraintInvalid(t *testing.T) { var x interface{} = 1.4 c := Constraint{ Target: "str", Name: ExclusiveMinimum, Rule: 1.5, Chain: nil, } require.Equal(t, strings.Contains(validateFloat(reflect.ValueOf(x), c).Error(), fmt.Sprintf("value must be greater than %v", c.Rule)), true) } func TestValidateFloat_ExclusiveMinimumConstraintBoundary(t *testing.T) { var x interface{} = 1.42 c := Constraint{ Target: "str", Name: ExclusiveMinimum, Rule: 1.42, Chain: nil, } require.Equal(t, strings.Contains(validateFloat(reflect.ValueOf(x), c).Error(), fmt.Sprintf("value must be greater than %v", c.Rule)), true) } func TestValidateFloat_exclusiveMaximumConstraintValid(t *testing.T) { c := Constraint{ Target: "str", Name: ExclusiveMaximum, Rule: 2.0, Chain: nil, } require.Nil(t, validateFloat(reflect.ValueOf(1.42), c)) } func TestValidateFloat_exclusiveMaximumConstraintInvalid(t *testing.T) { var x interface{} = 1.42 c := Constraint{ Target: "str", Name: ExclusiveMaximum, Rule: 1.2, Chain: nil, } require.Equal(t, strings.Contains(validateFloat(reflect.ValueOf(x), c).Error(), fmt.Sprintf("value must be less than %v", c.Rule)), true) } func TestValidateFloat_exclusiveMaximumConstraintBoundary(t *testing.T) { var x interface{} = 1.42 c := Constraint{ Target: "str", Name: ExclusiveMaximum, Rule: 1.42, Chain: nil, } require.Equal(t, strings.Contains(validateFloat(reflect.ValueOf(x), c).Error(), fmt.Sprintf("value must be less than %v", c.Rule)), true) } func TestValidateFloat_inclusiveMaximumConstraintValid(t *testing.T) { c := Constraint{ Target: "str", Name: InclusiveMaximum, Rule: 2.0, Chain: nil, } require.Nil(t, validateFloat(reflect.ValueOf(1.42), c)) } func TestValidateFloat_inclusiveMaximumConstraintInvalid(t *testing.T) { var x interface{} = 1.42 c := Constraint{ Target: "str", Name: InclusiveMaximum, Rule: 1.2, Chain: nil, } require.Equal(t, strings.Contains(validateFloat(reflect.ValueOf(x), c).Error(), fmt.Sprintf("value must be less than or equal to %v", c.Rule)), true) } func TestValidateFloat_inclusiveMaximumConstraintBoundary(t *testing.T) { c := Constraint{ Target: "str", Name: InclusiveMaximum, Rule: 1.42, Chain: nil, } require.Nil(t, validateFloat(reflect.ValueOf(1.42), c)) } func TestValidateFloat_InclusiveMinimumConstraintValid(t *testing.T) { c := Constraint{ Target: "str", Name: InclusiveMinimum, Rule: 1.0, Chain: nil, } require.Nil(t, validateFloat(reflect.ValueOf(1.42), c)) } func TestValidateFloat_InclusiveMinimumConstraintInvalid(t *testing.T) { var x interface{} = 1.42 c := Constraint{ Target: "str", Name: InclusiveMinimum, Rule: 1.5, Chain: nil, } require.Equal(t, strings.Contains(validateFloat(reflect.ValueOf(x), c).Error(), fmt.Sprintf("value must be greater than or equal to %v", c.Rule)), true) } func TestValidateFloat_InclusiveMinimumConstraintBoundary(t *testing.T) { c := Constraint{ Target: "str", Name: InclusiveMinimum, Rule: 1.42, Chain: nil, } require.Nil(t, validateFloat(reflect.ValueOf(1.42), c)) } func TestValidateInt_InvalidConstraint(t *testing.T) { var x interface{} = 1 c := Constraint{ Target: "str", Name: UniqueItems, Rule: 3, Chain: nil, } require.Equal(t, strings.Contains(validateInt(reflect.ValueOf(x), c).Error(), fmt.Sprintf("constraint %s is not applicable for type integer", c.Name)), true) } func TestValidateInt_InvalidRuleValue(t *testing.T) { var x interface{} = 1 c := Constraint{ Target: "str", Name: ExclusiveMinimum, Rule: 3.4, Chain: nil, } require.Equal(t, strings.Contains(validateInt(reflect.ValueOf(x), c).Error(), fmt.Sprintf("rule must be integer value for %v constraint; got: %v", c.Name, c.Rule)), true) } func TestValidateInt_ExclusiveMinimumConstraintValid(t *testing.T) { c := Constraint{ Target: "str", Name: ExclusiveMinimum, Rule: 1, Chain: nil, } require.Nil(t, validateInt(reflect.ValueOf(3), c)) } func TestValidateInt_ExclusiveMinimumConstraintInvalid(t *testing.T) { var x interface{} = 1 c := Constraint{ Target: "str", Name: ExclusiveMinimum, Rule: 3, Chain: nil, } require.Equal(t, validateInt(reflect.ValueOf(x), c).Error(), createError(reflect.ValueOf(x), c, fmt.Sprintf("value must be greater than %v", c.Rule)).Error()) } func TestValidateInt_ExclusiveMinimumConstraintBoundary(t *testing.T) { var x interface{} = 1 c := Constraint{ Target: "str", Name: ExclusiveMinimum, Rule: 1, Chain: nil, } require.Equal(t, validateInt(reflect.ValueOf(x), c).Error(), createError(reflect.ValueOf(x), c, fmt.Sprintf("value must be greater than %v", c.Rule)).Error()) } func TestValidateInt_exclusiveMaximumConstraintValid(t *testing.T) { c := Constraint{ Target: "str", Name: ExclusiveMaximum, Rule: 2, Chain: nil, } require.Nil(t, validateInt(reflect.ValueOf(1), c)) } func TestValidateInt_exclusiveMaximumConstraintInvalid(t *testing.T) { var x interface{} = 2 c := Constraint{ Target: "str", Name: ExclusiveMaximum, Rule: 1, Chain: nil, } require.Equal(t, validateInt(reflect.ValueOf(x), c).Error(), createError(reflect.ValueOf(x), c, fmt.Sprintf("value must be less than %v", c.Rule)).Error()) } func TestValidateInt_exclusiveMaximumConstraintBoundary(t *testing.T) { var x interface{} = 1 c := Constraint{ Target: "str", Name: ExclusiveMaximum, Rule: 1, Chain: nil, } require.Equal(t, validateInt(reflect.ValueOf(x), c).Error(), createError(reflect.ValueOf(x), c, fmt.Sprintf("value must be less than %v", c.Rule)).Error()) } func TestValidateInt_inclusiveMaximumConstraintValid(t *testing.T) { c := Constraint{ Target: "str", Name: InclusiveMaximum, Rule: 2, Chain: nil, } require.Nil(t, validateInt(reflect.ValueOf(1), c)) } func TestValidateInt_inclusiveMaximumConstraintInvalid(t *testing.T) { var x interface{} = 2 c := Constraint{ Target: "str", Name: InclusiveMaximum, Rule: 1, Chain: nil, } require.Equal(t, validateInt(reflect.ValueOf(x), c).Error(), createError(reflect.ValueOf(x), c, fmt.Sprintf("value must be less than or equal to %v", c.Rule)).Error()) } func TestValidateInt_inclusiveMaximumConstraintBoundary(t *testing.T) { c := Constraint{ Target: "str", Name: InclusiveMaximum, Rule: 1, Chain: nil, } require.Nil(t, validateInt(reflect.ValueOf(1), c)) } func TestValidateInt_InclusiveMinimumConstraintValid(t *testing.T) { c := Constraint{ Target: "str", Name: InclusiveMinimum, Rule: 1, Chain: nil, } require.Nil(t, validateInt(reflect.ValueOf(1), c)) } func TestValidateInt_InclusiveMinimumConstraintInvalid(t *testing.T) { var x interface{} = 1 c := Constraint{ Target: "str", Name: InclusiveMinimum, Rule: 2, Chain: nil, } require.Equal(t, validateInt(reflect.ValueOf(x), c).Error(), createError(reflect.ValueOf(x), c, fmt.Sprintf("value must be greater than or equal to %v", c.Rule)).Error()) } func TestValidateInt_InclusiveMinimumConstraintBoundary(t *testing.T) { c := Constraint{ Target: "str", Name: InclusiveMinimum, Rule: 1, Chain: nil, } require.Nil(t, validateInt(reflect.ValueOf(1), c)) } func TestValidateInt_MultipleOfWithoutError(t *testing.T) { c := Constraint{ Target: "str", Name: MultipleOf, Rule: 10, Chain: nil, } require.Nil(t, validateInt(reflect.ValueOf(2300), c)) } func TestValidateInt_MultipleOfWithError(t *testing.T) { c := Constraint{ Target: "str", Name: MultipleOf, Rule: 11, Chain: nil, } require.Equal(t, validateInt(reflect.ValueOf(2300), c).Error(), createError(reflect.ValueOf(2300), c, fmt.Sprintf("value must be a multiple of %v", c.Rule)).Error()) } func TestValidatePointer_NilTrue(t *testing.T) { var z *int var x interface{} = z c := Constraint{ Target: "ptr", Name: Null, Rule: true, // Required property Chain: nil, } require.Equal(t, validatePtr(reflect.ValueOf(x), c).Error(), createError(reflect.ValueOf(x), c, "value can not be null; required parameter").Error()) } func TestValidatePointer_NilFalse(t *testing.T) { var z *int var x interface{} = z c := Constraint{ Target: "ptr", Name: Null, Rule: false, // not required property Chain: nil, } require.Nil(t, validatePtr(reflect.ValueOf(x), c)) } func TestValidatePointer_NilReadonlyValid(t *testing.T) { var z *int var x interface{} = z c := Constraint{ Target: "ptr", Name: ReadOnly, Rule: true, Chain: nil, } require.Nil(t, validatePtr(reflect.ValueOf(x), c)) } func TestValidatePointer_NilReadonlyInvalid(t *testing.T) { z := 10 var x interface{} = &z c := Constraint{ Target: "ptr", Name: ReadOnly, Rule: true, Chain: nil, } require.Equal(t, validatePtr(reflect.ValueOf(x), c).Error(), createError(reflect.ValueOf(z), c, "readonly parameter; must send as nil or empty in request").Error()) } func TestValidatePointer_IntValid(t *testing.T) { z := 10 var x interface{} = &z c := Constraint{ Target: "ptr", Name: InclusiveMinimum, Rule: 3, Chain: nil, } require.Nil(t, validatePtr(reflect.ValueOf(x), c)) } func TestValidatePointer_IntInvalid(t *testing.T) { z := 10 var x interface{} = &z c := Constraint{ Target: "ptr", Name: Null, Rule: true, Chain: []Constraint{ { Target: "ptr", Name: InclusiveMinimum, Rule: 11, Chain: nil, }, }, } require.Equal(t, validatePtr(reflect.ValueOf(x), c).Error(), createError(reflect.ValueOf(10), c.Chain[0], "value must be greater than or equal to 11").Error()) } func TestValidatePointer_IntInvalidConstraint(t *testing.T) { z := 10 var x interface{} = &z c := Constraint{ Target: "ptr", Name: Null, Rule: true, Chain: []Constraint{ { Target: "ptr", Name: MaxItems, Rule: 3, Chain: nil, }, }, } require.Equal(t, validatePtr(reflect.ValueOf(x), c).Error(), createError(reflect.ValueOf(10), c.Chain[0], fmt.Sprintf("constraint %v is not applicable for type integer", MaxItems)).Error()) } func TestValidatePointer_ValidInt64(t *testing.T) { z := int64(10) var x interface{} = &z c := Constraint{ Target: "ptr", Name: Null, Rule: true, Chain: []Constraint{ { Target: "ptr", Name: InclusiveMinimum, Rule: 3, Chain: nil, }, }} require.Nil(t, validatePtr(reflect.ValueOf(x), c)) } func TestValidatePointer_InvalidConstraintInt64(t *testing.T) { z := int64(10) var x interface{} = &z c := Constraint{ Target: "ptr", Name: Null, Rule: true, Chain: []Constraint{ { Target: "ptr", Name: MaxItems, Rule: 3, Chain: nil, }, }, } require.Equal(t, validatePtr(reflect.ValueOf(x), c).Error(), createError(reflect.ValueOf(10), c.Chain[0], fmt.Sprintf("constraint %v is not applicable for type integer", MaxItems)).Error()) } func TestValidatePointer_ValidFloat(t *testing.T) { z := 10.1 var x interface{} = &z c := Constraint{ Target: "ptr", Name: Null, Rule: true, Chain: []Constraint{ { Target: "ptr", Name: InclusiveMinimum, Rule: 3.0, Chain: nil, }}} require.Nil(t, validatePtr(reflect.ValueOf(x), c)) } func TestValidatePointer_InvalidFloat(t *testing.T) { z := 10.1 var x interface{} = &z c := Constraint{ Target: "ptr", Name: Null, Rule: true, Chain: []Constraint{ { Target: "ptr", Name: InclusiveMinimum, Rule: 12.0, Chain: nil, }}, } require.Equal(t, validatePtr(reflect.ValueOf(x), c).Error(), createError(reflect.ValueOf(10.1), c.Chain[0], "value must be greater than or equal to 12").Error()) } func TestValidatePointer_InvalidConstraintFloat(t *testing.T) { z := 10.1 var x interface{} = &z c := Constraint{ Target: "ptr", Name: Null, Rule: true, Chain: []Constraint{ { Target: "ptr", Name: MaxItems, Rule: 3.0, Chain: nil, }}, } require.Equal(t, validatePtr(reflect.ValueOf(x), c).Error(), createError(reflect.ValueOf(10.1), c.Chain[0], fmt.Sprintf("constraint %v is not applicable for type float", MaxItems)).Error()) } func TestValidatePointer_StringValid(t *testing.T) { z := "hello" var x interface{} = &z c := Constraint{ Target: "ptr", Name: Null, Rule: true, Chain: []Constraint{ { Target: "ptr", Name: Pattern, Rule: "^[a-z]+$", Chain: nil, }}} require.Nil(t, validatePtr(reflect.ValueOf(x), c)) } func TestValidatePointer_StringInvalid(t *testing.T) { z := "hello" var x interface{} = &z c := Constraint{ Target: "ptr", Name: Null, Rule: true, Chain: []Constraint{ { Target: "ptr", Name: MaxLength, Rule: 2, Chain: nil, }}} require.Equal(t, validatePtr(reflect.ValueOf(x), c).Error(), createError(reflect.ValueOf("hello"), c.Chain[0], "value length must be less than or equal to 2").Error()) } func TestValidatePointer_ArrayValid(t *testing.T) { c := Constraint{ Target: "ptr", Name: Null, Rule: true, Chain: []Constraint{ { Target: "ptr", Name: UniqueItems, Rule: "true", Chain: nil, }}} require.Nil(t, validatePtr(reflect.ValueOf(&[]string{"1", "2"}), c)) } func TestValidatePointer_ArrayInvalid(t *testing.T) { z := []string{"1", "2", "2"} var x interface{} = &z c := Constraint{ Target: "ptr", Name: Null, Rule: true, Chain: []Constraint{{ Target: "ptr", Name: UniqueItems, Rule: true, Chain: nil, }}, } require.Equal(t, validatePtr(reflect.ValueOf(x), c).Error(), createError(reflect.ValueOf(z), c.Chain[0], fmt.Sprintf("all items in parameter %q must be unique; got:%v", c.Target, z)).Error()) } func TestValidatePointer_MapValid(t *testing.T) { c := Constraint{ Target: "ptr", Name: Null, Rule: true, Chain: []Constraint{ { Target: "ptr", Name: UniqueItems, Rule: true, Chain: nil, }}} require.Nil(t, validatePtr(reflect.ValueOf(&map[interface{}]string{1: "1", "1": "2"}), c)) } func TestValidatePointer_MapInvalid(t *testing.T) { z := map[interface{}]string{1: "1", "1": "2", 1.3: "2"} var x interface{} = &z c := Constraint{ Target: "ptr", Name: Null, Rule: true, Chain: []Constraint{{ Target: "ptr", Name: UniqueItems, Rule: true, Chain: nil, }}, } require.Equal(t, strings.Contains(validatePtr(reflect.ValueOf(x), c).Error(), fmt.Sprintf("all items in parameter %q must be unique;", c.Target)), true) } type Child struct { I string } type Product struct { C *Child Str *string Name string Arr *[]string M *map[string]string Num *int32 } type Sample struct { M *map[string]*string Name string } func TestValidatePointer_StructWithError(t *testing.T) { s := "hello" var x interface{} = &Product{ C: &Child{"100"}, Str: &s, Name: "Gopher", } c := Constraint{ "p", Null, "True", []Constraint{ {"C", Null, true, []Constraint{ {"I", MaxLength, 2, nil}, }}, {"Str", MaxLength, 2, nil}, {"Name", MaxLength, 5, nil}, }, } require.Equal(t, validatePtr(reflect.ValueOf(x), c).Error(), createError(reflect.ValueOf("100"), c.Chain[0].Chain[0], "value length must be less than or equal to 2").Error()) } func TestValidatePointer_WithNilStruct(t *testing.T) { var p *Product var x interface{} = p c := Constraint{ "p", Null, true, []Constraint{ {"C", Null, true, []Constraint{ {"I", Empty, true, []Constraint{ {"I", MaxLength, 5, nil}, }}, }}, {"Str", MaxLength, 2, nil}, {"Name", MaxLength, 5, nil}, }, } require.Equal(t, validatePtr(reflect.ValueOf(x), c).Error(), createError(reflect.ValueOf(x), c, fmt.Sprintf("value can not be null; required parameter")).Error()) } func TestValidatePointer_StructWithNoError(t *testing.T) { s := "hello" var x interface{} = &Product{ C: &Child{"100"}, Str: &s, Name: "Gopher", } c := Constraint{ "p", Null, true, []Constraint{ {"C", Null, true, []Constraint{ {"I", Empty, true, []Constraint{ {"I", MaxLength, 5, nil}, }}, }}, }, } require.Nil(t, validatePtr(reflect.ValueOf(x), c)) } func TestValidateStruct_FieldNotExist(t *testing.T) { s := "hello" var x interface{} = Product{ C: &Child{"100"}, Str: &s, Name: "Gopher", } c := Constraint{ "C", Null, true, []Constraint{ {"Name", Empty, true, nil}, }, } s = "Name" require.Equal(t, validateStruct(reflect.ValueOf(x), c).Error(), createError(reflect.ValueOf(Child{"100"}), c.Chain[0], fmt.Sprintf("field %q doesn't exist", s)).Error()) } func TestValidateStruct_WithChainConstraint(t *testing.T) { s := "hello" var x interface{} = Product{ C: &Child{"100"}, Str: &s, Name: "Gopher", } c := Constraint{ "C", Null, true, []Constraint{ {"I", Empty, true, []Constraint{ {"I", MaxLength, 2, nil}, }}, }, } require.Equal(t, validateStruct(reflect.ValueOf(x), c).Error(), createError(reflect.ValueOf("100"), c.Chain[0].Chain[0], "value length must be less than or equal to 2").Error()) } func TestValidateStruct_WithoutChainConstraint(t *testing.T) { s := "hello" var x interface{} = Product{ C: &Child{""}, Str: &s, Name: "Gopher", } c := Constraint{"C", Null, true, []Constraint{ {"I", Empty, true, nil}, // throw error for Empty }} require.Equal(t, validateStruct(reflect.ValueOf(x), c).Error(), createError(reflect.ValueOf(""), c.Chain[0], "value can not be null or empty; required parameter").Error()) } func TestValidateStruct_WithArrayNull(t *testing.T) { s := "hello" var x interface{} = Product{ C: &Child{""}, Str: &s, Name: "Gopher", Arr: nil, } c := Constraint{"Arr", Null, true, []Constraint{ {"Arr", MaxItems, 4, nil}, {"Arr", MinItems, 2, nil}, }, } require.Equal(t, validateStruct(reflect.ValueOf(x), c).Error(), createError(reflect.ValueOf(x.(Product).Arr), c, "value can not be null; required parameter").Error()) } func TestValidateStruct_WithArrayEmptyError(t *testing.T) { // arr := []string{} var x interface{} = Product{ Arr: &[]string{}, } c := Constraint{ "Arr", Null, true, []Constraint{ {"Arr", Empty, true, nil}, {"Arr", MaxItems, 4, nil}, {"Arr", MinItems, 2, nil}, }} require.Equal(t, validateStruct(reflect.ValueOf(x), c).Error(), createError(reflect.ValueOf(*(x.(Product).Arr)), c.Chain[0], fmt.Sprintf("value can not be null or empty; required parameter")).Error()) } func TestValidateStruct_WithArrayEmptyWithoutError(t *testing.T) { var x interface{} = Product{ Arr: &[]string{}, } c := Constraint{ "Arr", Null, true, []Constraint{ {"Arr", Empty, false, nil}, {"Arr", MaxItems, 4, nil}, }, } require.Nil(t, validateStruct(reflect.ValueOf(x), c)) } func TestValidateStruct_ArrayWithError(t *testing.T) { arr := []string{"1", "1"} var x interface{} = Product{ Arr: &arr, } c := Constraint{ "Arr", Null, true, []Constraint{ {"Arr", Empty, true, nil}, {"Arr", MaxItems, 4, nil}, {"Arr", UniqueItems, true, nil}, }, } s := "Arr" require.Equal(t, validateStruct(reflect.ValueOf(x), c).Error(), createError(reflect.ValueOf(*(x.(Product).Arr)), c.Chain[2], fmt.Sprintf("all items in parameter %q must be unique; got:%v", s, *(x.(Product).Arr))).Error()) } func TestValidateStruct_MapWithError(t *testing.T) { m := map[string]string{ "a": "hello", "b": "hello", } var x interface{} = Product{ M: &m, } c := Constraint{ "M", Null, true, []Constraint{ {"M", Empty, true, nil}, {"M", MaxItems, 4, nil}, {"M", UniqueItems, true, nil}, }, } s := "M" require.Equal(t, strings.Contains(validateStruct(reflect.ValueOf(x), c).Error(), fmt.Sprintf("all items in parameter %q must be unique;", s)), true) } func TestValidateStruct_MapWithNoError(t *testing.T) { m := map[string]string{} var x interface{} = Product{ M: &m, } c := Constraint{ "M", Null, true, []Constraint{ {"M", Empty, false, nil}, {"M", MaxItems, 4, nil}, }, } require.Nil(t, validateStruct(reflect.ValueOf(x), c)) } func TestValidateStruct_MapNilNoError(t *testing.T) { var m map[string]string var x interface{} = Product{ M: &m, } c := Constraint{ "M", Null, false, []Constraint{ {"M", Empty, false, nil}, {"M", MaxItems, 4, nil}, }, } require.Nil(t, validateStruct(reflect.ValueOf(x), c)) } func TestValidate_MapValidationWithError(t *testing.T) { var x1 interface{} = &Product{ Arr: &[]string{"1", "2"}, M: &map[string]string{"a": "hello"}, } s := "hello" var x2 interface{} = &Sample{ M: &map[string]*string{"a": &s}, } v := []Validation{ {x1, []Constraint{{"x1", Null, true, []Constraint{ {"Arr", Null, true, []Constraint{ {"Arr", Empty, true, nil}, {"Arr", MaxItems, 4, nil}, {"Arr", UniqueItems, true, nil}, }, }, {"M", Null, false, []Constraint{ {"M", Empty, false, nil}, {"M", MinItems, 1, nil}, {"M", UniqueItems, true, nil}, }, }, }, }}}, {x2, []Constraint{ {"x2", Null, true, []Constraint{ {"M", Null, false, []Constraint{ {"M", Empty, false, nil}, {"M", MinItems, 2, nil}, {"M", UniqueItems, true, nil}, }, }, }, }, {"Name", Empty, true, nil}, }}, } z := Validate(v).Error() require.Equal(t, strings.Contains(z, "minimum item limit is 2; got: 1"), true) require.Equal(t, strings.Contains(z, "MinItems"), true) } func TestValidate_MapValidationWithoutError(t *testing.T) { var x1 interface{} = &Product{ Arr: &[]string{"1", "2"}, M: &map[string]string{"a": "hello"}, } s := "hello" var x2 interface{} = &Sample{ M: &map[string]*string{"a": &s}, } v := []Validation{ {x1, []Constraint{{"x1", Null, true, []Constraint{ {"Arr", Null, true, []Constraint{ {"Arr", Empty, true, nil}, {"Arr", MaxItems, 4, nil}, {"Arr", UniqueItems, true, nil}, }, }, {"M", Null, false, []Constraint{ {"M", Empty, false, nil}, {"M", MinItems, 1, nil}, {"M", UniqueItems, true, nil}, {"M", Pattern, "^[a-z]+$", nil}, }, }, }, }}}, {x2, []Constraint{ {"x2", Null, true, []Constraint{ {"M", Null, false, []Constraint{ {"M", Empty, false, nil}, {"M", MinItems, 1, nil}, {"M", UniqueItems, true, nil}, {"M", Pattern, "^[a-z]+$", nil}, }, }, }, }, {"Name", Empty, true, nil}, }}, } require.Nil(t, Validate(v)) } func TestValidate_UnknownType(t *testing.T) { var c chan int v := []Validation{ {c, []Constraint{{"c", Null, true, nil}}}, } require.Equal(t, Validate(v).Error(), createError(reflect.ValueOf(c), v[0].Constraints[0], fmt.Sprintf("unknown type %v", reflect.ValueOf(c).Kind())).Error()) } func TestValidate_example1(t *testing.T) { var x1 interface{} = Product{ Arr: &[]string{"1", "1"}, M: &map[string]string{"a": "hello"}, } s := "hello" var x2 interface{} = Sample{ M: &map[string]*string{"a": &s}, } v := []Validation{ {x1, []Constraint{{"Arr", Null, true, []Constraint{ {"Arr", Empty, true, nil}, {"Arr", MaxItems, 4, nil}, {"Arr", UniqueItems, true, nil}, }}, {"M", Null, false, []Constraint{ {"M", Empty, false, nil}, {"M", MinItems, 1, nil}, {"M", UniqueItems, true, nil}, }, }, }}, {x2, []Constraint{ {"M", Null, false, []Constraint{ {"M", Empty, false, nil}, {"M", MinItems, 1, nil}, {"M", UniqueItems, true, nil}, }, }, {"Name", Empty, true, nil}, }}, } s = "Arr" require.Equal(t, Validate(v).Error(), createError(reflect.ValueOf([]string{"1", "1"}), v[0].Constraints[0].Chain[2], fmt.Sprintf("all items in parameter %q must be unique; got:%v", s, []string{"1", "1"})).Error()) } func TestValidate_Int(t *testing.T) { n := int32(100) v := []Validation{ {n, []Constraint{ {"n", MultipleOf, 10, nil}, {"n", ExclusiveMinimum, 100, nil}, }, }, } require.Equal(t, Validate(v).Error(), createError(reflect.ValueOf(n), v[0].Constraints[1], "value must be greater than 100").Error()) } func TestValidate_IntPointer(t *testing.T) { n := int32(100) p := &n v := []Validation{ {p, []Constraint{ {"p", Null, true, []Constraint{ {"p", ExclusiveMinimum, 100, nil}, }}, }, }, } require.Equal(t, Validate(v).Error(), createError(reflect.ValueOf(n), v[0].Constraints[0].Chain[0], "value must be greater than 100").Error()) // required paramter p = nil v = []Validation{ {p, []Constraint{ {"p", Null, true, []Constraint{ {"p", ExclusiveMinimum, 100, nil}, }}, }, }, } require.Equal(t, Validate(v).Error(), createError(reflect.ValueOf(v[0].TargetValue), v[0].Constraints[0], "value can not be null; required parameter").Error()) // Not required p = nil v = []Validation{ {p, []Constraint{ {"p", Null, false, []Constraint{ {"p", ExclusiveMinimum, 100, nil}, }}, }, }, } require.Nil(t, Validate(v)) } func TestValidate_IntStruct(t *testing.T) { n := int32(100) p := &Product{ Num: &n, } v := []Validation{ {p, []Constraint{{"p", Null, true, []Constraint{ {"Num", Null, true, []Constraint{ {"Num", ExclusiveMinimum, 100, nil}, }}, }, }}}, } require.Equal(t, Validate(v).Error(), createError(reflect.ValueOf(n), v[0].Constraints[0].Chain[0].Chain[0], "value must be greater than 100").Error()) // required paramter p = &Product{} v = []Validation{ {p, []Constraint{{"p", Null, true, []Constraint{ {"p.Num", Null, true, []Constraint{ {"p.Num", ExclusiveMinimum, 100, nil}, }}, }, }}}, } require.Equal(t, Validate(v).Error(), createError(reflect.ValueOf(p.Num), v[0].Constraints[0].Chain[0], "value can not be null; required parameter").Error()) // Not required p = &Product{} v = []Validation{ {p, []Constraint{{"p", Null, true, []Constraint{ {"Num", Null, false, []Constraint{ {"Num", ExclusiveMinimum, 100, nil}, }}, }, }}}, } require.Nil(t, Validate(v)) // Parent not required p = nil v = []Validation{ {p, []Constraint{{"p", Null, false, []Constraint{ {"Num", Null, false, []Constraint{ {"Num", ExclusiveMinimum, 100, nil}, }}, }, }}}, } require.Nil(t, Validate(v)) } func TestValidate_String(t *testing.T) { s := "hello" v := []Validation{ {s, []Constraint{ {"s", Empty, true, nil}, {"s", Empty, true, []Constraint{{"s", MaxLength, 3, nil}}}, }, }, } require.Equal(t, Validate(v).Error(), createError(reflect.ValueOf(s), v[0].Constraints[1].Chain[0], "value length must be less than or equal to 3").Error()) // required paramter s = "" v = []Validation{ {s, []Constraint{ {"s", Empty, true, nil}, {"s", Empty, true, []Constraint{{"s", MaxLength, 3, nil}}}, }, }, } require.Equal(t, Validate(v).Error(), createError(reflect.ValueOf(s), v[0].Constraints[1], "value can not be null or empty; required parameter").Error()) // not required paramter s = "" v = []Validation{ {s, []Constraint{ {"s", Empty, false, nil}, {"s", Empty, false, []Constraint{{"s", MaxLength, 3, nil}}}, }, }, } require.Nil(t, Validate(v)) } func TestValidate_StringStruct(t *testing.T) { s := "hello" p := &Product{ Str: &s, } v := []Validation{ {p, []Constraint{{"p", Null, true, []Constraint{ {"p.Str", Null, true, []Constraint{ {"p.Str", Empty, true, nil}, {"p.Str", MaxLength, 3, nil}, }}, }, }}}, } // e := ValidationError{ // Constraint: MaxLength, // Target: "Str", // TargetValue: s, // Details: fmt.Sprintf("value length must be less than 3", s), // } // if z := Validate(v); !reflect.DeepEqual(e, z) { // t.Fatalf("autorest/validation: Validate failed to return error \nexpect: %v\ngot: %v", e, z) // } require.Equal(t, Validate(v).Error(), createError(reflect.ValueOf(s), v[0].Constraints[0].Chain[0].Chain[1], "value length must be less than or equal to 3").Error()) // required paramter - can't be Empty s = "" p = &Product{ Str: &s, } v = []Validation{ {p, []Constraint{{"p", Null, true, []Constraint{ {"Str", Null, true, []Constraint{ {"Str", Empty, true, nil}, {"Str", MaxLength, 3, nil}, }}, }, }}}, } require.Equal(t, Validate(v).Error(), createError(reflect.ValueOf(s), v[0].Constraints[0].Chain[0].Chain[0], "value can not be null or empty; required parameter").Error()) // required paramter - can't be null p = &Product{} v = []Validation{ {p, []Constraint{{"p", Null, true, []Constraint{ {"p.Str", Null, true, []Constraint{ {"p.Str", Empty, true, nil}, {"p.Str", MaxLength, 3, nil}, }}, }, }}}, } require.Equal(t, Validate(v).Error(), createError(reflect.ValueOf(p.Str), v[0].Constraints[0].Chain[0], "value can not be null; required parameter").Error()) // Not required p = &Product{} v = []Validation{ {p, []Constraint{{"p", Null, true, []Constraint{ {"Str", Null, false, []Constraint{ {"Str", Empty, true, nil}, {"Str", MaxLength, 3, nil}, }}, }, }}}, } require.Nil(t, Validate(v)) // Parent not required p = nil v = []Validation{ {p, []Constraint{{"p", Null, false, []Constraint{ {"Str", Null, true, []Constraint{ {"Str", Empty, true, nil}, {"Str", MaxLength, 3, nil}, }}, }, }}}, } require.Nil(t, Validate(v)) } func TestValidate_Array(t *testing.T) { s := []string{"hello"} v := []Validation{ {s, []Constraint{ {"s", Null, true, []Constraint{ {"s", Empty, true, nil}, {"s", MinItems, 2, nil}, }}, }, }, } require.Equal(t, Validate(v).Error(), createError(reflect.ValueOf(s), v[0].Constraints[0].Chain[1], fmt.Sprintf("minimum item limit is 2; got: %v", len(s))).Error()) // Empty array v = []Validation{ {[]string{}, []Constraint{ {"s", Null, true, []Constraint{ {"s", Empty, true, nil}, {"s", MinItems, 2, nil}}}, }, }, } require.Equal(t, Validate(v).Error(), createError(reflect.ValueOf([]string{}), v[0].Constraints[0].Chain[0], "value can not be null or empty; required parameter").Error()) // null array var s1 []string v = []Validation{ {s1, []Constraint{ {"s1", Null, true, []Constraint{ {"s1", Empty, true, nil}, {"s1", MinItems, 2, nil}}}, }, }, } require.Equal(t, Validate(v).Error(), createError(reflect.ValueOf(s1), v[0].Constraints[0], "value can not be null; required parameter").Error()) // not required paramter v = []Validation{ {s1, []Constraint{ {"s1", Null, false, []Constraint{ {"s1", Empty, true, nil}, {"s1", MinItems, 2, nil}}}, }, }, } require.Nil(t, Validate(v)) } func TestValidate_ArrayPointer(t *testing.T) { s := []string{"hello"} v := []Validation{ {&s, []Constraint{ {"s", Null, true, []Constraint{ {"s", Empty, true, nil}, {"s", MinItems, 2, nil}, }}, }, }, } require.Equal(t, Validate(v).Error(), createError(reflect.ValueOf(s), v[0].Constraints[0].Chain[1], fmt.Sprintf("minimum item limit is 2; got: %v", len(s))).Error()) // Empty array v = []Validation{ {&[]string{}, []Constraint{ {"s", Null, true, []Constraint{ {"s", Empty, true, nil}, {"s", MinItems, 2, nil}}}, }, }, } require.Equal(t, Validate(v).Error(), createError(reflect.ValueOf([]string{}), v[0].Constraints[0].Chain[0], "value can not be null or empty; required parameter").Error()) // null array var s1 *[]string v = []Validation{ {s1, []Constraint{ {"s1", Null, true, []Constraint{ {"s1", Empty, true, nil}, {"s1", MinItems, 2, nil}}}, }, }, } require.Equal(t, Validate(v).Error(), createError(reflect.ValueOf(s1), v[0].Constraints[0], "value can not be null; required parameter").Error()) // not required paramter v = []Validation{ {s1, []Constraint{ {"s1", Null, false, []Constraint{ {"s1", Empty, true, nil}, {"s1", MinItems, 2, nil}}}, }, }, } require.Nil(t, Validate(v)) } func TestValidate_ArrayInStruct(t *testing.T) { s := []string{"hello"} p := &Product{ Arr: &s, } v := []Validation{ {p, []Constraint{{"p", Null, true, []Constraint{ {"p.Arr", Null, true, []Constraint{ {"p.Arr", Empty, true, nil}, {"p.Arr", MinItems, 2, nil}, }}, }, }}}, } require.Equal(t, Validate(v).Error(), createError(reflect.ValueOf(s), v[0].Constraints[0].Chain[0].Chain[1], fmt.Sprintf("minimum item limit is 2; got: %v", len(s))).Error()) // required paramter - can't be Empty p = &Product{ Arr: &[]string{}, } v = []Validation{ {p, []Constraint{{"p", Null, true, []Constraint{ {"p.Arr", Null, true, []Constraint{ {"p.Arr", Empty, true, nil}, {"p.Arr", MinItems, 2, nil}, }}, }, }}}, } require.Equal(t, Validate(v).Error(), createError(reflect.ValueOf([]string{}), v[0].Constraints[0].Chain[0].Chain[0], "value can not be null or empty; required parameter").Error()) // required paramter - can't be null p = &Product{} v = []Validation{ {p, []Constraint{{"p", Null, true, []Constraint{ {"p.Arr", Null, true, []Constraint{ {"p.Arr", Empty, true, nil}, {"p.Arr", MinItems, 2, nil}, }}, }, }}}, } require.Equal(t, Validate(v).Error(), createError(reflect.ValueOf(p.Arr), v[0].Constraints[0].Chain[0], "value can not be null; required parameter").Error()) // Not required v = []Validation{ {&Product{}, []Constraint{{"p", Null, true, []Constraint{ {"Arr", Null, false, []Constraint{ {"Arr", Empty, true, nil}, {"Arr", MinItems, 2, nil}, }}, }, }}}, } require.Nil(t, Validate(v)) // Parent not required p = nil v = []Validation{ {p, []Constraint{{"p", Null, false, []Constraint{ {"Arr", Null, true, []Constraint{ {"Arr", Empty, true, nil}, {"Arr", MinItems, 2, nil}, }}, }, }}}, } require.Nil(t, Validate(v)) } func TestValidate_StructInStruct(t *testing.T) { p := &Product{ C: &Child{I: "hello"}, } v := []Validation{ {p, []Constraint{{"p", Null, true, []Constraint{{"C", Null, true, []Constraint{{"I", MinLength, 7, nil}}}, }, }}}, } require.Equal(t, Validate(v).Error(), createError(reflect.ValueOf(p.C.I), v[0].Constraints[0].Chain[0].Chain[0], "value length must be greater than or equal to 7").Error()) // required paramter - can't be Empty p = &Product{ C: &Child{I: ""}, } v = []Validation{ {p, []Constraint{{"p", Null, true, []Constraint{{"C", Null, true, []Constraint{{"I", Empty, true, nil}}}, }, }}}, } require.Equal(t, Validate(v).Error(), createError(reflect.ValueOf(p.C.I), v[0].Constraints[0].Chain[0].Chain[0], "value can not be null or empty; required parameter").Error()) // required paramter - can't be null p = &Product{} v = []Validation{ {p, []Constraint{{"p", Null, true, []Constraint{{"C", Null, true, []Constraint{{"I", Empty, true, nil}}}, }, }}}, } require.Equal(t, Validate(v).Error(), createError(reflect.ValueOf(p.C), v[0].Constraints[0].Chain[0], "value can not be null; required parameter").Error()) // Not required v = []Validation{ {&Product{}, []Constraint{{"p", Null, true, []Constraint{{"p.C", Null, false, []Constraint{{"p.C.I", Empty, true, nil}}}, }, }}}, } require.Nil(t, Validate(v)) // Parent not required p = nil v = []Validation{ {p, []Constraint{{"p", Null, false, []Constraint{{"p.C", Null, false, []Constraint{{"p.C.I", Empty, true, nil}}}, }, }}}, } require.Nil(t, Validate(v)) } func TestNewErrorWithValidationError(t *testing.T) { p := &Product{} v := []Validation{ {p, []Constraint{{"p", Null, true, []Constraint{{"p.C", Null, true, []Constraint{{"p.C.I", Empty, true, nil}}}, }, }}}, } err := createError(reflect.ValueOf(p.C), v[0].Constraints[0].Chain[0], "value can not be null; required parameter") z := fmt.Sprintf("batch.AccountClient#Create: Invalid input: %s", err.Error()) require.Equal(t, NewErrorWithValidationError(err, "batch.AccountClient", "Create").Error(), z) } go-autorest-8.3.1/autorest/version.go000066400000000000000000000011601315133516500176570ustar00rootroot00000000000000package autorest import ( "bytes" "fmt" "strings" "sync" ) const ( major = 8 minor = 0 patch = 0 tag = "" ) var once sync.Once var version string // Version returns the semantic version (see http://semver.org). func Version() string { once.Do(func() { semver := fmt.Sprintf("%d.%d.%d", major, minor, patch) verBuilder := bytes.NewBufferString(semver) if tag != "" && tag != "-" { updated := strings.TrimPrefix(tag, "-") _, err := verBuilder.WriteString("-" + updated) if err == nil { verBuilder = bytes.NewBufferString(semver) } } version = verBuilder.String() }) return version } go-autorest-8.3.1/glide.lock000066400000000000000000000022351315133516500157370ustar00rootroot00000000000000hash: 51202aefdfe9c4a992f96ab58f6cacf21cdbd1b66efe955c9030bca736ac816d updated: 2017-02-14T17:07:23.015382703-08:00 imports: - name: github.com/dgrijalva/jwt-go version: a601269ab70c205d26370c16f7c81e9017c14e04 subpackages: - . - name: golang.org/x/crypto version: 453249f01cfeb54c3d549ddb75ff152ca243f9d8 repo: https://github.com/golang/crypto.git vcs: git subpackages: - pkcs12 - pkcs12/internal/rc2 - name: golang.org/x/net version: 61557ac0112b576429a0df080e1c2cef5dfbb642 repo: https://github.com/golang/net.git vcs: git subpackages: - . - name: golang.org/x/text version: 06d6eba81293389cafdff7fca90d75592194b2d9 repo: https://github.com/golang/text.git vcs: git subpackages: - . testImports: - name: github.com/davecgh/go-spew version: 346938d642f2ec3594ed81d874461961cd0faa76 subpackages: - spew - name: github.com/Masterminds/semver version: 59c29afe1a994eacb71c833025ca7acf874bb1da - name: github.com/pmezard/go-difflib version: 792786c7400a136282c1664665ae0a8db921c6c2 subpackages: - difflib - name: github.com/stretchr/testify version: 4d4bfba8f1d1027c4fdbe371823030df51419987 subpackages: - assert - require go-autorest-8.3.1/glide.yaml000066400000000000000000000011601315133516500157450ustar00rootroot00000000000000package: github.com/Azure/go-autorest import: - package: github.com/dgrijalva/jwt-go subpackages: - . - package: golang.org/x/crypto vcs: git repo: https://github.com/golang/crypto.git subpackages: - /pkcs12 - package: golang.org/x/net vcs: git repo: https://github.com/golang/net.git subpackages: - . - package: golang.org/x/text vcs: git repo: https://github.com/golang/text.git subpackages: - . testImports: - package: github.com/stretchr/testify vcs: git repo: https://github.com/stretchr/testify.git subpackages: - /require - package: github.com/Masterminds/semver version: ~1.2.2