├── go.sum ├── go.mod ├── analytics ├── testdata │ ├── Collections.GetByID.json │ ├── DateRanges.GetByID.json │ ├── Segments.GetByID.json │ ├── CalculatedMetrics.GetByID.json │ ├── Dimensions.GetByID.json │ ├── Metrics.GetByID.json │ ├── Users.GetCurrent.json │ ├── Collections.GetAll.json │ ├── DateRanges.GetAll.json │ ├── Segments.GetAll.json │ ├── Dimensions.GetAll.json │ ├── Metrics.GetAll.json │ ├── Reports.Run.Request.json │ ├── Users.GetAll.json │ ├── CalculatedMetrics.GetAll.json │ └── Reports.Run.json ├── reports.go ├── common-types.go ├── metrics-types.go ├── users.go ├── users-types.go ├── reports_test.go ├── dimensions-types.go ├── metrics.go ├── collections-types.go ├── dateranges-types.go ├── collections.go ├── dateranges.go ├── dimensions.go ├── calculatedmetrics-types.go ├── segments.go ├── users_test.go ├── calculatedmetrics.go ├── metrics_test.go ├── collections_test.go ├── dimensions_test.go ├── dateranges_test.go ├── segments-types.go ├── segments_test.go ├── calculatedmetrics_test.go ├── client_test.go ├── client.go └── reports-types.go ├── examples ├── go.mod ├── config │ ├── config.yaml │ └── config.go ├── users │ └── users.go ├── collections │ └── collections.go ├── dateranges │ └── dateranges.go ├── metrics │ └── metrics.go ├── segments │ └── segments.go ├── dimensions │ └── dimensions.go ├── calculatedmetrics │ └── calculatedmetrics.go ├── go.sum ├── reports │ └── reports.go └── utils │ └── utils.go ├── Makefile ├── .github ├── ISSUE_TEMPLATE │ ├── submit_request.md │ └── bug_report.md ├── workflows │ └── test.yaml └── PULL_REQUEST_TEMPLATE │ └── bug_fix.md ├── .gitignore ├── CONTRIBUTING.md ├── CODE_OF_CONDUCT.md ├── README.md └── LICENSE /go.sum: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/adobe/aa-client-go 2 | 3 | go 1.14 4 | -------------------------------------------------------------------------------- /analytics/testdata/Collections.GetByID.json: -------------------------------------------------------------------------------- 1 | { 2 | "collectionItemType": "reportsuite", 3 | "rsid": "amc.aem.prod" 4 | } 5 | -------------------------------------------------------------------------------- /analytics/testdata/DateRanges.GetByID.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "57a9ad685fe707f55ffb68f5", 3 | "name": "Last Week", 4 | "description": "", 5 | "owner": { 6 | "id": 565028 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /analytics/testdata/Segments.GetByID.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "s300003364_589ce94be4b0c29f29c4f07f", 3 | "name": "Product Solution Pages - UI Only", 4 | "rsid": "amc.aem.prod", 5 | "owner": { 6 | "id": 564569 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /analytics/testdata/CalculatedMetrics.GetByID.json: -------------------------------------------------------------------------------- 1 | { 2 | "polarity": "positive", 3 | "precision": 0, 4 | "type": "decimal", 5 | "id": "cm300003364_5ae7447df118f061698ddc31", 6 | "name": "Unique Feature Usage", 7 | "description": "", 8 | "rsid": "amc.aem.prod", 9 | "owner": { 10 | "id": 711989 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /analytics/testdata/Dimensions.GetByID.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "variables/evar1", 3 | "title": "EVar 1", 4 | "name": "EVar 1", 5 | "type": "string", 6 | "category": "Conversion", 7 | "support": ["oberon", "dataWarehouse"], 8 | "pathable": false, 9 | "extraTitleInfo": "evar1", 10 | "segmentable": true, 11 | "reportable": ["oberon"], 12 | "allowedForReporting": false 13 | } 14 | -------------------------------------------------------------------------------- /analytics/testdata/Metrics.GetByID.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "metrics/pageviews", 3 | "title": "Page Views", 4 | "name": "Page Views", 5 | "type": "int", 6 | "category": "Traffic", 7 | "support": ["oberon", "dataWarehouse"], 8 | "Allocation": false, 9 | "Precision": 0, 10 | "calculated": false, 11 | "segmentable": true, 12 | "polarity": "positive", 13 | "allowedForReporting": false 14 | } 15 | -------------------------------------------------------------------------------- /examples/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/adobe/aa-client-go/examples 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/adobe/aa-client-go v0.0.0-20200615175745-0cdcdb816ff2 7 | github.com/adobe/ims-go v0.5.1 8 | github.com/ghodss/yaml v1.0.0 // indirect 9 | github.com/hashicorp/go-retryablehttp v0.6.6 10 | github.com/tkanos/gonfig v0.0.0-20181112185242-896f3d81fadf 11 | gopkg.in/yaml.v2 v2.3.0 // indirect 12 | ) 13 | -------------------------------------------------------------------------------- /analytics/testdata/Users.GetCurrent.json: -------------------------------------------------------------------------------- 1 | { 2 | "companyId": 1, 3 | "loginId": 1, 4 | "login": "12345678-abc-defg-abc-123456789012@example.com", 5 | "changePassword": false, 6 | "createDate": "2020-01-01T00:00:00Z", 7 | "disabled": false, 8 | "email": "12345678-abc-defg-abc-123456789012@example.com", 9 | "firstName": "John", 10 | "fullName": "John Doe", 11 | "imsUserId": "12345678901234567890ABCD@example.com", 12 | "lastName": "Doe" 13 | } 14 | -------------------------------------------------------------------------------- /analytics/testdata/Collections.GetAll.json: -------------------------------------------------------------------------------- 1 | { 2 | "content": [ 3 | { 4 | "collectionItemType": "reportsuite", 5 | "rsid": "amc.aem.prod" 6 | }, 7 | { 8 | "collectionItemType": "reportsuite", 9 | "rsid": "amc.aem.dev" 10 | } 11 | ], 12 | "numberOfElements": 2, 13 | "totalElements": 2, 14 | "lastPage": true, 15 | "firstPage": true, 16 | "totalPages": 1, 17 | "sort": null, 18 | "size": 1000, 19 | "number": 0 20 | } 21 | -------------------------------------------------------------------------------- /examples/config/config.yaml: -------------------------------------------------------------------------------- 1 | Analytics: 2 | Endpoint: https://analytics.adobe.io/api 3 | ReportSuiteID: 4 | CompanyID: 5 | IMSEndpoint: https://ims-na1.adobelogin.com 6 | IMSClientID: 7 | IMSOrgID: 8 | IMSTechnicalAccountID: 9 | IMSClientSecret: 10 | IMSPrivateKey: | 11 | -----BEGIN PRIVATE KEY----- 12 | 13 | -----END PRIVATE KEY----- 14 | 15 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | all: fmt vet lint test 3 | 4 | .PHONY: fmt 5 | fmt: 6 | @go fmt ./... 7 | 8 | .PHONY: vet 9 | vet: 10 | @go vet -all ./... 11 | 12 | .PHONY: lint 13 | lint: 14 | @export PATH=$$PATH:$(go env GOPATH)/bin 15 | @go get -u golang.org/x/lint/golint >/dev/null 2>&1 16 | @golint -min_confidence=.6 -set_exit_status ./... 17 | 18 | .PHONY: test 19 | test: 20 | @go test ./analytics -cover 21 | 22 | .PHONY: coverage 23 | coverage: 24 | @go test -coverprofile=coverage.out ./analytics 25 | @go tool cover -html=coverage.out 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/submit_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "Submit a request" 3 | about: Surface a feature or problem that you think should be solved 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### Describe the feature or problem you’d like to solve 11 | 12 | A clear and concise description of what the feature or problem is. 13 | 14 | ### Proposed solution 15 | 16 | How will it benefit its users? 17 | 18 | ### Additional context 19 | 20 | Add any other context like screenshots or mockups are helpful, if applicable. 21 | -------------------------------------------------------------------------------- /analytics/testdata/DateRanges.GetAll.json: -------------------------------------------------------------------------------- 1 | { 2 | "content": [ 3 | { 4 | "id": "57a9ad685fe707f55ffb68f5", 5 | "name": "Last Week", 6 | "description": "", 7 | "owner": { 8 | "id": 565028 9 | } 10 | }, 11 | { 12 | "id": "57a9ad25cdb5172f10e9c785", 13 | "name": "Current Week", 14 | "description": "", 15 | "owner": { 16 | "id": 565028 17 | } 18 | } 19 | ], 20 | "totalElements": 2, 21 | "numberOfElements": 2, 22 | "lastPage": true, 23 | "totalPages": 1, 24 | "firstPage": true, 25 | "sort": null, 26 | "size": 1000, 27 | "number": 0 28 | } 29 | -------------------------------------------------------------------------------- /analytics/testdata/Segments.GetAll.json: -------------------------------------------------------------------------------- 1 | { 2 | "content": [ 3 | { 4 | "id": "s300003364_584b10f1e4b0d5eeaacd660b", 5 | "name": "AEM Product Pages", 6 | "rsid": "amc.aem.prod", 7 | "owner": { 8 | "id": 578193 9 | } 10 | }, 11 | { 12 | "id": "s300003364_589ce94be4b0c29f29c4f07f", 13 | "name": "Product Solution Pages - UI Only", 14 | "rsid": "amc.aem.prod", 15 | "owner": { 16 | "id": 564569 17 | } 18 | } 19 | ], 20 | "size": 10, 21 | "numberOfElements": 2, 22 | "totalElements": 2, 23 | "previousPage": false, 24 | "firstPage": true, 25 | "nextPage": false, 26 | "lastPage": true, 27 | "totalPages": 1 28 | } 29 | -------------------------------------------------------------------------------- /analytics/testdata/Dimensions.GetAll.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "variables/evar1", 4 | "title": "EVar 1", 5 | "name": "EVar 1", 6 | "type": "string", 7 | "category": "Conversion", 8 | "support": ["oberon", "dataWarehouse"], 9 | "pathable": false, 10 | "extraTitleInfo": "evar1", 11 | "segmentable": true, 12 | "reportable": ["oberon"], 13 | "allowedForReporting": false 14 | }, 15 | { 16 | "id": "variables/evar2", 17 | "title": "EVar 2", 18 | "name": "EVar 2", 19 | "type": "string", 20 | "category": "Conversion", 21 | "support": ["oberon", "dataWarehouse"], 22 | "pathable": false, 23 | "extraTitleInfo": "evar2", 24 | "segmentable": true, 25 | "reportable": ["oberon"], 26 | "allowedForReporting": false 27 | } 28 | ] 29 | -------------------------------------------------------------------------------- /analytics/testdata/Metrics.GetAll.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "metrics/pageviews", 4 | "title": "Page Views", 5 | "name": "Page Views", 6 | "type": "int", 7 | "category": "Traffic", 8 | "support": ["oberon", "dataWarehouse"], 9 | "Allocation": false, 10 | "Precision": 0, 11 | "calculated": false, 12 | "segmentable": true, 13 | "polarity": "positive", 14 | "allowedForReporting": false 15 | }, 16 | { 17 | "id": "metrics/visits", 18 | "title": "Visits", 19 | "name": "Visits", 20 | "type": "int", 21 | "category": "Traffic", 22 | "support": [ 23 | "oberon" 24 | ], 25 | "Allocation": false, 26 | "Precision": 0, 27 | "calculated": false, 28 | "segmentable": true, 29 | "polarity": "positive", 30 | "allowedForReporting": false 31 | } 32 | ] 33 | -------------------------------------------------------------------------------- /analytics/testdata/Reports.Run.Request.json: -------------------------------------------------------------------------------- 1 | { 2 | "rsid": "amc.aem.prod", 3 | "globalFilters": [ 4 | { 5 | "type": "dateRange", 6 | "dateRange": "2020-04-01T00:00:00.000/2020-04-02T00:00:00.000" 7 | } 8 | ], 9 | "metricContainer": { 10 | "metrics": [ 11 | { 12 | "columnId": "0", 13 | "id": "metrics/pageviews", 14 | "filters": ["0"] 15 | } 16 | ], 17 | "metricFilters": [ 18 | { 19 | "id": "0", 20 | "type": "dateRange", 21 | "dateRange": "2020-04-01T00:00:00.000/2020-04-02T00:00:00.000" 22 | } 23 | ] 24 | }, 25 | "dimension": "variables/evar6", 26 | "settings": { 27 | "countRepeatInstances": true, 28 | "dimensionSort": "asc", 29 | "limit": 100, 30 | "page": 0, 31 | "nonesBehavior": "return-nones" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "Bug report" 3 | about: Report a bug or unexpected behavior 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | --- 8 | 9 | 10 | 11 | 12 | ### Expected Behaviour 13 | 14 | ### Actual Behaviour 15 | 16 | ### Reproduce Scenario (including but not limited to) 17 | 18 | #### Steps to Reproduce 19 | 20 | #### Platform and Version 21 | 22 | #### Sample Code that illustrates the problem 23 | 24 | #### Logs taken while reproducing problem 25 | -------------------------------------------------------------------------------- /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | test: 7 | name: Test & Lint 8 | strategy: 9 | matrix: 10 | go-version: [1.14.x] 11 | platform: [ubuntu-latest, macos-latest] 12 | runs-on: ${{ matrix.platform }} 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - uses: actions/setup-go@v2 17 | with: 18 | go-version: ${{ matrix.go-version }} 19 | 20 | # Caching go modules to speed up the run 21 | - uses: actions/cache@v2 22 | with: 23 | path: ~/go/pkg/mod 24 | key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} 25 | restore-keys: | 26 | ${{ runner.os }}-go- 27 | 28 | - name: Run go fmt 29 | run: make fmt 30 | 31 | - name: Run go vet 32 | run: make vet 33 | 34 | - name: Run golint 35 | run: | 36 | export PATH=$PATH:$(go env GOPATH)/bin 37 | go get -u golang.org/x/lint/golint 38 | make lint 39 | 40 | - name: Run go test 41 | run: make test 42 | -------------------------------------------------------------------------------- /analytics/testdata/Users.GetAll.json: -------------------------------------------------------------------------------- 1 | { 2 | "content": [ 3 | { 4 | "companyId": 1, 5 | "loginId": 1, 6 | "login": "12345678-abc-defg-abc-123456789012@example.com", 7 | "changePassword": false, 8 | "createDate": "2020-01-01T00:00:00Z", 9 | "disabled": false, 10 | "email": "12345678-abc-defg-abc-123456789012@example.com", 11 | "firstName": "John", 12 | "fullName": "John Doe", 13 | "imsUserId": "12345678901234567890ABCD@example.com", 14 | "lastName": "Doe" 15 | }, 16 | { 17 | "companyId": 1, 18 | "loginId": 2, 19 | "login": "12345678-abc-defg-abc-123456789012@example.com", 20 | "changePassword": false, 21 | "createDate": "2020-02-01T00:00:00Z", 22 | "disabled": false, 23 | "email": "12345678-abc-defg-abc-123456789012@example.com", 24 | "firstName": "Jane", 25 | "fullName": "Jane Foo", 26 | "imsUserId": "12345678901234567890ABCD@example.com", 27 | "lastName": "Foo" 28 | } 29 | ], 30 | "number": 0, 31 | "size": 10, 32 | "numberOfElements": 2, 33 | "totalElements": 2, 34 | "previousPage": false, 35 | "firstPage": true, 36 | "nextPage": false, 37 | "lastPage": true, 38 | "totalPages": 1 39 | } 40 | -------------------------------------------------------------------------------- /analytics/testdata/CalculatedMetrics.GetAll.json: -------------------------------------------------------------------------------- 1 | { 2 | "content": [ 3 | { 4 | "polarity": "positive", 5 | "precision": 0, 6 | "type": "decimal", 7 | "id": "cm300003364_5ae7447df118f061698ddc31", 8 | "name": "Unique Feature Usage", 9 | "description": "", 10 | "rsid": "amc.aem.prod", 11 | "owner": { 12 | "id": 711989 13 | } 14 | }, 15 | { 16 | "polarity": "positive", 17 | "precision": 0, 18 | "type": "decimal", 19 | "id": "cm300003364_5aea12481d43f66d8f1ba045", 20 | "name": "Unique Page URL", 21 | "description": "", 22 | "rsid": "amc.aem.prod", 23 | "owner": { 24 | "id": 711989 25 | } 26 | } 27 | ], 28 | "totalPages": 1, 29 | "totalElements": 2, 30 | "number": 0, 31 | "numberOfElements": 2, 32 | "firstPage": true, 33 | "lastPage": true, 34 | "sort": [ 35 | { 36 | "direction": "ASC", 37 | "property": "id", 38 | "ignoreCase": false, 39 | "ascending": true 40 | } 41 | ], 42 | "size": 1000 43 | } 44 | -------------------------------------------------------------------------------- /examples/users/users.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Adobe. All rights reserved. 3 | This file is licensed to you under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. You may obtain a copy 5 | of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed under 7 | the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS 8 | OF ANY KIND, either express or implied. See the License for the specific language 9 | governing permissions and limitations under the License. 10 | */ 11 | 12 | package main 13 | 14 | import ( 15 | "log" 16 | 17 | "github.com/adobe/aa-client-go/examples/config" 18 | "github.com/adobe/aa-client-go/examples/utils" 19 | ) 20 | 21 | func main() { 22 | // Read configuration 23 | cfg, err := config.Read() 24 | if err != nil { 25 | log.Fatal(err) 26 | } 27 | 28 | // Create an Analytics client 29 | client, err := utils.NewAnalyticsClient(&cfg.Analytics) 30 | if err != nil { 31 | log.Fatal(err) 32 | } 33 | 34 | // Get users 35 | users, err := client.Users.GetAll(100, 0) 36 | if err != nil { 37 | log.Fatal(err) 38 | } 39 | 40 | utils.PrintJSON(users) 41 | 42 | // Gets user 43 | user, err := client.Users.GetCurrent() 44 | if err != nil { 45 | log.Fatal(err) 46 | } 47 | 48 | utils.PrintJSON(user) 49 | } 50 | -------------------------------------------------------------------------------- /analytics/reports.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Adobe. All rights reserved. 3 | This file is licensed to you under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. You may obtain a copy 5 | of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed under 7 | the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS 8 | OF ANY KIND, either express or implied. See the License for the specific language 9 | governing permissions and limitations under the License. 10 | */ 11 | 12 | package analytics 13 | 14 | import ( 15 | "encoding/json" 16 | "strings" 17 | ) 18 | 19 | // ReportsService handles reports. 20 | // Analytics docs: https://www.adobe.io/apis/experiencecloud/analytics/docs.html#!AdobeDocs/analytics-2.0-apis/master/reporting-guide.md 21 | type ReportsService struct { 22 | client *Client 23 | } 24 | 25 | // Run runs a report for the passed RankedRequest. 26 | // API docs: https://adobedocs.github.io/analytics-2.0-apis/#/reports 27 | func (s *ReportsService) Run(rankedRequest *RankedRequest) (*RankedReportData, error) { 28 | reqJSON, _ := json.Marshal(rankedRequest) 29 | reqBody := strings.NewReader(string(reqJSON)) 30 | 31 | var data RankedReportData 32 | err := s.client.post("/reports", map[string]string{}, reqBody, &data) 33 | if err != nil { 34 | return nil, err 35 | } 36 | return &data, err 37 | } 38 | -------------------------------------------------------------------------------- /examples/collections/collections.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Adobe. All rights reserved. 3 | This file is licensed to you under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. You may obtain a copy 5 | of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed under 7 | the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS 8 | OF ANY KIND, either express or implied. See the License for the specific language 9 | governing permissions and limitations under the License. 10 | */ 11 | 12 | package main 13 | 14 | import ( 15 | "log" 16 | 17 | "github.com/adobe/aa-client-go/examples/config" 18 | "github.com/adobe/aa-client-go/examples/utils" 19 | ) 20 | 21 | func main() { 22 | // Read configuration 23 | cfg, err := config.Read() 24 | if err != nil { 25 | log.Fatal(err) 26 | } 27 | 28 | // Create an Analytics client 29 | client, err := utils.NewAnalyticsClient(&cfg.Analytics) 30 | if err != nil { 31 | log.Fatal(err) 32 | } 33 | 34 | // Get collections 35 | collections, err := client.Collections.GetAll("", "", 100, 0, []string{"all"}) 36 | if err != nil { 37 | log.Fatal(err) 38 | } 39 | 40 | utils.PrintJSON(collections) 41 | 42 | // Get collection 43 | collection, err := client.Collections.GetByID("", []string{}) 44 | if err != nil { 45 | log.Fatal(err) 46 | } 47 | 48 | utils.PrintJSON(collection) 49 | } 50 | -------------------------------------------------------------------------------- /examples/dateranges/dateranges.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Adobe. All rights reserved. 3 | This file is licensed to you under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. You may obtain a copy 5 | of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed under 7 | the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS 8 | OF ANY KIND, either express or implied. See the License for the specific language 9 | governing permissions and limitations under the License. 10 | */ 11 | 12 | package main 13 | 14 | import ( 15 | "log" 16 | 17 | "github.com/adobe/aa-client-go/examples/config" 18 | "github.com/adobe/aa-client-go/examples/utils" 19 | ) 20 | 21 | func main() { 22 | // Read configuration 23 | cfg, err := config.Read() 24 | if err != nil { 25 | log.Fatal(err) 26 | } 27 | 28 | // Create an Analytics client 29 | client, err := utils.NewAnalyticsClient(&cfg.Analytics) 30 | if err != nil { 31 | log.Fatal(err) 32 | } 33 | 34 | // Get date ranges 35 | ranges, err := client.DateRanges.GetAll("", "", 100, 0, []string{}, []string{"all"}) 36 | if err != nil { 37 | log.Fatal(err) 38 | } 39 | 40 | utils.PrintJSON(ranges) 41 | 42 | // Get date range 43 | daterange, err := client.DateRanges.GetByID("", "", []string{}) 44 | if err != nil { 45 | log.Fatal(err) 46 | } 47 | 48 | utils.PrintJSON(daterange) 49 | } 50 | -------------------------------------------------------------------------------- /examples/metrics/metrics.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Adobe. All rights reserved. 3 | This file is licensed to you under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. You may obtain a copy 5 | of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed under 7 | the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS 8 | OF ANY KIND, either express or implied. See the License for the specific language 9 | governing permissions and limitations under the License. 10 | */ 11 | 12 | package main 13 | 14 | import ( 15 | "log" 16 | 17 | "github.com/adobe/aa-client-go/examples/config" 18 | "github.com/adobe/aa-client-go/examples/utils" 19 | ) 20 | 21 | func main() { 22 | // Read configuration 23 | cfg, err := config.Read() 24 | if err != nil { 25 | log.Fatal(err) 26 | } 27 | 28 | // Create an Analytics client 29 | client, err := utils.NewAnalyticsClient(&cfg.Analytics) 30 | if err != nil { 31 | log.Fatal(err) 32 | } 33 | 34 | // Get metrics 35 | metrics, err := client.Metrics.GetAll(cfg.Analytics.ReportSuiteID, "en_US", false, []string{}) 36 | if err != nil { 37 | log.Fatal(err) 38 | } 39 | 40 | utils.PrintJSON(metrics) 41 | 42 | // Get metric 43 | metric, err := client.Metrics.GetByID(cfg.Analytics.ReportSuiteID, "pageviews", "en_US", []string{}) 44 | if err != nil { 45 | log.Fatal(err) 46 | } 47 | 48 | utils.PrintJSON(metric) 49 | } 50 | -------------------------------------------------------------------------------- /examples/segments/segments.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Adobe. All rights reserved. 3 | This file is licensed to you under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. You may obtain a copy 5 | of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed under 7 | the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS 8 | OF ANY KIND, either express or implied. See the License for the specific language 9 | governing permissions and limitations under the License. 10 | */ 11 | 12 | package main 13 | 14 | import ( 15 | "log" 16 | 17 | "github.com/adobe/aa-client-go/examples/config" 18 | "github.com/adobe/aa-client-go/examples/utils" 19 | ) 20 | 21 | func main() { 22 | // Read configuration 23 | cfg, err := config.Read() 24 | if err != nil { 25 | log.Fatal(err) 26 | } 27 | 28 | // Create an Analytics client 29 | client, err := utils.NewAnalyticsClient(&cfg.Analytics) 30 | if err != nil { 31 | log.Fatal(err) 32 | } 33 | 34 | // Get segments 35 | segments, err := client.Segments.GetAll(cfg.Analytics.ReportSuiteID, "", "en_US", "", "", "", 400, 0, "", "", []string{}, []string{"all"}) 36 | if err != nil { 37 | log.Fatal(err) 38 | } 39 | 40 | utils.PrintJSON(segments) 41 | 42 | // Get segment 43 | segment, err := client.Segments.GetByID("", "en_US", []string{}) 44 | if err != nil { 45 | log.Fatal(err) 46 | } 47 | 48 | utils.PrintJSON(segment) 49 | } 50 | -------------------------------------------------------------------------------- /examples/dimensions/dimensions.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Adobe. All rights reserved. 3 | This file is licensed to you under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. You may obtain a copy 5 | of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed under 7 | the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS 8 | OF ANY KIND, either express or implied. See the License for the specific language 9 | governing permissions and limitations under the License. 10 | */ 11 | 12 | package main 13 | 14 | import ( 15 | "log" 16 | 17 | "github.com/adobe/aa-client-go/examples/config" 18 | "github.com/adobe/aa-client-go/examples/utils" 19 | ) 20 | 21 | func main() { 22 | // Read configuration 23 | cfg, err := config.Read() 24 | if err != nil { 25 | log.Fatal(err) 26 | } 27 | 28 | // Create an Analytics client 29 | client, err := utils.NewAnalyticsClient(&cfg.Analytics) 30 | if err != nil { 31 | log.Fatal(err) 32 | } 33 | 34 | // Get dimensions 35 | dimensions, err := client.Dimensions.GetByID(cfg.Analytics.ReportSuiteID, "evar1", "en_US", []string{}) 36 | if err != nil { 37 | log.Fatal(err) 38 | } 39 | 40 | utils.PrintJSON(dimensions) 41 | 42 | // Get dimension 43 | dimension, err := client.Dimensions.GetAll(cfg.Analytics.ReportSuiteID, "en_US", false, false, false, []string{}) 44 | if err != nil { 45 | log.Fatal(err) 46 | } 47 | 48 | utils.PrintJSON(dimension) 49 | } 50 | -------------------------------------------------------------------------------- /examples/calculatedmetrics/calculatedmetrics.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Adobe. All rights reserved. 3 | This file is licensed to you under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. You may obtain a copy 5 | of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed under 7 | the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS 8 | OF ANY KIND, either express or implied. See the License for the specific language 9 | governing permissions and limitations under the License. 10 | */ 11 | 12 | package main 13 | 14 | import ( 15 | "log" 16 | 17 | "github.com/adobe/aa-client-go/examples/config" 18 | "github.com/adobe/aa-client-go/examples/utils" 19 | ) 20 | 21 | func main() { 22 | // Read configuration 23 | cfg, err := config.Read() 24 | if err != nil { 25 | log.Fatal(err) 26 | } 27 | 28 | // Create an Analytics client 29 | client, err := utils.NewAnalyticsClient(&cfg.Analytics) 30 | if err != nil { 31 | log.Fatal(err) 32 | } 33 | 34 | // Get calculated metrics 35 | metric, err := client.CalculatedMetrics.GetAll( 36 | "", "", "", "", 37 | "en_US", "", "", 38 | false, false, 100, 0, 39 | "", "", 40 | []string{}, []string{"all"}, 41 | ) 42 | if err != nil { 43 | log.Fatal(err) 44 | } 45 | 46 | utils.PrintJSON(metric) 47 | 48 | // Get calculated metric 49 | metrics, err := client.CalculatedMetrics.GetByID("", "en_US", []string{}) 50 | if err != nil { 51 | log.Fatal(err) 52 | } 53 | 54 | utils.PrintJSON(metrics) 55 | } 56 | -------------------------------------------------------------------------------- /analytics/common-types.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Adobe. All rights reserved. 3 | This file is licensed to you under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. You may obtain a copy 5 | of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed under 7 | the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS 8 | OF ANY KIND, either express or implied. See the License for the specific language 9 | governing permissions and limitations under the License. 10 | */ 11 | 12 | package analytics 13 | 14 | // Common/Shared types 15 | 16 | // Owner represents an owner 17 | type Owner struct { 18 | ID int `json:"id"` 19 | Name string `json:"name,omitempty"` 20 | Login string `json:"login,omitempty"` 21 | } 22 | 23 | // Sort represents sorting properties 24 | type Sort struct { 25 | Direction string `json:"direction,omitempty"` 26 | Property string `json:"property,omitempty"` 27 | IgnoreCase bool `json:"ignoreCase"` 28 | Ascending bool `json:"ascending"` 29 | } 30 | 31 | // TaggedComponent represents a tagged component 32 | type TaggedComponent struct { 33 | ComponentType string `json:"componentType,omitempty"` 34 | ComponentID string `json:"componentId,omitempty"` 35 | Tags *[]Tag `json:"tags,omitempty"` 36 | } 37 | 38 | // Tag represents a tag 39 | type Tag struct { 40 | ID string `json:"id,omitempty"` 41 | Name string `json:"name,omitempty"` 42 | Description string `json:"description,omitempty"` 43 | Components *[]TaggedComponent `json:"components,omitempty"` 44 | } 45 | -------------------------------------------------------------------------------- /analytics/metrics-types.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Adobe. All rights reserved. 3 | This file is licensed to you under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. You may obtain a copy 5 | of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed under 7 | the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS 8 | OF ANY KIND, either express or implied. See the License for the specific language 9 | governing permissions and limitations under the License. 10 | */ 11 | 12 | package analytics 13 | 14 | // Response types 15 | 16 | // Metric represents a metric 17 | type Metric struct { 18 | ID string `json:"id,omitempty"` 19 | Title string `json:"title,omitempty"` 20 | Name string `json:"name,omitempty"` 21 | Type string `json:"type,omitempty"` 22 | ExtraTitleInfo string `json:"extraTitleInfo,omitempty"` 23 | Category string `json:"category,omitempty"` 24 | Categories []string `json:"categories,omitempty"` 25 | Support []string `json:"support,omitempty"` 26 | Allocation bool `json:"allocation"` 27 | Precision int `json:"precision"` 28 | Calculated bool `json:"calculated"` 29 | Segmentable bool `json:"segmentable"` 30 | Description string `json:"description,omitempty"` 31 | Polarity string `json:"polarity,omitempty"` 32 | HelpLink string `json:"helpLink,omitempty"` 33 | AllowedForReporting bool `json:"allowedForReporting"` 34 | Tags *[]Tag `json:"tags,omitempty"` 35 | } 36 | -------------------------------------------------------------------------------- /analytics/users.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Adobe. All rights reserved. 3 | This file is licensed to you under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. You may obtain a copy 5 | of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed under 7 | the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS 8 | OF ANY KIND, either express or implied. See the License for the specific language 9 | governing permissions and limitations under the License. 10 | */ 11 | 12 | package analytics 13 | 14 | import ( 15 | "strconv" 16 | ) 17 | 18 | // UsersService handles users. 19 | // Analytics docs: https://adobedocs.github.io/analytics-2.0-apis/#/users 20 | type UsersService struct { 21 | client *Client 22 | } 23 | 24 | // GetAll returns a list of users for the current users login company. 25 | // API docs: https://adobedocs.github.io/analytics-2.0-apis/#/users/findAll 26 | func (s *UsersService) GetAll(limit, page int64) (*Users, error) { 27 | var params = map[string]string{} 28 | params["limit"] = strconv.FormatInt(limit, 10) 29 | params["page"] = strconv.FormatInt(page, 10) 30 | 31 | var data Users 32 | err := s.client.get("/users", params, nil, &data) 33 | if err != nil { 34 | return nil, err 35 | } 36 | return &data, err 37 | } 38 | 39 | // GetCurrent returns the current user. 40 | // API docs: https://adobedocs.github.io/analytics-2.0-apis/#/users/getCurrentUser 41 | func (s *UsersService) GetCurrent() (*User, error) { 42 | var data User 43 | err := s.client.get("/users/me", map[string]string{}, nil, &data) 44 | if err != nil { 45 | return nil, err 46 | } 47 | return &data, err 48 | } 49 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/go,osx,windows,linux 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=go,osx,windows,linux 3 | 4 | ### Go ### 5 | # Binaries for programs and plugins 6 | *.exe 7 | *.exe~ 8 | *.dll 9 | *.so 10 | *.dylib 11 | 12 | # Test binary, built with `go test -c` 13 | *.test 14 | 15 | # Output of the go coverage tool, specifically when used with LiteIDE 16 | *.out 17 | 18 | # Local YAML config 19 | config.local.yaml 20 | 21 | ### Go Patch ### 22 | /vendor/ 23 | /Godeps/ 24 | 25 | ### Linux ### 26 | *~ 27 | 28 | # temporary files which can be created if a process still has a handle open of a deleted file 29 | .fuse_hidden* 30 | 31 | # KDE directory preferences 32 | .directory 33 | 34 | # Linux trash folder which might appear on any partition or disk 35 | .Trash-* 36 | 37 | # .nfs files are created when an open file is removed but is still being accessed 38 | .nfs* 39 | 40 | ### OSX ### 41 | # General 42 | .DS_Store 43 | .AppleDouble 44 | .LSOverride 45 | 46 | # Icon must end with two \r 47 | Icon 48 | 49 | # Thumbnails 50 | ._* 51 | 52 | # Files that might appear in the root of a volume 53 | .DocumentRevisions-V100 54 | .fseventsd 55 | .Spotlight-V100 56 | .TemporaryItems 57 | .Trashes 58 | .VolumeIcon.icns 59 | .com.apple.timemachine.donotpresent 60 | 61 | # Directories potentially created on remote AFP share 62 | .AppleDB 63 | .AppleDesktop 64 | Network Trash Folder 65 | Temporary Items 66 | .apdisk 67 | 68 | ### Windows ### 69 | # Windows thumbnail cache files 70 | Thumbs.db 71 | ehthumbs.db 72 | ehthumbs_vista.db 73 | 74 | # Dump file 75 | *.stackdump 76 | 77 | # Folder config file 78 | [Dd]esktop.ini 79 | 80 | # Recycle Bin used on file shares 81 | $RECYCLE.BIN/ 82 | 83 | # Windows Installer files 84 | *.cab 85 | *.msi 86 | *.msix 87 | *.msm 88 | *.msp 89 | 90 | # Windows shortcuts 91 | *.lnk 92 | 93 | # End of https://www.toptal.com/developers/gitignore/api/go,osx,windows,linux 94 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE/bug_fix.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | ## Description 10 | 11 | 12 | 13 | ## Related Issue 14 | 15 | 16 | 17 | 18 | 19 | 20 | ## Motivation and Context 21 | 22 | 23 | 24 | ## How Has This Been Tested? 25 | 26 | 27 | 28 | 29 | 30 | ## Screenshots (if appropriate): 31 | 32 | ## Types of changes 33 | 34 | 35 | 36 | - [ ] Bug fix (non-breaking change which fixes an issue) 37 | - [ ] New feature (non-breaking change which adds functionality) 38 | - [ ] Breaking change (fix or feature that would cause existing functionality to change) 39 | 40 | ## Checklist: 41 | 42 | 43 | 44 | 45 | - [ ] I have signed the [Adobe Open Source CLA](https://opensource.adobe.com/cla.html). 46 | - [ ] My code follows the code style of this project. 47 | - [ ] My change requires a change to the documentation. 48 | - [ ] I have updated the documentation accordingly. 49 | - [ ] I have read the **CONTRIBUTING** document. 50 | - [ ] I have added tests to cover my changes. 51 | - [ ] All new and existing tests passed. 52 | -------------------------------------------------------------------------------- /analytics/users-types.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Adobe. All rights reserved. 3 | This file is licensed to you under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. You may obtain a copy 5 | of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed under 7 | the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS 8 | OF ANY KIND, either express or implied. See the License for the specific language 9 | governing permissions and limitations under the License. 10 | */ 11 | 12 | package analytics 13 | 14 | // User represents an Analytics user 15 | type User struct { 16 | CompanyID int `json:"companyId"` 17 | LoginID int `json:"loginId"` 18 | Login string `json:"login,omitempty"` 19 | ChangePassword bool `json:"changePassword"` 20 | CreateDate string `json:"createDate,omitempty"` 21 | Disabled bool `json:"disabled"` 22 | Email string `json:"email,omitempty"` 23 | FirstName string `json:"firstName,omitempty"` 24 | FullName string `json:"fullName,omitempty"` 25 | IMSUserID string `json:"imsUserId,omitempty"` 26 | LastName string `json:"lastName,omitempty"` 27 | LastAccess string `json:"lastAccess,omitempty"` 28 | PhoneNumber string `json:"phoneNumber,omitempty"` 29 | TempLoginEnd string `json:"tempLoginEnd,omitempty"` 30 | Title string `json:"title,omitempty"` 31 | } 32 | 33 | // Users represents a page of Analytics users 34 | type Users struct { 35 | Content *[]User `json:"content,omitempty"` 36 | Number int `json:"number"` 37 | Size int `json:"size"` 38 | NumberOfElements int `json:"numberOfElements"` 39 | TotalElements int `json:"totalElements"` 40 | PreviousPage bool `json:"previousPage"` 41 | FirstPage bool `json:"firstPage"` 42 | NextPage bool `json:"nextPage"` 43 | LastPage bool `json:"lastPage"` 44 | Sort *[]Sort `json:"sort,omitempty"` 45 | TotalPages int `json:"totalPages"` 46 | } 47 | -------------------------------------------------------------------------------- /analytics/reports_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Adobe. All rights reserved. 3 | This file is licensed to you under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. You may obtain a copy 5 | of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed under 7 | the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS 8 | OF ANY KIND, either express or implied. See the License for the specific language 9 | governing permissions and limitations under the License. 10 | */ 11 | 12 | package analytics_test 13 | 14 | import ( 15 | "encoding/json" 16 | "fmt" 17 | "io/ioutil" 18 | "net/http" 19 | "testing" 20 | 21 | "github.com/adobe/aa-client-go/analytics" 22 | ) 23 | 24 | func TestReportsRun(t *testing.T) { 25 | setup() 26 | defer teardown() 27 | 28 | apiEndpoint := baseURL + "/reports" 29 | 30 | req, err := ioutil.ReadFile("./testdata/Reports.Run.Request.json") 31 | if err != nil { 32 | t.Error(err.Error()) 33 | } 34 | 35 | var rankedRequest analytics.RankedRequest 36 | json.Unmarshal(req, &rankedRequest) 37 | 38 | raw, err := ioutil.ReadFile("./testdata/Reports.Run.json") 39 | if err != nil { 40 | t.Error(err.Error()) 41 | } 42 | 43 | testMux.HandleFunc(apiEndpoint, func(w http.ResponseWriter, r *http.Request) { 44 | testMethod(t, r, "POST") 45 | testRequestURL(t, r, apiEndpoint) 46 | testRequestBody(t, r, req) 47 | fmt.Fprint(w, string(raw)) 48 | }) 49 | 50 | report, err := testClient.Reports.Run(&rankedRequest) 51 | if err != nil { 52 | t.Errorf("Error: %v", err) 53 | } 54 | 55 | if len(*report.Rows) != 68 { 56 | t.Errorf("Expected %d report rows but got %d", 68, len(*report.Rows)) 57 | } 58 | } 59 | 60 | func TestReportsRunError(t *testing.T) { 61 | setup() 62 | defer teardown() 63 | 64 | testMux.HandleFunc(baseURL+"/reports", func(w http.ResponseWriter, r *http.Request) { 65 | w.WriteHeader(http.StatusInternalServerError) 66 | }) 67 | 68 | _, err := testClient.Reports.Run(nil) 69 | if err == nil { 70 | t.Errorf("Expected error but got none") 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /analytics/dimensions-types.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Adobe. All rights reserved. 3 | This file is licensed to you under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. You may obtain a copy 5 | of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed under 7 | the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS 8 | OF ANY KIND, either express or implied. See the License for the specific language 9 | governing permissions and limitations under the License. 10 | */ 11 | 12 | package analytics 13 | 14 | // Response types 15 | 16 | // DimensionNoneSettings represents dimension 'none' settings 17 | type DimensionNoneSettings struct { 18 | IncludeNoneByDefault bool `json:"includeNoneByDefault,omitempty"` 19 | NoneChangeable bool `json:"noneChangeable,omitempty"` 20 | } 21 | 22 | // Dimension represents a dimension 23 | type Dimension struct { 24 | ID string `json:"id,omitempty"` 25 | Title string `json:"title,omitempty"` 26 | Name string `json:"name,omitempty"` 27 | Type string `json:"type,omitempty"` 28 | Category string `json:"category,omitempty"` 29 | Categories []string `json:"categories,omitempty"` 30 | Support []string `json:"support,omitempty"` 31 | Pathable bool `json:"pathable"` 32 | Parent string `json:"parent,omitempty"` 33 | ExtraTitleInfo string `json:"extraTitleInfo,omitempty"` 34 | Segmentable bool `json:"segmentable"` 35 | Reportable []string `json:"reportable,omitempty"` 36 | Description string `json:"description,omitempty"` 37 | AllowedForReporting bool `json:"allowedForReporting"` 38 | NoneSettings *DimensionNoneSettings `json:"noneSettings,omitempty"` 39 | Tags *[]Tag `json:"tags,omitempty"` 40 | } 41 | -------------------------------------------------------------------------------- /examples/go.sum: -------------------------------------------------------------------------------- 1 | github.com/adobe/ims-go v0.5.1 h1:p8uUh8e2tD/0VQrxXIgi/djBcdX2P3soC6iT2azCskw= 2 | github.com/adobe/ims-go v0.5.1/go.mod h1:4gkJwj6qOzGvS8keTbb8GkIfuzzH2ZpwO8qDPqA3ULA= 3 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 4 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 | github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= 6 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 7 | github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= 8 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 9 | github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= 10 | github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 11 | github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI= 12 | github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= 13 | github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM= 14 | github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= 15 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 16 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 17 | github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= 18 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 19 | github.com/tkanos/gonfig v0.0.0-20181112185242-896f3d81fadf h1:sepG1nOX39NO8y8E+sYMkkKSDxiAfZ0XL0l0+vogwBw= 20 | github.com/tkanos/gonfig v0.0.0-20181112185242-896f3d81fadf/go.mod h1:DaZPBuToMc2eezA9R9nDAnmS2RMwL7yEa5YD36ESQdI= 21 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 22 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 23 | gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= 24 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 25 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Thanks for choosing to contribute! 4 | 5 | The following are a set of guidelines to follow when contributing to this project. 6 | 7 | ## Code Of Conduct 8 | 9 | This project adheres to the Adobe [code of conduct](CODE_OF_CONDUCT.md). By participating, 10 | you are expected to uphold this code. Please report unacceptable behavior to 11 | [Grp-opensourceoffice@adobe.com](mailto:Grp-opensourceoffice@adobe.com). 12 | 13 | ## Have A Question 14 | 15 | Start by filing an issue. The existing committers on this project work to reach 16 | consensus around project direction and issue solutions within issue threads 17 | (when appropriate). 18 | 19 | ## Contributor License Agreement 20 | 21 | All third-party contributions to this project must be accompanied by a signed contributor 22 | license agreement. This gives Adobe permission to redistribute your contributions 23 | as part of the project. [Sign our CLA](https://opensource.adobe.com/cla.html). You 24 | only need to submit an Adobe CLA one time, so if you have submitted one previously, 25 | you are good to go! 26 | 27 | ## Code Reviews 28 | 29 | All submissions should come in the form of pull requests and need to be reviewed 30 | by project committers. Read [GitHub's pull request documentation](https://help.github.com/articles/about-pull-requests/) 31 | for more information on sending pull requests. 32 | 33 | Lastly, please follow the [pull request template](.github/PULL_REQUEST_TEMPLATE/bug_fix.md) when 34 | submitting a pull request! 35 | 36 | ## From Contributor To Committer 37 | 38 | We love contributions from our community! If you'd like to go a step beyond contributor 39 | and become a committer with full write access and a say in the project, you must 40 | be invited to the project. The existing committers employ an internal nomination 41 | process that must reach lazy consensus (silence is approval) before invitations 42 | are issued. If you feel you are qualified and want to get more deeply involved, 43 | feel free to reach out to existing committers to have a conversation about that. 44 | 45 | ## Security Issues 46 | 47 | Security issues shouldn't be reported on this issue tracker. Instead, [file an issue to our security experts](https://helpx.adobe.com/security/alertus.html). 48 | -------------------------------------------------------------------------------- /analytics/metrics.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Adobe. All rights reserved. 3 | This file is licensed to you under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. You may obtain a copy 5 | of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed under 7 | the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS 8 | OF ANY KIND, either express or implied. See the License for the specific language 9 | governing permissions and limitations under the License. 10 | */ 11 | 12 | package analytics 13 | 14 | import ( 15 | "fmt" 16 | "strconv" 17 | "strings" 18 | ) 19 | 20 | // MetricsService handles metrics. 21 | // Analytics docs: https://adobedocs.github.io/analytics-2.0-apis/#/metrics 22 | type MetricsService struct { 23 | client *Client 24 | } 25 | 26 | // GetAll returns a list of metrics for a given report suite. 27 | // API docs: https://adobedocs.github.io/analytics-2.0-apis/#/metrics/getMetrics 28 | func (s *MetricsService) GetAll(rsID, locale string, segmentable bool, expansion []string) (*[]Metric, error) { 29 | var params = map[string]string{} 30 | params["rsid"] = rsID 31 | if locale != "" { 32 | params["locale"] = locale 33 | } 34 | params["segmentable"] = strconv.FormatBool(segmentable) 35 | if len(expansion) > 0 { 36 | params["expansion"] = strings.Join(expansion[:], ",") 37 | } 38 | 39 | var data []Metric 40 | err := s.client.get("/metrics", params, nil, &data) 41 | if err != nil { 42 | return nil, err 43 | } 44 | return &data, err 45 | } 46 | 47 | // GetByID returns a metric for a given report suite. 48 | // API docs: https://adobedocs.github.io/analytics-2.0-apis/#/metrics/getMetric 49 | func (s *MetricsService) GetByID(rsID, id, locale string, expansion []string) (*Metric, error) { 50 | var params = map[string]string{} 51 | params["rsid"] = rsID 52 | if locale != "" { 53 | params["locale"] = locale 54 | } 55 | if len(expansion) > 0 { 56 | params["expansion"] = strings.Join(expansion[:], ",") 57 | } 58 | 59 | var data Metric 60 | err := s.client.get(fmt.Sprintf("/metrics/%s", id), params, nil, &data) 61 | if err != nil { 62 | return nil, err 63 | } 64 | return &data, err 65 | } 66 | -------------------------------------------------------------------------------- /analytics/collections-types.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Adobe. All rights reserved. 3 | This file is licensed to you under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. You may obtain a copy 5 | of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed under 7 | the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS 8 | OF ANY KIND, either express or implied. See the License for the specific language 9 | governing permissions and limitations under the License. 10 | */ 11 | 12 | package analytics 13 | 14 | // Response types 15 | 16 | // CollectionCalendarType represents a collection calendar type 17 | type CollectionCalendarType struct { 18 | RSID string `json:"rsid,omitempty"` 19 | Type string `json:"type,omitempty"` 20 | AnchorDate string `json:"anchorDate,omitempty"` 21 | } 22 | 23 | // Collection represents a collection 24 | type Collection struct { 25 | Name string `json:"name,omitempty"` 26 | TimezoneZoneInfo string `json:"timezoneZoneInfo,omitempty"` 27 | ParentRSID string `json:"parentRsid,omitempty"` 28 | CollectionItemType string `json:"collectionItemType,omitempty"` 29 | Currency string `json:"currency,omitempty"` 30 | CalendarType *CollectionCalendarType `json:"calendarType,omitempty"` 31 | RSID string `json:"rsid,omitempty"` 32 | } 33 | 34 | // Collections represents a page of collections 35 | type Collections struct { 36 | Content *[]Collection `json:"content,omitempty"` 37 | Number int `json:"number"` 38 | Size int `json:"size"` 39 | NumberOfElements int `json:"numberOfElements"` 40 | TotalElements int `json:"totalElements"` 41 | PreviousPage bool `json:"previousPage"` 42 | FirstPage bool `json:"firstPage"` 43 | NextPage bool `json:"nextPage"` 44 | LastPage bool `json:"lastPage"` 45 | Sort *[]Sort `json:"sort,omitempty"` 46 | TotalPages int `json:"totalPages"` 47 | } 48 | -------------------------------------------------------------------------------- /analytics/dateranges-types.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Adobe. All rights reserved. 3 | This file is licensed to you under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. You may obtain a copy 5 | of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed under 7 | the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS 8 | OF ANY KIND, either express or implied. See the License for the specific language 9 | governing permissions and limitations under the License. 10 | */ 11 | 12 | package analytics 13 | 14 | // Response types 15 | 16 | // DateRangeDefinition represents a date range definition 17 | type DateRangeDefinition struct { 18 | } 19 | 20 | // DateRange represents a date range 21 | type DateRange struct { 22 | ID string `json:"id,omitempty"` 23 | Name string `json:"name,omitempty"` 24 | Description string `json:"description,omitempty"` 25 | RSID string `json:"rsid,omitempty"` 26 | ReportSuiteName string `json:"reportSuiteName,omitempty"` 27 | Owner *Owner `json:"owner,omitempty"` 28 | Definition *DateRangeDefinition `json:"definition,omitempty"` 29 | Tags *[]Tag `json:"tags,omitempty"` 30 | SiteTitle string `json:"siteTitle,omitempty"` 31 | Modified string `json:"modified,omitempty"` 32 | Created string `json:"created,omitempty"` 33 | } 34 | 35 | // DateRanges represents a page of date ranges 36 | type DateRanges struct { 37 | Content *[]DateRange `json:"content,omitempty"` 38 | Number int `json:"number"` 39 | Size int `json:"size"` 40 | NumberOfElements int `json:"numberOfElements"` 41 | TotalElements int `json:"totalElements"` 42 | PreviousPage bool `json:"previousPage"` 43 | FirstPage bool `json:"firstPage"` 44 | NextPage bool `json:"nextPage"` 45 | LastPage bool `json:"lastPage"` 46 | Sort *[]Sort `json:"sort,omitempty"` 47 | TotalPages int `json:"totalPages"` 48 | } 49 | -------------------------------------------------------------------------------- /analytics/collections.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Adobe. All rights reserved. 3 | This file is licensed to you under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. You may obtain a copy 5 | of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed under 7 | the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS 8 | OF ANY KIND, either express or implied. See the License for the specific language 9 | governing permissions and limitations under the License. 10 | */ 11 | 12 | package analytics 13 | 14 | import ( 15 | "fmt" 16 | "strconv" 17 | "strings" 18 | ) 19 | 20 | // CollectionsService handles collections. 21 | // Analytics docs: https://adobedocs.github.io/analytics-2.0-apis/#/collections 22 | type CollectionsService struct { 23 | client *Client 24 | } 25 | 26 | // GetAll returns a list of report suites that match the given filters. 27 | // API docs: https://adobedocs.github.io/analytics-2.0-apis/#/collections/findAll 28 | func (s *CollectionsService) GetAll(rsids, rsidContains string, limit, page int64, expansion []string) (*Collections, error) { 29 | var params = map[string]string{} 30 | if rsids != "" { 31 | params["rsids"] = rsids 32 | } 33 | if rsidContains != "" { 34 | params["rsidContains"] = rsidContains 35 | } 36 | params["limit"] = strconv.FormatInt(limit, 10) 37 | params["page"] = strconv.FormatInt(page, 10) 38 | if len(expansion) > 0 { 39 | params["expansion"] = strings.Join(expansion[:], ",") 40 | } 41 | 42 | var data Collections 43 | err := s.client.get("/collections/suites", params, nil, &data) 44 | if err != nil { 45 | return nil, err 46 | } 47 | return &data, err 48 | } 49 | 50 | // GetByID returns a report suite by ID. 51 | // API docs: https://adobedocs.github.io/analytics-2.0-apis/#/collections/findOne 52 | func (s *CollectionsService) GetByID(id string, expansion []string) (*Collection, error) { 53 | var params = map[string]string{} 54 | if len(expansion) > 0 { 55 | params["expansion"] = strings.Join(expansion[:], ",") 56 | } 57 | 58 | var data Collection 59 | err := s.client.get(fmt.Sprintf("/collections/suites/%s", id), params, nil, &data) 60 | if err != nil { 61 | return nil, err 62 | } 63 | return &data, err 64 | } 65 | -------------------------------------------------------------------------------- /examples/reports/reports.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Adobe. All rights reserved. 3 | This file is licensed to you under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. You may obtain a copy 5 | of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed under 7 | the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS 8 | OF ANY KIND, either express or implied. See the License for the specific language 9 | governing permissions and limitations under the License. 10 | */ 11 | 12 | package main 13 | 14 | import ( 15 | "log" 16 | 17 | "github.com/adobe/aa-client-go/analytics" 18 | "github.com/adobe/aa-client-go/examples/config" 19 | "github.com/adobe/aa-client-go/examples/utils" 20 | ) 21 | 22 | func main() { 23 | // Read configuration 24 | cfg, err := config.Read() 25 | if err != nil { 26 | log.Fatal(err) 27 | } 28 | 29 | // Create an Analytics client 30 | client, err := utils.NewAnalyticsClient(&cfg.Analytics) 31 | if err != nil { 32 | log.Fatal(err) 33 | } 34 | 35 | report, err := client.Reports.Run(&analytics.RankedRequest{ 36 | ReportSuiteID: cfg.Analytics.ReportSuiteID, 37 | Dimension: "variables/daterangeday", 38 | GlobalFilters: &[]analytics.RankedRequestReportFilter{ 39 | analytics.RankedRequestReportFilter{ 40 | Type: "dateRange", 41 | DateRange: "2020-04-01T00:00:00/2020-04-02T00:00:00", 42 | }, 43 | }, 44 | MetricContainer: &analytics.RankedRequestReportMetrics{ 45 | Metrics: &[]analytics.RankedRequestReportMetric{ 46 | analytics.RankedRequestReportMetric{ 47 | ColumnID: "0", 48 | ID: "metrics/pageviews", 49 | Filters: []string{"0"}, 50 | }, 51 | }, 52 | MetricFilters: &[]analytics.RankedRequestReportFilter{ 53 | analytics.RankedRequestReportFilter{ 54 | ID: "0", 55 | Type: "dateRange", 56 | DateRange: "2020-04-01T00:00:00/2020-04-02T00:00:00", 57 | }, 58 | }, 59 | }, 60 | Settings: &analytics.RankedRequestSettings{ 61 | Page: 0, 62 | Limit: 400, 63 | DimensionSort: "asc", 64 | CountRepeatInstances: true, 65 | NonesBehavior: "exclude-nones", 66 | }, 67 | }) 68 | 69 | if err != nil { 70 | log.Fatal(err) 71 | } 72 | 73 | utils.PrintJSON(report) 74 | } 75 | -------------------------------------------------------------------------------- /examples/config/config.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Adobe. All rights reserved. 3 | This file is licensed to you under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. You may obtain a copy 5 | of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed under 7 | the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS 8 | OF ANY KIND, either express or implied. See the License for the specific language 9 | governing permissions and limitations under the License. 10 | */ 11 | 12 | package config 13 | 14 | import ( 15 | "fmt" 16 | "os" 17 | "strings" 18 | 19 | "github.com/tkanos/gonfig" 20 | ) 21 | 22 | //Configuration object for all commands 23 | type Configuration struct { 24 | Analytics AnalyticsAPIConfiguration 25 | } 26 | 27 | // AnalyticsAPIConfiguration Adobe I/O Analytics config 28 | type AnalyticsAPIConfiguration struct { 29 | Endpoint string 30 | ReportSuiteID string 31 | CompanyID string 32 | IMSEndpoint string 33 | IMSPrivateKey string 34 | IMSOrgID string 35 | IMSTechnicalAccountID string 36 | IMSClientID string 37 | IMSClientSecret string 38 | } 39 | 40 | type environment interface { 41 | Lookup(name string) (string, bool) 42 | } 43 | 44 | type osEnviroment struct{} 45 | 46 | func (osEnviroment) Lookup(name string) (string, bool) { 47 | return os.LookupEnv(name) 48 | } 49 | 50 | // Read reads the configuration from environment variables and optionally from a configuration file in YAML format. 51 | func Read() (*Configuration, error) { 52 | return read(osEnviroment{}) 53 | } 54 | 55 | func read(env environment) (*Configuration, error) { 56 | var configuration Configuration 57 | 58 | // get config file 59 | if configFile, ok := env.Lookup("CONFIG"); ok { 60 | if err := gonfig.GetConf(strings.TrimSpace(configFile), &configuration); err != nil { 61 | return nil, fmt.Errorf("read configuration file: %v", err) 62 | } 63 | } 64 | 65 | // Analytics configs 66 | if aaPrivateKey, ok := env.Lookup("ANALYTICS_PRIVATEKEY"); ok { 67 | configuration.Analytics.IMSPrivateKey = aaPrivateKey 68 | } 69 | 70 | if aaClientSecret, ok := env.Lookup("ANALYTICS_CLIENTSECRET"); ok { 71 | configuration.Analytics.IMSClientSecret = aaClientSecret 72 | } 73 | 74 | return &configuration, nil 75 | } 76 | -------------------------------------------------------------------------------- /analytics/dateranges.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Adobe. All rights reserved. 3 | This file is licensed to you under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. You may obtain a copy 5 | of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed under 7 | the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS 8 | OF ANY KIND, either express or implied. See the License for the specific language 9 | governing permissions and limitations under the License. 10 | */ 11 | 12 | package analytics 13 | 14 | import ( 15 | "fmt" 16 | "strconv" 17 | "strings" 18 | ) 19 | 20 | // DateRangesService handles date ranges. 21 | // Analytics docs: https://adobedocs.github.io/analytics-2.0-apis/#/dateranges 22 | type DateRangesService struct { 23 | client *Client 24 | } 25 | 26 | // GetAll returns a list of date ranges for the user. 27 | // API docs: https://adobedocs.github.io/analytics-2.0-apis/#/dateranges/getDateRanges 28 | func (s *DateRangesService) GetAll(locale, filterByIDs string, limit, page int64, expansion, includeType []string) (*DateRanges, error) { 29 | var params = map[string]string{} 30 | if locale != "" { 31 | params["locale"] = locale 32 | } 33 | if filterByIDs != "" { 34 | params["filterByIds"] = filterByIDs 35 | } 36 | params["limit"] = strconv.FormatInt(limit, 10) 37 | params["page"] = strconv.FormatInt(page, 10) 38 | if len(expansion) > 0 { 39 | params["expansion"] = strings.Join(expansion[:], ",") 40 | } 41 | if len(includeType) > 0 { 42 | params["includeType"] = strings.Join(includeType[:], ",") 43 | } 44 | 45 | var data DateRanges 46 | err := s.client.get("/dateranges", params, nil, &data) 47 | if err != nil { 48 | return nil, err 49 | } 50 | return &data, err 51 | } 52 | 53 | // GetByID returns configuration for a date range. 54 | // API docs: https://adobedocs.github.io/analytics-2.0-apis/#/dateranges/getDateRange 55 | func (s *DateRangesService) GetByID(id, locale string, expansion []string) (*DateRange, error) { 56 | var params = map[string]string{} 57 | if locale != "" { 58 | params["locale"] = locale 59 | } 60 | if len(expansion) > 0 { 61 | params["expansion"] = strings.Join(expansion[:], ",") 62 | } 63 | 64 | var data DateRange 65 | err := s.client.get(fmt.Sprintf("/dateranges/%s", id), params, nil, &data) 66 | if err != nil { 67 | return nil, err 68 | } 69 | return &data, err 70 | } 71 | -------------------------------------------------------------------------------- /analytics/dimensions.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Adobe. All rights reserved. 3 | This file is licensed to you under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. You may obtain a copy 5 | of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed under 7 | the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS 8 | OF ANY KIND, either express or implied. See the License for the specific language 9 | governing permissions and limitations under the License. 10 | */ 11 | 12 | package analytics 13 | 14 | import ( 15 | "fmt" 16 | "strconv" 17 | "strings" 18 | ) 19 | 20 | // DimensionsService handles dimensions. 21 | // Analytics docs: https://adobedocs.github.io/analytics-2.0-apis/#/dimensions 22 | type DimensionsService struct { 23 | client *Client 24 | } 25 | 26 | // GetAll returns a list of dimensions for a given report suite. 27 | // API docs: https://adobedocs.github.io/analytics-2.0-apis/#/dimensions/dimensions_getDimensions 28 | func (s *DimensionsService) GetAll(rsID, locale string, segmentable, reportable, classifiable bool, expansion []string) (*[]Dimension, error) { 29 | var params = map[string]string{} 30 | params["rsid"] = rsID 31 | if locale != "" { 32 | params["locale"] = locale 33 | } 34 | params["segmentable"] = strconv.FormatBool(segmentable) 35 | params["reportable"] = strconv.FormatBool(reportable) 36 | params["classifiable"] = strconv.FormatBool(classifiable) 37 | if len(expansion) > 0 { 38 | params["expansion"] = strings.Join(expansion[:], ",") 39 | } 40 | 41 | var data []Dimension 42 | err := s.client.get("/dimensions", params, nil, &data) 43 | if err != nil { 44 | return nil, err 45 | } 46 | return &data, err 47 | } 48 | 49 | // GetByID returns a dimension for a given report suite. 50 | // API docs: https://adobedocs.github.io/analytics-2.0-apis/#/dimensions/dimensions_getDimension 51 | func (s *DimensionsService) GetByID(rsID, id, locale string, expansion []string) (*Dimension, error) { 52 | var params = map[string]string{} 53 | params["rsid"] = rsID 54 | if locale != "" { 55 | params["locale"] = locale 56 | } 57 | if len(expansion) > 0 { 58 | params["expansion"] = strings.Join(expansion[:], ",") 59 | } 60 | 61 | var data Dimension 62 | err := s.client.get(fmt.Sprintf("/dimensions/%s", id), params, nil, &data) 63 | if err != nil { 64 | return nil, err 65 | } 66 | return &data, err 67 | } 68 | -------------------------------------------------------------------------------- /analytics/calculatedmetrics-types.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Adobe. All rights reserved. 3 | This file is licensed to you under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. You may obtain a copy 5 | of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed under 7 | the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS 8 | OF ANY KIND, either express or implied. See the License for the specific language 9 | governing permissions and limitations under the License. 10 | */ 11 | 12 | package analytics 13 | 14 | // Response types 15 | 16 | // CalculatedMetricDefinition represents a calculated metric definition 17 | type CalculatedMetricDefinition struct { 18 | } 19 | 20 | // CalculatedMetric represents a calculated metric 21 | type CalculatedMetric struct { 22 | ID string `json:"id,omitempty"` 23 | Name string `json:"name,omitempty"` 24 | Description string `json:"description,omitempty"` 25 | RSID string `json:"rsid,omitempty"` 26 | ReportSuiteName string `json:"reportSuiteName,omitempty"` 27 | Owner *Owner `json:"owner,omitempty"` 28 | Polarity string `json:"polarity,omitempty"` 29 | Precision int `json:"precision"` 30 | Type string `json:"type,omitempty"` 31 | Definition *CalculatedMetricDefinition `json:"definition,omitempty"` 32 | Categories []string `json:"categories,omitempty"` 33 | Tags *[]Tag `json:"tags,omitempty"` 34 | SiteTitle string `json:"siteTitle,omitempty"` 35 | Modified string `json:"modified,omitempty"` 36 | Created string `json:"created,omitempty"` 37 | } 38 | 39 | // CalculatedMetrics represents a page of calculated metrics 40 | type CalculatedMetrics struct { 41 | Content *[]CalculatedMetric `json:"content,omitempty"` 42 | Number int `json:"number"` 43 | Size int `json:"size"` 44 | NumberOfElements int `json:"numberOfElements"` 45 | TotalElements int `json:"totalElements"` 46 | PreviousPage bool `json:"previousPage"` 47 | FirstPage bool `json:"firstPage"` 48 | NextPage bool `json:"nextPage"` 49 | LastPage bool `json:"lastPage"` 50 | Sort *[]Sort `json:"sort,omitempty"` 51 | TotalPages int `json:"totalPages"` 52 | } 53 | -------------------------------------------------------------------------------- /analytics/segments.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Adobe. All rights reserved. 3 | This file is licensed to you under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. You may obtain a copy 5 | of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed under 7 | the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS 8 | OF ANY KIND, either express or implied. See the License for the specific language 9 | governing permissions and limitations under the License. 10 | */ 11 | 12 | package analytics 13 | 14 | import ( 15 | "fmt" 16 | "strconv" 17 | "strings" 18 | ) 19 | 20 | // SegmentsService handles segments. 21 | // Analytics docs: https://adobedocs.github.io/analytics-2.0-apis/#/segments 22 | type SegmentsService struct { 23 | client *Client 24 | } 25 | 26 | // GetAll returns all segments. 27 | // API docs: https://adobedocs.github.io/analytics-2.0-apis/#/segments/segments_getSegments 28 | func (s *SegmentsService) GetAll(rsids, segmentFilter, locale, name, tagNames, filterByPublishedSegments string, 29 | limit, page int64, sortDirection, sortProperty string, 30 | expansion []string, includeType []string) (*Segments, error) { 31 | 32 | var params = map[string]string{} 33 | params["rsids"] = rsids 34 | if segmentFilter != "" { 35 | params["segmentFilter"] = segmentFilter 36 | } 37 | if locale != "" { 38 | params["locale"] = locale 39 | } 40 | if name != "" { 41 | params["name"] = name 42 | } 43 | if tagNames != "" { 44 | params["tagNames"] = tagNames 45 | } 46 | if filterByPublishedSegments != "" { 47 | params["filterByPublishedSegments"] = filterByPublishedSegments 48 | } 49 | params["limit"] = strconv.FormatInt(limit, 10) 50 | params["page"] = strconv.FormatInt(page, 10) 51 | if sortDirection != "" { 52 | params["sortDirection"] = sortDirection 53 | } 54 | if sortProperty != "" { 55 | params["sortProperty"] = sortProperty 56 | } 57 | if len(expansion) > 0 { 58 | params["expansion"] = strings.Join(expansion[:], ",") 59 | } 60 | if len(includeType) > 0 { 61 | params["includeType"] = strings.Join(includeType[:], ",") 62 | } 63 | 64 | var data Segments 65 | err := s.client.get("/segments", params, nil, &data) 66 | if err != nil { 67 | return nil, err 68 | } 69 | return &data, err 70 | } 71 | 72 | // GetByID returns a single segment. 73 | // API docs: https://adobedocs.github.io/analytics-2.0-apis/#/segments/segments_getSegment 74 | func (s *SegmentsService) GetByID(id, locale string, expansion []string) (*Segment, error) { 75 | var params = map[string]string{} 76 | if locale != "" { 77 | params["locale"] = locale 78 | } 79 | if len(expansion) > 0 { 80 | params["expansion"] = strings.Join(expansion[:], ",") 81 | } 82 | 83 | var data Segment 84 | err := s.client.get(fmt.Sprintf("/segments/%s", id), params, nil, &data) 85 | if err != nil { 86 | return nil, err 87 | } 88 | return &data, err 89 | 90 | } 91 | -------------------------------------------------------------------------------- /analytics/users_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Adobe. All rights reserved. 3 | This file is licensed to you under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. You may obtain a copy 5 | of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed under 7 | the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS 8 | OF ANY KIND, either express or implied. See the License for the specific language 9 | governing permissions and limitations under the License. 10 | */ 11 | 12 | package analytics_test 13 | 14 | import ( 15 | "fmt" 16 | "io/ioutil" 17 | "net/http" 18 | "testing" 19 | ) 20 | 21 | func TestUsersGetAll(t *testing.T) { 22 | setup() 23 | defer teardown() 24 | 25 | apiEndpoint := baseURL + "/users" 26 | 27 | raw, err := ioutil.ReadFile("./testdata/Users.GetAll.json") 28 | if err != nil { 29 | t.Error(err.Error()) 30 | } 31 | 32 | testMux.HandleFunc(apiEndpoint, func(w http.ResponseWriter, r *http.Request) { 33 | testMethod(t, r, "GET") 34 | testRequestURL(t, r, apiEndpoint) 35 | fmt.Fprint(w, string(raw)) 36 | }) 37 | 38 | users, err := testClient.Users.GetAll(10, 0) 39 | if err != nil { 40 | t.Errorf("Error: %v", err) 41 | } 42 | 43 | if users.Content == nil { 44 | t.Error("Expected users list but was is nil") 45 | } 46 | 47 | if len(*users.Content) != 2 { 48 | t.Errorf("Expected %d users but got %d", 2, len(*users.Content)) 49 | } 50 | 51 | } 52 | 53 | func TestUsersGetAllError(t *testing.T) { 54 | setup() 55 | defer teardown() 56 | 57 | testMux.HandleFunc(baseURL+"/users", func(w http.ResponseWriter, r *http.Request) { 58 | w.WriteHeader(http.StatusInternalServerError) 59 | }) 60 | 61 | _, err := testClient.Users.GetAll(10, 0) 62 | if err == nil { 63 | t.Errorf("Expected error but got none") 64 | } 65 | } 66 | 67 | func TestUsersGetCurrent(t *testing.T) { 68 | setup() 69 | defer teardown() 70 | 71 | apiEndpoint := baseURL + "/users/me" 72 | 73 | raw, err := ioutil.ReadFile("./testdata/Users.GetCurrent.json") 74 | if err != nil { 75 | t.Error(err.Error()) 76 | } 77 | 78 | testMux.HandleFunc(apiEndpoint, func(w http.ResponseWriter, r *http.Request) { 79 | testMethod(t, r, "GET") 80 | testRequestURL(t, r, apiEndpoint) 81 | fmt.Fprint(w, string(raw)) 82 | }) 83 | 84 | user, err := testClient.Users.GetCurrent() 85 | if err != nil { 86 | t.Errorf("Error: %v", err) 87 | } 88 | 89 | if user.LoginID != 1 { 90 | t.Errorf("Expected loginId=1 but was loginId=%d", user.LoginID) 91 | } 92 | } 93 | 94 | func TestUsersGetCurrentError(t *testing.T) { 95 | setup() 96 | defer teardown() 97 | 98 | testMux.HandleFunc(baseURL+"/users/me", func(w http.ResponseWriter, r *http.Request) { 99 | w.WriteHeader(http.StatusInternalServerError) 100 | }) 101 | 102 | _, err := testClient.Users.GetCurrent() 103 | if err == nil { 104 | t.Errorf("Expected error but got none") 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /analytics/calculatedmetrics.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Adobe. All rights reserved. 3 | This file is licensed to you under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. You may obtain a copy 5 | of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed under 7 | the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS 8 | OF ANY KIND, either express or implied. See the License for the specific language 9 | governing permissions and limitations under the License. 10 | */ 11 | 12 | package analytics 13 | 14 | import ( 15 | "fmt" 16 | "strconv" 17 | "strings" 18 | ) 19 | 20 | // CalculatedMetricsService handles calculated metrics. 21 | // Analytics docs: https://adobedocs.github.io/analytics-2.0-apis/#/calculatedmetrics 22 | type CalculatedMetricsService struct { 23 | client *Client 24 | } 25 | 26 | // GetAll returns a list of calculated metrics that match the given filters. 27 | // API docs: https://adobedocs.github.io/analytics-2.0-apis/#/calculatedmetrics/findCalculatedMetrics 28 | func (s *CalculatedMetricsService) GetAll(rsids, ownerID, filterByIds, toBeUsedInRsid, locale, name, tagNames string, 29 | favorite, approved bool, 30 | limit, page int64, 31 | sortDirection, sortProperty string, 32 | expansion, includeType []string) (*CalculatedMetrics, error) { 33 | 34 | var params = map[string]string{} 35 | if rsids != "" { 36 | params["rsids"] = rsids 37 | } 38 | if ownerID != "" { 39 | params["ownerId"] = ownerID 40 | } 41 | if filterByIds != "" { 42 | params["filterByIds"] = filterByIds 43 | } 44 | if toBeUsedInRsid != "" { 45 | params["toBeUsedInRsid"] = toBeUsedInRsid 46 | } 47 | if locale != "" { 48 | params["locale"] = locale 49 | } 50 | if name != "" { 51 | params["name"] = name 52 | } 53 | if tagNames != "" { 54 | params["tagNames"] = tagNames 55 | } 56 | params["favorite"] = strconv.FormatBool(favorite) 57 | params["approved"] = strconv.FormatBool(approved) 58 | params["limit"] = strconv.FormatInt(limit, 10) 59 | params["page"] = strconv.FormatInt(page, 10) 60 | if sortDirection != "" { 61 | params["sortDirection"] = sortDirection 62 | } 63 | if sortProperty != "" { 64 | params["sortProperty"] = sortProperty 65 | } 66 | if len(expansion) > 0 { 67 | params["expansion"] = strings.Join(expansion[:], ",") 68 | } 69 | if len(includeType) > 0 { 70 | params["includeType"] = strings.Join(includeType[:], ",") 71 | } 72 | 73 | var data CalculatedMetrics 74 | err := s.client.get("/calculatedmetrics", params, nil, &data) 75 | if err != nil { 76 | return nil, err 77 | } 78 | return &data, err 79 | } 80 | 81 | // GetByID returns a single calculated metric by ID. 82 | // API docs: https://adobedocs.github.io/analytics-2.0-apis/#/calculatedmetrics/findOneCalculatedMetric 83 | func (s *CalculatedMetricsService) GetByID(id, locale string, expansion []string) (*CalculatedMetric, error) { 84 | var params = map[string]string{} 85 | if locale != "" { 86 | params["locale"] = locale 87 | } 88 | if len(expansion) > 0 { 89 | params["expansion"] = strings.Join(expansion[:], ",") 90 | } 91 | 92 | var data CalculatedMetric 93 | err := s.client.get(fmt.Sprintf("/calculatedmetrics/%s", id), params, nil, &data) 94 | if err != nil { 95 | return nil, err 96 | } 97 | return &data, err 98 | } 99 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Adobe Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language. 18 | * Being respectful of differing viewpoints and experiences. 19 | * Gracefully accepting constructive criticism. 20 | * Focusing on what is best for the community. 21 | * Showing empathy towards other community members. 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances. 27 | * Trolling, insulting/derogatory comments, and personal or political attacks. 28 | * Public or private harassment. 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission. 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting. 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at Grp-opensourceoffice@adobe.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [https://contributor-covenant.org/version/1/4][version]. 72 | 73 | [homepage]: https://contributor-covenant.org 74 | [version]: https://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /analytics/metrics_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Adobe. All rights reserved. 3 | This file is licensed to you under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. You may obtain a copy 5 | of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed under 7 | the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS 8 | OF ANY KIND, either express or implied. See the License for the specific language 9 | governing permissions and limitations under the License. 10 | */ 11 | 12 | package analytics_test 13 | 14 | import ( 15 | "fmt" 16 | "io/ioutil" 17 | "net/http" 18 | "testing" 19 | ) 20 | 21 | func TestMetricsGetAll(t *testing.T) { 22 | setup() 23 | defer teardown() 24 | 25 | apiEndpoint := baseURL + "/metrics" 26 | 27 | raw, err := ioutil.ReadFile("./testdata/Metrics.GetAll.json") 28 | if err != nil { 29 | t.Error(err.Error()) 30 | } 31 | 32 | testMux.HandleFunc(apiEndpoint, func(w http.ResponseWriter, r *http.Request) { 33 | testMethod(t, r, "GET") 34 | testRequestURL(t, r, apiEndpoint) 35 | testRequestParams(t, r, map[string]string{ 36 | "rsid": "rsId", 37 | "locale": "en_US", 38 | "segmentable": "false", 39 | "expansion": "a,b", 40 | }) 41 | fmt.Fprint(w, string(raw)) 42 | }) 43 | 44 | metrics, err := testClient.Metrics.GetAll("rsId", "en_US", false, []string{"a", "b"}) 45 | if err != nil { 46 | t.Errorf("Error: %v", err) 47 | return 48 | } 49 | 50 | if len(*metrics) != 2 { 51 | t.Errorf("Expected %d metrics but got %d", 2, len(*metrics)) 52 | return 53 | } 54 | } 55 | 56 | func TestMetricsGetAllError(t *testing.T) { 57 | setup() 58 | defer teardown() 59 | 60 | testMux.HandleFunc(baseURL+"/metrics", func(w http.ResponseWriter, r *http.Request) { 61 | w.WriteHeader(http.StatusInternalServerError) 62 | }) 63 | 64 | _, err := testClient.Metrics.GetAll("rsId", "en_US", false, []string{"a", "b"}) 65 | if err == nil { 66 | t.Errorf("Expected error but got none") 67 | } 68 | } 69 | 70 | func TestMetricsGetByID(t *testing.T) { 71 | setup() 72 | defer teardown() 73 | 74 | apiEndpoint := baseURL + "/metrics/pageviews" 75 | 76 | raw, err := ioutil.ReadFile("./testdata/Metrics.GetByID.json") 77 | if err != nil { 78 | t.Error(err.Error()) 79 | } 80 | 81 | testMux.HandleFunc(apiEndpoint, func(w http.ResponseWriter, r *http.Request) { 82 | testMethod(t, r, "GET") 83 | testRequestURL(t, r, apiEndpoint) 84 | testRequestParams(t, r, map[string]string{ 85 | "rsid": "rsId", 86 | "locale": "en_US", 87 | "expansion": "a,b", 88 | }) 89 | fmt.Fprint(w, string(raw)) 90 | }) 91 | 92 | metric, err := testClient.Metrics.GetByID("rsId", "pageviews", "en_US", []string{"a", "b"}) 93 | if err != nil { 94 | t.Errorf("Error: %v", err) 95 | } 96 | 97 | if metric.ID != "metrics/pageviews" { 98 | t.Errorf("Expected metric with ID=metrics/pageviews but got ID=%s", metric.ID) 99 | } 100 | } 101 | 102 | func TestMetricsGetByIDError(t *testing.T) { 103 | setup() 104 | defer teardown() 105 | 106 | testMux.HandleFunc(baseURL+"/metrics/pageviews", func(w http.ResponseWriter, r *http.Request) { 107 | w.WriteHeader(http.StatusInternalServerError) 108 | }) 109 | 110 | _, err := testClient.Metrics.GetByID("rsId", "pageviews", "en_US", []string{"a", "b"}) 111 | if err == nil { 112 | t.Errorf("Expected error but got none") 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /analytics/collections_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Adobe. All rights reserved. 3 | This file is licensed to you under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. You may obtain a copy 5 | of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed under 7 | the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS 8 | OF ANY KIND, either express or implied. See the License for the specific language 9 | governing permissions and limitations under the License. 10 | */ 11 | 12 | package analytics_test 13 | 14 | import ( 15 | "fmt" 16 | "io/ioutil" 17 | "net/http" 18 | "testing" 19 | ) 20 | 21 | func TestCollectionsGetAll(t *testing.T) { 22 | setup() 23 | defer teardown() 24 | 25 | apiEndpoint := baseURL + "/collections/suites" 26 | 27 | raw, err := ioutil.ReadFile("./testdata/Collections.GetAll.json") 28 | if err != nil { 29 | t.Error(err.Error()) 30 | } 31 | 32 | testMux.HandleFunc(apiEndpoint, func(w http.ResponseWriter, r *http.Request) { 33 | testMethod(t, r, "GET") 34 | testRequestURL(t, r, apiEndpoint) 35 | testRequestParams(t, r, map[string]string{ 36 | "rsids": "a,b", 37 | "rsidContains": "prod", 38 | "limit": "10", 39 | "page": "0", 40 | "expansion": "a,b", 41 | }) 42 | fmt.Fprint(w, string(raw)) 43 | }) 44 | 45 | collections, err := testClient.Collections.GetAll("a,b", "prod", 10, 0, []string{"a", "b"}) 46 | if err != nil { 47 | t.Errorf("Error: %v", err) 48 | } 49 | 50 | if len(*collections.Content) != 2 { 51 | t.Errorf("Expected %d collections but got %d", 2, len(*collections.Content)) 52 | return 53 | } 54 | } 55 | 56 | func TestCollectionsGetAllError(t *testing.T) { 57 | setup() 58 | defer teardown() 59 | 60 | testMux.HandleFunc(baseURL+"/collections/suites", func(w http.ResponseWriter, r *http.Request) { 61 | w.WriteHeader(http.StatusInternalServerError) 62 | }) 63 | 64 | _, err := testClient.Collections.GetAll("rsIds", "rsidContains", 10, 0, []string{"a", "b"}) 65 | if err == nil { 66 | t.Errorf("Expected error but got none") 67 | } 68 | } 69 | 70 | func TestCollectionsGetByID(t *testing.T) { 71 | setup() 72 | defer teardown() 73 | 74 | apiEndpoint := baseURL + "/collections/suites/amc.aem.prod" 75 | 76 | raw, err := ioutil.ReadFile("./testdata/Collections.GetByID.json") 77 | if err != nil { 78 | t.Error(err.Error()) 79 | } 80 | 81 | testMux.HandleFunc(apiEndpoint, func(w http.ResponseWriter, r *http.Request) { 82 | testMethod(t, r, "GET") 83 | testRequestURL(t, r, apiEndpoint) 84 | testRequestParams(t, r, map[string]string{ 85 | "expansion": "a,b", 86 | }) 87 | fmt.Fprint(w, string(raw)) 88 | }) 89 | 90 | collection, err := testClient.Collections.GetByID("amc.aem.prod", []string{"a", "b"}) 91 | if err != nil { 92 | t.Errorf("Error: %v", err) 93 | } 94 | 95 | if collection.RSID != "amc.aem.prod" { 96 | t.Errorf("Expected collection with RSID=amc.aem.prod but got RSID=%s", collection.RSID) 97 | } 98 | } 99 | 100 | func TestCollectionsGetByIDError(t *testing.T) { 101 | setup() 102 | defer teardown() 103 | 104 | testMux.HandleFunc(baseURL+"/collections/suites/amc.aem.prod", func(w http.ResponseWriter, r *http.Request) { 105 | w.WriteHeader(http.StatusInternalServerError) 106 | }) 107 | 108 | _, err := testClient.Collections.GetByID("amc.aem.prod", []string{"a", "b"}) 109 | if err == nil { 110 | t.Errorf("Expected error but got none") 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /analytics/dimensions_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Adobe. All rights reserved. 3 | This file is licensed to you under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. You may obtain a copy 5 | of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed under 7 | the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS 8 | OF ANY KIND, either express or implied. See the License for the specific language 9 | governing permissions and limitations under the License. 10 | */ 11 | 12 | package analytics_test 13 | 14 | import ( 15 | "fmt" 16 | "io/ioutil" 17 | "net/http" 18 | "testing" 19 | ) 20 | 21 | func TestDimensionsGetAll(t *testing.T) { 22 | setup() 23 | defer teardown() 24 | 25 | apiEndpoint := baseURL + "/dimensions" 26 | 27 | raw, err := ioutil.ReadFile("./testdata/Dimensions.GetAll.json") 28 | if err != nil { 29 | t.Error(err.Error()) 30 | } 31 | 32 | testMux.HandleFunc(apiEndpoint, func(w http.ResponseWriter, r *http.Request) { 33 | testMethod(t, r, "GET") 34 | testRequestURL(t, r, apiEndpoint) 35 | testRequestParams(t, r, map[string]string{ 36 | "rsid": "rsId", 37 | "locale": "en_US", 38 | "segmentable": "false", 39 | "reportable": "false", 40 | "classifiable": "false", 41 | "expansion": "a,b", 42 | }) 43 | fmt.Fprint(w, string(raw)) 44 | }) 45 | 46 | dimensions, err := testClient.Dimensions.GetAll("rsId", "en_US", false, false, false, []string{"a", "b"}) 47 | if err != nil { 48 | t.Errorf("Error: %v", err) 49 | } 50 | 51 | if len(*dimensions) != 2 { 52 | t.Errorf("Expected %d dimensions but got %d", 2, len(*dimensions)) 53 | return 54 | } 55 | } 56 | 57 | func TestDimensionsGetAllError(t *testing.T) { 58 | setup() 59 | defer teardown() 60 | 61 | testMux.HandleFunc(baseURL+"/dimensions", func(w http.ResponseWriter, r *http.Request) { 62 | w.WriteHeader(http.StatusInternalServerError) 63 | }) 64 | 65 | _, err := testClient.Dimensions.GetAll("rsId", "en_US", false, false, false, []string{"a", "b"}) 66 | if err == nil { 67 | t.Errorf("Expected error but got none") 68 | } 69 | } 70 | 71 | func TestDimensionsGetByID(t *testing.T) { 72 | setup() 73 | defer teardown() 74 | 75 | apiEndpoint := baseURL + "/dimensions/evar1" 76 | 77 | raw, err := ioutil.ReadFile("./testdata/Dimensions.GetByID.json") 78 | if err != nil { 79 | t.Error(err.Error()) 80 | } 81 | 82 | testMux.HandleFunc(apiEndpoint, func(w http.ResponseWriter, r *http.Request) { 83 | testMethod(t, r, "GET") 84 | testRequestURL(t, r, apiEndpoint) 85 | testRequestParams(t, r, map[string]string{ 86 | "rsid": "rsId", 87 | "locale": "en_US", 88 | "expansion": "a,b", 89 | }) 90 | fmt.Fprint(w, string(raw)) 91 | }) 92 | 93 | dimension, err := testClient.Dimensions.GetByID("rsId", "evar1", "en_US", []string{"a", "b"}) 94 | if err != nil { 95 | t.Errorf("Error: %v", err) 96 | } 97 | 98 | if dimension.ID != "variables/evar1" { 99 | t.Errorf("Expected dimension with ID=variables/evar1 but got ID=%s", dimension.ID) 100 | } 101 | } 102 | 103 | func TestDimensionsGetByIDError(t *testing.T) { 104 | setup() 105 | defer teardown() 106 | 107 | testMux.HandleFunc(baseURL+"/dimensions/evar1", func(w http.ResponseWriter, r *http.Request) { 108 | w.WriteHeader(http.StatusInternalServerError) 109 | }) 110 | 111 | _, err := testClient.Dimensions.GetByID("rsId", "evar1", "en_US", []string{"a", "b"}) 112 | if err == nil { 113 | t.Errorf("Expected error but got none") 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /analytics/dateranges_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Adobe. All rights reserved. 3 | This file is licensed to you under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. You may obtain a copy 5 | of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed under 7 | the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS 8 | OF ANY KIND, either express or implied. See the License for the specific language 9 | governing permissions and limitations under the License. 10 | */ 11 | 12 | package analytics_test 13 | 14 | import ( 15 | "fmt" 16 | "io/ioutil" 17 | "net/http" 18 | "testing" 19 | ) 20 | 21 | func TestDateRangesGetAll(t *testing.T) { 22 | setup() 23 | defer teardown() 24 | 25 | apiEndpoint := baseURL + "/dateranges" 26 | 27 | raw, err := ioutil.ReadFile("./testdata/DateRanges.GetAll.json") 28 | if err != nil { 29 | t.Error(err.Error()) 30 | } 31 | 32 | testMux.HandleFunc(apiEndpoint, func(w http.ResponseWriter, r *http.Request) { 33 | testMethod(t, r, "GET") 34 | testRequestURL(t, r, apiEndpoint) 35 | testRequestParams(t, r, map[string]string{ 36 | "locale": "en_US", 37 | "filterByIds": "1,2", 38 | "limit": "10", 39 | "page": "0", 40 | "expansion": "a,b", 41 | "includeType": "all", 42 | }) 43 | fmt.Fprint(w, string(raw)) 44 | }) 45 | 46 | dateranges, err := testClient.DateRanges.GetAll("en_US", "1,2", 10, 0, []string{"a", "b"}, []string{"all"}) 47 | if err != nil { 48 | t.Errorf("Error: %v", err) 49 | } 50 | 51 | if len(*dateranges.Content) != 2 { 52 | t.Errorf("Expected %d dateranges but got %d", 2, len(*dateranges.Content)) 53 | return 54 | } 55 | } 56 | 57 | func TestDateRangesGetAllError(t *testing.T) { 58 | setup() 59 | defer teardown() 60 | 61 | testMux.HandleFunc(baseURL+"/dateranges", func(w http.ResponseWriter, r *http.Request) { 62 | w.WriteHeader(http.StatusInternalServerError) 63 | }) 64 | 65 | _, err := testClient.DateRanges.GetAll("en_US", "1,2", 10, 0, []string{"a", "b"}, []string{"all"}) 66 | if err == nil { 67 | t.Errorf("Expected error but got none") 68 | } 69 | } 70 | 71 | func TestDateRangesGetByID(t *testing.T) { 72 | setup() 73 | defer teardown() 74 | 75 | apiEndpoint := baseURL + "/dateranges/57a9ad685fe707f55ffb68f5" 76 | 77 | raw, err := ioutil.ReadFile("./testdata/DateRanges.GetByID.json") 78 | if err != nil { 79 | t.Error(err.Error()) 80 | } 81 | 82 | testMux.HandleFunc(apiEndpoint, func(w http.ResponseWriter, r *http.Request) { 83 | testMethod(t, r, "GET") 84 | testRequestURL(t, r, apiEndpoint) 85 | testRequestParams(t, r, map[string]string{ 86 | "locale": "en_US", 87 | "expansion": "a,b", 88 | }) 89 | fmt.Fprint(w, string(raw)) 90 | }) 91 | 92 | daterange, err := testClient.DateRanges.GetByID("57a9ad685fe707f55ffb68f5", "en_US", []string{"a", "b"}) 93 | if err != nil { 94 | t.Errorf("Error: %v", err) 95 | } 96 | 97 | if daterange.ID != "57a9ad685fe707f55ffb68f5" { 98 | t.Errorf("Expected dimension with ID=57a9ad685fe707f55ffb68f5 but got ID=%s", daterange.ID) 99 | } 100 | } 101 | 102 | func TestDateRangesGetByIDError(t *testing.T) { 103 | setup() 104 | defer teardown() 105 | 106 | testMux.HandleFunc(baseURL+"/dateranges/?", func(w http.ResponseWriter, r *http.Request) { 107 | w.WriteHeader(http.StatusInternalServerError) 108 | }) 109 | 110 | _, err := testClient.DateRanges.GetByID("?", "en_US", []string{"a", "b"}) 111 | if err == nil { 112 | t.Errorf("Expected error but got none") 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Adobe Analytics API 2.0 Client 2 | 3 | This project provides a client library for accessing the Adobe Analytics API 2.0. 4 | 5 | ## Installation 6 | 7 | Use the standard Go tool chain to use this library in your project. 8 | 9 | Example: 10 | 11 | ```shell 12 | go get -u github.com/adobe/aa-client-go 13 | ``` 14 | 15 | ## Usage 16 | 17 | Create a new Analytics client with a `http.DefaultClient`. 18 | 19 | ```go 20 | client, err := analytics.NewClient(&analytics.Config{ 21 | BaseURL: "https://analytics.adobe.io/api", 22 | ClientID: "", 23 | OrgID: "", 24 | AccessToken: "", 25 | CompanyID: "", 26 | }) 27 | ``` 28 | 29 | Use specific API endpoint. For example, get all available metrics. 30 | 31 | ```go 32 | metrics, err := client.Metrics.GetAll("", "en_US", false, []string{}) 33 | ``` 34 | 35 | See [examples](./examples) for more information about using the various API endpoints. 36 | 37 | ### Authentication 38 | 39 | Use [ims-go](github.com/adobe/ims-go) to get an authentication token. 40 | 41 | ```go 42 | client, err := ims.NewClient(&ims.ClientConfig{ 43 | URL: "https://ims-na1.adobelogin.com", 44 | }) 45 | 46 | resp, err := client.ExchangeJWT(&ims.ExchangeJWTRequest{ 47 | Expiration: time.Now().Add(12 * time.Hour), 48 | PrivateKey: []byte(""), 49 | Issuer: "", 50 | Subject: "", 51 | ClientID: "", 52 | ClientSecret: "", 53 | MetaScope: []ims.MetaScope{ims.MetaScopeAnalyticsBulkIngest}, 54 | }) 55 | 56 | accessToken := resp.AccessToken 57 | ``` 58 | 59 | ### Handling 429 status codes 60 | 61 | To handle `429` response status codes (returned if the API rate limit is hit), a HTTP client with retry/backoff like [go-retryablehttp](github.com/hashicorp/go-retryablehttp) can be used. 62 | 63 | See the [examples/utils/utils.go](./examples/utils/utils.go) for an example of a custom rate limit policy. 64 | 65 | ```go 66 | retryClient := retryablehttp.NewClient() 67 | retryClient.RetryMax = 10 68 | retryClient.CheckRetry = rateLimitPolicy 69 | 70 | client, err := analytics.NewClient(&analytics.Config{ 71 | HTTPClient: retryClient.StandardClient(), 72 | BaseURL: "https://analytics.adobe.io/api", 73 | ClientID: "", 74 | OrgID: "", 75 | AccessToken: "", 76 | CompanyID: "", 77 | }) 78 | ``` 79 | 80 | ## Development 81 | 82 | The module provides a `Makefile` with following targets. 83 | 84 | * `all` - Runs `fmt`, `vet`, `lint` and `test`. 85 | * `fmt` - Formats all code. 86 | Runs `go fmt ./...` 87 | * `vet` - Vets all code. 88 | Runs `go vet -all ./...` 89 | * `lint` - Lints all code. 90 | Runs `golint ./...` 91 | * `test` - Runs the test of the `analytics` package. 92 | Runs `go test ./analytics -cover` 93 | * `coverage` - Runs the tests of the `analytics` package and opens the coverage report. 94 | Runs `go test -coverprofile=coverage.out ./analytics & go tool cover -html=coverage.out` 95 | 96 | A specific target can be executed by running the following command (Linux, macOS). 97 | 98 | ```shell 99 | make [target] 100 | ``` 101 | 102 | Windows users can simply bypass `make` and run the plain commands as indicated above or install `make` via [GNUWin32](http://gnuwin32.sourceforge.net/packages/make.htm), [Chocolatey](https://chocolatey.org/install) or [Windows Subsystem for Linux](https://docs.microsoft.com/en-us/windows/wsl/install-win10). 103 | 104 | ## Contributing 105 | 106 | Contributions are welcome! Read the [Contributing Guide](./CONTRIBUTING.md) for more information. 107 | 108 | ## Licensing 109 | 110 | This project is licensed under the Apache 2.0 License. See [LICENSE](./LICENSE) for more information. 111 | -------------------------------------------------------------------------------- /analytics/segments-types.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Adobe. All rights reserved. 3 | This file is licensed to you under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. You may obtain a copy 5 | of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed under 7 | the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS 8 | OF ANY KIND, either express or implied. See the License for the specific language 9 | governing permissions and limitations under the License. 10 | */ 11 | 12 | package analytics 13 | 14 | // Response types 15 | 16 | // SegmentDefinitionContainerPredicateValue represents a segment definition predicate value 17 | type SegmentDefinitionContainerPredicateValue struct { 18 | Description string `json:"description,omitempty"` 19 | Name string `json:"name,omitempty"` 20 | Func string `json:"func,omitempty"` 21 | } 22 | 23 | // SegmentDefinitionContainerPredicate represents a segment definition predicate 24 | type SegmentDefinitionContainerPredicate struct { 25 | Val *SegmentDefinitionContainerPredicateValue `json:"val,omitempty"` 26 | Str string `json:"str,omitempty"` 27 | Func string `json:"func,omitempty"` 28 | } 29 | 30 | // SegmentDefinitionContainer represents a segment definition container 31 | type SegmentDefinitionContainer struct { 32 | Context string `json:"context,omitempty"` 33 | Func string `json:"func,omitempty"` 34 | Pred string `json:"pred,omitempty"` 35 | } 36 | 37 | // SegmentDefinition represents a segment definition 38 | type SegmentDefinition struct { 39 | Container *SegmentDefinitionContainer `json:"container,omitempty"` 40 | Func string `json:"func,omitempty"` 41 | Version []int `json:"version"` 42 | } 43 | 44 | // SegmentCompatibility represents segment compatibility settings 45 | type SegmentCompatibility struct { 46 | Valid bool `json:"valid"` 47 | Message string `json:"message,omitempty"` 48 | ValidatorVersion string `json:"validator_version,omitempty"` 49 | SupportedProducts []string `json:"supported_products,omitempty"` 50 | SupportedSchema []string `json:"supported_schema,omitempty"` 51 | SuppportedFeatures []string `json:"supported_features,omitempty"` 52 | } 53 | 54 | // Segment represents a segment 55 | type Segment struct { 56 | ID string `json:"id,omitempty"` 57 | Name string `json:"name,omitempty"` 58 | Description string `json:"description,omitempty"` 59 | ReportSuiteID string `json:"rsid,omitempty"` 60 | ReportSuiteName string `json:"reportSuiteName,omitempty"` 61 | Owner *Owner `json:"owner,omitempty"` 62 | Definition *SegmentDefinition `json:"definition,omitempty"` 63 | Compatibility *SegmentCompatibility `json:"compatibility,omitempty"` 64 | DefinitionLastModified string `json:"definitionLastModified,omitempty"` 65 | Categories []string `json:"categories,omitempty"` 66 | SiteTitle string `json:"siteTitle,omitempty"` 67 | Tags *[]Tag `json:"tags,omitempty"` 68 | Modified string `json:"modified,omitempty"` 69 | Created string `json:"created,omitempty"` 70 | } 71 | 72 | // Segments represents a page of segments 73 | type Segments struct { 74 | Content *[]Segment `json:"content,omitempty"` 75 | Number int `json:"number"` 76 | Size int `json:"size"` 77 | NumberOfElements int `json:"numberOfElements"` 78 | TotalElements int `json:"totalElements"` 79 | PreviousPage bool `json:"previousPage"` 80 | FirstPage bool `json:"firstPage"` 81 | NextPage bool `json:"nextPage"` 82 | LastPage bool `json:"lastPage"` 83 | Sort *[]Sort `json:"sort,omitempty"` 84 | TotalPages int `json:"totalPages"` 85 | } 86 | -------------------------------------------------------------------------------- /analytics/segments_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Adobe. All rights reserved. 3 | This file is licensed to you under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. You may obtain a copy 5 | of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed under 7 | the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS 8 | OF ANY KIND, either express or implied. See the License for the specific language 9 | governing permissions and limitations under the License. 10 | */ 11 | 12 | package analytics_test 13 | 14 | import ( 15 | "fmt" 16 | "io/ioutil" 17 | "net/http" 18 | "testing" 19 | ) 20 | 21 | func TestSegmentsGetAll(t *testing.T) { 22 | setup() 23 | defer teardown() 24 | 25 | apiEndpoint := baseURL + "/segments" 26 | 27 | raw, err := ioutil.ReadFile("./testdata/Segments.GetAll.json") 28 | if err != nil { 29 | t.Error(err.Error()) 30 | } 31 | 32 | testMux.HandleFunc(apiEndpoint, func(w http.ResponseWriter, r *http.Request) { 33 | testMethod(t, r, "GET") 34 | testRequestURL(t, r, apiEndpoint) 35 | testRequestParams(t, r, map[string]string{ 36 | "rsids": "rsIds", 37 | "segmentFilter": "segmentFilter", 38 | "locale": "en_US", 39 | "name": "name", 40 | "tagNames": "tagNames", 41 | "filterByPublishedSegments": "filterByPublishedSegments", 42 | "limit": "10", 43 | "page": "0", 44 | "sortDirection": "sortDirection", 45 | "sortProperty": "sortProperty", 46 | "expansion": "a,b", 47 | "includeType": "all", 48 | }) 49 | fmt.Fprint(w, string(raw)) 50 | }) 51 | 52 | segments, err := testClient.Segments.GetAll("rsIds", "segmentFilter", "en_US", "name", "tagNames", "filterByPublishedSegments", 10, 0, "sortDirection", "sortProperty", []string{"a", "b"}, []string{"all"}) 53 | if err != nil { 54 | t.Errorf("Error: %v", err) 55 | } 56 | 57 | if len(*segments.Content) != 2 { 58 | t.Errorf("Expected %d dimensions but got %d", 2, len(*segments.Content)) 59 | return 60 | } 61 | } 62 | 63 | func TestSegmentsGetAllError(t *testing.T) { 64 | setup() 65 | defer teardown() 66 | 67 | testMux.HandleFunc(baseURL+"/segments", func(w http.ResponseWriter, r *http.Request) { 68 | w.WriteHeader(http.StatusInternalServerError) 69 | }) 70 | 71 | _, err := testClient.Segments.GetAll("rsIds", "segmentFilter", "en_US", "name", "tagNames", "filterByPublishedSegments", 10, 0, "sortDirection", "sortProperty", []string{"a", "b"}, []string{"all"}) 72 | if err == nil { 73 | t.Errorf("Expected error but got none") 74 | } 75 | } 76 | 77 | func TestSegmentsGetByID(t *testing.T) { 78 | setup() 79 | defer teardown() 80 | 81 | apiEndpoint := baseURL + "/segments/s300003364_589ce94be4b0c29f29c4f07f" 82 | 83 | raw, err := ioutil.ReadFile("./testdata/Segments.GetByID.json") 84 | if err != nil { 85 | t.Error(err.Error()) 86 | } 87 | 88 | testMux.HandleFunc(apiEndpoint, func(w http.ResponseWriter, r *http.Request) { 89 | testMethod(t, r, "GET") 90 | testRequestURL(t, r, apiEndpoint) 91 | testRequestParams(t, r, map[string]string{ 92 | "locale": "en_US", 93 | "expansion": "a,b", 94 | }) 95 | fmt.Fprint(w, string(raw)) 96 | }) 97 | 98 | segment, err := testClient.Segments.GetByID("s300003364_589ce94be4b0c29f29c4f07f", "en_US", []string{"a", "b"}) 99 | if err != nil { 100 | t.Errorf("Error: %v", err) 101 | } 102 | 103 | if segment.ID != "s300003364_589ce94be4b0c29f29c4f07f" { 104 | t.Errorf("Expected dimension with ID=s300003364_589ce94be4b0c29f29c4f07f but got ID=%s", segment.ID) 105 | } 106 | } 107 | 108 | func TestSegmentsGetByIDError(t *testing.T) { 109 | setup() 110 | defer teardown() 111 | 112 | testMux.HandleFunc(baseURL+"/segments/s300003364_589ce94be4b0c29f29c4f07f", func(w http.ResponseWriter, r *http.Request) { 113 | w.WriteHeader(http.StatusInternalServerError) 114 | }) 115 | 116 | _, err := testClient.Segments.GetByID("s300003364_589ce94be4b0c29f29c4f07f", "en_US", []string{"a", "b"}) 117 | if err == nil { 118 | t.Errorf("Expected error but got none") 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /analytics/calculatedmetrics_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Adobe. All rights reserved. 3 | This file is licensed to you under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. You may obtain a copy 5 | of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed under 7 | the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS 8 | OF ANY KIND, either express or implied. See the License for the specific language 9 | governing permissions and limitations under the License. 10 | */ 11 | 12 | package analytics_test 13 | 14 | import ( 15 | "fmt" 16 | "io/ioutil" 17 | "net/http" 18 | "testing" 19 | ) 20 | 21 | func TestCalculatedMetricsGetAll(t *testing.T) { 22 | setup() 23 | defer teardown() 24 | 25 | apiEndpoint := baseURL + "/calculatedmetrics" 26 | 27 | raw, err := ioutil.ReadFile("./testdata/CalculatedMetrics.GetAll.json") 28 | if err != nil { 29 | t.Error(err.Error()) 30 | } 31 | 32 | testMux.HandleFunc(apiEndpoint, func(w http.ResponseWriter, r *http.Request) { 33 | testMethod(t, r, "GET") 34 | testRequestURL(t, r, apiEndpoint) 35 | testRequestParams(t, r, map[string]string{ 36 | "rsids": "1,2", 37 | "ownerId": "ownerId", 38 | "filterByIds": "filterByIds", 39 | "toBeUsedInRsid": "toBeUsedInRsid", 40 | "locale": "en_US", 41 | "name": "name", 42 | "tagNames": "tagNames", 43 | "favorite": "false", 44 | "approved": "false", 45 | "limit": "10", 46 | "page": "0", 47 | "sortDirection": "asc", 48 | "sortProperty": "date", 49 | "expansion": "a,b", 50 | "includeType": "all", 51 | }) 52 | fmt.Fprint(w, string(raw)) 53 | }) 54 | 55 | collections, err := testClient.CalculatedMetrics.GetAll( 56 | "1,2", "ownerId", "filterByIds", "toBeUsedInRsid", 57 | "en_US", "name", "tagNames", 58 | false, false, 10, 0, 59 | "asc", "date", 60 | []string{"a", "b"}, []string{"all"}, 61 | ) 62 | 63 | if err != nil { 64 | t.Errorf("Error: %v", err) 65 | } 66 | 67 | if len(*collections.Content) != 2 { 68 | t.Errorf("Expected %d collections but got %d", 2, len(*collections.Content)) 69 | return 70 | } 71 | } 72 | 73 | func TestCalculatedMetricsGetAllError(t *testing.T) { 74 | setup() 75 | defer teardown() 76 | 77 | testMux.HandleFunc(baseURL+"/calculatedmetrics", func(w http.ResponseWriter, r *http.Request) { 78 | w.WriteHeader(http.StatusInternalServerError) 79 | }) 80 | 81 | _, err := testClient.CalculatedMetrics.GetAll( 82 | "1,2", "ownerId", "filterByIds", "toBeUsedInRsid", 83 | "en_US", "name", "tagNames", 84 | false, false, 10, 0, 85 | "asc", "date", 86 | []string{"a", "b"}, []string{"all"}, 87 | ) 88 | 89 | if err == nil { 90 | t.Errorf("Expected error but got none") 91 | } 92 | } 93 | 94 | func TestCalculatedMetricsGetByID(t *testing.T) { 95 | setup() 96 | defer teardown() 97 | 98 | apiEndpoint := baseURL + "/calculatedmetrics/cm300003364_5ae7447df118f061698ddc31" 99 | 100 | raw, err := ioutil.ReadFile("./testdata/CalculatedMetrics.GetByID.json") 101 | if err != nil { 102 | t.Error(err.Error()) 103 | } 104 | 105 | testMux.HandleFunc(apiEndpoint, func(w http.ResponseWriter, r *http.Request) { 106 | testMethod(t, r, "GET") 107 | testRequestURL(t, r, apiEndpoint) 108 | testRequestParams(t, r, map[string]string{ 109 | "locale": "en_US", 110 | "expansion": "a,b", 111 | }) 112 | fmt.Fprint(w, string(raw)) 113 | }) 114 | 115 | metric, err := testClient.CalculatedMetrics.GetByID("cm300003364_5ae7447df118f061698ddc31", "en_US", []string{"a", "b"}) 116 | if err != nil { 117 | t.Errorf("Error: %v", err) 118 | } 119 | 120 | if metric.ID != "cm300003364_5ae7447df118f061698ddc31" { 121 | t.Errorf("Expected calculated metric with ID=cm300003364_5ae7447df118f061698ddc31 but got ID=%s", metric.ID) 122 | } 123 | } 124 | 125 | func TestCalculatedMetricsGetByIDError(t *testing.T) { 126 | setup() 127 | defer teardown() 128 | 129 | testMux.HandleFunc(baseURL+"/calculatedmetrics/cm300003364_5ae7447df118f061698ddc31", func(w http.ResponseWriter, r *http.Request) { 130 | w.WriteHeader(http.StatusInternalServerError) 131 | }) 132 | 133 | _, err := testClient.CalculatedMetrics.GetByID("cm300003364_5ae7447df118f061698ddc31", "en_US", []string{"a", "b"}) 134 | if err == nil { 135 | t.Errorf("Expected error but got none") 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /examples/utils/utils.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Adobe. All rights reserved. 3 | This file is licensed to you under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. You may obtain a copy 5 | of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed under 7 | the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS 8 | OF ANY KIND, either express or implied. See the License for the specific language 9 | governing permissions and limitations under the License. 10 | */ 11 | 12 | package utils 13 | 14 | import ( 15 | "context" 16 | "crypto/x509" 17 | "encoding/json" 18 | "fmt" 19 | "log" 20 | "net/http" 21 | "net/url" 22 | "regexp" 23 | "time" 24 | 25 | "github.com/adobe/ims-go/ims" 26 | "github.com/hashicorp/go-retryablehttp" 27 | 28 | "github.com/adobe/aa-client-go/analytics" 29 | "github.com/adobe/aa-client-go/examples/config" 30 | ) 31 | 32 | // PrintJSON pretty prints the JSON data 33 | func PrintJSON(data interface{}) { 34 | var p []byte 35 | p, err := json.MarshalIndent(data, "", " ") 36 | if err != nil { 37 | fmt.Println(err) 38 | return 39 | } 40 | fmt.Printf("%s \n", p) 41 | } 42 | 43 | // NewAnalyticsClient returns a new Analytics client 44 | func NewAnalyticsClient(aaConfig *config.AnalyticsAPIConfiguration) (*analytics.Client, error) { 45 | // Exchange JWT for Auth Token 46 | responseToken, err := getAuthToken(aaConfig) 47 | if err != nil { 48 | log.Fatal(err) 49 | } 50 | 51 | log.Printf("IMS AuthToken received, expires in %v", responseToken.ExpiresIn) 52 | 53 | // Create resilient client 54 | httpClient := newHTTPClient() 55 | 56 | // Create Analytics client 57 | client, err := analytics.NewClient(&analytics.Config{ 58 | HTTPClient: httpClient, 59 | BaseURL: aaConfig.Endpoint, 60 | ClientID: aaConfig.IMSClientID, 61 | OrgID: aaConfig.IMSOrgID, 62 | AccessToken: responseToken.AccessToken, 63 | CompanyID: aaConfig.CompanyID, 64 | }) 65 | if err != nil { 66 | log.Fatal(err) 67 | } 68 | 69 | log.Printf("Analytics client created for [ClientID:%s, CompanyID:%s]", aaConfig.IMSClientID, aaConfig.CompanyID) 70 | 71 | return client, nil 72 | } 73 | 74 | // rateLimitPolicy implements a retryablehttp.CheckRetry for handling 429, 5xx status codes 75 | func rateLimitPolicy(ctx context.Context, resp *http.Response, err error) (bool, error) { 76 | var ( 77 | // A regular expression to match the error returned by net/http when the 78 | // configured number of redirects is exhausted. This error isn't typed 79 | // specifically so we resort to matching on the error string. 80 | redirectsErrorRe = regexp.MustCompile(`stopped after \d+ redirects\z`) 81 | 82 | // A regular expression to match the error returned by net/http when the 83 | // scheme specified in the URL is invalid. This error isn't typed 84 | // specifically so we resort to matching on the error string. 85 | schemeErrorRe = regexp.MustCompile(`unsupported protocol scheme`) 86 | ) 87 | 88 | // do not retry on context.Canceled or context.DeadlineExceeded 89 | if ctx.Err() != nil { 90 | return false, ctx.Err() 91 | } 92 | 93 | if err != nil { 94 | if v, ok := err.(*url.Error); ok { 95 | // Don't retry if the error was due to too many redirects. 96 | if redirectsErrorRe.MatchString(v.Error()) { 97 | return false, nil 98 | } 99 | 100 | // Don't retry if the error was due to an invalid protocol scheme. 101 | if schemeErrorRe.MatchString(v.Error()) { 102 | return false, nil 103 | } 104 | 105 | // Don't retry if the error was due to TLS cert verification failure. 106 | if _, ok := v.Err.(x509.UnknownAuthorityError); ok { 107 | return false, nil 108 | } 109 | } 110 | 111 | // The error is likely recoverable so retry. 112 | return true, nil 113 | } 114 | 115 | // Check the response code. We retry on 500-range responses to allow 116 | // the server time to recover, as 500's are typically not permanent 117 | // errors and may relate to outages on the server side. This will catch 118 | // invalid response codes as well, like 0 and 999. 119 | if resp.StatusCode == 0 || resp.StatusCode == 429 || (resp.StatusCode >= 500 && resp.StatusCode != 501) { 120 | return true, nil 121 | } 122 | 123 | return false, nil 124 | } 125 | 126 | // newHTTPClient create a new resilient HTTP client 127 | func newHTTPClient() *http.Client { 128 | retryClient := retryablehttp.NewClient() 129 | retryClient.Logger = nil 130 | retryClient.RetryMax = 10 131 | retryClient.CheckRetry = rateLimitPolicy 132 | 133 | return retryClient.StandardClient() 134 | } 135 | 136 | // getAuthToken returns an IMS AuthToken in exchange of a JWT 137 | func getAuthToken(aaConfig *config.AnalyticsAPIConfiguration) (*ims.ExchangeJWTResponse, error) { 138 | c, err := ims.NewClient(&ims.ClientConfig{ 139 | URL: aaConfig.IMSEndpoint, 140 | }) 141 | 142 | if err != nil { 143 | return nil, err 144 | } 145 | 146 | // get token response 147 | r, err := c.ExchangeJWT(&ims.ExchangeJWTRequest{ 148 | PrivateKey: []byte(aaConfig.IMSPrivateKey), 149 | Expiration: time.Now().Add(12 * time.Hour), 150 | Issuer: aaConfig.IMSOrgID, 151 | Subject: aaConfig.IMSTechnicalAccountID, 152 | ClientID: aaConfig.IMSClientID, 153 | ClientSecret: aaConfig.IMSClientSecret, 154 | MetaScope: []ims.MetaScope{ims.MetaScopeAnalyticsBulkIngest}, 155 | }) 156 | 157 | return r, err 158 | } 159 | -------------------------------------------------------------------------------- /analytics/client_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Adobe. All rights reserved. 3 | This file is licensed to you under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. You may obtain a copy 5 | of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed under 7 | the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS 8 | OF ANY KIND, either express or implied. See the License for the specific language 9 | governing permissions and limitations under the License. 10 | */ 11 | 12 | package analytics_test 13 | 14 | import ( 15 | "bytes" 16 | "encoding/json" 17 | "io/ioutil" 18 | "net/http" 19 | "net/http/httptest" 20 | "reflect" 21 | "strings" 22 | "testing" 23 | 24 | "github.com/adobe/aa-client-go/analytics" 25 | ) 26 | 27 | var ( 28 | baseURL string 29 | testMux *http.ServeMux 30 | testConfig *analytics.Config 31 | testClient *analytics.Client 32 | testServer *httptest.Server 33 | ) 34 | 35 | func setup() { 36 | // mock server 37 | testMux = http.NewServeMux() 38 | testServer = httptest.NewServer(testMux) 39 | 40 | // test config 41 | baseURL = "/api/aaCompanyId" 42 | testConfig = &analytics.Config{ 43 | BaseURL: string(testServer.URL + "/api"), 44 | ClientID: "imsClientId", 45 | OrgID: "imsOrgId", 46 | AccessToken: "imsAuthToken", 47 | CompanyID: "aaCompanyId", 48 | } 49 | 50 | // client configured to use test server 51 | testClient, _ = analytics.NewClient(testConfig) 52 | } 53 | 54 | func teardown() { 55 | testServer.Close() 56 | } 57 | 58 | func testMethod(t *testing.T, r *http.Request, want string) { 59 | if got := r.Method; got != want { 60 | t.Errorf("Request method: %v, want %v", got, want) 61 | } 62 | } 63 | 64 | func testRequestURL(t *testing.T, r *http.Request, want string) { 65 | if got := r.URL.String(); !strings.HasPrefix(got, want) { 66 | t.Errorf("Request URL: %v, want %v", got, want) 67 | } 68 | } 69 | 70 | func testRequestParams(t *testing.T, r *http.Request, want map[string]string) { 71 | params := r.URL.Query() 72 | 73 | if len(params) != len(want) { 74 | t.Errorf("Request params: %d, want %d", len(params), len(want)) 75 | } 76 | 77 | for key, val := range want { 78 | if got := params.Get(key); val != got { 79 | t.Errorf("Request params: %s, want %s", got, val) 80 | } 81 | 82 | } 83 | } 84 | 85 | func testRequestBody(t *testing.T, r *http.Request, want []byte) { 86 | data, _ := ioutil.ReadAll(r.Body) 87 | var buf bytes.Buffer 88 | json.Indent(&buf, data, "", " ") 89 | got := buf.Bytes() 90 | 91 | if eq, _ := jsonBytesEqual(got, want); !eq { 92 | t.Errorf("Request Body:\n%v\nwant:\n%v", string(got), string(want)) 93 | } 94 | } 95 | 96 | func jsonBytesEqual(a, b []byte) (bool, error) { 97 | var j, j2 interface{} 98 | if err := json.Unmarshal(a, &j); err != nil { 99 | return false, err 100 | } 101 | if err := json.Unmarshal(b, &j2); err != nil { 102 | return false, err 103 | } 104 | return reflect.DeepEqual(j, j2), nil 105 | } 106 | 107 | func TestNewClientNoScheme(t *testing.T) { 108 | _, err := analytics.NewClient( 109 | &analytics.Config{ 110 | BaseURL: "domain.com/api", 111 | ClientID: "imsClientId", 112 | OrgID: "imsOrgId", 113 | AccessToken: "imsAccessToken", 114 | CompanyID: "aaCompanyId", 115 | }, 116 | ) 117 | if err == nil { 118 | t.Fatalf("expected error") 119 | } 120 | if err.Error() != "missing URL scheme" { 121 | t.Fatalf("unexpected error: %v", err) 122 | } 123 | } 124 | 125 | func TestNewClientNoHost(t *testing.T) { 126 | _, err := analytics.NewClient( 127 | &analytics.Config{ 128 | BaseURL: "https:///api", 129 | ClientID: "imsClientId", 130 | OrgID: "imsOrgId", 131 | AccessToken: "imsAccessToken", 132 | CompanyID: "aaCompanyId", 133 | }, 134 | ) 135 | if err == nil { 136 | t.Fatalf("expected error") 137 | } 138 | if err.Error() != "missing URL host" { 139 | t.Fatalf("unexpected error: %v", err) 140 | } 141 | } 142 | 143 | func TestNewClientMalformedURL(t *testing.T) { 144 | _, err := analytics.NewClient( 145 | &analytics.Config{ 146 | BaseURL: ":", 147 | ClientID: "imsClientId", 148 | OrgID: "imsOrgId", 149 | AccessToken: "imsAccessToken", 150 | CompanyID: "aaCompanyId", 151 | }, 152 | ) 153 | if err == nil { 154 | t.Fatalf("expected error") 155 | } 156 | if err.Error() != "malformed URL" { 157 | t.Fatalf("unexpected error: %v", err) 158 | } 159 | } 160 | 161 | func TestNewClientNoClientID(t *testing.T) { 162 | err := testNewClientInvalidAuth(t, "", "OrgID", "AccessToken", "CompanyID") 163 | if err.Error() != "missing ClientID" { 164 | t.Fatalf("unexpected error: %v", err) 165 | } 166 | } 167 | 168 | func TestNewClientNoIMSOrgID(t *testing.T) { 169 | err := testNewClientInvalidAuth(t, "ClientID", "", "AccessToken", "CompanyID") 170 | if err.Error() != "missing OrgID" { 171 | t.Fatalf("unexpected error: %v", err) 172 | } 173 | } 174 | 175 | func TestNewClientNoIMSAuthToken(t *testing.T) { 176 | err := testNewClientInvalidAuth(t, "ClientID", "OrgID", "", "CompanyID") 177 | if err.Error() != "missing AccessToken" { 178 | t.Fatalf("unexpected error: %v", err) 179 | } 180 | } 181 | 182 | func TestNewClientNoCompanyID(t *testing.T) { 183 | err := testNewClientInvalidAuth(t, "ClientID", "OrgID", "AccessToken", "") 184 | if err.Error() != "missing CompanyID" { 185 | t.Fatalf("unexpected error: %v", err) 186 | } 187 | } 188 | 189 | func testNewClientInvalidAuth(t *testing.T, clientID, orgID, accessToken, companyID string) error { 190 | _, err := analytics.NewClient( 191 | &analytics.Config{ 192 | BaseURL: "https://domain.com/api", 193 | ClientID: clientID, 194 | OrgID: orgID, 195 | AccessToken: accessToken, 196 | CompanyID: companyID, 197 | }, 198 | ) 199 | if err == nil { 200 | t.Fatalf("expected error") 201 | } 202 | return err 203 | } 204 | 205 | func TestNewClientTrimSuffix(t *testing.T) { 206 | c, err := analytics.NewClient( 207 | &analytics.Config{ 208 | BaseURL: "https://domain.com/api/", 209 | ClientID: "imsClientId", 210 | OrgID: "imsOrgId", 211 | AccessToken: "imsAccessToken", 212 | CompanyID: "companyId", 213 | }, 214 | ) 215 | if err != nil { 216 | t.Fatalf("unexpected error") 217 | } 218 | if strings.HasSuffix(c.GetBaseURL(), "/") { 219 | t.Fatalf("unexpected trailing slash") 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /analytics/client.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Adobe. All rights reserved. 3 | This file is licensed to you under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. You may obtain a copy 5 | of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed under 7 | the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS 8 | OF ANY KIND, either express or implied. See the License for the specific language 9 | governing permissions and limitations under the License. 10 | */ 11 | 12 | package analytics 13 | 14 | import ( 15 | "encoding/json" 16 | "fmt" 17 | "io" 18 | "io/ioutil" 19 | "net/http" 20 | "net/url" 21 | "strings" 22 | ) 23 | 24 | // Config holds configuration values 25 | type Config struct { 26 | HTTPClient *http.Client 27 | BaseURL string 28 | ClientID string 29 | OrgID string 30 | AccessToken string 31 | CompanyID string 32 | } 33 | 34 | // Auth holds authentication information 35 | type auth struct { 36 | imsClientID string 37 | imsOrgID string 38 | imsAccessToken string 39 | companyID string 40 | } 41 | 42 | // Client is used to make HTTP requests against the Analytics API 2.0. 43 | // It wraps a HTTP client and handles authentication. 44 | type Client struct { 45 | httpClient *http.Client 46 | baseURL *url.URL 47 | auth *auth 48 | 49 | // Services used for communicating to different parts of the API. 50 | CalculatedMetrics *CalculatedMetricsService 51 | Collections *CollectionsService 52 | DateRanges *DateRangesService 53 | Dimensions *DimensionsService 54 | Metrics *MetricsService 55 | Reports *ReportsService 56 | Segments *SegmentsService 57 | Users *UsersService 58 | } 59 | 60 | // NewClient returns a new Analytics API 2.0 client. 61 | func NewClient(config *Config) (*Client, error) { 62 | var httpClient *http.Client 63 | if config.HTTPClient != nil { 64 | httpClient = config.HTTPClient 65 | } else { 66 | httpClient = http.DefaultClient 67 | } 68 | 69 | parsedBaseURL, err := verifyBaseURL(config.BaseURL) 70 | if err != nil { 71 | return nil, err 72 | } 73 | 74 | auth := &auth{ 75 | imsClientID: config.ClientID, 76 | imsOrgID: config.OrgID, 77 | imsAccessToken: config.AccessToken, 78 | companyID: config.CompanyID, 79 | } 80 | err = verifyAuth(auth) 81 | if err != nil { 82 | return nil, err 83 | } 84 | 85 | c := &Client{ 86 | httpClient: httpClient, 87 | baseURL: parsedBaseURL, 88 | auth: auth, 89 | } 90 | 91 | c.CalculatedMetrics = &CalculatedMetricsService{client: c} 92 | c.Collections = &CollectionsService{client: c} 93 | c.DateRanges = &DateRangesService{client: c} 94 | c.Dimensions = &DimensionsService{client: c} 95 | c.Metrics = &MetricsService{client: c} 96 | c.Reports = &ReportsService{client: c} 97 | c.Segments = &SegmentsService{client: c} 98 | c.Users = &UsersService{client: c} 99 | 100 | return c, nil 101 | } 102 | 103 | // verifyBaseURL verifies the specified URL 104 | func verifyBaseURL(baseURL string) (*url.URL, error) { 105 | if strings.HasSuffix(baseURL, "/") { 106 | baseURL = strings.TrimSuffix(baseURL, "/") 107 | } 108 | 109 | parsedBaseURL, err := url.Parse(baseURL) 110 | if err != nil { 111 | return nil, fmt.Errorf("malformed URL") 112 | } 113 | 114 | if parsedBaseURL.Scheme == "" { 115 | return nil, fmt.Errorf("missing URL scheme") 116 | } 117 | 118 | if parsedBaseURL.Host == "" { 119 | return nil, fmt.Errorf("missing URL host") 120 | } 121 | return parsedBaseURL, nil 122 | } 123 | 124 | // verifyAuth verifies the specified auth 125 | func verifyAuth(auth *auth) error { 126 | if auth.imsClientID == "" { 127 | return fmt.Errorf("missing ClientID") 128 | } 129 | if auth.imsAccessToken == "" { 130 | return fmt.Errorf("missing AccessToken") 131 | } 132 | if auth.imsOrgID == "" { 133 | return fmt.Errorf("missing OrgID") 134 | } 135 | if auth.companyID == "" { 136 | return fmt.Errorf("missing CompanyID") 137 | } 138 | return nil 139 | } 140 | 141 | // GetBaseURL returns the API base URL. 142 | func (client *Client) GetBaseURL() string { 143 | return client.baseURL.String() 144 | } 145 | 146 | // get is a convenience method to send an HTTP GET request 147 | func (client *Client) get(path string, params map[string]string, body io.Reader, model interface{}) error { 148 | return apiRequest(client, http.MethodGet, path, params, body, model) 149 | } 150 | 151 | // post is a convenience method to send an HTTP POST request 152 | func (client *Client) post(path string, params map[string]string, body io.Reader, model interface{}) error { 153 | return apiRequest(client, http.MethodPost, path, params, body, model) 154 | } 155 | 156 | // apiRequest does a HTTP request and unmarshals the response into the specified model. 157 | func apiRequest(client *Client, method, path string, params map[string]string, body io.Reader, model interface{}) error { 158 | resp, respErr := request(client, method, path, params, body) 159 | if respErr != nil { 160 | return respErr 161 | } 162 | defer resp.Body.Close() 163 | 164 | bodyStr, bodyErr := ioutil.ReadAll(resp.Body) 165 | if bodyErr != nil { 166 | return bodyErr 167 | } 168 | 169 | jsonErr := json.Unmarshal(bodyStr, &model) 170 | if jsonErr != nil { 171 | return jsonErr 172 | } 173 | 174 | return jsonErr 175 | } 176 | 177 | // request does a HTTP request with the specified client, method, path, params and body. 178 | func request(client *Client, method, path string, params map[string]string, body io.Reader) (*http.Response, error) { 179 | // ensure path has prefix 180 | if strings.HasPrefix(path, "/") { 181 | path = strings.TrimPrefix(path, "/") 182 | } 183 | 184 | // URL format is // 185 | // join // 186 | rel := &url.URL{Path: fmt.Sprintf("%s/%s/%s", client.baseURL.Path, client.auth.companyID, path)} 187 | u := client.baseURL.ResolveReference(rel) 188 | addParams(u, params) 189 | 190 | httpClient := client.httpClient 191 | 192 | req, err := http.NewRequest(method, u.String(), body) 193 | if err != nil { 194 | return nil, err 195 | } 196 | 197 | // Set required headers 198 | req.Header.Set("Accept", "application/json") 199 | req.Header.Set("Content-Type", "application/json") 200 | req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", client.auth.imsAccessToken)) 201 | req.Header.Set("x-api-key", client.auth.imsClientID) 202 | req.Header.Set("x-gw-ims-org-id", client.auth.imsOrgID) 203 | req.Header.Set("x-proxy-global-company-id", client.auth.companyID) 204 | 205 | res, err := httpClient.Do(req) 206 | if err != nil { 207 | return nil, err 208 | } 209 | 210 | err = checkResponse(res) 211 | if err != nil { 212 | // we return the response in case the caller wants to inspect it 213 | return res, err 214 | } 215 | 216 | return res, err 217 | } 218 | 219 | // addParams adds the specified params to the URL. 220 | func addParams(u *url.URL, params map[string]string) string { 221 | q, _ := url.ParseQuery(u.RawQuery) 222 | 223 | for k, v := range params { 224 | q.Add(k, v) 225 | } 226 | 227 | u.RawQuery = q.Encode() 228 | return u.String() 229 | } 230 | 231 | // checkResponse checks the API response for errors. 232 | // A response is considered an error if it has a status code outside the 200 range. 233 | // The caller is responsible to analyze the response body. 234 | func checkResponse(r *http.Response) error { 235 | if c := r.StatusCode; 200 <= c && c <= 299 { 236 | return nil 237 | } 238 | 239 | err := fmt.Errorf("received unexpected status code %d", r.StatusCode) 240 | return err 241 | } 242 | -------------------------------------------------------------------------------- /analytics/testdata/Reports.Run.json: -------------------------------------------------------------------------------- 1 | { 2 | "totalPages": 1, 3 | "firstPage": true, 4 | "lastPage": true, 5 | "numberOfElements": 68, 6 | "number": 0, 7 | "totalElements": 68, 8 | "columns": { 9 | "dimension": { 10 | "id": "variables/evar6", 11 | "type": "string" 12 | }, 13 | "columnIds": ["0"] 14 | }, 15 | "rows": [ 16 | { 17 | "itemId": "0", 18 | "value": "Unspecified", 19 | "data": [282548.0] 20 | }, 21 | { 22 | "itemId": "2450395", 23 | "value": "#", 24 | "data": [42.0] 25 | }, 26 | { 27 | "itemId": "10604929", 28 | "value": "ae81e66e59307f730a495efa@adobeorg", 29 | "data": [54.0] 30 | }, 31 | { 32 | "itemId": "30827442", 33 | "value": "2ca24cce5d35bd4a0a495cc7@adobeorg", 34 | "data": [281.0] 35 | }, 36 | { 37 | "itemId": "81141778", 38 | "value": "6a3df65a5832d31c0a495c35@adobeorg", 39 | "data": [230.0] 40 | }, 41 | { 42 | "itemId": "85221166", 43 | "value": "ea76ade95776d2ec7f000101@adobeorg", 44 | "data": [85.0] 45 | }, 46 | { 47 | "itemId": "262468684", 48 | "value": "ed8d65e655fac7797f000101@adobeorg", 49 | "data": [2.0] 50 | }, 51 | { 52 | "itemId": "326211842", 53 | "value": "a65235975cf982d50a495fa9@adobeorg", 54 | "data": [24.0] 55 | }, 56 | { 57 | "itemId": "360723141", 58 | "value": "aad53bc75245b4ba0a490d4d@adobeorg", 59 | "data": [2.0] 60 | }, 61 | { 62 | "itemId": "378387902", 63 | "value": "255b459a5d94570b0a495c24@adobeorg", 64 | "data": [5.0] 65 | }, 66 | { 67 | "itemId": "397498255", 68 | "value": "f84634a45cef97930a495e4c@adobeorg", 69 | "data": [201.0] 70 | }, 71 | { 72 | "itemId": "529954830", 73 | "value": "84681d9f5c019be70a495e92@adobeorg", 74 | "data": [460.0] 75 | }, 76 | { 77 | "itemId": "560474860", 78 | "value": "9355f0cc5405d58c0a4c98a1@adobeorg", 79 | "data": [303.0] 80 | }, 81 | { 82 | "itemId": "561392978", 83 | "value": "b4ab28ae56d80caf7f000101@adobeorg", 84 | "data": [2.0] 85 | }, 86 | { 87 | "itemId": "719219534", 88 | "value": "907136ed5d35cbf50a495cd4@adobeorg", 89 | "data": [1.0] 90 | }, 91 | { 92 | "itemId": "834929436", 93 | "value": "acb31c8b53308d980a490d4d@adobeorg", 94 | "data": [342.0] 95 | }, 96 | { 97 | "itemId": "841390801", 98 | "value": "8d3e67c25245aeb70a490d4c@adobeorg", 99 | "data": [32.0] 100 | }, 101 | { 102 | "itemId": "987689894", 103 | "value": "261f337b54e73b180a4c98a1@adobeorg", 104 | "data": [452.0] 105 | }, 106 | { 107 | "itemId": "1012538123", 108 | "value": "a8833bc75245af9e0a490d4d@adobeorg", 109 | "data": [414.0] 110 | }, 111 | { 112 | "itemId": "1015964931", 113 | "value": "d58d123f524457240a490d45@adobeorg", 114 | "data": [3.0] 115 | }, 116 | { 117 | "itemId": "1039447821", 118 | "value": "12d7329f5bfd792e0a494004@adobeorg", 119 | "data": [125.0] 120 | }, 121 | { 122 | "itemId": "1104445885", 123 | "value": "ed3301ac512d2a290a490d4c@adobeorg", 124 | "data": [83.0] 125 | }, 126 | { 127 | "itemId": "1137780513", 128 | "value": "f460562c5c7d0db10a495eb6@adobeorg", 129 | "data": [1349.0] 130 | }, 131 | { 132 | "itemId": "1195160195", 133 | "value": "none:anonymous", 134 | "data": [1.0] 135 | }, 136 | { 137 | "itemId": "1290285334", 138 | "value": "b28447b65d88a19f0a495c33@adobeorg", 139 | "data": [7.0] 140 | }, 141 | { 142 | "itemId": "1353455879", 143 | "value": "na", 144 | "data": [3503.0] 145 | }, 146 | { 147 | "itemId": "1398704355", 148 | "value": "2ccee7b4564203767f000101@adobeorg", 149 | "data": [10.0] 150 | }, 151 | { 152 | "itemId": "1408446848", 153 | "value": "99911cfe5329657b0a490d45@adobeorg", 154 | "data": [58.0] 155 | }, 156 | { 157 | "itemId": "1484059963", 158 | "value": "76b329395df155d60a495e2c@adobeorg", 159 | "data": [101.0] 160 | }, 161 | { 162 | "itemId": "1558480757", 163 | "value": "0b5c4f9a5192bca70a490d4d@adobeorg", 164 | "data": [39.0] 165 | }, 166 | { 167 | "itemId": "1762544207", 168 | "value": "908936ed5d35cc220a495cd4@adobeorg", 169 | "data": [3.0] 170 | }, 171 | { 172 | "itemId": "1848661081", 173 | "value": "598f4bab5e20f1220a495fd8@adobeorg", 174 | "data": [302.0] 175 | }, 176 | { 177 | "itemId": "2136544452", 178 | "value": "direct:", 179 | "data": [53797.0] 180 | }, 181 | { 182 | "itemId": "2173412750", 183 | "value": "93be1c8b532956910a490d4d@adobeorg", 184 | "data": [36.0] 185 | }, 186 | { 187 | "itemId": "2331940577", 188 | "value": "e6d328ae56cdaf037f000101@adobeorg", 189 | "data": [1.0] 190 | }, 191 | { 192 | "itemId": "2408296724", 193 | "value": "9901e79d5926a32a0a495c9e@adobeorg", 194 | "data": [10.0] 195 | }, 196 | { 197 | "itemId": "2410829348", 198 | "value": "5400f0f555954e8c7f000101@adobeorg", 199 | "data": [71.0] 200 | }, 201 | { 202 | "itemId": "2438083683", 203 | "value": ":", 204 | "data": [178600.0] 205 | }, 206 | { 207 | "itemId": "2653772644", 208 | "value": "d40c28b35e442ae60a495f8c@adobeorg", 209 | "data": [13.0] 210 | }, 211 | { 212 | "itemId": "2727029419", 213 | "value": "6c2b3d5b5a4df5e60a495c11@adobeorg", 214 | "data": [46.0] 215 | }, 216 | { 217 | "itemId": "2807748926", 218 | "value": "4113f97c594104260a495ec0@adobeorg", 219 | "data": [7.0] 220 | }, 221 | { 222 | "itemId": "2811207931", 223 | "value": "28f63a295c73dc210a495ea4@adobeorg", 224 | "data": [514.0] 225 | }, 226 | { 227 | "itemId": "2823961605", 228 | "value": "e7e7a0335576c5bc7f000101@adobeorg", 229 | "data": [19.0] 230 | }, 231 | { 232 | "itemId": "2958932139", 233 | "value": "e6d235355cf7c1de0a495eec@adobeorg", 234 | "data": [410.0] 235 | }, 236 | { 237 | "itemId": "2975376654", 238 | "value": "8d4667c25245aec10a490d4c@adobeorg", 239 | "data": [170.0] 240 | }, 241 | { 242 | "itemId": "3047914312", 243 | "value": "af172ddd5e444feb0a495e5a@adobeorg", 244 | "data": [66.0] 245 | }, 246 | { 247 | "itemId": "3089202674", 248 | "value": "01a7b808561e59f17f000101@adobeorg", 249 | "data": [6.0] 250 | }, 251 | { 252 | "itemId": "3127400877", 253 | "value": "dbad55eb5dd571680a495ffc@adobeorg", 254 | "data": [44.0] 255 | }, 256 | { 257 | "itemId": "3134094529", 258 | "value": "9d0725c05e44fe1a0a49411c@adobeorg", 259 | "data": [18.0] 260 | }, 261 | { 262 | "itemId": "3180456614", 263 | "value": "ims:df469dd055390ee80a4c98a7@adobeorg", 264 | "data": [63.0] 265 | }, 266 | { 267 | "itemId": "3183097394", 268 | "value": "ba1d57835d5414860a495e83@adobeorg", 269 | "data": [118.0] 270 | }, 271 | { 272 | "itemId": "3198814921", 273 | "value": "9644ad4e5628b1ed7f000101@adobeorg", 274 | "data": [103.0] 275 | }, 276 | { 277 | "itemId": "3203154472", 278 | "value": "812b47145dc5a2450a495c14@adobeorg", 279 | "data": [973.0] 280 | }, 281 | { 282 | "itemId": "3252049490", 283 | "value": "8e6307bf5d775fa00a495ef9@adobeorg", 284 | "data": [1.0] 285 | }, 286 | { 287 | "itemId": "3597459021", 288 | "value": "ebf146125d8dc0f60a495e21", 289 | "data": [1992.0] 290 | }, 291 | { 292 | "itemId": "3631924926", 293 | "value": "5b5243a15589607e7f000101@adobeorg", 294 | "data": [7.0] 295 | }, 296 | { 297 | "itemId": "3638582957", 298 | "value": "cc46541b5e4466f30a495fce@adobeorg", 299 | "data": [2.0] 300 | }, 301 | { 302 | "itemId": "3657701607", 303 | "value": "9075a2b154de8af80a4c98a7@adobeorg", 304 | "data": [114.0] 305 | }, 306 | { 307 | "itemId": "3672496546", 308 | "value": "5e16123f5245b2970a490d45@adobeorg", 309 | "data": [7.0] 310 | }, 311 | { 312 | "itemId": "3724171465", 313 | "value": "ims:fc80403d53c3ed6c0a490d4c@adobeorg", 314 | "data": [133.0] 315 | }, 316 | { 317 | "itemId": "3775848419", 318 | "value": "ims:5a9f45ff5d88b5600a494034@adobeorg", 319 | "data": [3.0] 320 | }, 321 | { 322 | "itemId": "3834491570", 323 | "value": "972d35f65a8d3c500a495ede@adobeorg", 324 | "data": [3.0] 325 | }, 326 | { 327 | "itemId": "3861529038", 328 | "value": "0899356c53e9ce880a490d44@adobeorg", 329 | "data": [117.0] 330 | }, 331 | { 332 | "itemId": "3894130929", 333 | "value": "e34b42a45e442d260a495fd2@adobeorg", 334 | "data": [13.0] 335 | }, 336 | { 337 | "itemId": "3919810190", 338 | "value": "5e35755f5b7c1b910a495c46@adobeorg", 339 | "data": [521.0] 340 | }, 341 | { 342 | "itemId": "4027824762", 343 | "value": "a7ce15785d6e8b2c0a495fe7@adobeorg", 344 | "data": [813.0] 345 | }, 346 | { 347 | "itemId": "4230246519", 348 | "value": "827443115b7c135d0a495e78@adobeorg", 349 | "data": [3.0] 350 | }, 351 | { 352 | "itemId": "4266005747", 353 | "value": "ea7f7dbc5776b93c7f000101@adobeorg", 354 | "data": [336.0] 355 | } 356 | ], 357 | "summaryData": { 358 | "filteredTotals": [530214.0], 359 | "totals": [530214.0] 360 | } 361 | } 362 | -------------------------------------------------------------------------------- /analytics/reports-types.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Adobe. All rights reserved. 3 | This file is licensed to you under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. You may obtain a copy 5 | of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software distributed under 7 | the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS 8 | OF ANY KIND, either express or implied. See the License for the specific language 9 | governing permissions and limitations under the License. 10 | */ 11 | 12 | package analytics 13 | 14 | // Request types 15 | 16 | // RankedRequestLocale represents a ranked request locale 17 | type RankedRequestLocale struct { 18 | Language string `json:"language,omitempty"` 19 | Script string `json:"script,omitempty"` 20 | Country string `json:"country,omitempty"` 21 | Variant string `json:"variant,omitempty"` 22 | ExtensionKey []string `json:"extensionKeys,omitempty"` 23 | UnicodeLocaleAttributes []string `json:"unicodeLocaleAttributes,omitempty"` 24 | UnicodeLocaleKeys []string `json:"unicodeLocaleKeys,omitempty"` 25 | Iso3Language string `json:"iso3Language,omitempty"` 26 | Iso3Country string `json:"iso3Country,omitempty"` 27 | DisplayLanguage string `json:"displayLanguage,omitempty"` 28 | DisplayScript string `json:"displayScript,omitempty"` 29 | DisplayCountry string `json:"displayCountry,omitempty"` 30 | DisplayVariant string `json:"displayVariant,omitempty"` 31 | DisplayName string `json:"displayName,omitempty"` 32 | } 33 | 34 | // RankedRequestReportFilter represents a ranked request report filter 35 | type RankedRequestReportFilter struct { 36 | ID string `json:"id,omitempty"` 37 | Type string `json:"type,omitempty"` 38 | Dimension string `json:"dimension,omitempty"` 39 | ItemID string `json:"itemId,omitempty"` 40 | ItemIDs []string `json:"itemIds,omitempty"` 41 | SegmentID string `json:"segmentId,omitempty"` 42 | SegmentDefinition *RankedRequestSegmentDefinition `json:"segmentDefinition,omitempty"` 43 | DateRange string `json:"dateRange,omitempty"` 44 | ExcludeItemIDs []string `json:"excludeItemIds,omitempty"` 45 | } 46 | 47 | // RankedRequestSegmentDefinition represents a segment definition 48 | type RankedRequestSegmentDefinition struct { 49 | Container *RankedRequestSegmentDefinitionContainer `json:"container,omitempty"` 50 | Func string `json:"func,omitempty"` 51 | Version []int `json:"version,omitempty"` 52 | } 53 | 54 | // RankedRequestSegmentDefinitionContainer represents a segment definition container 55 | type RankedRequestSegmentDefinitionContainer struct { 56 | Func string `json:"func,omitempty"` 57 | Context string `json:"context,omitempty"` 58 | Pred *RankedRequestSegmentDefinitionPredicate `json:"pred,omitempty"` 59 | } 60 | 61 | // RankedRequestSegmentDefinitionPredicate represents a segment definition predicate 62 | type RankedRequestSegmentDefinitionPredicate struct { 63 | Func string `json:"func,omitempty"` 64 | Val *RankedRequestSegmentDefinitionPredicateValue `json:"val,omitempty"` 65 | Description string `json:"description,omitempty"` 66 | } 67 | 68 | // RankedRequestSegmentDefinitionPredicateValue represents a segment definition predicate value 69 | type RankedRequestSegmentDefinitionPredicateValue struct { 70 | Func string `json:"func,omitempty"` 71 | Name string `json:"name,omitempty"` 72 | } 73 | 74 | // RankedRequestSearch represents a request search 75 | type RankedRequestSearch struct { 76 | Clause string `json:"clause,omitempty"` 77 | ExcludeItemIDs []string `json:"excludeItemIds,omitempty"` 78 | ItemIDs []string `json:"itemIds,omitempty"` 79 | IncludeSearchTotal bool `json:"includeSearchTotal"` 80 | Empty bool `json:"empty"` 81 | } 82 | 83 | // RankedRequestStatistics represents the request statistics 84 | type RankedRequestStatistics struct { 85 | Functions []string `json:"functions,omitempty"` 86 | IngoreZeroes bool `json:"ignoreZeroes"` 87 | } 88 | 89 | // RankedRequestSettings represents the request settings 90 | type RankedRequestSettings struct { 91 | Limit int `json:"limit"` 92 | Page int `json:"page"` 93 | DimensionSort string `json:"dimensionSort,omitempty"` 94 | CountRepeatInstances bool `json:"countRepeatInstances,omitempty"` 95 | ReflectRequest bool `json:"reflectRequest,omitempty"` 96 | IncludeAnomalyDetection bool `json:"includeAnomalyDetection,omitempty"` 97 | IncludePercentChange bool `json:"includePercentChange,omitempty"` 98 | IncludeLatLong bool `json:"includeLatLong,omitempty"` 99 | NonesBehavior string `json:"nonesBehavior,omitempty"` 100 | } 101 | 102 | // RankedRequestReportMetrics represents the report metrics 103 | type RankedRequestReportMetrics struct { 104 | MetricFilters *[]RankedRequestReportFilter `json:"metricFilters,omitempty"` 105 | Metrics *[]RankedRequestReportMetric `json:"metrics,omitempty"` 106 | } 107 | 108 | // RankedRequestReportMetric represents a report metric 109 | type RankedRequestReportMetric struct { 110 | ID string `json:"id,omitempty"` 111 | ColumnID string `json:"columnId,omitempty"` 112 | Filters []string `json:"filters,omitempty"` 113 | Sort string `json:"sort,omitempty"` 114 | MetricDefinition string `json:"metricDefinition,omitempty"` 115 | Predictive *RankedRequestReportMetricPredictiveSettings `json:"predictive,omitempty"` 116 | } 117 | 118 | // RankedRequestReportMetricPredictiveSettings represents metric predictive settings 119 | type RankedRequestReportMetricPredictiveSettings struct { 120 | AnomalyConfidence float64 `json:"anomalyConfidence,omitempty"` 121 | } 122 | 123 | // RankedRequestReportRows represents report rows 124 | type RankedRequestReportRows struct { 125 | RowFilters []RankedRequestReportFilter `json:"rowFilters,omitempty"` 126 | Rows []RankedRequestReportRow `json:"rows,omitempty"` 127 | } 128 | 129 | // RankedRequestReportRow represents a report row 130 | type RankedRequestReportRow struct { 131 | RowID string `json:"rowId,omitempty"` 132 | Filters []string `json:"filters,omitempty"` 133 | } 134 | 135 | // RankedRequest represents a ranked report request 136 | type RankedRequest struct { 137 | ReportSuiteID string `json:"rsid"` 138 | Dimension string `json:"dimension"` 139 | Locale *RankedRequestLocale `json:"locale,omitempty"` 140 | GlobalFilters *[]RankedRequestReportFilter `json:"globalFilters,omitempty"` 141 | Search *RankedRequestSearch `json:"search,omitempty"` 142 | Settings *RankedRequestSettings `json:"settings,omitempty"` 143 | Statistics *RankedRequestStatistics `json:"statistics,omitempty"` 144 | MetricContainer *RankedRequestReportMetrics `json:"metricContainer,omitempty"` 145 | RowContainer *RankedRequestReportRows `json:"rowContainer,omitempty"` 146 | AnchorDate string `json:"anchorDate,omitempty"` 147 | } 148 | 149 | // Response types 150 | 151 | // RankedReportDimension represents the report dimension 152 | type RankedReportDimension struct { 153 | ID string `json:"id,omitempty"` 154 | Type string `json:"type,omitempty"` 155 | } 156 | 157 | // RankedReportColumnError represents the report column error 158 | type RankedReportColumnError struct { 159 | ColumnID string `json:"columnId,omitempty"` 160 | ErrorCode string `json:"errorCode,omitempty"` 161 | ErrorID string `json:"errorId,omitempty"` 162 | ErrorDescription string `json:"errorDescription,omitempty"` 163 | } 164 | 165 | // RankedReportColumnMetaData represents the report column meta data 166 | type RankedReportColumnMetaData struct { 167 | Dimension *RankedReportDimension `json:"dimension,omitempty"` 168 | ColumnIDs []string `json:"columnIds,omitempty"` 169 | ColumnErrors *[]RankedReportColumnError `json:"columnErrors,omitempty"` 170 | } 171 | 172 | // RankedReportRowData represents the report row data 173 | type RankedReportRowData struct { 174 | ItemID string `json:"itemId,omitempty"` 175 | Value string `json:"value,omitempty"` 176 | RowID string `json:"rowId,omitempty"` 177 | Data []float32 `json:"data,omitempty"` 178 | DataExpected []float32 `json:"dataExpected,omitempty"` 179 | DataUpperBound []float32 `json:"dataUpperBound,omitempty"` 180 | DataLowerBound []float32 `json:"dataLowerBound,omitempty"` 181 | DataAnomalyDetected bool `json:"dataAnomalyDetected"` 182 | PercentChange []float32 `json:"percentChange,omitempty"` 183 | Latitude float32 `json:"latitude,omitempty"` 184 | Longitude float32 `json:"longitude,omitempty"` 185 | } 186 | 187 | // RankedReportSummaryData represents the report summary data 188 | type RankedReportSummaryData struct { 189 | *RankedReportSummaryDataTotals `json:"summaryData,omitempty"` 190 | } 191 | 192 | // RankedReportSummaryDataTotals represents the report summary data totals 193 | type RankedReportSummaryDataTotals struct { 194 | FilteredTotals []int `json:"filteredTotals,omitempty"` 195 | Totals []int `json:"totals,omitempty"` 196 | } 197 | 198 | // RankedReportData represents the report data 199 | type RankedReportData struct { 200 | TotalPages int `json:"totalPages,omitempty"` 201 | FirstPage bool `json:"firstPage"` 202 | LastPage bool `json:"lastPage"` 203 | NumberOfElements int `json:"numberOfElements,omitempty"` 204 | Number int `json:"number,omitempty"` 205 | TotalElements int `json:"totalElements"` 206 | Message string `json:"message,omitempty"` 207 | Request *RankedRequest `json:"request,omitempty"` 208 | Columns *RankedReportColumnMetaData `json:"columns,omitempty"` 209 | Rows *[]RankedReportRowData `json:"rows,omitempty"` 210 | SummaryData *RankedReportSummaryData `json:"summaryData,omitempty"` 211 | } 212 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2020 Adobe 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------