diff --git a/eng/config.json b/eng/config.json index 711b2f4c3575..c06a0ba0ca4d 100644 --- a/eng/config.json +++ b/eng/config.json @@ -10,7 +10,11 @@ }, { "Name": "internal", - "CoverageGoal": 0.90 + "CoverageGoal": 0.70 + }, + { + "Name": "tables", + "CoverageGoal": 0.01 } ] } diff --git a/eng/pipelines/templates/steps/build-test.yml b/eng/pipelines/templates/steps/build-test.yml index 3fb124547094..46ba835be976 100644 --- a/eng/pipelines/templates/steps/build-test.yml +++ b/eng/pipelines/templates/steps/build-test.yml @@ -30,6 +30,8 @@ steps: displayName: "Install Coverage and Junit Dependencies" workingDirectory: '${{parameters.GoWorkspace}}' + - template: /eng/common/testproxy/test-proxy-docker.yml + - pwsh: | $testDirs = (./eng/scripts/get_test_dirs.ps1 -serviceDir $(SCOPE)) foreach ($td in $testDirs) { @@ -48,10 +50,14 @@ steps: workingDirectory: '${{parameters.GoWorkspace}}' env: GO111MODULE: 'on' + AZURE_RECORD_MODE: 'playback' + PROXY_CERT: $(Build.SourcesDirectory)/eng/common/testproxy/dotnet-devcert.crt - pwsh: ../eng/scripts/create_coverage.ps1 ${{parameters.ServiceDirectory}} displayName: 'Generate Coverage XML' workingDirectory: '${{parameters.GoWorkspace}}sdk' + env: + GO111MODULE: 'off' - task: PublishTestResults@2 condition: succeededOrFailed() diff --git a/eng/pipelines/templates/steps/configure-proxy.yml b/eng/pipelines/templates/steps/configure-proxy.yml deleted file mode 100644 index c7308e930c69..000000000000 --- a/eng/pipelines/templates/steps/configure-proxy.yml +++ /dev/null @@ -1,22 +0,0 @@ -parameters: - ServiceDirectory: '' - -steps: - - pwsh: | - $certUriPfx = "https://github.com/Azure/azure-sdk-tools/raw/main/tools/test-proxy/docker/dev_certificate/dotnet-devcert.pfx" - $certUriCrt = "https://github.com/Azure/azure-sdk-tools/raw/main/tools/test-proxy/docker/dev_certificate/dotnet-devcert.crt" - $certLocationPfx = "$(Build.SourcesDirectory)/dotnet-devcert.pfx" - $certLocationCrt = "$(Build.SourcesDirectory)/dotnet-devcert.crt" - - Invoke-WebRequest ` - -Uri $certUriPfx ` - -OutFile $certLocationPfx -UseBasicParsing - - Invoke-WebRequest ` - -Uri $certUriCrt ` - -OutFile $certLocationCrt -UseBasicParsing - - dotnet dev-certs https --clean --import $certLocationPfx -p "password" - - Write-Host "##vso[task.setvariable variable=PROXY_CERT]$certLocationCrt" - displayName: 'Download and Trust Certificate' diff --git a/eng/scripts/proxy-server.ps1 b/eng/scripts/proxy-server.ps1 deleted file mode 100644 index 75f0b376712f..000000000000 --- a/eng/scripts/proxy-server.ps1 +++ /dev/null @@ -1,66 +0,0 @@ -param( - [ValidateSet("start", "stop")] - [String] - $mode, - [String] - $targetFolder = "." -) - -try { - docker --version | Out-Null -} -catch { - Write-Error "A invocation of docker --version failed. This indicates that docker is not properly installed or running." - Write-Error "Please check your docker invocation and try running the script again." -} - -$repoRoot = (Resolve-Path $targetFolder).Path.Replace("`\", "/") -Write-Host $repoRoot - -$CONTAINER_NAME = "ambitious_azsdk_test_proxy" -$IMAGE_SOURCE = "azsdkengsys.azurecr.io/engsys/testproxy-lin:1037115" -$Initial = "" - -if ($IsWindows -and $env:TF_BUILD){ - $IMAGE_SOURCE = "azsdkengsys.azurecr.io/engsys/testproxy-win:1037115" - $Initial = "C:" -} - -function Get-Proxy-Container(){ - return (docker container ls -a --format "{{ json . }}" --filter "name=$CONTAINER_NAME" ` - | ConvertFrom-Json ` - | Select-Object -First 1) -} - -if ($mode -eq "start"){ - $proxyContainer = Get-Proxy-Container - - # if we already have one, we just need to check the state - if($proxyContainer){ - if ($proxyContainer.State -eq "running") - { - Write-Host "Discovered an already running instance of the test-proxy!. Exiting" - exit(0) - } - } - # else we need to create it - else { - Write-Host "Attempting creation of Docker host $CONTAINER_NAME" - Write-Host "docker container create -v `"${repoRoot}:${Initial}/etc/testproxy`" -p 5001:5001 -p 5000:5000 --name $CONTAINER_NAME $IMAGE_SOURCE" - docker container create -v "${repoRoot}:${Initial}/etc/testproxy" -p 5001:5001 -p 5000:5000 --name $CONTAINER_NAME $IMAGE_SOURCE - } - - Write-Host "Attempting start of Docker host $CONTAINER_NAME" - docker container start $CONTAINER_NAME -} - -if ($mode -eq "stop"){ - $proxyContainer = Get-Proxy-Container - - if($proxyContainer){ - if($proxyContainer.State -eq "running"){ - Write-Host "Found a running instance of $CONTAINER_NAME, shutting it down." - docker container stop $CONTAINER_NAME - } - } -} diff --git a/sdk/tables/aztable/byte_array_response.go b/sdk/tables/aztable/byte_array_response.go deleted file mode 100644 index eeb1eece86ae..000000000000 --- a/sdk/tables/aztable/byte_array_response.go +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package aztable - -import ( - "encoding/json" - "net/http" - "time" -) - -// ByteArrayResponse is the return type for a GetEntity operation. The entities properties are stored in the Value property -type ByteArrayResponse struct { - // ClientRequestID contains the information returned from the x-ms-client-request-id header response. - ClientRequestID *string - - // ContentType contains the information returned from the Content-Type header response. - ContentType *string - - // Date contains the information returned from the Date header response. - Date *time.Time - - // ETag contains the information returned from the ETag header response. - ETag *string - - // PreferenceApplied contains the information returned from the Preference-Applied header response. - PreferenceApplied *string - - // RawResponse contains the underlying HTTP response. - RawResponse *http.Response - - // RequestID contains the information returned from the x-ms-request-id header response. - RequestID *string - - // The other properties of the table entity. - Value []byte - - // Version contains the information returned from the x-ms-version header response. - Version *string - - // XMSContinuationNextPartitionKey contains the information returned from the x-ms-continuation-NextPartitionKey header response. - XMSContinuationNextPartitionKey *string - - // XMSContinuationNextRowKey contains the information returned from the x-ms-continuation-NextRowKey header response. - XMSContinuationNextRowKey *string -} - -// newByteArrayResponse converts a MapofInterfaceResponse from a map[string]interface{} to a []byte. -func newByteArrayResponse(m MapOfInterfaceResponse) (ByteArrayResponse, error) { - marshalledValue, err := json.Marshal(m.Value) - if err != nil { - return ByteArrayResponse{}, err - } - return ByteArrayResponse{ - ClientRequestID: m.ClientRequestID, - ContentType: m.ContentType, - Date: m.Date, - ETag: m.ETag, - PreferenceApplied: m.PreferenceApplied, - RawResponse: m.RawResponse, - RequestID: m.RequestID, - Value: marshalledValue, - Version: m.Version, - XMSContinuationNextPartitionKey: m.XMSContinuationNextPartitionKey, - XMSContinuationNextRowKey: m.XMSContinuationNextRowKey, - }, nil -} - -// TableEntityListByteResponseResponse is the response envelope for operations that return a TableEntityQueryResponse type. -type TableEntityListByteResponseResponse struct { - // ClientRequestID contains the information returned from the x-ms-client-request-id header response. - ClientRequestID *string - - // Date contains the information returned from the Date header response. - Date *time.Time - - // RawResponse contains the underlying HTTP response. - RawResponse *http.Response - - // RequestID contains the information returned from the x-ms-request-id header response. - RequestID *string - - // The properties for the table entity query response. - TableEntityQueryResponse *TableEntityQueryByteResponse - - // Version contains the information returned from the x-ms-version header response. - Version *string - - // XMSContinuationNextPartitionKey contains the information returned from the x-ms-continuation-NextPartitionKey header response. - XMSContinuationNextPartitionKey *string - - // XMSContinuationNextRowKey contains the information returned from the x-ms-continuation-NextRowKey header response. - XMSContinuationNextRowKey *string -} - -// TableEntityQueryByteResponse - The properties for the table entity query response. -type TableEntityQueryByteResponse struct { - // The metadata response of the table. - OdataMetadata *string - - // List of table entities. - Value [][]byte -} - -func castToByteResponse(resp *TableEntityQueryResponseResponse) (TableEntityListByteResponseResponse, error) { - marshalledValue := make([][]byte, 0) - for _, e := range resp.TableEntityQueryResponse.Value { - m, err := json.Marshal(e) - if err != nil { - return TableEntityListByteResponseResponse{}, err - } - marshalledValue = append(marshalledValue, m) - } - - t := TableEntityQueryByteResponse{ - OdataMetadata: resp.TableEntityQueryResponse.OdataMetadata, - Value: marshalledValue, - } - - return TableEntityListByteResponseResponse{ - ClientRequestID: resp.ClientRequestID, - Date: resp.Date, - RawResponse: resp.RawResponse, - RequestID: resp.RequestID, - TableEntityQueryResponse: &t, - Version: resp.Version, - XMSContinuationNextPartitionKey: resp.XMSContinuationNextPartitionKey, - XMSContinuationNextRowKey: resp.XMSContinuationNextRowKey, - }, nil -} - -type TableListResponse struct { - // The metadata response of the table. - OdataMetadata *string `json:"odata.metadata,omitempty"` - - // List of tables. - Value []*TableResponseProperties `json:"value,omitempty"` -} - -func tableListResponseFromQueryResponse(q *TableQueryResponse) *TableListResponse { - return &TableListResponse{ - OdataMetadata: q.OdataMetadata, - Value: q.Value, - } -} - -// TableListResponseResponse stores the results of a ListTables operation -type TableListResponseResponse struct { - // ClientRequestID contains the information returned from the x-ms-client-request-id header response. - ClientRequestID *string - - // Date contains the information returned from the Date header response. - Date *time.Time - - // RawResponse contains the underlying HTTP response. - RawResponse *http.Response - - // RequestID contains the information returned from the x-ms-request-id header response. - RequestID *string - - // The properties for the table query response. - TableListResponse *TableListResponse - - // Version contains the information returned from the x-ms-version header response. - Version *string - - // XMSContinuationNextTableName contains the information returned from the x-ms-continuation-NextTableName header response. - XMSContinuationNextTableName *string -} - -func listResponseFromQueryResponse(q TableQueryResponseResponse) *TableListResponseResponse { - return &TableListResponseResponse{ - ClientRequestID: q.ClientRequestID, - Date: q.Date, - RawResponse: q.RawResponse, - RequestID: q.RequestID, - TableListResponse: tableListResponseFromQueryResponse(q.TableQueryResponse), - Version: q.Version, - XMSContinuationNextTableName: q.XMSContinuationNextTableName, - } -} diff --git a/sdk/tables/aztable/connection_string.go b/sdk/tables/aztable/connection_string.go new file mode 100644 index 000000000000..d8bbb31f4a6c --- /dev/null +++ b/sdk/tables/aztable/connection_string.go @@ -0,0 +1,105 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package aztable + +import ( + "fmt" + "strings" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore" +) + +// NewTableClientFromConnectionString creates a new TableClient struct from a connection string. The connection +// string must contain either an account name and account key or an account name and a shared access signature. +func NewTableClientFromConnectionString(tableName string, connectionString string, options *TableClientOptions) (*TableClient, error) { + if options == nil { + options = &TableClientOptions{} + } + endpoint, credential, err := parseConnectionString(connectionString) + if err != nil { + return nil, err + } + return NewTableClient(tableName, endpoint, credential, options) +} + +// NewTableServiceClientFromConnectionString creates a new TableServiceClient struct from a connection string. The connection +// string must contain either an account name and account key or an account name and a shared access signature. +func NewTableServiceClientFromConnectionString(connectionString string, options *TableClientOptions) (*TableServiceClient, error) { + endpoint, credential, err := parseConnectionString(connectionString) + if err != nil { + return nil, err + } + return NewTableServiceClient(endpoint, credential, options) +} + +// convertConnStrToMap converts a connection string (in format key1=value1;key2=value2;key3=value3;) into a map of key-value pairs +func convertConnStrToMap(connStr string) (map[string]string, error) { + ret := make(map[string]string) + connStr = strings.TrimRight(connStr, ";") + + splitString := strings.Split(connStr, ";") + if len(splitString) == 0 { + return ret, errConnectionString + } + for _, stringPart := range splitString { + parts := strings.SplitN(stringPart, "=", 2) + if len(parts) != 2 { + return ret, errConnectionString + } + ret[parts[0]] = parts[1] + } + return ret, nil +} + +// parseConnectionString parses a connection string into a service URL and a SharedKeyCredential or a service url with the +// SharedAccessSignature combined. +func parseConnectionString(connStr string) (string, azcore.Credential, error) { + var serviceURL string + var cred azcore.Credential + + defaultScheme := "https" + defaultSuffix := "core.windows.net" + + connStrMap, err := convertConnStrToMap(connStr) + if err != nil { + return "", nil, err + } + + accountName, ok := connStrMap["AccountName"] + if !ok { + return "", nil, errConnectionString + } + accountKey, ok := connStrMap["AccountKey"] + if !ok { + sharedAccessSignature, ok := connStrMap["SharedAccessSignature"] + if !ok { + return "", nil, errConnectionString + } + return fmt.Sprintf("%v://%v.table.%v/?%v", defaultScheme, accountName, defaultSuffix, sharedAccessSignature), azcore.NewAnonymousCredential(), nil + } + + protocol, ok := connStrMap["DefaultEndpointsProtocol"] + if !ok { + protocol = defaultScheme + } + + suffix, ok := connStrMap["EndpointSuffix"] + if !ok { + suffix = defaultSuffix + } + + tableEndpoint, ok := connStrMap["TableEndpoint"] + if ok { + cred, err = NewSharedKeyCredential(accountName, accountKey) + return tableEndpoint, cred, err + } + serviceURL = fmt.Sprintf("%v://%v.table.%v", protocol, accountName, suffix) + + cred, err = NewSharedKeyCredential(accountName, accountKey) + if err != nil { + return "", nil, err + } + + return serviceURL, cred, nil +} diff --git a/sdk/tables/aztable/connection_string_test.go b/sdk/tables/aztable/connection_string_test.go new file mode 100644 index 000000000000..49138372fc01 --- /dev/null +++ b/sdk/tables/aztable/connection_string_test.go @@ -0,0 +1,198 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package aztable + +import ( + "encoding/base64" + "strings" + "testing" + + "github.com/stretchr/testify/require" +) + +func getAccountKey(cred *SharedKeyCredential) string { + return base64.StdEncoding.EncodeToString(cred.accountKey.Load().([]byte)) +} + +func TestConnectionStringParser(t *testing.T) { + connStr := "DefaultEndpointsProtocol=https;AccountName=dummyaccount;AccountKey=secretkeykey;EndpointSuffix=core.windows.net" + serviceURL, cred, err := parseConnectionString(connStr) + require.NoError(t, err) + require.Equal(t, serviceURL, "https://dummyaccount.table.core.windows.net") + require.NotNil(t, cred) + + sharedKeyCred, ok := cred.(*SharedKeyCredential) + require.True(t, ok) + require.Equal(t, sharedKeyCred.accountName, "dummyaccount") + require.Equal(t, getAccountKey(sharedKeyCred), "secretkeykey") + + client, err := NewTableClientFromConnectionString("tableName", connStr, nil) + require.NoError(t, err) + require.NotNil(t, client) + sharedKeyCred, ok = client.cred.(*SharedKeyCredential) + require.True(t, ok) + require.Equal(t, sharedKeyCred.accountName, "dummyaccount") + require.Equal(t, getAccountKey(sharedKeyCred), "secretkeykey") + require.True(t, strings.HasPrefix(client.client.con.u, "https://")) + require.True(t, strings.Contains(client.client.con.u, "core.windows.net")) +} + +func TestConnectionStringParserHTTP(t *testing.T) { + connStr := "DefaultEndpointsProtocol=http;AccountName=dummyaccount;AccountKey=secretkeykey;EndpointSuffix=core.windows.net" + serviceURL, cred, err := parseConnectionString(connStr) + require.NoError(t, err) + require.Equal(t, serviceURL, "http://dummyaccount.table.core.windows.net") + require.NotNil(t, cred) + + sharedKeyCred, ok := cred.(*SharedKeyCredential) + require.True(t, ok) + require.Equal(t, sharedKeyCred.accountName, "dummyaccount") + require.Equal(t, getAccountKey(sharedKeyCred), "secretkeykey") + + client, err := NewTableClientFromConnectionString("tableName", connStr, nil) + require.NoError(t, err) + require.NotNil(t, client) + sharedKeyCred, ok = client.cred.(*SharedKeyCredential) + require.True(t, ok) + require.Equal(t, sharedKeyCred.accountName, "dummyaccount") + require.Equal(t, getAccountKey(sharedKeyCred), "secretkeykey") + require.True(t, strings.HasPrefix(client.client.con.u, "http://")) + require.True(t, strings.Contains(client.client.con.u, "core.windows.net")) +} + +func TestConnectionStringParserBasic(t *testing.T) { + connStr := "AccountName=dummyaccount;AccountKey=secretkeykey" + serviceURL, cred, err := parseConnectionString(connStr) + require.NoError(t, err) + require.Equal(t, serviceURL, "https://dummyaccount.table.core.windows.net") + require.NotNil(t, cred) + + sharedKeyCred, ok := cred.(*SharedKeyCredential) + require.True(t, ok) + require.Equal(t, sharedKeyCred.accountName, "dummyaccount") + require.Equal(t, getAccountKey(sharedKeyCred), "secretkeykey") + + client, err := NewTableClientFromConnectionString("tableName", connStr, nil) + require.NoError(t, err) + require.NotNil(t, client) + sharedKeyCred, ok = client.cred.(*SharedKeyCredential) + require.True(t, ok) + require.Equal(t, sharedKeyCred.accountName, "dummyaccount") + require.Equal(t, getAccountKey(sharedKeyCred), "secretkeykey") + require.True(t, strings.HasPrefix(client.client.con.u, "https://")) + require.True(t, strings.Contains(client.client.con.u, "core.windows.net")) +} + +func TestConnectionStringParserCustomDomain(t *testing.T) { + connStr := "AccountName=dummyaccount;AccountKey=secretkeykey;TableEndpoint=www.mydomain.com;" + serviceURL, cred, err := parseConnectionString(connStr) + require.NoError(t, err) + require.Equal(t, serviceURL, "www.mydomain.com") + require.NotNil(t, cred) + + sharedKeyCred, ok := cred.(*SharedKeyCredential) + require.True(t, ok) + require.Equal(t, sharedKeyCred.accountName, "dummyaccount") + require.Equal(t, getAccountKey(sharedKeyCred), "secretkeykey") + + client, err := NewTableClientFromConnectionString("tableName", connStr, nil) + require.NoError(t, err) + require.NotNil(t, client) + sharedKeyCred, ok = client.cred.(*SharedKeyCredential) + require.True(t, ok) + require.Equal(t, sharedKeyCred.accountName, "dummyaccount") + require.Equal(t, getAccountKey(sharedKeyCred), "secretkeykey") + require.True(t, strings.HasPrefix(client.client.con.u, "www.")) + require.True(t, strings.Contains(client.client.con.u, "mydomain.com")) +} + +func TestConnectionStringParserInvalid(t *testing.T) { + badConnectionStrings := []string{ + "", + "foobar", + "foo;bar;baz", + "foo=;bar=;", + "=", + ";", + "=;==", + "foobar=baz=foo", + } + + for _, badConnStr := range badConnectionStrings { + _, _, err := parseConnectionString(badConnStr) + require.Error(t, err) + require.Contains(t, err.Error(), errConnectionString.Error()) + } +} + +func TestConnectionStringSAS(t *testing.T) { + connStr := "AccountName=dummyaccount;SharedAccessSignature=fakesharedaccesssignature;" + serviceURL, cred, err := parseConnectionString(connStr) + require.NoError(t, err) + require.Equal(t, serviceURL, "https://dummyaccount.table.core.windows.net/?fakesharedaccesssignature") + require.NotNil(t, cred) + + client, err := NewTableClientFromConnectionString("tableName", connStr, nil) + require.NoError(t, err) + require.NotNil(t, client) + require.True(t, strings.HasPrefix(client.client.con.u, "https://")) + require.True(t, strings.Contains(client.client.con.u, "core.windows.net")) +} + +func TestConnectionStringCosmos(t *testing.T) { + connStr := "DefaultEndpointsProtocol=https;AccountName=dummyaccountname;AccountKey=secretkeykey;TableEndpoint=https://dummyaccountname.table.cosmos.azure.com:443/;" + serviceURL, cred, err := parseConnectionString(connStr) + require.NoError(t, err) + require.Equal(t, serviceURL, "https://dummyaccountname.table.cosmos.azure.com:443/") + require.NotNil(t, cred) + + client, err := NewTableClientFromConnectionString("tableName", connStr, nil) + require.NoError(t, err) + require.NotNil(t, client) + require.True(t, strings.HasPrefix(client.client.con.u, "https://")) + require.True(t, strings.Contains(client.client.con.u, "cosmos.azure.com:443")) + + sharedKey, ok := client.cred.(*SharedKeyCredential) + require.True(t, ok) + require.Equal(t, sharedKey.accountName, "dummyaccountname") + require.Equal(t, getAccountKey(sharedKey), "secretkeykey") +} + +func TestConnectionStringChinaCloud(t *testing.T) { + connStr := "AccountName=dummyaccountname;AccountKey=secretkeykey;DefaultEndpointsProtocol=http;EndpointSuffix=core.chinacloudapi.cn;" + serviceURL, cred, err := parseConnectionString(connStr) + require.NoError(t, err) + require.Equal(t, serviceURL, "http://dummyaccountname.table.core.chinacloudapi.cn") + require.NotNil(t, cred) + + client, err := NewTableClientFromConnectionString("tableName", connStr, nil) + require.NoError(t, err) + require.NotNil(t, client) + require.True(t, strings.HasPrefix(client.client.con.u, "http://")) + require.True(t, strings.Contains(client.client.con.u, "core.chinacloudapi.cn")) + + sharedKey, ok := client.cred.(*SharedKeyCredential) + require.True(t, ok) + require.Equal(t, sharedKey.accountName, "dummyaccountname") + require.Equal(t, getAccountKey(sharedKey), "secretkeykey") +} + +func TestConnectionStringAzurite(t *testing.T) { + connStr := "DefaultEndpointsProtocol=http;AccountName=dummyaccountname;AccountKey=secretkeykey;TableEndpoint=http://local-machine:11002/custom/account/path/faketokensignature;" + serviceURL, cred, err := parseConnectionString(connStr) + require.NoError(t, err) + require.Equal(t, serviceURL, "http://local-machine:11002/custom/account/path/faketokensignature") + require.NotNil(t, cred) + + client, err := NewTableClientFromConnectionString("tableName", connStr, nil) + require.NoError(t, err) + require.NotNil(t, client) + require.True(t, strings.HasPrefix(client.client.con.u, "http://")) + require.True(t, strings.Contains(client.client.con.u, "http://local-machine:11002/custom/account/path/faketokensignature")) + + sharedKey, ok := client.cred.(*SharedKeyCredential) + require.True(t, ok) + require.Equal(t, sharedKey.accountName, "dummyaccountname") + require.Equal(t, getAccountKey(sharedKey), "secretkeykey") +} diff --git a/sdk/tables/aztable/errors.go b/sdk/tables/aztable/errors.go index b2b883f50136..adcc22d4c0d4 100644 --- a/sdk/tables/aztable/errors.go +++ b/sdk/tables/aztable/errors.go @@ -5,6 +5,8 @@ package aztable import "errors" +var errConnectionString = errors.New("connection string is either blank or malformed. The expected connection string should contain key value pairs separated by semicolons. For example 'DefaultEndpointsProtocol=https;AccountName=;AccountKey=;EndpointSuffix=core.windows.net'") + var errInvalidUpdateMode = errors.New("invalid EntityUpdateMode") func checkEntityForPkRk(entity *map[string]interface{}, err error) error { diff --git a/sdk/tables/aztable/table_client.go b/sdk/tables/aztable/table_client.go index 4d7e86c94e14..67f0c9cf2d54 100644 --- a/sdk/tables/aztable/table_client.go +++ b/sdk/tables/aztable/table_client.go @@ -29,6 +29,9 @@ const ( // NewTableClient creates a TableClient struct in the context of the table specified in tableName, using the specified serviceURL, credential, and options. func NewTableClient(tableName string, serviceURL string, cred azcore.Credential, options *TableClientOptions) (*TableClient, error) { + if options == nil { + options = &TableClientOptions{} + } s, err := NewTableServiceClient(serviceURL, cred, options) return s.NewTableClient(tableName), err } diff --git a/sdk/tables/aztable/table_pagers.go b/sdk/tables/aztable/table_pagers.go index 2aa6875205db..5d2b98bf64f7 100644 --- a/sdk/tables/aztable/table_pagers.go +++ b/sdk/tables/aztable/table_pagers.go @@ -5,6 +5,9 @@ package aztable import ( "context" + "encoding/json" + "net/http" + "time" ) // TableEntityListResponsePager is a Pager for Table entity query results. @@ -142,3 +145,174 @@ func (p *tableQueryResponsePager) PageResponse() TableListResponseResponse { func (p *tableQueryResponsePager) Err() error { return p.err } + +// ByteArrayResponse is the return type for a GetEntity operation. The entities properties are stored in the Value property +type ByteArrayResponse struct { + // ClientRequestID contains the information returned from the x-ms-client-request-id header response. + ClientRequestID *string + + // ContentType contains the information returned from the Content-Type header response. + ContentType *string + + // Date contains the information returned from the Date header response. + Date *time.Time + + // ETag contains the information returned from the ETag header response. + ETag *string + + // PreferenceApplied contains the information returned from the Preference-Applied header response. + PreferenceApplied *string + + // RawResponse contains the underlying HTTP response. + RawResponse *http.Response + + // RequestID contains the information returned from the x-ms-request-id header response. + RequestID *string + + // The other properties of the table entity. + Value []byte + + // Version contains the information returned from the x-ms-version header response. + Version *string + + // XMSContinuationNextPartitionKey contains the information returned from the x-ms-continuation-NextPartitionKey header response. + XMSContinuationNextPartitionKey *string + + // XMSContinuationNextRowKey contains the information returned from the x-ms-continuation-NextRowKey header response. + XMSContinuationNextRowKey *string +} + +// newByteArrayResponse converts a MapofInterfaceResponse from a map[string]interface{} to a []byte. +func newByteArrayResponse(m MapOfInterfaceResponse) (ByteArrayResponse, error) { + marshalledValue, err := json.Marshal(m.Value) + if err != nil { + return ByteArrayResponse{}, err + } + return ByteArrayResponse{ + ClientRequestID: m.ClientRequestID, + ContentType: m.ContentType, + Date: m.Date, + ETag: m.ETag, + PreferenceApplied: m.PreferenceApplied, + RawResponse: m.RawResponse, + RequestID: m.RequestID, + Value: marshalledValue, + Version: m.Version, + XMSContinuationNextPartitionKey: m.XMSContinuationNextPartitionKey, + XMSContinuationNextRowKey: m.XMSContinuationNextRowKey, + }, nil +} + +// TableEntityListByteResponseResponse is the response envelope for operations that return a TableEntityQueryResponse type. +type TableEntityListByteResponseResponse struct { + // ClientRequestID contains the information returned from the x-ms-client-request-id header response. + ClientRequestID *string + + // Date contains the information returned from the Date header response. + Date *time.Time + + // RawResponse contains the underlying HTTP response. + RawResponse *http.Response + + // RequestID contains the information returned from the x-ms-request-id header response. + RequestID *string + + // The properties for the table entity query response. + TableEntityQueryResponse *TableEntityQueryByteResponse + + // Version contains the information returned from the x-ms-version header response. + Version *string + + // XMSContinuationNextPartitionKey contains the information returned from the x-ms-continuation-NextPartitionKey header response. + XMSContinuationNextPartitionKey *string + + // XMSContinuationNextRowKey contains the information returned from the x-ms-continuation-NextRowKey header response. + XMSContinuationNextRowKey *string +} + +// TableEntityQueryByteResponse - The properties for the table entity query response. +type TableEntityQueryByteResponse struct { + // The metadata response of the table. + OdataMetadata *string + + // List of table entities. + Value [][]byte +} + +func castToByteResponse(resp *TableEntityQueryResponseResponse) (TableEntityListByteResponseResponse, error) { + marshalledValue := make([][]byte, 0) + for _, e := range resp.TableEntityQueryResponse.Value { + m, err := json.Marshal(e) + if err != nil { + return TableEntityListByteResponseResponse{}, err + } + marshalledValue = append(marshalledValue, m) + } + + t := TableEntityQueryByteResponse{ + OdataMetadata: resp.TableEntityQueryResponse.OdataMetadata, + Value: marshalledValue, + } + + return TableEntityListByteResponseResponse{ + ClientRequestID: resp.ClientRequestID, + Date: resp.Date, + RawResponse: resp.RawResponse, + RequestID: resp.RequestID, + TableEntityQueryResponse: &t, + Version: resp.Version, + XMSContinuationNextPartitionKey: resp.XMSContinuationNextPartitionKey, + XMSContinuationNextRowKey: resp.XMSContinuationNextRowKey, + }, nil +} + +type TableListResponse struct { + // The metadata response of the table. + OdataMetadata *string `json:"odata.metadata,omitempty"` + + // List of tables. + Value []*TableResponseProperties `json:"value,omitempty"` +} + +func tableListResponseFromQueryResponse(q *TableQueryResponse) *TableListResponse { + return &TableListResponse{ + OdataMetadata: q.OdataMetadata, + Value: q.Value, + } +} + +// TableListResponseResponse stores the results of a ListTables operation +type TableListResponseResponse struct { + // ClientRequestID contains the information returned from the x-ms-client-request-id header response. + ClientRequestID *string + + // Date contains the information returned from the Date header response. + Date *time.Time + + // RawResponse contains the underlying HTTP response. + RawResponse *http.Response + + // RequestID contains the information returned from the x-ms-request-id header response. + RequestID *string + + // The properties for the table query response. + TableListResponse *TableListResponse + + // Version contains the information returned from the x-ms-version header response. + Version *string + + // XMSContinuationNextTableName contains the information returned from the x-ms-continuation-NextTableName header response. + XMSContinuationNextTableName *string +} + +func listResponseFromQueryResponse(q TableQueryResponseResponse) *TableListResponseResponse { + return &TableListResponseResponse{ + ClientRequestID: q.ClientRequestID, + Date: q.Date, + RawResponse: q.RawResponse, + RequestID: q.RequestID, + TableListResponse: tableListResponseFromQueryResponse(q.TableQueryResponse), + Version: q.Version, + XMSContinuationNextTableName: q.XMSContinuationNextTableName, + } +} diff --git a/sdk/tables/ci.yml b/sdk/tables/ci.yml index 3e9703ae327e..107eb650032f 100644 --- a/sdk/tables/ci.yml +++ b/sdk/tables/ci.yml @@ -13,3 +13,4 @@ stages: - template: /eng/pipelines/templates/jobs/archetype-sdk-client.yml parameters: ServiceDirectory: 'tables' + RunTests: true