diff --git a/.azure-pipelines/buildAndPackage.yml b/.azure-pipelines/buildAndPackage.yml
deleted file mode 100644
index 417ca04dd..000000000
--- a/.azure-pipelines/buildAndPackage.yml
+++ /dev/null
@@ -1,105 +0,0 @@
-#Copyright (c) Microsoft Corporation. All rights reserved.
-#Licensed under the MIT License.
-#Building and packaging the artifacts of the Java-Core libraries using the build.gradle file.
-#Ready the package for deployment and release.
-
-trigger:
- branches:
- include:
- - dev
- - main
- - master
- paths:
- include:
- - src/*
- exclude:
- - .gitignore
- - CONTRIBUTING.md
- - LICENSE
- - THIRD PARTY NOTICES
- - build.gradle
- - gradle.properties
- - gradlew
- - gradlew.bat
- - readme.md
- - settings.gradle
- - Scripts/*
-
-pr: none
-
-pool:
- vmImage: 'windows-latest'
-
-steps:
-- checkout: self
- clean: true
- fetchDepth: 1
-
-- task: securedevelopmentteam.vss-secure-development-tools.build-task-credscan.CredScan@2
- displayName: 'Run CredScan'
- inputs:
- debugMode: false
-
-- task: DownloadSecureFile@1
- inputs:
- secureFile: 'local.properties'
-
-- task: DownloadSecureFile@1
- inputs:
- secureFile: 'secring.gpg'
-
-- task: DownloadSecureFile@1
- inputs:
- secureFile: 'secring.gpg.lock'
-
-- task: CopyFiles@2
- inputs:
- SourceFolder: '$(Agent.TempDirectory)'
- Contents: '**'
- TargetFolder: '$(System.DefaultWorkingDirectory)'
-
-- task: Gradle@2
- inputs:
- gradleWrapperFile: 'gradlew'
- tasks: 'build'
- publishJUnitResults: true
- testResultsFiles: '**/TEST-*.xml'
- javaHomeOption: 'JDKVersion'
- sonarQubeRunAnalysis: false
-
-- task: PublishBuildArtifacts@1
- displayName: 'Publish Artifact: drop'
- inputs:
- PathtoPublish: '$(Build.ArtifactStagingDirectory)'
-
-- task: CopyFiles@2
- inputs:
- SourceFolder: '$(System.DefaultWorkingDirectory)'
- Contents: |
- **/libs/*
- build/generated-pom.xml
- build/generated-pom.xml.asc
- build.gradle
- gradlew
- gradlew.bat
- settings.gradle
- gradle.properties
- **/gradle/wrapper/*
- Scripts/getLatestVersion.ps1
- TargetFolder: '$(Build.ArtifactStagingDirectory)/'
-
-- task: PublishBuildArtifacts@1
- inputs:
- PathtoPublish: '$(Build.ArtifactStagingDirectory)'
- ArtifactName: 'drop'
- publishLocation: 'Container'
-
-- task: YodLabs.O365PostMessage.O365PostMessageBuild.O365PostMessageBuild@0
- displayName: 'Graph Client Tooling pipeline fail notification'
- inputs:
- addressType: serviceEndpoint
- serviceEndpointName: 'microsoftgraph pipeline status'
- title: '$(Build.DefinitionName) failure notification'
- text: 'This pipeline has failed. View the build details for further information. This is a blocking failure.'
- condition: and(failed(), ne(variables['Build.Reason'], 'Manual'))
- enabled: true
diff --git a/.azure-pipelines/prValidate.yml b/.azure-pipelines/prValidate.yml
deleted file mode 100644
index 64bbf4c0b..000000000
--- a/.azure-pipelines/prValidate.yml
+++ /dev/null
@@ -1,70 +0,0 @@
-#Copyright (c) Microsoft Corporation. All rights reserved.
-#Licensed under the MIT License.
-#Build and test Java Core to make sure a valid pull request is being made
-#Validate that the versions dont conflict with those online in case a pull request is made to main or master
-
-pr:
- branches:
- include:
- - master
- - main
- - dev
- paths:
- exclude:
- - .azure-pipelines
- - .gradle/wrapper
- - .gitignore
- - CONTRIBUTING.md
- - LICENSE
- - THIRD PARTY NOTICES
- - gradle.properties
- - gradlew
- - gradlew.bat
- - readme.md
- - settings.gradle
- - Scripts/*
-
-trigger: none # disable triggers based on commits.
-
-variables:
- PACKAGE_NAME: 'microsoft-graph-core'
- PROPERTIES_PATH: '.\gradle.properties'
-
-pool:
- vmImage: windows-latest
-
-steps:
-- checkout: self
- clean: true
- fetchDepth: 1
-
-- task: securedevelopmentteam.vss-secure-development-tools.build-task-credscan.CredScan@2
- displayName: 'Run CredScan'
- inputs:
- debugMode: false
-
-- task: PowerShell@2
- condition: and(failed(), eq(variables['Build.SourceBranchName'], 'dev'))
- inputs:
- filePath: '$(System.DefaultWorkingDirectory)\Scripts\validateMavenVersion.ps1'
- pwsh: true
- arguments: '-packageName "$(PACKAGE_NAME)" -propertiesPath "$(PROPERTIES_PATH)"'
-
-- task: Gradle@2
- inputs:
- gradleWrapperFile: 'gradlew'
- tasks: 'build'
- publishJUnitResults: true
- testResultsFiles: '**/TEST-*.xml'
- javaHomeOption: 'JDKVersion'
- sonarQubeRunAnalysis: false
-
-- task: YodLabs.O365PostMessage.O365PostMessageBuild.O365PostMessageBuild@0
- displayName: 'Graph Client Tooling pipeline fail notification'
- inputs:
- addressType: serviceEndpoint
- serviceEndpointName: 'microsoftgraph pipeline status'
- title: '$(Build.DefinitionName) failure notification'
- text: 'This pipeline has failed. View the build details for further information. This is a blocking failure. '
- condition: and(failed(), ne(variables['Build.Reason'], 'Manual'))
- enabled: true
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 000000000..3bf6f4f2b
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,5 @@
+[*.java]
+indent_style = space
+indent_size = 4
+insert_final_newline = true
+trim_trailing_whitespace = true
\ No newline at end of file
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
new file mode 100644
index 000000000..451f5cccf
--- /dev/null
+++ b/.github/CODEOWNERS
@@ -0,0 +1 @@
+* @baywet @ddyett @MichaelMainer @nikithauc @zengin @ramsessanchez @andrueastman
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 767270516..96bdbee06 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -5,8 +5,28 @@ updates:
schedule:
interval: daily
open-pull-requests-limit: 10
+- package-ecosystem: gradle
+ directory: "/android"
+ schedule:
+ interval: daily
+ open-pull-requests-limit: 10
+- package-ecosystem: gradle
+ directory: "/samples/deviceCodeSample"
+ schedule:
+ interval: daily
+ open-pull-requests-limit: 10
+- package-ecosystem: gradle
+ directory: "/samples/interactiveBrowserSample"
+ schedule:
+ interval: daily
+ open-pull-requests-limit: 10
- package-ecosystem: maven
directory: "/"
schedule:
interval: daily
open-pull-requests-limit: 10
+- package-ecosystem: github-actions
+ directory: "/"
+ schedule:
+ interval: daily
+ open-pull-requests-limit: 10
diff --git a/.github/fabricbot.json b/.github/fabricbot.json
new file mode 100644
index 000000000..feaa1c0eb
--- /dev/null
+++ b/.github/fabricbot.json
@@ -0,0 +1,628 @@
+{
+ "version": "1.0",
+ "tasks": [
+ {
+ "taskType": "trigger",
+ "capabilityId": "IssueResponder",
+ "subCapability": "IssuesOnlyResponder",
+ "version": "1.0",
+ "config": {
+ "taskName": "Add ToTriage label to new issues",
+ "conditions": {
+ "operator": "and",
+ "operands": [
+ {
+ "name": "isAction",
+ "parameters": {
+ "action": "opened"
+ }
+ },
+ {
+ "operator": "not",
+ "operands": [
+ {
+ "name": "isPartOfProject",
+ "parameters": {}
+ }
+ ]
+ },
+ {
+ "operator": "not",
+ "operands": [
+ {
+ "name": "isAssignedToSomeone",
+ "parameters": {}
+ }
+ ]
+ }
+ ]
+ },
+ "actions": [
+ {
+ "name": "addLabel",
+ "parameters": {
+ "label": "ToTriage"
+ }
+ },
+ {
+ "name": "addToProject",
+ "parameters": {
+ "projectName": "Graph SDK - Triage",
+ "columnName": "Issues to triage",
+ "isOrgProject": true
+ }
+ }
+ ],
+ "eventType": "issue",
+ "eventNames": [
+ "issues",
+ "project_card"
+ ]
+ },
+ "id": "3S9VQGzLh"
+ },
+ {
+ "taskType": "trigger",
+ "capabilityId": "IssueResponder",
+ "subCapability": "IssueCommentResponder",
+ "version": "1.0",
+ "config": {
+ "taskName": "Replace needs author feedback label with needs attention label when the author comments on an issue",
+ "conditions": {
+ "operator": "and",
+ "operands": [
+ {
+ "name": "isAction",
+ "parameters": {
+ "action": "created"
+ }
+ },
+ {
+ "name": "isActivitySender",
+ "parameters": {
+ "user": {
+ "type": "author"
+ }
+ }
+ },
+ {
+ "name": "hasLabel",
+ "parameters": {
+ "label": "Needs: author feedback"
+ }
+ }
+ ]
+ },
+ "actions": [
+ {
+ "name": "addLabel",
+ "parameters": {
+ "label": "needs attention"
+ }
+ },
+ {
+ "name": "removeLabel",
+ "parameters": {
+ "label": "Needs: author feedback"
+ }
+ }
+ ],
+ "eventType": "issue",
+ "eventNames": [
+ "issue_comment"
+ ]
+ },
+ "id": "kPh1SPQ4vv"
+ },
+ {
+ "taskType": "trigger",
+ "capabilityId": "IssueResponder",
+ "subCapability": "IssuesOnlyResponder",
+ "version": "1.0",
+ "config": {
+ "taskName": "Remove no recent activity label from issues",
+ "conditions": {
+ "operator": "and",
+ "operands": [
+ {
+ "operator": "not",
+ "operands": [
+ {
+ "name": "isAction",
+ "parameters": {
+ "action": "closed"
+ }
+ }
+ ]
+ },
+ {
+ "name": "hasLabel",
+ "parameters": {
+ "label": "no-recent-activity"
+ }
+ }
+ ]
+ },
+ "actions": [
+ {
+ "name": "removeLabel",
+ "parameters": {
+ "label": "no-recent-activity"
+ }
+ }
+ ],
+ "eventType": "issue",
+ "eventNames": [
+ "issues",
+ "project_card"
+ ]
+ },
+ "id": "7wrWTLPtQO"
+ },
+ {
+ "taskType": "scheduled",
+ "capabilityId": "ScheduledSearch",
+ "subCapability": "ScheduledSearch",
+ "version": "1.1",
+ "config": {
+ "taskName": "Close stale issues",
+ "frequency": [
+ {
+ "weekDay": 0,
+ "hours": [
+ 1,
+ 7,
+ 13,
+ 19
+ ]
+ },
+ {
+ "weekDay": 1,
+ "hours": [
+ 1,
+ 7,
+ 13,
+ 19
+ ]
+ },
+ {
+ "weekDay": 2,
+ "hours": [
+ 1,
+ 7,
+ 13,
+ 19
+ ]
+ },
+ {
+ "weekDay": 3,
+ "hours": [
+ 1,
+ 7,
+ 13,
+ 19
+ ]
+ },
+ {
+ "weekDay": 4,
+ "hours": [
+ 1,
+ 7,
+ 13,
+ 19
+ ]
+ },
+ {
+ "weekDay": 5,
+ "hours": [
+ 1,
+ 7,
+ 13,
+ 19
+ ]
+ },
+ {
+ "weekDay": 6,
+ "hours": [
+ 1,
+ 7,
+ 13,
+ 19
+ ]
+ }
+ ],
+ "searchTerms": [
+ {
+ "name": "isIssue",
+ "parameters": {}
+ },
+ {
+ "name": "isOpen",
+ "parameters": {}
+ },
+ {
+ "name": "hasLabel",
+ "parameters": {
+ "label": "Needs: author feedback"
+ }
+ },
+ {
+ "name": "hasLabel",
+ "parameters": {
+ "label": "no-recent-activity"
+ }
+ },
+ {
+ "name": "noActivitySince",
+ "parameters": {
+ "days": 3
+ }
+ },
+ {
+ "name": "noLabel",
+ "parameters": {
+ "label": "service bug"
+ }
+ }
+ ],
+ "actions": [
+ {
+ "name": "closeIssue",
+ "parameters": {}
+ }
+ ]
+ },
+ "id": "LoWL0pmcGB",
+ "disabled": false
+ },
+ {
+ "taskType": "scheduled",
+ "capabilityId": "ScheduledSearch",
+ "subCapability": "ScheduledSearch",
+ "version": "1.1",
+ "config": {
+ "taskName": "Add no recent activity label to issues",
+ "frequency": [
+ {
+ "weekDay": 0,
+ "hours": [
+ 1,
+ 7,
+ 13,
+ 19
+ ]
+ },
+ {
+ "weekDay": 1,
+ "hours": [
+ 1,
+ 7,
+ 13,
+ 19
+ ]
+ },
+ {
+ "weekDay": 2,
+ "hours": [
+ 1,
+ 7,
+ 13,
+ 19
+ ]
+ },
+ {
+ "weekDay": 3,
+ "hours": [
+ 1,
+ 7,
+ 13,
+ 19
+ ]
+ },
+ {
+ "weekDay": 4,
+ "hours": [
+ 1,
+ 7,
+ 13,
+ 19
+ ]
+ },
+ {
+ "weekDay": 5,
+ "hours": [
+ 1,
+ 7,
+ 13,
+ 19
+ ]
+ },
+ {
+ "weekDay": 6,
+ "hours": [
+ 1,
+ 7,
+ 13,
+ 19
+ ]
+ }
+ ],
+ "searchTerms": [
+ {
+ "name": "isIssue",
+ "parameters": {}
+ },
+ {
+ "name": "isOpen",
+ "parameters": {}
+ },
+ {
+ "name": "hasLabel",
+ "parameters": {
+ "label": "Needs: author feedback"
+ }
+ },
+ {
+ "name": "noActivitySince",
+ "parameters": {
+ "days": 4
+ }
+ },
+ {
+ "name": "noLabel",
+ "parameters": {
+ "label": "no-recent-activity"
+ }
+ }
+ ],
+ "actions": [
+ {
+ "name": "addLabel",
+ "parameters": {
+ "label": "no-recent-activity"
+ }
+ },
+ {
+ "name": "addReply",
+ "parameters": {
+ "comment": "This issue has been automatically marked as stale because it has been marked as requiring author feedback but has not had any activity for **4 days**. It will be closed if no further activity occurs **within 3 days of this comment**."
+ }
+ }
+ ]
+ },
+ "id": "rxcR2jZDQd",
+ "disabled": false
+ },
+ {
+ "taskType": "scheduled",
+ "capabilityId": "ScheduledSearch",
+ "subCapability": "ScheduledSearch",
+ "version": "1.1",
+ "config": {
+ "taskName": "Close duplicate issues",
+ "frequency": [
+ {
+ "weekDay": 0,
+ "hours": [
+ 1,
+ 7,
+ 13,
+ 19
+ ]
+ },
+ {
+ "weekDay": 1,
+ "hours": [
+ 1,
+ 7,
+ 13,
+ 19
+ ]
+ },
+ {
+ "weekDay": 2,
+ "hours": [
+ 1,
+ 7,
+ 13,
+ 19
+ ]
+ },
+ {
+ "weekDay": 3,
+ "hours": [
+ 1,
+ 7,
+ 13,
+ 19
+ ]
+ },
+ {
+ "weekDay": 4,
+ "hours": [
+ 1,
+ 7,
+ 13,
+ 19
+ ]
+ },
+ {
+ "weekDay": 5,
+ "hours": [
+ 1,
+ 7,
+ 13,
+ 19
+ ]
+ },
+ {
+ "weekDay": 6,
+ "hours": [
+ 1,
+ 7,
+ 13,
+ 19
+ ]
+ }
+ ],
+ "searchTerms": [
+ {
+ "name": "isIssue",
+ "parameters": {}
+ },
+ {
+ "name": "isOpen",
+ "parameters": {}
+ },
+ {
+ "name": "hasLabel",
+ "parameters": {
+ "label": "duplicate"
+ }
+ },
+ {
+ "name": "noActivitySince",
+ "parameters": {
+ "days": 1
+ }
+ }
+ ],
+ "actions": [
+ {
+ "name": "addReply",
+ "parameters": {
+ "comment": "This issue has been marked as duplicate and has not had any activity for **1 day**. It will be closed for housekeeping purposes."
+ }
+ },
+ {
+ "name": "closeIssue",
+ "parameters": {}
+ }
+ ]
+ },
+ "id": "q9_57QI7gW",
+ "disabled": false
+ },
+ {
+ "taskType": "trigger",
+ "capabilityId": "IssueResponder",
+ "subCapability": "IssuesOnlyResponder",
+ "version": "1.0",
+ "config": {
+ "eventType": "issue",
+ "eventNames": [
+ "issues",
+ "project_card"
+ ],
+ "conditions": {
+ "operator": "and",
+ "operands": [
+ {
+ "name": "labelAdded",
+ "parameters": {
+ "label": "service bug"
+ }
+ }
+ ]
+ },
+ "taskName": "Add service issue to Service Issues project",
+ "actions": [
+ {
+ "name": "addToProject",
+ "parameters": {
+ "projectName": "Service issues",
+ "columnName": "Issues to triage",
+ "isOrgProject": true
+ }
+ }
+ ]
+ },
+ "id": "0LMKPZRJfy"
+ },
+ {
+ "taskType": "trigger",
+ "capabilityId": "IssueResponder",
+ "subCapability": "IssueCommentResponder",
+ "version": "1.0",
+ "config": {
+ "conditions": {
+ "operator": "and",
+ "operands": [
+ {
+ "name": "activitySenderHasAssociation",
+ "parameters": {
+ "permissions": "write",
+ "association": "CONTRIBUTOR"
+ }
+ },
+ {
+ "name": "bodyContains",
+ "parameters": {
+ "bodyPattern": "?"
+ }
+ },
+ {
+ "name": "bodyContains",
+ "parameters": {
+ "bodyPattern": "@"
+ }
+ }
+ ]
+ },
+ "eventType": "issue",
+ "eventNames": [
+ "issue_comment"
+ ],
+ "taskName": "Set 'Needs Feedback' if we respond with a question and at mention",
+ "actions": [
+ {
+ "name": "addLabel",
+ "parameters": {
+ "label": "Needs: author feedback"
+ }
+ }
+ ]
+ },
+ "id": "UdAXCHZzhq"
+ },
+ {
+ "taskType": "trigger",
+ "capabilityId": "IssueResponder",
+ "subCapability": "IssuesOnlyResponder",
+ "version": "1.0",
+ "config": {
+ "conditions": {
+ "operator": "or",
+ "operands": [
+ {
+ "name": "isAssignedToSomeone",
+ "parameters": {}
+ },
+ {
+ "name": "isAction",
+ "parameters": {
+ "action": "closed"
+ }
+ }
+ ]
+ },
+ "eventType": "issue",
+ "eventNames": [
+ "issues",
+ "project_card"
+ ],
+ "taskName": "Remove ToTriage when assigned or response provided",
+ "actions": [
+ {
+ "name": "removeLabel",
+ "parameters": {
+ "label": "ToTriage"
+ }
+ }
+ ]
+ },
+ "id": "MyMQeNnzO3"
+ }
+ ],
+ "userGroups": []
+}
diff --git a/.github/workflows/api-level-lint.yml b/.github/workflows/api-level-lint.yml
new file mode 100644
index 000000000..902e7347e
--- /dev/null
+++ b/.github/workflows/api-level-lint.yml
@@ -0,0 +1,38 @@
+name: "Checks the SDK only using APIs from the targeted API level"
+
+on:
+ workflow_dispatch:
+ push:
+ branches:
+ - dev
+ - master
+ pull_request:
+ branches:
+ - dev
+ - master
+
+jobs:
+ lint-api-level:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - uses: actions/setup-java@v3
+ with:
+ distribution: 'adopt'
+ java-version: 18
+ - name: Setup Android SDK
+ uses: android-actions/setup-android@v2.0.10
+ - name: Add execution right to the script
+ run: chmod +x gradlew
+ working-directory: ./android
+ - name: Build SDK with Android project configuration
+ id: lint
+ run: ./gradlew --no-daemon build
+ working-directory: ./android
+ - name: Upload linting results
+ if: failure() && steps.lint.outcome == 'failure'
+ uses: actions/upload-artifact@v3
+ with:
+ name: lint-report
+ path: ./android/build/reports
+
diff --git a/.github/workflows/build-and-publish.yml b/.github/workflows/build-and-publish.yml
new file mode 100644
index 000000000..767b486a6
--- /dev/null
+++ b/.github/workflows/build-and-publish.yml
@@ -0,0 +1,118 @@
+name: Build and Publish
+
+on:
+ push:
+ branches: [ dev , master, v3/longTermBranch ]
+ paths-ignore:
+ - '.gradle/wrapper'
+ - '.gitignore'
+ - 'LICENSE'
+ - 'THIRD PARTY NOTICES'
+ - '*.md'
+env:
+ PREVIEW_TASK: publishSnapshotPublicationToSonatypeSnapshotRepository
+ PUBLISH_TASK: publishMavenCentralReleasePublicationToSonatypeRepository
+
+jobs:
+ maven_Preview:
+ if: ${{ github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/v3/longTermBranch'}}
+ environment:
+ name: maven_central_snapshot
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - name: Easy detect-secrets
+ uses: RobertFischer/detect-secrets-action@v2.0.0
+ - name: Set up JDK
+ uses: actions/setup-java@v3
+ with:
+ java-version: 18
+ distribution: 'adopt'
+ cache: gradle
+ - name: Download file
+ run: .\scripts\decodeAndWrite.ps1 -encodedValue $env:ENCODED_VALUE -outputPath $env:OUTPUT_PATH
+ shell: pwsh
+ env:
+ ENCODED_VALUE: ${{ secrets.LOCAL_PROPERTIES }}
+ OUTPUT_PATH: '.\local.properties'
+ - name: Download file
+ run: .\scripts\decodeAndWrite.ps1 -encodedValue $env:ENCODED_VALUE -outputPath $env:OUTPUT_PATH
+ shell: pwsh
+ env:
+ ENCODED_VALUE: ${{ secrets.SECRING_GPG }}
+ OUTPUT_PATH: '.\secring.gpg'
+ - name: Grant execute permission for gradlew
+ run: chmod +x gradlew
+ - name: Publish
+ run: ./gradlew $PREVIEW_TASK
+
+ maven_Release:
+ if: ${{ github.ref == 'refs/heads/master' }}
+ environment:
+ name: maven_central_release
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - name: Easy detect-secrets
+ uses: RobertFischer/detect-secrets-action@v2.0.0
+ - name: Set up JDK
+ uses: actions/setup-java@v3
+ with:
+ java-version: 18
+ distribution: 'adopt'
+ cache: gradle
+ - name: Download file
+ run: .\scripts\decodeAndWrite.ps1 -encodedValue $env:ENCODED_VALUE -outputPath $env:OUTPUT_PATH
+ shell: pwsh
+ env:
+ ENCODED_VALUE: ${{ secrets.LOCAL_PROPERTIES }}
+ OUTPUT_PATH: '.\local.properties'
+ - name: Download file
+ run: .\scripts\decodeAndWrite.ps1 -encodedValue $env:ENCODED_VALUE -outputPath $env:OUTPUT_PATH
+ shell: pwsh
+ env:
+ ENCODED_VALUE: ${{ secrets.SECRING_GPG }}
+ OUTPUT_PATH: '.\secring.gpg'
+ - name: Grant execute permission for gradlew
+ run: chmod +x gradlew
+ - name: Publish
+ run: ./gradlew $PUBLISH_TASK
+ - name: Upload Build Artifact
+ uses: actions/upload-artifact@v3
+ with:
+ name: drop
+ path: |
+ **/libs/*
+ build/generated-pom.xml
+ build/generated-pom.xml.asc
+ build.gradle
+ gradlew
+ gradlew.bat
+ settings.gradle
+ gradle.properties
+ **/gradle/**
+ Scripts/**
+
+ create_Tag:
+ needs: maven_Release
+ runs-on: ubuntu-latest
+ env:
+ RELEASE_TAG: ""
+ steps:
+ - uses: actions/checkout@v3
+ - name: Get Version
+ id: GetVersion
+ run: .\scripts\getLatestVersion.ps1
+ shell: pwsh
+ - name: Create tag
+ uses: rickstaa/action-create-tag@v1.5.4
+ with:
+ tag: ${{ steps.GetVersion.outputs.tag }}
+ - name: Queue Git Release
+ uses: benc-uk/workflow-dispatch@v1
+ with:
+ workflow: Git Release
+ token: ${{ secrets.PERSONAL_TOKEN }}
+ ref: ${{ steps.GetVersion.outputs.tag }}
+
+
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index acbe219cf..d024376b5 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -1,6 +1,7 @@
name: "CodeQL"
on:
+ workflow_dispatch:
push:
branches: [dev, master]
pull_request:
@@ -16,20 +17,16 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v2
- with:
- # We must fetch at least the immediate parents so that if this is
- # a pull request then we can checkout the head.
- fetch-depth: 2
+ uses: actions/checkout@v3
- # If this run was triggered by a pull request event, then checkout
- # the head of the pull request instead of the merge commit.
- - run: git checkout HEAD^2
- if: ${{ github.event_name == 'pull_request' }}
+ - uses: actions/setup-java@v3
+ with:
+ distribution: 'adopt'
+ java-version: 18
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
- uses: github/codeql-action/init@v1
+ uses: github/codeql-action/init@v2
# Override language selection by uncommenting this and choosing your languages
with:
languages: java
@@ -37,7 +34,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
- uses: github/codeql-action/autobuild@v1
+ uses: github/codeql-action/autobuild@v2
# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@@ -51,4 +48,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v1
+ uses: github/codeql-action/analyze@v2
diff --git a/.github/workflows/conflicting-pr-label.yml b/.github/workflows/conflicting-pr-label.yml
index 135603af1..c8ba2b21b 100644
--- a/.github/workflows/conflicting-pr-label.yml
+++ b/.github/workflows/conflicting-pr-label.yml
@@ -5,6 +5,7 @@ name: PullRequestConflicting
# Controls when the action will run. Triggers the workflow on push or pull request
# events but only for the master branch
on:
+ workflow_dispatch:
push:
branches: [ master, dev ]
pull_request:
diff --git a/.github/workflows/git-release.yml b/.github/workflows/git-release.yml
new file mode 100644
index 000000000..d86d1a56b
--- /dev/null
+++ b/.github/workflows/git-release.yml
@@ -0,0 +1,33 @@
+name: Git Release
+
+on:
+ push:
+ tags:
+ - "v[0-9]+.[0-9]+.[0-9]+"
+ workflow_dispatch:
+
+jobs:
+ Git_Release:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - name: Download Build Artifact
+ uses: dawidd6/action-download-artifact@v2.24.3
+ with:
+ workflow: build-and-publish.yml
+ workflow_conclusion: success
+ branch: master
+ event: push
+ name: drop
+ path: drop
+ - name: Github Release
+ uses: anton-yurchenko/git-release@v5.0
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN}}
+ DRAFT_RELEASE: "false"
+ PRE_RELEASE: "false"
+ CHANGELOG_FILE: "CHANGELOG.md"
+ ALLOW_EMPTY_CHANGELOG: "true"
+ with:
+ args: |
+ drop/build/**/*.jar
diff --git a/.github/workflows/gradle-build.yml b/.github/workflows/gradle-build.yml
new file mode 100644
index 000000000..ea1f6ef13
--- /dev/null
+++ b/.github/workflows/gradle-build.yml
@@ -0,0 +1,45 @@
+name: Java CI with Gradle
+
+on:
+ pull_request:
+ branches: [ dev, master ]
+ paths-ignore:
+ - '.gradle/wrapper'
+ - '.gitignore'
+ - 'LICENSE'
+ - 'THIRD PARTY NOTICES'
+ - '*.md'
+ workflow_dispatch:
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - name: Set up JDK
+ uses: actions/setup-java@v3
+ with:
+ java-version: 18
+ distribution: 'adopt'
+ cache: gradle
+ - name: Easy detect-secrets
+ uses: RobertFischer/detect-secrets-action@v2.0.0
+ - name: Grant execute permission for gradlew
+ run: chmod +x gradlew
+ - name: Build with Gradle
+ run: ./gradlew build
+ - name: Upload a Build Artifact
+ uses: actions/upload-artifact@v3
+ with:
+ name: drop
+ path: |
+ **/libs/*
+ build/generated-pom.xml
+ build/generated-pom.xml.asc
+ build.gradle
+ gradlew
+ gradlew.bat
+ settings.gradle
+ gradle.properties
+ **/gradle/**
+ Scripts/**
\ No newline at end of file
diff --git a/.github/workflows/projectsbot.yml b/.github/workflows/projectsbot.yml
new file mode 100644
index 000000000..03da15c4e
--- /dev/null
+++ b/.github/workflows/projectsbot.yml
@@ -0,0 +1,81 @@
+# This workflow is used to add new issues to GitHub Projects (Beta)
+
+name: Add PR to project
+on:
+ issues:
+ types: [opened]
+jobs:
+ track_issue:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Generate token
+ id: generate_token
+ uses: tibdex/github-app-token@021a2405c7f990db57f5eae5397423dcc554159c
+ with:
+ app_id: ${{ secrets.GRAPHBOT_APP_ID }}
+ private_key: ${{ secrets.GRAPHBOT_APP_PEM }}
+
+ - name: Get project data
+ env:
+ GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }}
+ ORGANIZATION: microsoftgraph
+ PROJECT_NUMBER: 25
+ run: |
+ gh api graphql -f query='
+ query($org: String!, $number: Int!) {
+ organization(login: $org){
+ projectNext(number: $number) {
+ id
+ fields(first:20) {
+ nodes {
+ id
+ name
+ settings
+ }
+ }
+ }
+ }
+ }' -f org=$ORGANIZATION -F number=$PROJECT_NUMBER > project_data.json
+
+ echo 'PROJECT_ID='$(jq '.data.organization.projectNext.id' project_data.json) >> $GITHUB_ENV
+ echo 'STATUS_FIELD_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "Status") | .id' project_data.json) >> $GITHUB_ENV
+ echo 'TRIAGE_OPTION_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "Status") |.settings | fromjson.options[] | select(.name=="Needs Triage 🔍") |.id' project_data.json) >> $GITHUB_ENV
+
+ - name: Add Issue to project
+ env:
+ GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }}
+ ISSUE_ID: ${{ github.event.issue.node_id }}
+ run: |
+ item_id="$( gh api graphql -f query='
+ mutation($project:ID!, $issue:ID!) {
+ addProjectNextItem(input: {projectId: $project, contentId: $issue}) {
+ projectNextItem {
+ id
+ }
+ }
+ }' -f project=$PROJECT_ID -f issue=$ISSUE_ID --jq '.data.addProjectNextItem.projectNextItem.id')"
+
+ echo 'ITEM_ID='$item_id >> $GITHUB_ENV
+
+ - name: Set Triage
+ env:
+ GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }}
+ run: |
+ gh api graphql -f query='
+ mutation (
+ $project: ID!
+ $item: ID!
+ $status_field: ID!
+ $status_value: String!
+ ) {
+ set_status: updateProjectNextItemField(input: {
+ projectId: $project
+ itemId: $item
+ fieldId: $status_field
+ value: $status_value
+ }) {
+ projectNextItem {
+ id
+ }
+ }
+ }' -f project=$PROJECT_ID -f item=$ITEM_ID -f status_field=$STATUS_FIELD_ID -f status_value=${{ env.TRIAGE_OPTION_ID }} --silent
diff --git a/.github/workflows/sample-build-check.yml b/.github/workflows/sample-build-check.yml
new file mode 100644
index 000000000..6c2bfc43d
--- /dev/null
+++ b/.github/workflows/sample-build-check.yml
@@ -0,0 +1,38 @@
+name: "samples build check"
+
+on:
+ workflow_dispatch:
+ push:
+ paths:
+ - '**/*.java'
+ - '**/*.yml'
+
+jobs:
+ samples-build-check-device-code:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - uses: actions/setup-java@v3
+ with:
+ distribution: 'adopt'
+ java-version: 18
+ - name: Add execution right to the script
+ run: chmod +x gradlew
+ working-directory: ./samples/deviceCodeSample
+ - name: Build and run type summary project
+ run: ./gradlew --no-daemon build
+ working-directory: ./samples/deviceCodeSample
+ samples-build-check-interactive:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - uses: actions/setup-java@v3
+ with:
+ distribution: 'adopt'
+ java-version: 18
+ - name: Add execution right to the script
+ run: chmod +x gradlew
+ working-directory: ./samples/interactiveBrowserSample
+ - name: Build and run type summary project
+ run: ./gradlew --no-daemon build
+ working-directory: ./samples/interactiveBrowserSample
\ No newline at end of file
diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml
new file mode 100644
index 000000000..12a91e118
--- /dev/null
+++ b/.github/workflows/sonarcloud.yml
@@ -0,0 +1,41 @@
+name: Static analysis with SonarCloud
+on:
+ workflow_dispatch:
+ push:
+ branches:
+ - master
+ - dev
+ - feature/v2
+ pull_request:
+ types: [opened, synchronize, reopened]
+jobs:
+ build:
+ name: Build
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
+ - name: Set up JDK
+ uses: actions/setup-java@v3
+ with:
+ java-version: 18
+ distribution: 'adopt'
+ cache: gradle
+ - name: Cache SonarCloud packages
+ uses: actions/cache@v3
+ with:
+ path: ~/.sonar/cache
+ key: ${{ runner.os }}-sonar
+ restore-keys: ${{ runner.os }}-sonar
+ - name: Cache Gradle packages
+ uses: actions/cache@v3
+ with:
+ path: ~/.gradle/caches
+ key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }}
+ restore-keys: ${{ runner.os }}-gradle
+ - name: Build and analyze
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
+ SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
+ run: ./gradlew build sonarqube --info
diff --git a/.gitignore b/.gitignore
index f121de793..26e2d94d5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -29,7 +29,10 @@ hs_err_pid*
.classpath
.settings
+# IntelliJ
+*.iml
+.idea/
+
# Maven
/target/
-/pom.xml
local.properties
\ No newline at end of file
diff --git a/.secrets.baseline b/.secrets.baseline
new file mode 100644
index 000000000..b83d75a8c
--- /dev/null
+++ b/.secrets.baseline
@@ -0,0 +1,100 @@
+{
+ "version": "1.0.3",
+ "plugins_used": [
+ {
+ "name": "ArtifactoryDetector"
+ },
+ {
+ "name": "AWSKeyDetector"
+ },
+ {
+ "name": "AzureStorageKeyDetector"
+ },
+ {
+ "name": "Base64HighEntropyString",
+ "limit": 4.5
+ },
+ {
+ "name": "BasicAuthDetector"
+ },
+ {
+ "name": "CloudantDetector"
+ },
+ {
+ "name": "HexHighEntropyString",
+ "limit": 3.0
+ },
+ {
+ "name": "IbmCloudIamDetector"
+ },
+ {
+ "name": "IbmCosHmacDetector"
+ },
+ {
+ "name": "JwtTokenDetector"
+ },
+ {
+ "name": "KeywordDetector",
+ "keyword_exclude": ""
+ },
+ {
+ "name": "MailchimpDetector"
+ },
+ {
+ "name": "NpmDetector"
+ },
+ {
+ "name": "PrivateKeyDetector"
+ },
+ {
+ "name": "SlackDetector"
+ },
+ {
+ "name": "SoftlayerDetector"
+ },
+ {
+ "name": "SquareOAuthDetector"
+ },
+ {
+ "name": "StripeDetector"
+ },
+ {
+ "name": "TwilioKeyDetector"
+ }
+ ],
+ "filters_used": [
+ {
+ "path": "detect_secrets.filters.allowlist.is_line_allowlisted"
+ },
+ {
+ "path": "detect_secrets.filters.common.is_ignored_due_to_verification_policies",
+ "min_level": 2
+ },
+ {
+ "path": "detect_secrets.filters.heuristic.is_indirect_reference"
+ },
+ {
+ "path": "detect_secrets.filters.heuristic.is_likely_id_string"
+ },
+ {
+ "path": "detect_secrets.filters.heuristic.is_potential_uuid"
+ },
+ {
+ "path": "detect_secrets.filters.heuristic.is_prefixed_with_dollar_sign"
+ },
+ {
+ "path": "detect_secrets.filters.heuristic.is_sequential_string"
+ },
+ {
+ "path": "detect_secrets.filters.heuristic.is_templated_secret"
+ },
+ {
+ "path": "detect_secrets.filters.regex.should_exclude_file",
+ "pattern": [
+ "gradle.properties"
+ ]
+ }
+ ],
+ "results": {},
+ "generated_at": "2021-09-09T20:53:20Z"
+}
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 000000000..0c1fbc7f0
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,116 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [Unreleased]
+
+### Added
+
+### Changed
+
+## [2.0.14] - 2022-10-06
+
+### Added
+
+### Changed
+
+- Fixes an issue where the error code 'ErrorItemNotFound' is not accounted for. #606
+- Bumps azure-core to 1.32.0 #603, #604, #605
+- Bumps azure-identity to 1.6.0 #633, #635
+
+## [2.0.13] - 2022-05-26
+
+### Added
+
+### Changed
+
+- Fixed an issue where the error message would be logged twice. #514
+- Bumps azure-core to 1.28.0 #503, #504, #506
+- Bumps azure-identity to 1.5.1 #505, #507
+
+## [2.0.12] - 2022-04-22
+
+### Added
+
+- Add ability to add custom hosts to BaseAuthenticationProvider #484
+
+### Changed
+
+- Bumps Azure Core to 1.27.0 #474, #473, #472
+- Bumps mockito-inline to 4.5.1 #494, #493, #491
+- Bumps gradle wrappers to 7.4.0 #454
+- Bumps gradle to 7.1.3 in /android #477
+- Bumps gradle-enterprise-gradle-plugin 3.10.0 in /android #488
+- Bumps dawidd6/action-download-artifact to 2.19.0 #482
+- Bumps com.github.spotbugs to 5.0.6 #442, #443, #444
+- Bumps spotbugs-annotations to 4.6.0 #460, #461
+- Bumps azure-identity to 1.5.0 #475, #476
+- Bumps actions/cache to 3.0.0 #469
+- Bumps actions/upload-artifact to 3.0.0 #479
+- Bumps actions/setup-java to 3.0.0 #478
+- Bumps rickstaa/action-create-tag to 1.2.2 #453
+- Bumps guava to 31.1-jre #451, #450, #449
+- Bumps gson to 2.9.0 #438, #439, #440
+
+## [2.0.11] - 2022-02-04
+
+### Added
+
+- Removing lock on Http protocol 1.1 in preparation for Graph service support of Http2 #429
+
+### Changed
+
+- Bumps Azure Core to 1.24.1 #408, #409, #410
+- Bumps mockito-inline to 4.3.1 #422, #423, #424
+- Bumps okhttp to 4.9.3 #371, #372
+- Bumps junit to 4.13.2 #391, #394
+- Bumps junit-jupiter-api to 5.8.2 #379, #382
+- Bumps junit-jupiter-egine to 5.8.2 #380
+- Bumps junit-jupiter-params to 5.8.2 #381, #383
+- Bumps gradle wrappers to 7.3.3 #426
+- Bumps gradle-versions-plugin to 0.42.0 in /android #428
+- Bumps gradle from 7.1.0 in /android #425
+- Bumps gradle-enterprise-gradle-plugin 3.8.1 in /android #413
+- Bumps dawidd6/action-download-artifact to 2.17.0 #427
+- Bumps com.github.spotbugs to 5.0.5 #416
+- Bumps spotbugs-annotations to 4.5.3 #407
+- Bumps azure-identity to 1.4.3 #411, #412
+- Bumps anton-yurchenko/git-release to 4.2 #378
+- Bumps actions/cache from to 2.1.7 #375
+
+## [2.0.10] - 2021-11-16
+
+### Added
+
+- Added support for cancelling requests #361
+
+### Changed
+
+- Fixed a bug where batching would fail for national clouds
+- Bumps Azure Core from 1.20.0 to 1.22.0 #359, #360, #341, #342
+- Bumps gson from 2.8.8 to 2.8.9 #356, #355
+- Bumps actions/checkout from 2.3.5 to 2.4.0 #358, #349
+- Upgrades CI pipeline to Java 17 #348, #330
+- Bumps mockito-inline from 3.12.4 to 4.0.0 #346, #345
+- Bumps action-download-artifact from 2.14.1 to 2.15.0 #344
+- Bumps okhttp from 4.9.1 to 4.9.2 #339, #340
+- Bumps guava from 30.1.1 to 31.0.1 #338, #337, #335, #336
+- Bumps junit-jupiter-api from 5.8.0 to 5.8.1 #332, #334
+- Bumps junit-jupiter-egine from 5.8.0 to 5.8.1 #333
+
+## [2.0.9] - 2021-09-17
+
+### Added
+
+- Workflow for Maven preview and GitHub Release
+- Workflow for build validation
+- Proper handling for InterruptedException
+
+### Changed
+
+- Casing fixed for Odata type parsing
+- Removed .azure-pipelines/**
+
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 000000000..f9ba8cf65
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,9 @@
+# Microsoft Open Source Code of Conduct
+
+This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
+
+Resources:
+
+- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/)
+- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)
+- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 000000000..f7b89984f
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,41 @@
+
+
+## Security
+
+Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).
+
+If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below.
+
+## Reporting Security Issues
+
+**Please do not report security vulnerabilities through public GitHub issues.**
+
+Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report).
+
+If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc).
+
+You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc).
+
+Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
+
+ * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
+ * Full paths of source file(s) related to the manifestation of the issue
+ * The location of the affected source code (tag/branch/commit or direct URL)
+ * Any special configuration required to reproduce the issue
+ * Step-by-step instructions to reproduce the issue
+ * Proof-of-concept or exploit code (if possible)
+ * Impact of the issue, including how an attacker might exploit the issue
+
+This information will help us triage your report more quickly.
+
+If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs.
+
+## Preferred Languages
+
+We prefer all communications to be in English.
+
+## Policy
+
+Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd).
+
+
\ No newline at end of file
diff --git a/Scripts/validateMavenVersion.ps1 b/Scripts/validateMavenVersion.ps1
deleted file mode 100644
index da1742bd2..000000000
--- a/Scripts/validateMavenVersion.ps1
+++ /dev/null
@@ -1,67 +0,0 @@
-# Copyright (c) Microsoft Corporation. All rights reserved.
-# Licensed under the MIT License.
-
-<#
-.Synopsis
- Ensure the maven version is updated in the case that the pull request is
- to the main/master branch of the repo.
-.Description
- Retrieves the local, Maven, and Bintray versions of the Java-Core build.
- Checks that the Maven and Bintray versions are aligned, trigger warning if not.
- Checks that the current local version is greater than those currently deployed.
-.Parameter propertiesPath
-#>
-
-
-Param(
- [string]$propertiesPath
-)
-
-#Find the local version from the Gradle.Properties file
-if($propertiesPath -eq "" -or $null -eq $propertiesPath) {
- $propertiesPath = Join-Path -Path $PSScriptRoot -ChildPath "../gradle.properties"
-}
-$file = get-item $propertiesPath
-$findLocalVersions = $file | Select-String -Pattern "mavenMajorVersion" -Context 0,2
-$findLocalVersions = $findLocalVersions -split "`r`n"
-$packageName = ($file | Select-String -Pattern "mavenArtifactId").Line.Split("=")[1].Trim()
-
-$localMajor = $findLocalVersions[0].Substring($findLocalVersions[0].Length-1)
-$localMinor = $findLocalVersions[1].Substring($findLocalVersions[1].Length-1)
-$localPatch = $findLocalVersions[2].Substring($findLocalVersions[2].Length-1)
-$localVersion = [version]"$localMajor.$localMinor.$localPatch"
-
-#Set Web Client and retrieve Maven and Bintray versions from their respective repos.
-$web_client = New-Object System.Net.WebClient
-
-$mavenAPIurl = "https://search.maven.org/solrsearch/select?q=$packageName&rows=20&wt=json"
-$jsonResult = $web_client.DownloadString($mavenAPIurl) | ConvertFrom-Json
-$mavenVersion = [version]$jsonResult.response.docs.latestVersion
-
-$bintrayAPIurl = "https://api.bintray.com/search/packages?name=$packageName"
-$jsonResult = $web_client.DownloadString($bintrayAPIurl) | ConvertFrom-Json
-$bintrayVersion = [version]$jsonResult.latest_version
-
-#If the api calls return empty then this library cannot be compared to the online versions
-#may proceed with the pull request
-if(($mavenVersion -eq $null) -and ($bintrayVersion -eq $null))
-{
- Write-Information "This package does not exist yet in the online repository, therefore there are no versions to compare."
- return
-}
-
-#Inform host of current Maven and Bintray versions
-Write-Host 'The current version in the Maven central repository is:' $mavenVersion
-Write-Host 'The current version in the Bintray central repository is:' $bintrayVersion
-
-#Warn in case Maven and Bintray versions are not the same.
-if($mavenVersion -ne $bintrayVersion){
- Write-Warning "The current Maven and Bintray versions are not the same"
-}
-#Success if Local version has been updated, Error otherwise.
-if($localVersion -gt $bintrayVersion -and $localVersion -gt $mavenVersion){
- Write-Host "The current pull request is of a greater version"
-}
-else{
- Write-Error "The current local version is not updated. Please update the local version in the Gradle.Properties file."
-}
diff --git a/android/.gitignore b/android/.gitignore
new file mode 100644
index 000000000..f06dfad69
--- /dev/null
+++ b/android/.gitignore
@@ -0,0 +1,2 @@
+.gradle
+build
\ No newline at end of file
diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml
new file mode 100644
index 000000000..da53f5433
--- /dev/null
+++ b/android/AndroidManifest.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/build.gradle b/android/build.gradle
new file mode 100644
index 000000000..0e9d48c00
--- /dev/null
+++ b/android/build.gradle
@@ -0,0 +1,71 @@
+buildscript {
+ repositories {
+ google()
+ gradlePluginPortal()
+ maven {
+ url "https://plugins.gradle.org/m2/"
+ }
+ }
+
+ dependencies {
+ classpath "com.gradle:gradle-enterprise-gradle-plugin:3.12.2"
+ classpath "com.android.tools.build:gradle:7.4.0"
+ classpath "com.github.ben-manes:gradle-versions-plugin:0.44.0"
+ }
+}
+
+repositories {
+ google()
+ gradlePluginPortal()
+}
+
+apply plugin: "com.android.library"
+apply plugin: "com.github.ben-manes.versions"
+
+android {
+ compileSdkVersion 33
+
+ defaultConfig {
+ versionCode 1
+ versionName "1.0"
+ minSdkVersion 26
+ targetSdkVersion 33
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ }
+ }
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ lintOptions {
+ textOutput "stdout"
+ checkAllWarnings true
+ warningsAsErrors true
+ disable "UnusedResources" // Unused will be removed on release
+ disable "IconExpectedSize" // Using the material icons provided from Google
+ disable "GoogleAppIndexingApiWarning" // We might want to index our app later
+ disable "InvalidPackage" // Butterknife, Okio and Realm
+ disable "ResourceType" // Annotation binding
+ disable "GradleDependency"
+ disable "NewerVersionAvailable"
+ disable "DuplicatePlatformClasses" // xpp3 added by azure-identity
+ }
+ sourceSets {
+ main {
+ java.srcDirs = ['../src/main/java']
+ res.srcDirs = ['../src/main/java']
+ manifest.srcFile 'AndroidManifest.xml'
+ }
+ androidTest {
+ setRoot '../src/test'
+ }
+ }
+}
+
+apply from: "../gradle/dependencies.gradle"
diff --git a/android/gradle.properties b/android/gradle.properties
new file mode 100644
index 000000000..2c9a00dfc
--- /dev/null
+++ b/android/gradle.properties
@@ -0,0 +1,43 @@
+# Project-wide Gradle settings.
+
+# IDE users:
+# Settings specified in this file will override any Gradle settings
+# configured through the IDE.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+# Default value: -Xmx10248m
+# org.gradle.jvmargs=-Xmx2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+
+# The size of the library demands a large amount of RAM to build. Increase as necessary if you get GC errors
+## linux requires 10G, OSX requires 11G
+org.gradle.jvmargs=-Xmx2g
+org.gradle.parallel=true
+org.gradle.caching=true
+
+mavenGroupId = com.microsoft.graph
+mavenArtifactId = microsoft-graph-core
+mavenMajorVersion = 2
+mavenMinorVersion = 3
+mavenPatchVersion = 1
+mavenArtifactSuffix =
+
+#These values are used to run functional tests
+#If you wish to run the functional tests, edit the gradle.properties
+#file in your user directory instead of adding them here.
+#ex: C:\Users\username\.gradle\gradle.properties
+ClientId="CLIENT_ID"
+Username="USERNAME"
+Password="PASSWORD"
+
+#enable mavenCentralPublishingEnabled to publish to maven central
+mavenCentralSnapshotArtifactSuffix = -SNAPSHOT
+mavenCentralPublishingEnabled=false
diff --git a/android/gradle/wrapper/gradle-wrapper.jar b/android/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 000000000..249e5832f
Binary files /dev/null and b/android/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 000000000..ae04661ee
--- /dev/null
+++ b/android/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/android/gradlew b/android/gradlew
new file mode 100644
index 000000000..a69d9cb6c
--- /dev/null
+++ b/android/gradlew
@@ -0,0 +1,240 @@
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
+done
+
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
+
+APP_NAME="Gradle"
+APP_BASE_NAME=${0##*/}
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD=maximum
+
+warn () {
+ echo "$*"
+} >&2
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+} >&2
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD=$JAVA_HOME/jre/sh/java
+ else
+ JAVACMD=$JAVA_HOME/bin/java
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD=java
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
+ fi
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
+ done
+fi
+
+# Collect all arguments for the java command;
+# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
+# shell script including quotes and variable substitutions, so put them in
+# double quotes to make sure that they get re-expanded; and
+# * put everything else in single quotes, so that it's not re-expanded.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ org.gradle.wrapper.GradleWrapperMain \
+ "$@"
+
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+ die "xargs is not available"
+fi
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
+
+exec "$JAVACMD" "$@"
diff --git a/android/gradlew.bat b/android/gradlew.bat
new file mode 100644
index 000000000..f127cfd49
--- /dev/null
+++ b/android/gradlew.bat
@@ -0,0 +1,91 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%"=="" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%"=="" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if %ERRORLEVEL% equ 0 goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if %ERRORLEVEL% equ 0 goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/android/settings.gradle b/android/settings.gradle
new file mode 100644
index 000000000..ab8f7824a
--- /dev/null
+++ b/android/settings.gradle
@@ -0,0 +1,10 @@
+/*
+ * This file was generated by the Gradle 'init' task.
+ *
+ * The settings file is used to specify which projects to include in your build.
+ *
+ * Detailed information about configuring a multi-project build in Gradle can be found
+ * in the user manual at https://docs.gradle.org/6.6/userguide/multi_project_builds.html
+ */
+
+rootProject.name = 'msgraph-sdk-java-core'
diff --git a/build.gradle b/build.gradle
index 5cfd7cbd4..c290589b0 100644
--- a/build.gradle
+++ b/build.gradle
@@ -13,29 +13,71 @@ plugins {
id 'eclipse'
id 'maven-publish'
id 'signing'
- id 'com.jfrog.bintray' version '1.8.5'
+ id 'jacoco'
+ id 'com.github.spotbugs' version '5.0.13'
+ id "org.sonarqube" version "3.5.0.2730"
}
java {
modularity.inferModulePath = true
}
+test {
+ useJUnitPlatform()
+ finalizedBy jacocoTestReport // report is always generated after tests run
+}
+jacocoTestReport {
+ dependsOn test // tests are required to run before generating the report
+}
+
+jacoco {
+ toolVersion = "0.8.7"
+}
+
+spotbugsMain {
+ excludeFilter = file("spotBugsExcludeFilter.xml")
+ reports {
+ html {
+ enabled = true
+ destination = file("$buildDir/reports/spotbugs/main/spotbugs.html")
+ stylesheet = 'fancy-hist.xsl'
+ }
+ }
+}
+
+spotbugsTest {
+ excludeFilter = file("spotBugsExcludeFilter.xml")
+ reports {
+ html {
+ enabled = true
+ destination = file("$buildDir/reports/spotbugs/test/spotbugs.html")
+ stylesheet = 'fancy-hist.xsl'
+ }
+ }
+}
+
+jacocoTestReport {
+ reports {
+ xml.enabled true
+ }
+}
+
+sourceSets {
+ main {
+ java {
+ exclude 'pom.xml'
+ }
+ }
+}
+
// In this section you declare where to find the dependencies of your project
repositories {
- // Use jcenter for resolving your dependencies.
// You can declare any Maven/Ivy/file repository here.
- jcenter()
mavenCentral()
+ maven { url 'https://oss.sonatype.org/content/repositories/snapshots' } // for jacoco, until 0.8.7 gets released
}
-dependencies {
- // Use JUnit test framework
- testImplementation 'junit:junit:4.13'
-
- api 'com.squareup.okhttp3:okhttp:3.12.1'
-
- implementation 'com.google.code.gson:gson:2.8.6'
-}
+apply from: "gradle/dependencies.gradle"
def pomConfig = {
licenses {
@@ -47,6 +89,14 @@ def pomConfig = {
}
}
+sonarqube {
+ properties {
+ property "sonar.projectKey", "microsoftgraph_msgraph-sdk-java-core"
+ property "sonar.organization", "microsoftgraph2"
+ property "sonar.host.url", "https://sonarcloud.io"
+ }
+}
+
//Publishing tasks-
//Maven Central Snapshot: publishSnapshotPublicationToMavenRepository
//Maven Central Release: publishMavenCentralReleasePublicationToMaven2Repository
@@ -145,34 +195,6 @@ publishing {
}
}
}
-
- maven {
- url = 'http://oss.jfrog.org/artifactory/oss-snapshot-local'
- name = 'jfrogSnapshot'
-
- credentials {
- if (project.rootProject.file('local.properties').exists()) {
- Properties properties = new Properties()
- properties.load(project.rootProject.file('local.properties').newDataInputStream())
- username = (properties.containsKey('bintray.user')) ? properties.getProperty('bintray.user').toLowerCase() : "BINTRAY_USERNAME"
- password = properties.getProperty('bintray.apikey')
- }
- }
- }
-
- maven {
- url = 'https://oss.jfrog.org/artifactory/libs-release'
- name = 'jfrog'
-
- credentials {
- if (project.rootProject.file('local.properties').exists()) {
- Properties properties = new Properties()
- properties.load(project.rootProject.file('local.properties').newDataInputStream())
- username = (properties.containsKey('bintray.user')) ? properties.getProperty('bintray.user').toLowerCase() : "BINTRAY_USERNAME"
- password = properties.getProperty('bintray.apikey')
- }
- }
- }
}
}
@@ -188,42 +210,10 @@ def fixAscNames = { name ->
name.replace('msgraph-sdk-java-core', "${project.property('mavenArtifactId')}-${mavenMajorVersion}.${mavenMinorVersion}.${mavenPatchVersion}")
}
}
-bintray {
- if (project.rootProject.file('local.properties').exists()) {
- Properties properties = new Properties()
- properties.load(project.rootProject.file('local.properties').newDataInputStream())
- user = (properties.containsKey('bintray.user')) ? properties.getProperty('bintray.user').toLowerCase() : "BINTRAY_USERNAME"
- key = properties.getProperty('bintray.apikey')
- }
- publications = ['mavenCentralRelease']
- filesSpec {
- from ('build/libs') {
- include 'msgraph-sdk-java-core*.jar.asc'
- rename fixAscNames
- }
- from ('build') {
- include 'generated-pom.xml.asc'
- rename fixAscNames
- }
- into "com/microsoft/graph/${project.property('mavenArtifactId')}/${mavenMajorVersion}.${mavenMinorVersion}.${mavenPatchVersion}/"
- }
- pkg {
- repo = 'Maven'
- name = project.property('mavenArtifactId')
- userOrg = 'microsoftgraph'
- licenses = ['MIT']
- vcsUrl = 'https://github.com/microsoftgraph/msgraph-sdk-java-core.git'
- publicDownloadNumbers = true
- version {
- name = "${mavenMajorVersion}.${mavenMinorVersion}.${mavenPatchVersion}"
- vcsTag = "${mavenMajorVersion}.${mavenMinorVersion}.${mavenPatchVersion}"
- }
- }
-}
compileJava {
- sourceCompatibility = 1.7
- targetCompatibility = 1.7
+ sourceCompatibility = 1.8
+ targetCompatibility = 1.8
}
def getVersionCode() {
diff --git a/devx.yml b/devx.yml
new file mode 100644
index 000000000..f52c6708d
--- /dev/null
+++ b/devx.yml
@@ -0,0 +1,6 @@
+languages:
+- java
+extensions:
+ services:
+ - Microsoft Graph
+dependencyFile: /gradle/dependencies.gradle
diff --git a/gradle.properties b/gradle.properties
index 74406c825..c5bde8423 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -9,8 +9,8 @@
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
-# Default value: -Xmx10248m -XX:MaxPermSize=256m
-# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
+# Default value: -Xmx10248m
+# org.gradle.jvmargs=-Xmx2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
@@ -19,15 +19,14 @@
# The size of the library demands a large amount of RAM to build. Increase as necessary if you get GC errors
## linux requires 10G, OSX requires 11G
-org.gradle.jvmargs=-XX:MaxPermSize=512m -Xmx2g
+org.gradle.jvmargs=-Xmx2g
mavenGroupId = com.microsoft.graph
mavenArtifactId = microsoft-graph-core
-mavenMajorVersion = 1
+mavenMajorVersion = 2
mavenMinorVersion = 0
-mavenPatchVersion = 5
+mavenPatchVersion = 15
mavenArtifactSuffix =
-nightliesUrl = http://dl.bintray.com/MicrosoftGraph/Maven
#These values are used to run functional tests
#If you wish to run the functional tests, edit the gradle.properties
diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle
new file mode 100644
index 000000000..9628dd6e4
--- /dev/null
+++ b/gradle/dependencies.gradle
@@ -0,0 +1,17 @@
+dependencies {
+ // Use JUnit test framework
+ testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.2'
+ testImplementation 'org.junit.jupiter:junit-jupiter-params:5.9.2'
+ testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.2'
+ testImplementation 'org.mockito:mockito-inline:5.0.0'
+
+ api 'com.squareup.okhttp3:okhttp:4.10.0'
+
+ implementation 'com.google.guava:guava:31.1-jre'
+
+ implementation 'com.google.code.gson:gson:2.10.1'
+ api 'com.azure:azure-core:1.35.0'
+
+ api 'net.jcip:jcip-annotations:1.0'
+ api 'com.github.spotbugs:spotbugs-annotations:4.7.3'
+}
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index e708b1c02..249e5832f 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 6c9a22477..ae04661ee 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.6-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
old mode 100644
new mode 100755
index 4f906e0c8..a69d9cb6c
--- a/gradlew
+++ b/gradlew
@@ -1,7 +1,7 @@
-#!/usr/bin/env sh
+#!/bin/sh
#
-# Copyright 2015 the original author or authors.
+# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -17,67 +17,101 @@
#
##############################################################################
-##
-## Gradle start up script for UN*X
-##
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
##############################################################################
# Attempt to set APP_HOME
+
# Resolve links: $0 may be a link
-PRG="$0"
-# Need this for relative symlinks.
-while [ -h "$PRG" ] ; do
- ls=`ls -ld "$PRG"`
- link=`expr "$ls" : '.*-> \(.*\)$'`
- if expr "$link" : '/.*' > /dev/null; then
- PRG="$link"
- else
- PRG=`dirname "$PRG"`"/$link"
- fi
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
done
-SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >/dev/null
-APP_HOME="`pwd -P`"
-cd "$SAVED" >/dev/null
+
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
-APP_BASE_NAME=`basename "$0"`
+APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD="maximum"
+MAX_FD=maximum
warn () {
echo "$*"
-}
+} >&2
die () {
echo
echo "$*"
echo
exit 1
-}
+} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
-case "`uname`" in
- CYGWIN* )
- cygwin=true
- ;;
- Darwin* )
- darwin=true
- ;;
- MINGW* )
- msys=true
- ;;
- NONSTOP* )
- nonstop=true
- ;;
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
@@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
- JAVACMD="$JAVA_HOME/jre/sh/java"
+ JAVACMD=$JAVA_HOME/jre/sh/java
else
- JAVACMD="$JAVA_HOME/bin/java"
+ JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@@ -98,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
- JAVACMD="java"
+ JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
@@ -106,80 +140,101 @@ location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
- MAX_FD_LIMIT=`ulimit -H -n`
- if [ $? -eq 0 ] ; then
- if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
- MAX_FD="$MAX_FD_LIMIT"
- fi
- ulimit -n $MAX_FD
- if [ $? -ne 0 ] ; then
- warn "Could not set maximum file descriptor limit: $MAX_FD"
- fi
- else
- warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
- fi
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
fi
-# For Darwin, add options to specify how the application appears in the dock
-if $darwin; then
- GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
-fi
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
-if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
- APP_HOME=`cygpath --path --mixed "$APP_HOME"`
- CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
-
- JAVACMD=`cygpath --unix "$JAVACMD"`
-
- # We build the pattern for arguments to be converted via cygpath
- ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
- SEP=""
- for dir in $ROOTDIRSRAW ; do
- ROOTDIRS="$ROOTDIRS$SEP$dir"
- SEP="|"
- done
- OURCYGPATTERN="(^($ROOTDIRS))"
- # Add a user-defined pattern to the cygpath arguments
- if [ "$GRADLE_CYGPATTERN" != "" ] ; then
- OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
- fi
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
+
# Now convert the arguments - kludge to limit ourselves to /bin/sh
- i=0
- for arg in "$@" ; do
- CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
- CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
-
- if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
- eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
- else
- eval `echo args$i`="\"$arg\""
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
fi
- i=`expr $i + 1`
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
done
- case $i in
- 0) set -- ;;
- 1) set -- "$args0" ;;
- 2) set -- "$args0" "$args1" ;;
- 3) set -- "$args0" "$args1" "$args2" ;;
- 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
- 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
- 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
- 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
- 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
- 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
- esac
fi
-# Escape application args
-save () {
- for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
- echo " "
-}
-APP_ARGS=`save "$@"`
+# Collect all arguments for the java command;
+# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
+# shell script including quotes and variable substitutions, so put them in
+# double quotes to make sure that they get re-expanded; and
+# * put everything else in single quotes, so that it's not re-expanded.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ org.gradle.wrapper.GradleWrapperMain \
+ "$@"
+
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+ die "xargs is not available"
+fi
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
-# Collect all arguments for the java command, following the shell quoting and substitution rules
-eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
index 107acd32c..f127cfd49 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -14,7 +14,7 @@
@rem limitations under the License.
@rem
-@if "%DEBUG%" == "" @echo off
+@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@@ -25,7 +25,7 @@
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
-if "%DIRNAME%" == "" set DIRNAME=.
+if "%DIRNAME%"=="" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto execute
+if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@@ -75,13 +75,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
:end
@rem End local scope for the variables with windows NT shell
-if "%ERRORLEVEL%"=="0" goto mainEnd
+if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
-if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
-exit /b 1
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 000000000..5ed54cf85
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,64 @@
+
+
+ 4.0.0
+
+ com.microsoft.graph
+ microsoft-graph-core
+ 2.0.12
+ pom
+
+
+ 1.8
+ 1.8
+
+
+
+
+ com.google.code.gson
+ gson
+ 2.10.1
+
+
+ com.google.guava
+ guava
+ 31.1-jre
+
+
+ com.squareup.okhttp3
+ okhttp
+ 4.10.0
+
+
+ com.azure
+ azure-core
+ 1.35.0
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ 5.9.2
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-params
+ 5.9.2
+ test
+
+
+ org.mockito
+ mockito-inline
+ 5.0.0
+ test
+
+
+ com.github.spotbugs
+ spotbugs-annotations
+ 4.7.3
+
+
+
diff --git a/readme.md b/readme.md
index 53619c747..451e98170 100644
--- a/readme.md
+++ b/readme.md
@@ -1,11 +1,13 @@
# Microsoft Graph Core SDK for Java
-Get started with the Microsoft Graph Core SDK for Java by integrating the [Microsoft Graph API](https://graph.microsoft.io/en-us/getting-started) into your Java and Android application!
+[](https://search.maven.org/artifact/com.microsoft.graph/microsoft-graph-core) [](https://sonarcloud.io/dashboard?id=microsoftgraph_msgraph-sdk-java-core) [](https://sonarcloud.io/dashboard?id=microsoftgraph_msgraph-sdk-java-core)
+
+Get started with the Microsoft Graph Core SDK for Java by integrating the [Microsoft Graph API](https://developer.microsoft.com/en-us/graph/get-started/java) into your Java and Android application! You can also have a look at the [Javadoc](https://docs.microsoft.com/en-us/java/api/com.microsoft.graph.httpcore?view=graph-core-java)
## Samples and usage guide
-- [Middleware usage](https://github.com/microsoftgraph/msgraph-sdk-java-core/wiki)
-- [Batching](https://github.com/microsoftgraph/msgraph-sdk-java-core/wiki/Batching)
+- [Middleware usage](https://github.com/microsoftgraph/msgraph-sdk-design/)
+- [Batching](https://docs.microsoft.com/en-us/graph/sdks/batch-requests?tabs=java)
## 1. Installation
@@ -13,14 +15,16 @@ Get started with the Microsoft Graph Core SDK for Java by integrating the [Micro
Add the repository and a compile dependency for `microsoft-graph-core` to your project's `build.gradle`:
-```gradle
+```groovy
repositories {
- jcenter()
+ mavenCentral()
}
dependencies {
// Include the sdk as a dependency
- implementation 'com.microsoft.graph:microsoft-graph-core:1.0.5'
+ implementation 'com.microsoft.graph:microsoft-graph-core:2.0.12'
+ // This dependency is only needed if you are using the TokenCrendentialAuthProvider
+ implementation 'com.azure:azure-identity:1.3.1'
}
```
@@ -30,9 +34,14 @@ Add the dependency in `dependencies` in pom.xml
```xml
- com.microsoft.graph
- microsoft-graph-core
- 1.0.5
+
+ com.microsoft.graph
+ microsoft-graph-core
+ 2.0.12
+
+ com.azure
+ azure-identity
+ 1.3.1
```
@@ -44,24 +53,21 @@ The nature of the Graph API is such that the SDK needs quite a large set of clas
### 2.1 Register your application
-Register your application by following the steps at [Register your app with the Azure AD v2.0 endpoint](https://developer.microsoft.com/en-us/graph/docs/concepts/auth_register_app_v2).
+Register your application by following the steps at [Register your app with the Microsoft Identity Platform](https://docs.microsoft.com/graph/auth-register-app-v2).
### 2.2 Create an IAuthenticationProvider object
-An instance of the **HttpClients** class handles building client. To create a new instance of this class, you need to provide an instance of `ICoreAuthenticationProvider`, which can authenticate requests to Microsoft Graph.
-
-### To get instance of HttpClients
-
-Auth in Java app [here](https://github.com/microsoftgraph/msgraph-sdk-java-auth)
+An instance of the **HttpClients** class handles building client. To create a new instance of this class, you need to provide an instance of `IAuthenticationProvider`, which can authenticate requests to Microsoft Graph.
-Auth in Android app [here](https://github.com/microsoftgraph/msgraph-sdk-android-auth)
+For an example of how to get an authentication provider, see [choose a Microsoft Graph authentication provider](https://docs.microsoft.com/graph/sdks/choose-authentication-providers?tabs=Java).
### 2.3 Get a HttpClients object
+
You must get a **HttpClients** object to make requests against the service.
```java
-OkHttpClient client = HttpClients.createDefault(iCoreAuthenticationProvider);
+OkHttpClient client = HttpClients.createDefault(iAuthenticationProvider);
```
## 3. Make requests against the service
@@ -120,7 +126,8 @@ The Microsoft Graph SDK is open for contribution. To contribute to this project,
## 6. Supported Java versions
-The Microsoft Graph SDK for Java library is supported at runtime for Java 7+ and [Android API revision 15](http://source.android.com/source/build-numbers.html) and greater.
+The Microsoft Graph SDK for Java library is supported at runtime for Java 8 and [Android API revision 26](http://source.android.com/source/build-numbers.html) or greater.
+
## 7. License
diff --git a/samples/deviceCodeSample/.gitignore b/samples/deviceCodeSample/.gitignore
new file mode 100644
index 000000000..e34a1d606
--- /dev/null
+++ b/samples/deviceCodeSample/.gitignore
@@ -0,0 +1,34 @@
+# Compiled class file
+*.class
+
+# Log file
+*.log
+
+# BlueJ files
+*.ctxt
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.jar
+*.war
+*.ear
+*.zip
+*.tar.gz
+*.rar
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+/.gradle/
+/build/
+/bin/
+
+#Eclipse
+.project
+.classpath
+.settings
+
+# Maven
+/target/
+local.properties
\ No newline at end of file
diff --git a/samples/deviceCodeSample/build.gradle b/samples/deviceCodeSample/build.gradle
new file mode 100644
index 000000000..bfe49f47a
--- /dev/null
+++ b/samples/deviceCodeSample/build.gradle
@@ -0,0 +1,16 @@
+plugins {
+ id 'java'
+}
+
+group 'org.example'
+version '1.0-SNAPSHOT'
+
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ testImplementation group: 'junit', name: 'junit', version: '4.13.2'
+ implementation project(':coreLibrary')
+ implementation 'com.azure:azure-identity:1.7.3'
+}
diff --git a/samples/deviceCodeSample/gradle/wrapper/gradle-wrapper.jar b/samples/deviceCodeSample/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 000000000..249e5832f
Binary files /dev/null and b/samples/deviceCodeSample/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/samples/deviceCodeSample/gradle/wrapper/gradle-wrapper.properties b/samples/deviceCodeSample/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 000000000..ae04661ee
--- /dev/null
+++ b/samples/deviceCodeSample/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/samples/deviceCodeSample/gradlew b/samples/deviceCodeSample/gradlew
new file mode 100644
index 000000000..a69d9cb6c
--- /dev/null
+++ b/samples/deviceCodeSample/gradlew
@@ -0,0 +1,240 @@
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
+done
+
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
+
+APP_NAME="Gradle"
+APP_BASE_NAME=${0##*/}
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD=maximum
+
+warn () {
+ echo "$*"
+} >&2
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+} >&2
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD=$JAVA_HOME/jre/sh/java
+ else
+ JAVACMD=$JAVA_HOME/bin/java
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD=java
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
+ fi
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
+ done
+fi
+
+# Collect all arguments for the java command;
+# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
+# shell script including quotes and variable substitutions, so put them in
+# double quotes to make sure that they get re-expanded; and
+# * put everything else in single quotes, so that it's not re-expanded.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ org.gradle.wrapper.GradleWrapperMain \
+ "$@"
+
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+ die "xargs is not available"
+fi
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
+
+exec "$JAVACMD" "$@"
diff --git a/samples/deviceCodeSample/gradlew.bat b/samples/deviceCodeSample/gradlew.bat
new file mode 100644
index 000000000..f127cfd49
--- /dev/null
+++ b/samples/deviceCodeSample/gradlew.bat
@@ -0,0 +1,91 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%"=="" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%"=="" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if %ERRORLEVEL% equ 0 goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if %ERRORLEVEL% equ 0 goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/samples/deviceCodeSample/settings.gradle b/samples/deviceCodeSample/settings.gradle
new file mode 100644
index 000000000..79a5cd38a
--- /dev/null
+++ b/samples/deviceCodeSample/settings.gradle
@@ -0,0 +1,4 @@
+include 'coreLibrary'
+
+project(':coreLibrary').projectDir = new File("../../")
+
diff --git a/samples/deviceCodeSample/src/main/java/DeviceCodeFlowMain.java b/samples/deviceCodeSample/src/main/java/DeviceCodeFlowMain.java
new file mode 100644
index 000000000..b39fb3ad7
--- /dev/null
+++ b/samples/deviceCodeSample/src/main/java/DeviceCodeFlowMain.java
@@ -0,0 +1,45 @@
+import com.azure.identity.DeviceCodeCredential;
+import com.azure.identity.DeviceCodeCredentialBuilder;
+import com.microsoft.graph.authentication.TokenCredentialAuthProvider;
+import com.microsoft.graph.httpcore.HttpClients;
+import okhttp3.*;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+
+
+public class DeviceCodeFlowMain {
+
+ //Replace CLIENT_ID with your own client id from an app that is configured according to the requirements below
+ //for requirements visit:
+ //https://github.com/Azure/azure-sdk-for-java/wiki/Set-up-Your-Environment-for-Authentication#enable-applications-for-device-code-flow
+ private final static String CLIENT_ID = "";
+
+ //Set the scopes for your ms-graph request
+ private final static List SCOPES = Arrays.asList("User.ReadBasic.All", "User.Read");
+
+ public static void main(String[] args) throws Exception {
+ final DeviceCodeCredential deviceCodeCred = new DeviceCodeCredentialBuilder()
+ .clientId(CLIENT_ID)
+ .challengeConsumer(challenge -> System.out.println(challenge.getMessage()))
+ .build();
+
+ final TokenCredentialAuthProvider tokenCredAuthProvider = new TokenCredentialAuthProvider(SCOPES, deviceCodeCred);
+ final OkHttpClient httpClient = HttpClients.createDefault(tokenCredAuthProvider);
+
+ final Request request = new Request.Builder().url("https://graph.microsoft.com/v1.0/me/").build();
+
+ httpClient.newCall(request).enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) throws IOException {
+ System.out.println(response.body().string());
+ }
+
+ @Override
+ public void onFailure(Call call, IOException e) {
+ e.printStackTrace();
+ }
+ });
+ }
+}
diff --git a/samples/interactiveBrowserSample/.gitignore b/samples/interactiveBrowserSample/.gitignore
new file mode 100644
index 000000000..e34a1d606
--- /dev/null
+++ b/samples/interactiveBrowserSample/.gitignore
@@ -0,0 +1,34 @@
+# Compiled class file
+*.class
+
+# Log file
+*.log
+
+# BlueJ files
+*.ctxt
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.jar
+*.war
+*.ear
+*.zip
+*.tar.gz
+*.rar
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+/.gradle/
+/build/
+/bin/
+
+#Eclipse
+.project
+.classpath
+.settings
+
+# Maven
+/target/
+local.properties
\ No newline at end of file
diff --git a/samples/interactiveBrowserSample/build.gradle b/samples/interactiveBrowserSample/build.gradle
new file mode 100644
index 000000000..bfe49f47a
--- /dev/null
+++ b/samples/interactiveBrowserSample/build.gradle
@@ -0,0 +1,16 @@
+plugins {
+ id 'java'
+}
+
+group 'org.example'
+version '1.0-SNAPSHOT'
+
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ testImplementation group: 'junit', name: 'junit', version: '4.13.2'
+ implementation project(':coreLibrary')
+ implementation 'com.azure:azure-identity:1.7.3'
+}
diff --git a/samples/interactiveBrowserSample/gradle/wrapper/gradle-wrapper.jar b/samples/interactiveBrowserSample/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 000000000..249e5832f
Binary files /dev/null and b/samples/interactiveBrowserSample/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/samples/interactiveBrowserSample/gradle/wrapper/gradle-wrapper.properties b/samples/interactiveBrowserSample/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 000000000..ae04661ee
--- /dev/null
+++ b/samples/interactiveBrowserSample/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/samples/interactiveBrowserSample/gradlew b/samples/interactiveBrowserSample/gradlew
new file mode 100644
index 000000000..a69d9cb6c
--- /dev/null
+++ b/samples/interactiveBrowserSample/gradlew
@@ -0,0 +1,240 @@
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
+done
+
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
+
+APP_NAME="Gradle"
+APP_BASE_NAME=${0##*/}
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD=maximum
+
+warn () {
+ echo "$*"
+} >&2
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+} >&2
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD=$JAVA_HOME/jre/sh/java
+ else
+ JAVACMD=$JAVA_HOME/bin/java
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD=java
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
+ fi
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
+ done
+fi
+
+# Collect all arguments for the java command;
+# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
+# shell script including quotes and variable substitutions, so put them in
+# double quotes to make sure that they get re-expanded; and
+# * put everything else in single quotes, so that it's not re-expanded.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ org.gradle.wrapper.GradleWrapperMain \
+ "$@"
+
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+ die "xargs is not available"
+fi
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
+
+exec "$JAVACMD" "$@"
diff --git a/samples/interactiveBrowserSample/gradlew.bat b/samples/interactiveBrowserSample/gradlew.bat
new file mode 100644
index 000000000..f127cfd49
--- /dev/null
+++ b/samples/interactiveBrowserSample/gradlew.bat
@@ -0,0 +1,91 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%"=="" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%"=="" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if %ERRORLEVEL% equ 0 goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if %ERRORLEVEL% equ 0 goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/samples/interactiveBrowserSample/settings.gradle b/samples/interactiveBrowserSample/settings.gradle
new file mode 100644
index 000000000..b6260dfbe
--- /dev/null
+++ b/samples/interactiveBrowserSample/settings.gradle
@@ -0,0 +1,5 @@
+include 'coreLibrary'
+
+project(':coreLibrary').projectDir = new File("../../")
+
+
diff --git a/samples/interactiveBrowserSample/src/main/java/InteractiveBrowserMain.java b/samples/interactiveBrowserSample/src/main/java/InteractiveBrowserMain.java
new file mode 100644
index 000000000..f9eb5a767
--- /dev/null
+++ b/samples/interactiveBrowserSample/src/main/java/InteractiveBrowserMain.java
@@ -0,0 +1,43 @@
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+import com.azure.identity.InteractiveBrowserCredential;
+import com.azure.identity.InteractiveBrowserCredentialBuilder;
+import com.microsoft.graph.authentication.*;
+import okhttp3.*;
+import com.microsoft.graph.httpcore.HttpClients;
+
+public class InteractiveBrowserMain {
+
+ //Replace CLIENT_ID with your own client id from an adequately configured app
+ //for requirements visit:
+ //https://github.com/Azure/azure-sdk-for-java/wiki/Set-up-Your-Environment-for-Authentication#enable-applications-for-interactive-browser-oauth-2-flow
+ private final static String CLIENT_ID = "";
+
+ //Set the scopes for your ms-graph request
+ private final static List SCOPES = Arrays.asList("User.ReadBasic.All");
+
+ public static void main(String args[]) throws Exception {
+ final InteractiveBrowserCredential interactiveBrowserCredential = new InteractiveBrowserCredentialBuilder()
+ .clientId(CLIENT_ID)
+ .redirectUrl("http://localhost:8765")
+ .build();
+
+ final TokenCredentialAuthProvider tokenCredentialAuthProvider = new TokenCredentialAuthProvider(SCOPES, interactiveBrowserCredential);
+ final OkHttpClient httpClient = HttpClients.createDefault(tokenCredentialAuthProvider);
+
+ final Request request = new Request.Builder().url("https://graph.microsoft.com/v1.0/me/").build();
+
+ httpClient.newCall(request).enqueue(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) throws IOException {
+ System.out.println(response.body().string());
+ }
+
+ @Override
+ public void onFailure(Call call, IOException e) {
+ e.printStackTrace();
+ }
+ });
+ }
+}
diff --git a/scripts/decodeAndWrite.ps1 b/scripts/decodeAndWrite.ps1
new file mode 100644
index 000000000..4b24dfa89
--- /dev/null
+++ b/scripts/decodeAndWrite.ps1
@@ -0,0 +1,32 @@
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# Licensed under the MIT License.
+
+<#
+.Synopsis
+ Decode the encoded string and write it to a local file.
+.Description
+ Recieves an encoded string value and decodes it using base64.
+ Write the new decoded string to a local file for later consumption.
+.Parameter encodedValue
+ The encoded string we wish to decode.
+.Parameter outputPath
+ The file path that we wish to write the decoded value to.
+#>
+
+Param(
+ [string]$encodedValue ,
+ [string]$outputPath
+)
+
+if($outputPath -eq "" -or $null -eq $outputPath) {
+ Write-Output "Value of Variable: outputPath is Null or Empty. Exiting."
+ Exit
+}
+if($encodedValue -eq "" -or $null -eq $encodedValue) {
+ Write-Output "Value of Variable: encodedValue is Null of Empty. Exiting."
+ Exit
+}
+
+$decodedValue = [System.Convert]::FromBase64String($encodedValue)
+$targetFullPath = Join-Path $PWD -ChildPath $outputPath
+[System.IO.File]::WriteAllBytes($targetFullPath, $decodedValue)
diff --git a/Scripts/getLatestVersion.ps1 b/scripts/getLatestVersion.ps1
similarity index 55%
rename from Scripts/getLatestVersion.ps1
rename to scripts/getLatestVersion.ps1
index 4e935336b..27056771f 100644
--- a/Scripts/getLatestVersion.ps1
+++ b/scripts/getLatestVersion.ps1
@@ -6,9 +6,11 @@
Retrieve the latest version of the library
.Description
Retrieves the latest version specified in the Gradle.Properties file
- Uses the retrieved values to update the enviornment variable VERSION_STRING
+ Uses the retrieved values to update the environment variable VERSION_STRING
.Parameter propertiesPath
+ The path pointing to the gradle.properties file.
#>
+
Param(
[string]$propertiesPath
)
@@ -19,13 +21,17 @@ if($propertiesPath -eq "" -or $null -eq $propertiesPath) {
}
$file = get-item $propertiesPath
$findVersions = $file | Select-String -Pattern "mavenMajorVersion" -Context 0,2
-$findVersions = $findVersions -split "`r`n"
+$content = Get-Content $propertiesPath
-$majorVersion = $findVersions[0].Substring($findVersions[0].Length-1)
-$minorVersion = $findVersions[1].Substring($findVersions[1].Length-1)
-$patchVersion = $findVersions[2].Substring($findVersions[2].Length-1)
+$lineNumber = $findVersions.LineNumber - 1
+$versionIndex = $content[$lineNumber].IndexOf("=")
+$versionIndex += 2 # skipping =[space]
+$majorVersion = $content[$lineNumber].Substring($versionIndex)
+$lineNumber++
+$minorVersion = $content[$lineNumber].Substring($versionIndex)
+$lineNumber++
+$patchVersion = $content[$lineNumber].Substring($versionIndex)
$version = "$majorVersion.$minorVersion.$patchVersion"
-#Update the VERSION_STRING env variable and inform the user
-Write-Host "##vso[task.setVariable variable=VERSION_STRING]$($version)";
-Write-Host "Updated the VERSION_STRING enviornment variable with the current Gradle.Properties, $version"
\ No newline at end of file
+#Set Task output to create tag
+Write-Output "::set-output name=tag::v${version}"
\ No newline at end of file
diff --git a/scripts/incrementMinorVersion.ps1 b/scripts/incrementMinorVersion.ps1
new file mode 100644
index 000000000..3d5758f1e
--- /dev/null
+++ b/scripts/incrementMinorVersion.ps1
@@ -0,0 +1,59 @@
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# Licensed under the MIT License.
+
+<#
+.Synopsis
+ Increment the minor version string in the gradle.properties if the major,
+ minor, or patch version hasn't been manually updated.
+.Description
+ Assumptions:
+ Targets Gradle.properties
+ This script assumes it is run from the repo root.
+ Minor version is typically auto-incremented.
+
+#>
+
+function Update-ReadmeVersion([string]$readmeFilePath, [version]$version) {
+ $readmeFileContent = Get-Content -Path $readmeFilePath -Raw
+ $readmeFileContent = $readmeFileContent -replace "\d{1,}\.\d{1,}\.\d{1,}", $version.ToString()
+ Set-Content -Path $readmeFilePath $readmeFileContent
+}
+
+function Update-TelemetryVersion([string]$telemetryFilePath, [version]$version) {
+ $telemetryFileContent = Get-Content -Path $telemetryFilePath -Raw
+ $telemetryFileContent = $telemetryFileContent -replace "\d{1,}\.\d{1,}\.\d{1,}", $version.ToString()
+ Set-Content -Path $telemetryFilePath $telemetryFileContent
+}
+
+function Update-PackageVersion([string]$propertiesFilePath, [version]$version) {
+ $propertiesFileContent = Get-Content -Path $propertiesFilePath -Raw
+ $propertiesFileContent = $propertiesFileContent -replace "mavenMajorVersion\s+=\s+\d{1,}", "mavenMajorVersion = $($version.Major)"
+ $propertiesFileContent = $propertiesFileContent -replace "mavenMinorVersion\s+=\s+\d{1,}", "mavenMinorVersion = $($version.Minor)"
+ $propertiesFileContent = $propertiesFileContent -replace "mavenPatchVersion\s+=\s+\d{1,}", "mavenPatchVersion = $($version.Build)"
+ Set-Content -Path $propertiesFilePath $propertiesFileContent
+}
+function Get-CurrentTelemetryVersion([string]$telemetryFilePath) {
+ $telemetryFileContent = Get-Content -Path $telemetryFilePath -Raw
+ if($telemetryFileContent -match "(\d{1,}\.\d{1,}\.\d{1,})") {
+ return [version]::Parse($Matches[1])
+ } else {
+ Write-Error "Invalid version number format"
+ return $null;
+ }
+}
+
+function Update-MinorVersionNumber([version]$currentVersion) {
+ return [version]::new($currentVersion.Major, $currentVersion.Minor + 1, 0);
+}
+
+function Update-MinorVersion() {
+ $readmeFilePath = Join-Path -Path $PWD.ToString() -ChildPath "../readme.md"
+ $propertiesFilePath = Join-Path -Path $PWD.ToString() -ChildPath "../gradle.properties"
+ $telemetryFilePath = Join-Path -Path $PWD.ToString() -ChildPath "../src/main/java/com/microsoft/graph/httpcore/TelemetryHandler.java"
+ $currentVersion = Get-CurrentTelemetryVersion -telemetryFilePath $telemetryFilePath
+ $nextVersion = Update-MinorVersionNumber -currentVersion $currentVersion
+ Update-ReadmeVersion -version $nextVersion -readmeFilePath $readmeFilePath
+ Update-TelemetryVersion -version $nextVersion -telemetryFilePath $telemetryFilePath
+ Update-PackageVersion -version $nextVersion -propertiesFilePath $propertiesFilePath
+}
+Update-MinorVersion
\ No newline at end of file
diff --git a/spotBugsExcludeFilter.xml b/spotBugsExcludeFilter.xml
new file mode 100644
index 000000000..c6b2fff2e
--- /dev/null
+++ b/spotBugsExcludeFilter.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/com/microsoft/graph/authentication/BaseAuthenticationProvider.java b/src/main/java/com/microsoft/graph/authentication/BaseAuthenticationProvider.java
new file mode 100644
index 000000000..d2fb35a9f
--- /dev/null
+++ b/src/main/java/com/microsoft/graph/authentication/BaseAuthenticationProvider.java
@@ -0,0 +1,50 @@
+package com.microsoft.graph.authentication;
+
+import java.net.URL;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Locale;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+/**
+ * Provides basic common methods for all authentication providers
+ */
+public abstract class BaseAuthenticationProvider implements IAuthenticationProvider {
+ private static final HashSet validGraphHostNames = new HashSet<>(Arrays.asList("graph.microsoft.com", "graph.microsoft.us", "dod-graph.microsoft.us", "graph.microsoft.de", "microsoftgraph.chinacloudapi.cn", "canary.graph.microsoft.com"));
+ private HashSet customHosts;
+
+ /**
+ * Allow the user to add custom hosts by passing in Array
+ * @param customHosts custom hosts passed in by user.
+ */
+ public void setCustomHosts(@Nonnull String[] customHosts) {
+ if(this.customHosts == null){
+ this.customHosts = new HashSet();
+ }
+ for(String host: customHosts){
+ this.customHosts.add(host.toLowerCase(Locale.ROOT));
+ }
+ }
+ /**
+ * Get the custom hosts set by user.
+ * @return the custom hosts set by user.
+ */
+ @Nullable
+ public String[] getCustomHosts(){
+ return customHosts.toArray(new String[customHosts.size()]);
+ }
+ /**
+ * Determines whether a request should be authenticated or not based on it's url.
+ * If you're implementing a custom provider, call that method first before getting the token
+ * @param requestUrl request URL that is about to be executed
+ * @return whether a token should be attached to this request
+ */
+ protected boolean shouldAuthenticateRequestWithUrl(@Nonnull final URL requestUrl) {
+ if (requestUrl == null || !requestUrl.getProtocol().toLowerCase(Locale.ROOT).equals("https"))
+ return false;
+ final String hostName = requestUrl.getHost().toLowerCase(Locale.ROOT);
+ return customHosts == null ? (validGraphHostNames.contains(hostName)) : (customHosts.contains(hostName));
+ }
+}
diff --git a/src/main/java/com/microsoft/graph/authentication/IAuthenticationProvider.java b/src/main/java/com/microsoft/graph/authentication/IAuthenticationProvider.java
new file mode 100644
index 000000000..ae1f478f4
--- /dev/null
+++ b/src/main/java/com/microsoft/graph/authentication/IAuthenticationProvider.java
@@ -0,0 +1,20 @@
+package com.microsoft.graph.authentication;
+
+import java.net.URL;
+import java.util.concurrent.CompletableFuture;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Authenticates requests to be sent to the API
+ */
+public interface IAuthenticationProvider {
+ /**
+ * Authenticates the request
+ *
+ * @param requestUrl the outgoing request URL
+ * @return a future with the token
+ */
+ @Nonnull
+ CompletableFuture getAuthorizationTokenAsync(@Nonnull final URL requestUrl);
+}
diff --git a/src/main/java/com/microsoft/graph/authentication/TokenCredentialAuthProvider.java b/src/main/java/com/microsoft/graph/authentication/TokenCredentialAuthProvider.java
new file mode 100644
index 000000000..de9a5d9b3
--- /dev/null
+++ b/src/main/java/com/microsoft/graph/authentication/TokenCredentialAuthProvider.java
@@ -0,0 +1,64 @@
+package com.microsoft.graph.authentication;
+
+import com.azure.core.credential.AccessToken;
+import com.azure.core.credential.TokenCredential;
+import com.azure.core.credential.TokenRequestContext;
+
+import javax.annotation.Nonnull;
+import java.net.URL;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * An implementation of the Authentication Provider with Azure-identity
+ */
+public class TokenCredentialAuthProvider extends BaseAuthenticationProvider {
+ /** TokenCredential expected from user */
+ private final TokenCredential tokenCredential;
+ /** Context options which can be optionally set by the user */
+ private final TokenRequestContext context;
+ /** Default scope to use when no scopes are provided */
+ private static final String DEFAULT_GRAPH_SCOPE = "https://graph.microsoft.com/.default";
+
+ /**
+ * Creates an Authentication provider using a passed in TokenCredential
+ *
+ * @param tokenCredential Credential object inheriting the TokenCredential interface used to instantiate the Auth Provider
+ */
+ public TokenCredentialAuthProvider(@Nonnull final TokenCredential tokenCredential) {
+ this(Collections.singletonList(DEFAULT_GRAPH_SCOPE), tokenCredential);
+ }
+
+ /**
+ * Creates an Authentication provider using a TokenCredential and list of scopes
+ *
+ * @param tokenCredential Credential object inheriting the TokenCredential interface used to instantiate the Auth Provider
+ * @param scopes Specified desired scopes of the Auth Provider
+ */
+ public TokenCredentialAuthProvider(@Nonnull final List scopes, @Nonnull final TokenCredential tokenCredential) {
+ if(scopes == null || scopes.isEmpty()) {
+ throw new IllegalArgumentException("scopes parameter cannot be null or empty");
+ }
+ this.context = new TokenRequestContext();
+ this.context.setScopes(scopes);
+ this.tokenCredential = Objects.requireNonNull(tokenCredential, "tokenCredential parameter cannot be null.");
+ }
+
+ /**
+ * Returns an AccessToken as a string
+ *
+ * @return String representing the retrieved AccessToken
+ */
+ @Nonnull
+ public CompletableFuture getAuthorizationTokenAsync(@Nonnull final URL requestUrl) {
+ if(shouldAuthenticateRequestWithUrl(Objects.requireNonNull(requestUrl, "requestUrl parameter cannot be null")))
+ return this.tokenCredential
+ .getToken(this.context)
+ .toFuture()
+ .thenApply(AccessToken::getToken);
+ else
+ return CompletableFuture.completedFuture((String)null);
+ }
+}
diff --git a/src/main/java/com/microsoft/graph/content/BatchRequest.java b/src/main/java/com/microsoft/graph/content/BatchRequest.java
new file mode 100644
index 000000000..ae7b732c4
--- /dev/null
+++ b/src/main/java/com/microsoft/graph/content/BatchRequest.java
@@ -0,0 +1,85 @@
+// ------------------------------------------------------------------------------
+// Copyright (c) 2021 Microsoft Corporation
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+// ------------------------------------------------------------------------------
+
+package com.microsoft.graph.content;
+
+import java.util.List;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import com.microsoft.graph.core.ClientException;
+import com.microsoft.graph.core.IBaseClient;
+import com.microsoft.graph.http.BaseRequest;
+import com.microsoft.graph.http.HttpMethod;
+import com.microsoft.graph.options.Option;
+
+/** Request for batch requests */
+public class BatchRequest extends BaseRequest {
+
+ /**
+ * Instantiates a new batch request
+ *
+ * @param requestUrl the URL to send the request to
+ * @param client the client to use to execute the request
+ * @param options the options to apply to the request
+ */
+ public BatchRequest(@Nonnull final String requestUrl, @Nonnull final IBaseClient> client, @Nonnull final List extends Option> options) {
+ super(requestUrl, client, options, BatchResponseContent.class);
+ }
+
+ /**
+ * Send this request
+ *
+ * @return the response object
+ * @param content content of the batch request to execute
+ * @throws ClientException an exception occurs if there was an error while the request was sent
+ */
+ @Nullable
+ public BatchResponseContent post(@Nullable final BatchRequestContent content) throws ClientException {
+ this.setHttpMethod(HttpMethod.POST);
+ final BatchResponseContent response = this.getClient().getHttpProvider().send(this, BatchResponseContent.class, content);
+ setSerializerOnSteps(response);
+ return response;
+ }
+ /**
+ * Send this request
+ *
+ * @return the response object
+ * @param content content of the batch request to execute
+ * @throws ClientException an exception occurs if there was an error while the request was sent
+ */
+ @Nullable
+ public java.util.concurrent.CompletableFuture postAsync(@Nullable final BatchRequestContent content) throws ClientException {
+ this.setHttpMethod(HttpMethod.POST);
+ return this.getClient().getHttpProvider().sendAsync(this, BatchResponseContent.class, content).thenApply(response -> {
+ setSerializerOnSteps(response);
+ return response;
+ });
+ }
+ private void setSerializerOnSteps(final BatchResponseContent response) {
+ if(response.responses != null)
+ for(final BatchResponseStep> step : response.responses) {
+ step.serializer = this.getClient().getSerializer();
+ }
+ }
+}
diff --git a/src/main/java/com/microsoft/graph/content/BatchRequestBuilder.java b/src/main/java/com/microsoft/graph/content/BatchRequestBuilder.java
new file mode 100644
index 000000000..c6ceda75d
--- /dev/null
+++ b/src/main/java/com/microsoft/graph/content/BatchRequestBuilder.java
@@ -0,0 +1,66 @@
+// ------------------------------------------------------------------------------
+// Copyright (c) 2021 Microsoft Corporation
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+// ------------------------------------------------------------------------------
+
+package com.microsoft.graph.content;
+
+import java.util.List;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import com.microsoft.graph.core.IBaseClient;
+import com.microsoft.graph.http.BaseRequestBuilder;
+import com.microsoft.graph.options.Option;
+
+/** Request builder for batch requests */
+public class BatchRequestBuilder extends BaseRequestBuilder {
+ /**
+ * Instantiates a new batch request builder
+ * @param requestUrl the URL for the request
+ * @param client the client to use to execute the request
+ * @param options the request options
+ */
+ public BatchRequestBuilder(@Nonnull final String requestUrl, @Nonnull final IBaseClient> client, @Nonnull final List extends Option> options) {
+ super(requestUrl, client, options);
+ }
+ /**
+ * Creates the request
+ *
+ * @param requestOptions the options for this request
+ * @return the IUserRequest instance
+ */
+ @Nonnull
+ public BatchRequest buildRequest(@Nullable final com.microsoft.graph.options.Option... requestOptions) {
+ return buildRequest(getOptions(requestOptions));
+ }
+
+ /**
+ * Creates the request
+ *
+ * @param requestOptions the options for this request
+ * @return the IUserRequest instance
+ */
+ @Nonnull
+ public BatchRequest buildRequest(@Nonnull final java.util.List extends com.microsoft.graph.options.Option> requestOptions) {
+ return new BatchRequest(this.getRequestUrl(), getClient(), requestOptions);
+ }
+}
diff --git a/src/main/java/com/microsoft/graph/content/BatchRequestContent.java b/src/main/java/com/microsoft/graph/content/BatchRequestContent.java
new file mode 100644
index 000000000..31c0ac766
--- /dev/null
+++ b/src/main/java/com/microsoft/graph/content/BatchRequestContent.java
@@ -0,0 +1,186 @@
+// ------------------------------------------------------------------------------
+// Copyright (c) 2021 Microsoft Corporation
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+// ------------------------------------------------------------------------------
+
+package com.microsoft.graph.content;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Objects;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import com.google.gson.annotations.Expose;
+import com.google.gson.annotations.SerializedName;
+import com.microsoft.graph.http.HttpMethod;
+import com.microsoft.graph.http.IHttpRequest;
+import com.microsoft.graph.options.HeaderOption;
+import com.microsoft.graph.serializer.IJsonBackedObject;
+
+/** Respresents the content of a JSON batch request */
+public class BatchRequestContent {
+ /** Steps part of the batch request */
+ @Expose
+ @Nullable
+ @SerializedName("requests")
+ public List> requests;
+
+ /**
+ * Adds a request as a step to the current batch. Defaults to GET if the HTTP method is not set in the request.
+ * @param request the request to add as a step
+ * @return the id of the step that was just added to the batch
+ */
+ @Nonnull
+ public String addBatchRequestStep(@Nonnull final IHttpRequest request) {
+ Objects.requireNonNull(request, "request parameter cannot be null");
+ return addBatchRequestStep(request, request.getHttpMethod() == null ? HttpMethod.GET : request.getHttpMethod());
+ }
+ /**
+ * Adds a request as a step to the current batch
+ * @param request the request to add as a step
+ * @param httpMethod the HttpMethod to execute the request with
+ * @return the id of the step that was just added to the batch
+ */
+ @Nonnull
+ public String addBatchRequestStep(@Nonnull final IHttpRequest request, @Nonnull final HttpMethod httpMethod) {
+ return addBatchRequestStep(request, httpMethod, null);
+ }
+ /**
+ * Adds a request as a step to the current batch
+ * @param request the request to add as a step
+ * @param httpMethod the HttpMethod to execute the request with
+ * @param the type of the request body
+ * @param serializableBody the body of the request to be serialized
+ * @return the id of the step that was just added to the batch
+ */
+ @Nonnull
+ public String addBatchRequestStep(@Nonnull final IHttpRequest request, @Nonnull final HttpMethod httpMethod, @Nullable final T serializableBody) {
+ return addBatchRequestStep(request, httpMethod, serializableBody, (String[])null);
+ }
+ /**
+ * Adds a request as a step to the current batch
+ * @param request the request to add as a step
+ * @param httpMethod the HttpMethod to execute the request with
+ * @param the type of the request body
+ * @param serializableBody the body of the request to be serialized
+ * @param dependsOnRequestsIds the ids of steps this request depends on
+ * @return the id of the step that was just added to the batch
+ */
+ @Nonnull
+ public String addBatchRequestStep(@Nonnull final IHttpRequest request, @Nonnull final HttpMethod httpMethod, @Nullable final T serializableBody, @Nullable final String ...dependsOnRequestsIds) {
+ Objects.requireNonNull(request, "request parameter cannot be null");
+ Objects.requireNonNull(httpMethod, "httpMethod parameter cannot be null");
+ if(dependsOnRequestsIds != null)
+ for(final String id : dependsOnRequestsIds) {
+ if(getStepById(id) == null)
+ throw new IllegalArgumentException("the current request depends on a inexisting request");
+ }
+
+ if(requests == null)
+ requests = new ArrayList<>();
+
+ final Matcher protocolAndHostReplacementMatcher = protocolAndHostReplacementPattern.matcher(request.getRequestUrl().toString());
+ final BatchRequestStep step = new BatchRequestStep() {{
+ url = protocolAndHostReplacementMatcher.replaceAll("");
+ body = serializableBody;
+ method = httpMethod.toString().toUpperCase(Locale.getDefault());
+ dependsOn = dependsOnRequestsIds != null && dependsOnRequestsIds.length > 0 ? new HashSet<>(Arrays.asList(dependsOnRequestsIds)) : null;
+ id = getNextRequestId();
+ }};
+
+ if(!request.getHeaders().isEmpty()) {
+ step.headers = new HashMap<>();
+ for(final HeaderOption headerOption : request.getHeaders())
+ step.headers.putIfAbsent(headerOption.getName().toLowerCase(Locale.getDefault()), headerOption.getValue().toString());
+ }
+ if(step.body != null && step.body instanceof IJsonBackedObject &&
+ (step.headers == null || !step.headers.containsKey(contentTypeHeaderKey))) {
+ if(step.headers == null)
+ step.headers = new HashMap<>();
+ step.headers.putIfAbsent(contentTypeHeaderKey, "application/json");
+ }
+ requests.add(step);
+ return step.id;
+ }
+ private static final String contentTypeHeaderKey = "content-type";
+ /**
+ * Removes requests from the requests to be executed by the batch. Also removes any dependency references that might exist.
+ * @param stepIds ids of steps to be removed.
+ */
+ public void removeBatchRequestStepWithId(@Nonnull final String ...stepIds) {
+ if(requests == null) return;
+
+ for(final String stepId : stepIds) {
+ Objects.requireNonNull(stepId, "parameter stepIds cannot contain null values");
+ requests.removeIf(x -> stepId.equals(x.id));
+ for(final BatchRequestStep> step : requests) {
+ if(step.dependsOn != null) {
+ step.dependsOn.removeIf(stepId::equals);
+ if(step.dependsOn.isEmpty())
+ step.dependsOn = null; // so we don't send dependsOn: [] over the wire
+ }
+ }
+ }
+ }
+ /**
+ * Gets a step by its step id
+ * @param the type of the request body
+ * @param stepId the request step id returned from the add method
+ * @return the request corresponding to the provided id or null
+ */
+ @Nullable
+ @SuppressWarnings("unchecked")
+ public BatchRequestStep getStepById(@Nonnull final String stepId) {
+ Objects.requireNonNull(stepId, "parameter stepId cannot be null");
+ if(requests == null) return null;
+
+ for(final BatchRequestStep> step : requests) {
+ if(stepId.equals(step.id))
+ return (BatchRequestStep)step;
+ }
+ return null;
+ }
+ /** pattern to replace the protocol and host part of the request if specified */
+ @Nonnull
+ protected static final Pattern protocolAndHostReplacementPattern =
+ Pattern.compile("(?i)^http[s]?:\\/\\/(?:graph|dod-graph|microsoftgraph)\\.(?:microsoft|chinacloudapi)\\.(?:com|cn|us|de)\\/(?:v1\\.0|beta)"); // (?i) case insensitive
+ //https://docs.microsoft.com/en-us/graph/deployments#microsoft-graph-and-graph-explorer-service-root-endpoints
+ /**
+ * Generates a randomly available request id
+ * @return a random request id
+ */
+ @Nonnull
+ protected String getNextRequestId() {
+ String requestId;
+ do {
+ requestId = Integer.toString(ThreadLocalRandom.current().nextInt(1, Integer.MAX_VALUE));
+ } while(getStepById(requestId) != null);
+ return requestId;
+ }
+}
diff --git a/src/main/java/com/microsoft/graph/content/BatchRequestStep.java b/src/main/java/com/microsoft/graph/content/BatchRequestStep.java
new file mode 100644
index 000000000..cb6ae415a
--- /dev/null
+++ b/src/main/java/com/microsoft/graph/content/BatchRequestStep.java
@@ -0,0 +1,49 @@
+// ------------------------------------------------------------------------------
+// Copyright (c) 2021 Microsoft Corporation
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+// ------------------------------------------------------------------------------
+
+package com.microsoft.graph.content;
+
+import java.util.HashSet;
+
+import javax.annotation.Nullable;
+
+import com.google.gson.annotations.Expose;
+import com.google.gson.annotations.SerializedName;
+
+/** the http request for the batch step */
+public class BatchRequestStep extends BatchStep {
+ /** The URL to query for the step */
+ @Nullable
+ @Expose
+ @SerializedName("url")
+ public String url;
+ /** The HTTP method to use to execute the request */
+ @Nullable
+ @Expose
+ @SerializedName("method")
+ public String method;
+ /** The IDs of the steps this step depends on before being executed */
+ @Nullable
+ @Expose
+ @SerializedName("dependsOn")
+ public HashSet dependsOn;
+}
diff --git a/src/main/java/com/microsoft/graph/content/BatchResponseContent.java b/src/main/java/com/microsoft/graph/content/BatchResponseContent.java
new file mode 100644
index 000000000..fe8022524
--- /dev/null
+++ b/src/main/java/com/microsoft/graph/content/BatchResponseContent.java
@@ -0,0 +1,59 @@
+// ------------------------------------------------------------------------------
+// Copyright (c) 2021 Microsoft Corporation
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+// ------------------------------------------------------------------------------
+
+package com.microsoft.graph.content;
+
+import java.util.List;
+import java.util.Objects;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import com.google.gson.JsonElement;
+import com.google.gson.annotations.Expose;
+import com.google.gson.annotations.SerializedName;
+
+/** Respresents the result of a JSON batch request */
+public class BatchResponseContent {
+ /** Responses to the steps from the request */
+ @Nullable
+ @Expose
+ @SerializedName("responses")
+ public List> responses;
+
+ /**
+ * Gets a response to a request in the batch by its id
+ * @param stepId Id of the request step in the batch request
+ * @return The step response corresponding to the ID or null
+ */
+ @Nullable
+ public BatchResponseStep getResponseById(@Nonnull final String stepId) {
+ Objects.requireNonNull(stepId, "parameter stepId cannot be null");
+ if(responses == null) return null;
+
+ for(final BatchResponseStep step : responses) {
+ if(stepId.equals(step.id))
+ return step;
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/com/microsoft/graph/content/BatchResponseStep.java b/src/main/java/com/microsoft/graph/content/BatchResponseStep.java
new file mode 100644
index 000000000..659761385
--- /dev/null
+++ b/src/main/java/com/microsoft/graph/content/BatchResponseStep.java
@@ -0,0 +1,76 @@
+// ------------------------------------------------------------------------------
+// Copyright (c) 2021 Microsoft Corporation
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+// ------------------------------------------------------------------------------
+package com.microsoft.graph.content;
+
+import java.util.ArrayList;
+import java.util.Objects;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import com.google.gson.JsonElement;
+import com.google.gson.annotations.Expose;
+import com.google.gson.annotations.SerializedName;
+import com.microsoft.graph.http.GraphErrorResponse;
+import com.microsoft.graph.http.GraphFatalServiceException;
+import com.microsoft.graph.http.GraphServiceException;
+import com.microsoft.graph.logger.ILogger;
+import com.microsoft.graph.logger.LoggerLevel;
+import com.microsoft.graph.serializer.DefaultSerializer;
+import com.microsoft.graph.serializer.ISerializer;
+
+/** Response for the batch step */
+public class BatchResponseStep extends BatchStep {
+ /** Http status code of the response */
+ @Expose
+ @SerializedName("status")
+ public int status;
+ /** Serializer to use for response deserialization */
+ @Nullable
+ protected ISerializer serializer;
+
+ /**
+ * Returned the deserialized response body of the current step
+ * @param type of the response body
+ * @param resultClass class of the resulting response body
+ * @return the deserialized response body
+ * @throws GraphServiceException when a bad request was sent
+ * @throws GraphFatalServiceException when the service did not complete the operation as expected because of an internal error
+ */
+ @Nullable
+ public T2 getDeserializedBody(@Nonnull final Class resultClass) throws GraphServiceException, GraphFatalServiceException {
+ Objects.requireNonNull(resultClass, "parameter resultClass cannot be null");
+ if(serializer == null || body == null || !(body instanceof JsonElement)) return null;
+
+ final GraphErrorResponse error = serializer.deserializeObject((JsonElement)body, GraphErrorResponse.class);
+ if(error == null || error.error == null) {
+ return serializer.deserializeObject((JsonElement)body, resultClass);
+ } else {
+ boolean verboseError = false;
+ if(serializer instanceof DefaultSerializer) {
+ final ILogger logger = ((DefaultSerializer)serializer).getLogger();
+ verboseError = logger != null && logger.getLoggingLevel() == LoggerLevel.DEBUG;
+ }
+ throw GraphServiceException.createFromResponse("", "", new ArrayList<>(), "", headers, "", status, error, verboseError);
+ }
+ }
+}
diff --git a/src/main/java/com/microsoft/graph/content/BatchStep.java b/src/main/java/com/microsoft/graph/content/BatchStep.java
new file mode 100644
index 000000000..89773904e
--- /dev/null
+++ b/src/main/java/com/microsoft/graph/content/BatchStep.java
@@ -0,0 +1,49 @@
+// ------------------------------------------------------------------------------
+// Copyright (c) 2021 Microsoft Corporation
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+// ------------------------------------------------------------------------------
+
+package com.microsoft.graph.content;
+
+import java.util.HashMap;
+
+import javax.annotation.Nullable;
+
+import com.google.gson.annotations.Expose;
+import com.google.gson.annotations.SerializedName;
+
+/** Common abstractions between batch request steps and batch response steps */
+public abstract class BatchStep {
+ /** The Id of the step */
+ @Expose
+ @Nullable
+ @SerializedName("id")
+ public String id;
+ /** The request/response headers for the step */
+ @Expose
+ @Nullable
+ @SerializedName("headers")
+ public HashMap headers;
+ /** The body of request/response for the step */
+ @Nullable
+ @Expose
+ @SerializedName("body")
+ public T body;
+}
diff --git a/src/main/java/com/microsoft/graph/content/MSBatchRequestContent.java b/src/main/java/com/microsoft/graph/content/MSBatchRequestContent.java
deleted file mode 100644
index 64eed6c23..000000000
--- a/src/main/java/com/microsoft/graph/content/MSBatchRequestContent.java
+++ /dev/null
@@ -1,182 +0,0 @@
-package com.microsoft.graph.content;
-
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ThreadLocalRandom;
-
-import com.google.gson.JsonArray;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import com.google.gson.JsonParser;
-import com.google.gson.JsonPrimitive;
-import com.google.gson.JsonParseException;
-
-import okhttp3.Headers;
-import okhttp3.Request;
-import okhttp3.RequestBody;
-import okio.Buffer;
-
-public class MSBatchRequestContent {
- private final LinkedHashMap batchRequestStepsHashMap;
-
- // Maximum number of requests that can be sent in a batch
- public static final int MAX_NUMBER_OF_REQUESTS = 20;
-
- /*
- * Creates Batch request content using list provided
- *
- * @param batchRequestStepsArray List of batch steps for batching
- */
- public MSBatchRequestContent(final List batchRequestStepsArray) {
- if (batchRequestStepsArray.size() > MAX_NUMBER_OF_REQUESTS)
- throw new IllegalArgumentException("Number of batch request steps cannot exceed " + MAX_NUMBER_OF_REQUESTS);
-
- this.batchRequestStepsHashMap = new LinkedHashMap<>();
- for (final MSBatchRequestStep requestStep : batchRequestStepsArray)
- addBatchRequestStep(requestStep);
- }
-
- /*
- * Creates empty batch request content
- */
- public MSBatchRequestContent() {
- this.batchRequestStepsHashMap = new LinkedHashMap<>();
- }
-
- /*
- * @param batchRequestStep Batch request step adding to batch content
- *
- * @return true or false based on addition or no addition of batch request step
- * given
- */
- public boolean addBatchRequestStep(final MSBatchRequestStep batchRequestStep) {
- if (batchRequestStepsHashMap.containsKey(batchRequestStep.getRequestId()) ||
- batchRequestStepsHashMap.size() >= MAX_NUMBER_OF_REQUESTS)
- return false;
- batchRequestStepsHashMap.put(batchRequestStep.getRequestId(), batchRequestStep);
- return true;
- }
-
- /**
- * Add steps to batch from OkHttp.Request
- * @param request the request to add to the batch
- * @param arrayOfDependsOnIds ids of steps this step depends on
- * @return the step id
- */
- public String addBatchRequestStep(final Request request, final String... arrayOfDependsOnIds) {
- String requestId;
- do {
- requestId = Integer.toString(ThreadLocalRandom.current().nextInt(1, Integer.MAX_VALUE));
- } while(batchRequestStepsHashMap.keySet().contains(requestId));
- if(addBatchRequestStep(new MSBatchRequestStep(requestId, request, Arrays.asList(arrayOfDependsOnIds))))
- return requestId;
- else
- throw new IllegalArgumentException("unable to add step to batch. Number of batch request steps cannot exceed " + MAX_NUMBER_OF_REQUESTS);
- }
-
- /*
- * @param requestId Id of Batch request step to be removed
- *
- * @return true or false based on removal or no removal of batch request step
- * with given id
- */
- public boolean removeBatchRequestStepWithId(final String requestId) {
- boolean removed = false;
- if (batchRequestStepsHashMap.containsKey(requestId)) {
- batchRequestStepsHashMap.remove(requestId);
- removed = true;
- for (final Map.Entry steps : batchRequestStepsHashMap.entrySet()) {
- if (steps.getValue() != null && steps.getValue().getArrayOfDependsOnIds() != null) {
- while (steps.getValue().getArrayOfDependsOnIds().remove(requestId))
- ;
- }
- }
- }
- return removed;
- }
-
- /*
- * @return Batch request content's json as String
- */
- public String getBatchRequestContent() {
- final JsonObject batchRequestContentMap = new JsonObject();
- final JsonArray batchContentArray = new JsonArray();
- for (final Map.Entry requestStep : batchRequestStepsHashMap.entrySet()) {
- batchContentArray.add(getBatchRequestObjectFromRequestStep(requestStep.getValue()));
- }
- batchRequestContentMap.add("requests", batchContentArray);
-
- final String content = batchRequestContentMap.toString();
- return content;
- }
-
- private JsonObject getBatchRequestObjectFromRequestStep(final MSBatchRequestStep batchRequestStep) {
- final JsonObject contentmap = new JsonObject();
- contentmap.add("id", new JsonPrimitive(batchRequestStep.getRequestId()));
-
- final String url = batchRequestStep.getRequest().url().toString()
- .replaceAll("https://graph.microsoft.com/v1.0/", "").replaceAll("http://graph.microsoft.com/v1.0/", "")
- .replaceAll("https://graph.microsoft.com/beta/", "").replaceAll("http://graph.microsoft.com/beta/", "");
- contentmap.add("url", new JsonPrimitive(url));
-
- contentmap.add("method", new JsonPrimitive(batchRequestStep.getRequest().method().toString()));
-
- final Headers headers = batchRequestStep.getRequest().headers();
- if (headers != null && headers.size() != 0) {
- final JsonObject headerMap = new JsonObject();
- for (final Map.Entry> entry : headers.toMultimap().entrySet()) {
- headerMap.add(entry.getKey(), new JsonPrimitive(getHeaderValuesAsString(entry.getValue())));
- }
- contentmap.add("headers", headerMap);
- }
-
- final List arrayOfDependsOnIds = batchRequestStep.getArrayOfDependsOnIds();
- if (arrayOfDependsOnIds != null) {
- final JsonArray array = new JsonArray();
- for (final String dependsOnId : arrayOfDependsOnIds)
- array.add(dependsOnId);
- contentmap.add("dependsOn", array);
- }
-
- final RequestBody body = batchRequestStep.getRequest().body();
- if (body != null) {
- try {
- contentmap.add("body", requestBodyToJSONObject(batchRequestStep.getRequest()));
- } catch (IOException | JsonParseException e) {
- e.printStackTrace();
- }
- }
- return contentmap;
- }
-
- private String getHeaderValuesAsString(final List list) {
- if (list == null || list.size() == 0)
- return "";
- final StringBuilder builder = new StringBuilder(list.get(0));
- for (int i = 1; i < list.size(); i++) {
- builder.append(";");
- builder.append(list.get(i));
- }
- return builder.toString();
- }
-
- private JsonObject requestBodyToJSONObject(final Request request) throws IOException, JsonParseException {
- if (request == null || request.body() == null)
- return null;
- final Request copy = request.newBuilder().build();
- final Buffer buffer = new Buffer();
- copy.body().writeTo(buffer);
- final String requestBody = buffer.readUtf8();
- if(requestBody == null || requestBody == "")
- return null;
- final JsonElement requestBodyElement = JsonParser.parseString(requestBody);
- if(requestBodyElement == null || !requestBodyElement.isJsonObject())
- return null;
- else
- return requestBodyElement.getAsJsonObject();
- }
-
-}
diff --git a/src/main/java/com/microsoft/graph/content/MSBatchRequestStep.java b/src/main/java/com/microsoft/graph/content/MSBatchRequestStep.java
deleted file mode 100644
index ac13bb0ab..000000000
--- a/src/main/java/com/microsoft/graph/content/MSBatchRequestStep.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package com.microsoft.graph.content;
-
-import java.util.List;
-
-import okhttp3.Request;
-
-public class MSBatchRequestStep {
- private String requestId;
- private Request request;
- private List arrayOfDependsOnIds;
-
- public MSBatchRequestStep(String requestId, Request request, List arrayOfDependsOnIds) {
- if(requestId == null)
- throw new IllegalArgumentException("Request Id cannot be null.");
- if(requestId.length() == 0)
- throw new IllegalArgumentException("Request Id cannot be empty.");
- if(request == null)
- new IllegalArgumentException("Request cannot be null.");
-
- this.requestId = requestId;
- this.request = request;
- this.arrayOfDependsOnIds = arrayOfDependsOnIds;
- }
-
- public String getRequestId() {
- return requestId;
- }
-
- public Request getRequest() {
- return request;
- }
-
- public List getArrayOfDependsOnIds(){
- return arrayOfDependsOnIds;
- }
-}
diff --git a/src/main/java/com/microsoft/graph/content/MSBatchResponseContent.java b/src/main/java/com/microsoft/graph/content/MSBatchResponseContent.java
deleted file mode 100644
index ca40384e9..000000000
--- a/src/main/java/com/microsoft/graph/content/MSBatchResponseContent.java
+++ /dev/null
@@ -1,257 +0,0 @@
-package com.microsoft.graph.content;
-
-import java.io.IOException;
-import java.util.LinkedHashMap;
-import java.util.Iterator;
-import java.util.Map;
-
-import com.google.gson.JsonArray;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import com.google.gson.JsonParser;
-import com.google.gson.JsonParseException;
-
-import okhttp3.MediaType;
-import okhttp3.Request;
-import okhttp3.RequestBody;
-import okhttp3.Response;
-import okhttp3.ResponseBody;
-import okio.Buffer;
-
-public class MSBatchResponseContent {
-
- private final Response batchResponse;
- private LinkedHashMap batchRequestsHashMap;
- private JsonArray batchResponseArray;
- private String nextLink;
-
- /*
- * @param batchResponse OkHttp batch response on execution of batch requests
- */
- public MSBatchResponseContent(final Response batchResponse) {
- this.batchResponse = batchResponse;
- update(batchResponse);
- }
-
- /*
- * Returns OkHttp Response of given request Id
- *
- * @param requestId Request Id of batch step
- *
- * @return OkHttp Response corresponding to requestId
- */
- public Response getResponseById(final String requestId) {
- if (batchResponseArray == null)
- return null;
-
- final JsonArray responses = batchResponseArray;
-
- for (final JsonElement response : responses) {
- if(!response.isJsonObject())
- continue;
- final JsonObject jsonresponse = response.getAsJsonObject();
- final JsonElement idElement = jsonresponse.get("id");
- if (idElement != null && idElement.isJsonPrimitive()) {
- final String id = idElement.getAsString();
- if (id.compareTo(requestId) == 0) {
- final Response.Builder builder = new Response.Builder();
-
- // Put corresponding request into the constructed response
- builder.request(batchRequestsHashMap.get(requestId));
- // copy protocol and message same as of batch response
- builder.protocol(batchResponse.protocol());
- builder.message(batchResponse.message());
-
- // Put status code of the corresponding request in JsonArray
- final JsonElement statusElement = jsonresponse.get("status");
- if (statusElement != null && statusElement.isJsonPrimitive()) {
- final Long status = statusElement.getAsLong();
- builder.code(status.intValue());
- }
-
- // Put body from response array for corresponding id into constructing response
- final JsonElement jsonBodyElement = jsonresponse.get("body");
- if (jsonBodyElement != null && jsonBodyElement.isJsonObject()) {
- final JsonObject JsonObject = jsonBodyElement.getAsJsonObject();
- final String bodyAsString = JsonObject.toString();
- final ResponseBody responseBody = ResponseBody
- .create(MediaType.parse("application/json; charset=utf-8"), bodyAsString);
- builder.body(responseBody);
- }
-
- // Put headers from response array for corresponding id into constructing
- // response
- final JsonElement jsonheadersElement = jsonresponse.get("headers");
- if (jsonheadersElement != null && jsonheadersElement.isJsonObject()) {
- final JsonObject jsonheaders = jsonheadersElement.getAsJsonObject();
- for (final String key : jsonheaders.keySet()) {
- final JsonElement strValueElement = jsonheaders.get(key);
- if (strValueElement != null && strValueElement.isJsonPrimitive()) {
- final String strvalue = strValueElement.getAsString();
- for (final String value : strvalue.split(";")) {
- builder.header(key, value);
- }
- }
- }
- }
- return builder.build();
- }
- }
- }
- return null;
- }
-
- /**
- * Get map of id and responses
- *
- * @return responses in Map of id and response
- */
- public Map getResponses() {
- if (batchResponseArray == null)
- return null;
- final Map responsesMap = new LinkedHashMap<>();
- for (final String id : batchRequestsHashMap.keySet()) {
- responsesMap.put(id, getResponseById(id));
- }
- return responsesMap;
- }
-
- /**
- * Get iterator over the responses
- *
- * @return iterator for responses
- */
- public Iterator> getResponsesIterator() {
- final Map responsesMap = getResponses();
- return responsesMap != null ? responsesMap.entrySet().iterator() : null;
- }
-
- public void update(final Response batchResponse) {
- if (batchResponse == null)
- throw new IllegalArgumentException("Batch Response cannot be null");
-
- final Map requestMap = createBatchRequestsHashMap(batchResponse);
- if (batchRequestsHashMap == null)
- batchRequestsHashMap = new LinkedHashMap<>();
- if (requestMap != null)
- batchRequestsHashMap.putAll(requestMap);
-
- if (batchResponse.body() != null) {
- try {
- final String batchResponseData = batchResponse.body().string();
- if (batchResponseData != null) {
- final JsonObject batchResponseObj = stringToJSONObject(batchResponseData);
- if (batchResponseObj != null) {
-
- final JsonElement nextLinkElement = batchResponseObj.get("@odata.nextLink");
- if (nextLinkElement != null && nextLinkElement.isJsonPrimitive())
- nextLink = nextLinkElement.getAsString();
-
- if (batchResponseArray == null)
- batchResponseArray = new JsonArray();
-
- final JsonElement responseArrayElement = batchResponseObj.get("responses");
- if (responseArrayElement != null && responseArrayElement.isJsonArray()) {
- final JsonArray responseArray = responseArrayElement.getAsJsonArray();
- batchResponseArray.addAll(responseArray);
- }
- }
- }
- } catch (final IOException e) {
- e.printStackTrace();
- }
- }
- }
-
- /*
- * @return nextLink of batch response
- */
- public String nextLink() {
- return nextLink;
- }
-
- private Map createBatchRequestsHashMap(final Response batchResponse) {
- if (batchResponse == null)
- return null;
- try {
- final Map batchRequestsHashMap = new LinkedHashMap<>();
- final JsonObject requestJSONObject = requestBodyToJSONObject(batchResponse.request());
- final JsonElement requestArrayElement = requestJSONObject.get("requests");
- if (requestArrayElement != null && requestArrayElement.isJsonArray()) {
- final JsonArray requestArray = requestArrayElement.getAsJsonArray();
- for (final JsonElement item : requestArray) {
- if(!item.isJsonObject())
- continue;
- final JsonObject requestObject = item.getAsJsonObject();
-
- final Request.Builder builder = new Request.Builder();
-
- final JsonElement urlElement = requestObject.get("url");
- if (urlElement != null && urlElement.isJsonPrimitive()) {
- final StringBuilder fullUrl = new StringBuilder(
- batchResponse.request().url().toString().replace("$batch", ""));
- fullUrl.append(urlElement.getAsString());
- builder.url(fullUrl.toString());
- }
- final JsonElement jsonHeadersElement = requestObject.get("headers");
- if (jsonHeadersElement != null && jsonHeadersElement.isJsonObject()) {
- final JsonObject jsonheaders = jsonHeadersElement.getAsJsonObject();
- for (final String key : jsonheaders.keySet()) {
- final JsonElement strvalueElement = jsonheaders.get(key);
- if (strvalueElement != null && strvalueElement.isJsonPrimitive()) {
- final String strvalue = strvalueElement.getAsString();
- for (final String value : strvalue.split("; ")) {
- builder.header(key, value);
- }
- }
- }
- }
- final JsonElement jsonBodyElement = requestObject.get("body");
- final JsonElement jsonMethodElement = requestObject.get("method");
- if (jsonBodyElement != null && jsonMethodElement != null
- && jsonBodyElement.isJsonObject() && jsonMethodElement.isJsonPrimitive()) {
- final JsonObject JsonObject = jsonBodyElement.getAsJsonObject();
- final String bodyAsString = JsonObject.toString();
- final RequestBody requestBody = RequestBody
- .create(MediaType.parse("application/json; charset=utf-8"), bodyAsString);
- builder.method(jsonMethodElement.getAsString(), requestBody);
- } else if (jsonMethodElement != null) {
- builder.method(jsonMethodElement.getAsString(), null);
- }
- final JsonElement jsonIdElement = requestObject.get("id");
- if (jsonIdElement != null && jsonIdElement.isJsonPrimitive()) {
- batchRequestsHashMap.put(jsonIdElement.getAsString(), builder.build());
- }
- }
- }
- return batchRequestsHashMap;
-
- } catch (IOException | JsonParseException e) {
- e.printStackTrace();
- }
- return null;
- }
-
- private JsonObject stringToJSONObject(final String input) {
- JsonObject JsonObject = null;
- try {
- if (input != null) {
- JsonObject = JsonParser.parseString(input).getAsJsonObject();
- }
- } catch (final Exception e) {
- e.printStackTrace();
- }
- return JsonObject;
- }
-
- private JsonObject requestBodyToJSONObject(final Request request) throws IOException, JsonParseException {
- if (request == null || request.body() == null)
- return null;
- final Request copy = request.newBuilder().build();
- final Buffer buffer = new Buffer();
- copy.body().writeTo(buffer);
- final String requestBody = buffer.readUtf8();
- final JsonObject JsonObject = JsonParser.parseString(requestBody).getAsJsonObject();
- return JsonObject;
- }
-}
diff --git a/src/main/java/com/microsoft/graph/core/BaseClient.java b/src/main/java/com/microsoft/graph/core/BaseClient.java
new file mode 100644
index 000000000..cbdbac4e7
--- /dev/null
+++ b/src/main/java/com/microsoft/graph/core/BaseClient.java
@@ -0,0 +1,381 @@
+// ------------------------------------------------------------------------------
+// Copyright (c) 2017 Microsoft Corporation
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+// ------------------------------------------------------------------------------
+
+package com.microsoft.graph.core;
+
+import com.google.gson.JsonElement;
+import com.microsoft.graph.http.CoreHttpProvider;
+import com.microsoft.graph.http.IHttpProvider;
+import com.microsoft.graph.httpcore.HttpClients;
+import com.microsoft.graph.authentication.IAuthenticationProvider;
+import com.microsoft.graph.content.BatchRequestBuilder;
+import com.microsoft.graph.logger.DefaultLogger;
+import com.microsoft.graph.logger.ILogger;
+import com.microsoft.graph.serializer.DefaultSerializer;
+import com.microsoft.graph.serializer.ISerializer;
+
+import javax.annotation.Nullable;
+
+import java.util.Collections;
+import java.util.Objects;
+
+import javax.annotation.Nonnull;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+
+/**
+ * A client that communications with an OData service
+ * @param type of a request for the native http client
+ */
+public class BaseClient implements IBaseClient {
+ /**
+ * Restricted constructor
+ */
+ protected BaseClient() {
+ }
+
+ /**
+ * The default endpoint for the Microsoft Graph Service
+ */
+ public static final String DEFAULT_GRAPH_ENDPOINT = "https://graph.microsoft.com/v1.0";
+
+ /**
+ * The current endpoint
+ */
+ private String endpoint;
+
+ @Override
+ @Nonnull
+ public String getServiceRoot() {
+ if (endpoint == null) {
+ endpoint = DEFAULT_GRAPH_ENDPOINT;
+ }
+ return endpoint;
+ }
+
+ @Override
+ public void setServiceRoot(@Nonnull final String value) {
+ Objects.requireNonNull(value, "value parameter cannot be null");
+ endpoint = String.valueOf(value);
+ }
+
+ /**
+ * Send a custom request to Graph
+ *
+ * @param url
+ * the full URL to make a request with
+ * @param responseType
+ * the response class to deserialize the response into
+ * @return the instance of this builder
+ */
+ @Nonnull
+ public CustomRequestBuilder customRequest(@Nonnull final String url, @Nonnull final Class responseType) {
+ Objects.requireNonNull(url, "url parameter cannot be null");
+ Objects.requireNonNull(responseType, "responseType parameter cannot be null");
+ return new CustomRequestBuilder<>(getServiceRoot() + url, this, null, responseType);
+ }
+
+ /**
+ * Send a custom request to Graph
+ *
+ * @param url
+ * the full URL to make a request with
+ * @return the instance of this builder
+ */
+ @Nonnull
+ public CustomRequestBuilder customRequest(@Nonnull final String url) {
+ return this.customRequest(url, JsonElement.class);
+ }
+
+ /**
+ * Get the batch request builder.
+ * @return a request builder to execute a batch.
+ */
+ @Nonnull
+ public BatchRequestBuilder batch() {
+ return new BatchRequestBuilder(getServiceRoot() + "/$batch", this, Collections.emptyList());
+ }
+
+ /**
+ * Gets the builder to start configuring the client
+ *
+ * @return builder to start configuring the client
+ */
+ @Nonnull
+ public static Builder builder() {
+ return builder(OkHttpClient.class, Request.class);
+ }
+
+ /**
+ * Gets the builder to start configuring the client
+ *
+ * @param the type of the native http client
+ * @param the type of the native http request
+ * @param nativeClientClass the class of the native http client
+ * @param nativeRequestClass the class of the native http request
+ * @return builder to start configuring the client
+ */
+ @Nonnull
+ public static Builder builder(@Nonnull final Class nativeClientClass, @Nonnull final Class nativeRequestClass) {
+ return new Builder<>();
+ }
+
+ /**
+ * Builder to help configure the Graph service client
+ * @param type of the native http library client
+ * @param type of a request for the native http client
+ */
+ public static class Builder {
+ private ISerializer serializer;
+ private IHttpProvider httpProvider;
+ private ILogger logger;
+ private httpClientType httpClient;
+ private IAuthenticationProvider auth;
+
+ private IAuthenticationProvider getAuthenticationProvider() {
+ if(auth == null) {
+ throw new NullPointerException("auth");
+ } else {
+ return auth;
+ }
+ }
+ private ILogger getLogger() {
+ if(logger == null) {
+ return new DefaultLogger();
+ } else {
+ return logger;
+ }
+ }
+ private ISerializer getSerializer() {
+ if(serializer == null) {
+ return new DefaultSerializer(getLogger());
+ } else {
+ return serializer;
+ }
+ }
+ @SuppressWarnings("unchecked")
+ private httpClientType getHttpClient() {
+ if(httpClient == null) {
+ return (httpClientType)HttpClients.createDefault(getAuthenticationProvider());
+ } else {
+ return httpClient;
+ }
+ }
+ @SuppressWarnings("unchecked")
+ private IHttpProvider getHttpProvider() {
+ if(httpProvider == null) {
+ return (IHttpProvider)new CoreHttpProvider(getSerializer(), getLogger(), (OkHttpClient)getHttpClient());
+ } else {
+ return httpProvider;
+ }
+ }
+
+ /**
+ * Sets the serializer.
+ *
+ * @param serializer
+ * the serializer
+ * @return the instance of this builder
+ */
+ @Nonnull
+ public Builder serializer(@Nonnull final ISerializer serializer) {
+ Objects.requireNonNull(serializer, "parameter serializer cannot be null");
+ this.serializer = serializer;
+ return this;
+ }
+
+ /**
+ * Sets the httpProvider
+ *
+ * @param httpProvider
+ * the httpProvider
+ * @return the instance of this builder
+ */
+ @Nonnull
+ public Builder httpProvider(@Nonnull final IHttpProvider httpProvider) {
+ Objects.requireNonNull(httpProvider, "parameter httpProvider cannot be null");
+ this.httpProvider = httpProvider;
+ return this;
+ }
+
+ /**
+ * Sets the logger
+ *
+ * @param logger
+ * the logger
+ * @return the instance of this builder
+ */
+ @Nonnull
+ @SuppressFBWarnings
+ public Builder logger(@Nonnull final ILogger logger) {
+ Objects.requireNonNull(logger, "parameter logger cannot be null");
+ this.logger = logger;
+ return this;
+ }
+
+ /**
+ * Sets the http client
+ *
+ * @param client the http client
+ *
+ * @return the instance of this builder
+ */
+ @Nonnull
+ public Builder httpClient(@Nonnull final httpClientType client) {
+ Objects.requireNonNull(client, "parameter client cannot be null");
+ this.httpClient = client;
+ return this;
+ }
+
+ /**
+ * Sets the authentication provider
+ *
+ * @param auth the authentication provider
+ * @return the instance of this builder
+ */
+ @Nonnull
+ public Builder authenticationProvider(@Nonnull final IAuthenticationProvider auth) {
+ Objects.requireNonNull(auth, "parameter auth cannot be null");
+ this.auth = auth;
+ return this;
+ }
+
+ /**
+ * Builds and returns the Graph service client.
+ *
+ * @param instance the instance to set the information for
+ * @param the type of the client to return
+ * @return the Graph service client object
+ * @throws ClientException if there was an exception creating the client
+ */
+ @Nonnull
+ protected > ClientType buildClient(@Nonnull ClientType instance) throws ClientException {
+ Objects.requireNonNull(instance, "The instance cannot be null");
+ instance.setHttpProvider(this.getHttpProvider());
+ instance.setLogger(this.getLogger());
+ instance.setSerializer(this.getSerializer());
+ return instance;
+ }
+
+ /**
+ * Builds and returns the Graph service client.
+ *
+ * @return the Graph service client object
+ * @throws ClientException
+ * if there was an exception creating the client
+ */
+ @Nonnull
+ public IBaseClient buildClient() throws ClientException {
+ return buildClient(new BaseClient<>());
+ }
+ }
+
+ /**
+ * The HTTP provider instance
+ */
+ private IHttpProvider httpProvider;
+
+ /**
+ * The logger
+ */
+ private ILogger logger;
+
+ /**
+ * The serializer instance
+ */
+ private ISerializer serializer;
+
+ /**
+ * Gets the HTTP provider
+ *
+ * @return The HTTP provider
+ */
+ @Override
+ @Nullable
+ public IHttpProvider getHttpProvider() {
+ return httpProvider;
+ }
+
+ /**
+ * Gets the logger
+ *
+ * @return The logger
+ */
+ @Nullable
+ @SuppressFBWarnings
+ public ILogger getLogger() {
+ return logger;
+ }
+
+ /**
+ * Gets the serializer
+ *
+ * @return The serializer
+ */
+ @Override
+ @Nullable
+ public ISerializer getSerializer() {
+ return serializer;
+ }
+
+ /**
+ * Sets the logger
+ *
+ * @param logger The logger
+ */
+ protected void setLogger(@Nonnull final ILogger logger) {
+ Objects.requireNonNull(logger, "parameter logger cannot be null");
+ this.logger = logger;
+ }
+
+ /**
+ * Sets the HTTP provider
+ *
+ * @param httpProvider The HTTP provider
+ */
+ protected void setHttpProvider(@Nonnull final IHttpProvider httpProvider) {
+ Objects.requireNonNull(httpProvider, "parameter httpProvider cannot be null");
+ this.httpProvider = httpProvider;
+ }
+
+ /**
+ * Sets the serializer
+ *
+ * @param serializer The serializer
+ */
+ public void setSerializer(@Nonnull final ISerializer serializer) {
+ Objects.requireNonNull(serializer, "parameter serializer cannot be null");
+ this.serializer = serializer;
+ }
+
+ /**
+ * Gets the service SDK version if the service SDK is in use, null otherwise
+ * @return the service SDK version if the service SDK is in use, null otherwise
+ */
+ @Override
+ @Nullable
+ public String getServiceSDKVersion() {
+ return null;
+ }
+}
diff --git a/src/main/java/com/microsoft/graph/core/ClientException.java b/src/main/java/com/microsoft/graph/core/ClientException.java
new file mode 100644
index 000000000..a5ab34469
--- /dev/null
+++ b/src/main/java/com/microsoft/graph/core/ClientException.java
@@ -0,0 +1,44 @@
+// ------------------------------------------------------------------------------
+// Copyright (c) 2017 Microsoft Corporation
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+// ------------------------------------------------------------------------------
+
+package com.microsoft.graph.core;
+
+import javax.annotation.Nullable;
+import javax.annotation.Nonnull;
+
+/**
+ * An exception from the client.
+ */
+public class ClientException extends RuntimeException {
+
+ private static final long serialVersionUID = -1066560879567392559L;
+
+ /**
+ * Creates the client exception
+ *
+ * @param message the message to display
+ * @param ex the exception from
+ */
+ public ClientException(@Nonnull final String message, @Nullable final Throwable ex) {
+ super(message, ex);
+ }
+}
diff --git a/src/main/java/com/microsoft/graph/core/CustomRequestBuilder.java b/src/main/java/com/microsoft/graph/core/CustomRequestBuilder.java
new file mode 100644
index 000000000..468dcdde8
--- /dev/null
+++ b/src/main/java/com/microsoft/graph/core/CustomRequestBuilder.java
@@ -0,0 +1,56 @@
+package com.microsoft.graph.core;
+
+import java.util.List;
+import java.util.Objects;
+
+import javax.annotation.Nullable;
+import javax.annotation.Nonnull;
+
+import com.microsoft.graph.http.BaseRequestBuilder;
+import com.microsoft.graph.http.CustomRequest;
+import com.microsoft.graph.options.Option;
+
+/**
+ * The class for the CustomRequestBuilder
+ *
+ * If the provided URL is malformed, the ClientException will contain a MalformedURLException
+ */
+public class CustomRequestBuilder extends BaseRequestBuilder {
+ /** Type to use for response deserialization */
+ public final Class responseType;
+
+ /**
+ * Instanciates a new custom request builder
+ *
+ * @param requestUrl the URL to send the request to
+ * @param client the client to use for the request
+ * @param requestOptions options to apply to the request
+ * @param responseType type to use for response deserialization
+ */
+ public CustomRequestBuilder(@Nonnull final String requestUrl, @Nonnull final IBaseClient> client, @Nullable final List extends Option> requestOptions, @Nonnull final Class responseType) {
+ super(requestUrl, client, requestOptions);
+ this.responseType = Objects.requireNonNull(responseType, "parameter responseType cannot be null");
+ }
+
+ /**
+ * Builds the request to be executed
+ *
+ * @return the request to be executed
+ * @param requestOptions the options to apply to the request
+ */
+ @Nonnull
+ public CustomRequest buildRequest(@Nullable final com.microsoft.graph.options.Option... requestOptions) {
+ return buildRequest(getOptions(requestOptions));
+ }
+
+ /**
+ * Builds the request to be executed
+ *
+ * @return the request to be executed
+ * @param requestOptions the options to apply to the request
+ */
+ @Nonnull
+ public CustomRequest buildRequest(@Nullable final List extends Option> requestOptions) {
+ return new CustomRequest<>(getRequestUrl(), getClient(), requestOptions, responseType);
+ }
+}
diff --git a/src/main/java/com/microsoft/graph/core/DateOnly.java b/src/main/java/com/microsoft/graph/core/DateOnly.java
new file mode 100644
index 000000000..e5a805092
--- /dev/null
+++ b/src/main/java/com/microsoft/graph/core/DateOnly.java
@@ -0,0 +1,112 @@
+package com.microsoft.graph.core;
+
+
+import java.text.ParseException;
+import java.util.Locale;
+import java.util.Objects;
+
+import javax.annotation.Nullable;
+import javax.annotation.Nonnull;
+
+/**
+ * A timezone-nonspecific date
+ */
+public class DateOnly {
+
+ /**
+ * The year
+ */
+ private final int mYear;
+
+ /**
+ * The month
+ */
+ private final int mMonth;
+
+ /**
+ * The day
+ */
+ private final int mDay;
+
+ /**
+ * Constructs a timezone-nonspecific DateOnly
+ *
+ * @param dateStr date string of the form yyyy-mm-dd
+ * @return the parsed DateOnly instance
+ * @exception ParseException If there was a failure parsing the dateStr
+ */
+ @Nullable
+ public static DateOnly parse(@Nonnull final String dateStr) throws ParseException {
+ Objects.requireNonNull(dateStr, "parameter dateStr cannot be null");
+ // break the date up into its constituent parts
+ final String[] dateInfo = dateStr.split("-");
+
+ // validate the split date string
+ final int expectedLength = 3;
+ if (dateInfo.length != expectedLength) {
+ throw new ParseException(
+ "Expected datestring format 'yyyy-mm-dd' but found: " + dateStr, 0
+ );
+ }
+
+ // array indices for date parsing
+ final int indYear = 0;
+ final int indMonth = 1;
+ final int indDay = 2;
+
+ // unpack this array
+ int year = Integer.parseInt(dateInfo[indYear]);
+ int month = Integer.parseInt(dateInfo[indMonth]);
+ int day = Integer.parseInt(dateInfo[indDay]);
+
+ return new DateOnly(year, month, day);
+ }
+
+ /**
+ * Constructs a timezone-nonspecific DateOnly
+ *
+ * @param year the year
+ * @param month 1-indexed month value (Jan == 1)
+ * @param day day of the month
+ */
+ public DateOnly(final int year, final int month, final int day) {
+ mYear = year;
+ mMonth = month;
+ mDay = day;
+ }
+
+ /**
+ * Gets the year
+ *
+ * @return the year
+ */
+ public int getYear() {
+ return mYear;
+ }
+
+ /**
+ * Gets the month
+ *
+ * @return the month
+ */
+ public int getMonth() {
+ return mMonth;
+ }
+
+ /**
+ * Gets the day
+ *
+ * @return the day
+ */
+ public int getDay() {
+ return mDay;
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ Locale.ROOT,
+ "%04d-%02d-%02d", mYear, mMonth, mDay
+ );
+ }
+}
diff --git a/src/main/java/com/microsoft/graph/core/GraphErrorCodes.java b/src/main/java/com/microsoft/graph/core/GraphErrorCodes.java
new file mode 100644
index 000000000..2ec00b40b
--- /dev/null
+++ b/src/main/java/com/microsoft/graph/core/GraphErrorCodes.java
@@ -0,0 +1,163 @@
+// ------------------------------------------------------------------------------
+// Copyright (c) 2017 Microsoft Corporation
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+// ------------------------------------------------------------------------------
+
+package com.microsoft.graph.core;
+
+/**
+ * The common Graph error codes
+ * @see https://docs.microsoft.com/en-us/graph/errors
+ */
+public enum GraphErrorCodes {
+ // Standard error codes.
+ /** The collar doesn't have permission to perform the action */
+ ACCESS_DENIED,
+ /** The app or user has been throttled */
+ ACTIVITY_LIMIT_REACHED,
+ /** No description available */
+ ASYNC_TASK_FAILED,
+ /** No description available */
+ ASYNC_TASK_NOT_COMPLETED,
+ /** No description available */
+ AUTHENTICATION_CANCELLED,
+ /** No description available */
+ AUTHENTICATION_FAILURE,
+ /** An unspecified error has occurred. */
+ GENERAL_EXCEPTION,
+ /** The resource could not be found, specific to the Exchange workload. */
+ ERROR_ITEM_NOT_FOUND,
+ /** The specified byte range is invalid or unavailable. */
+ INVALID_RANGE,
+ /** The request is malformed or incorrect. */
+ INVALID_REQUEST,
+ /** The resource could not be found. */
+ ITEM_NOT_FOUND,
+ /** Malware was detected in the requested resource. */
+ MALWARE_DETECTED,
+ /** The specified item name already exists. */
+ NAME_ALREADY_EXISTS,
+ /** The action is not allowed by the system. */
+ NOT_ALLOWED,
+ /** The request is not supported by the system. */
+ NOT_SUPPORTED,
+ /** The user has reached their quota limit. */
+ QUOTA_LIMIT_REACHED,
+ /** The resource being updated has changed since the caller last read it, usually an eTag mismatch. */
+ RESOURCE_MODIFIED,
+ /** The delta token is no longer valid, and the app must reset the sync state. */
+ RESYNC_REQUIRED,
+ /** The service is not available. Try the request again after a delay. There may be a Retry-After header. */
+ SERVICE_NOT_AVAILABLE,
+ /** The application is sending too many requests to the service. Try the request again after a delay. There may be a Retry-After header. */
+ TOO_MANY_REDIRECTS,
+ /** The caller is not authenticated. */
+ UNAUTHENTICATED,
+
+ // Extend error codes.
+ /** No description available */
+ ACCESS_RESTRICTED,
+ /** Access restricted to the item's owner. */
+ AUTHORIZATION_REQUEST_DENIED,
+ /** Failed to get a consistent delta snapshot. Try again later. */
+ CANNOT_SNAPSHOT_TREE,
+ /** Max limit on the number of child items was reached. */
+ CHILD_ITEM_COUNT_EXCEEDED,
+ /** No description available */
+ ERROR_INVALID_ID_MALFORMED,
+ /** No description available */
+ ERROR_INVALID_USER,
+ /** ETag does not match the current item's value. */
+ ENTITY_TAG_DOES_NOT_MATCH,
+ /** Declared total size for this fragment is different from that of the upload session. */
+ FRAGMENT_LENGTH_MISMATCH,
+ /** Uploaded fragment is out of order. */
+ FRAGMENT_OUT_OF_ORDER,
+ /** Uploaded fragment overlaps with existing data. */
+ FRAGMENT_OVERLAP,
+ /** Invalid accept type. */
+ INVALID_ACCEPT_TYPE,
+ /** Invalid parameter format. */
+ INVALID_PARAMETER_FORMAT,
+ /** Name contains invalid characters. */
+ INVALID_PATH,
+ /** Invalid query option. */
+ INVALID_QUERY_OPTION,
+ /** Invalid start index. */
+ INVALID_START_INDEX,
+ /** Lock token does not match existing lock. */
+ LOCK_MISMATCH,
+ /** There is currently no unexpired lock on the item. */
+ LOCK_NOT_FOUND_OR_ALREADY_EXPIRED,
+ /** Lock Owner ID does not match provided ID. */
+ LOCK_OWNER_MISMATCH,
+ /** ETag header is malformed. ETags must be quoted strings. */
+ MALFORMED_ENTITY_TAG,
+ /** Max limit on number of Documents is reached. */
+ MAX_DOCUMENT_COUNT_EXCEEDED,
+ /** Max file size exceeded. */
+ MAX_FILE_SIZE_EXCEEDED,
+ /** Max limit on number of Folders is reached. */
+ MAX_FOLDER_COUNT_EXCEEDED,
+ /** Max file size exceeded. */
+ MAX_FRAGMENT_LENGTH_EXCEEDED,
+ /** Max limit on number of Items is reached. */
+ MAX_ITEM_COUNT_EXCEEDED,
+ /** Max query length exceeded. */
+ MAX_QUERY_LENGTH_EXCEEDED,
+ /** Maximum stream size exceeded. */
+ MAX_STREAM_SIZE_EXCEEDED,
+ /** Parameter Exceeds Maximum Length. */
+ PARAMETER_IS_TOO_LONG,
+ /** Parameter is smaller then minimum value. */
+ PARAMETER_IS_TOO_SMALL,
+ /** Path exceeds maximum length. */
+ PATH_IS_TOO_LONG,
+ /** Folder hierarchy depth limit reached. */
+ PATH_TOO_DEEP,
+ /** Property not updateable. */
+ PROPERTY_NOT_UPDATEABLE,
+ /** Resync required. Replace any local items with the server's version (including deletes) if you're sure that the service was up to date with your local changes when you last sync'd. Upload any local changes that the server doesn't know about. */
+ RESYNC_APPLY_DIFFERENCES,
+ /** Resync required. Upload any local items that the service did not return, and upload any files that differ from the server's version (keeping both copies if you're not sure which one is more up-to-date). */
+ RESYNC_UPLOAD_DIFFERENCES,
+ /** Resource is temporarily read-only. */
+ SERVICE_READ_ONLY,
+ /** Too many requests. */
+ THROTTLED_REQUEST,
+ /** Too many results requested. */
+ TOO_MANY_RESULTS_REQUESTED,
+ /** Too many terms in the query. */
+ TOO_MANY_TERMS_IN_QUERY,
+ /** Operation is not allowed because the number of affected items exceeds threshold. */
+ TOTAL_AFFECTED_ITEM_COUNT_EXCEEDED,
+ /** Data truncation is not allowed. */
+ TRUNCATION_NOT_ALLOWED,
+ /** Upload session failed. */
+ UPLOAD_SESSION_FAILED,
+ /** Upload session incomplete. */
+ UPLOAD_SESSION_INCOMPLETE,
+ /** Upload session not found. */
+ UPLOAD_SESSION_NOT_FOUND,
+ /** This document is suspicious and may have a virus. */
+ VIRUS_SUSPICIOUS,
+ /** Zero or fewer results requested. */
+ ZERO_OR_FEWER_RESULTS_REQUESTED,
+}
diff --git a/src/main/java/com/microsoft/graph/core/IBaseClient.java b/src/main/java/com/microsoft/graph/core/IBaseClient.java
new file mode 100644
index 000000000..b3b0e7d32
--- /dev/null
+++ b/src/main/java/com/microsoft/graph/core/IBaseClient.java
@@ -0,0 +1,111 @@
+// ------------------------------------------------------------------------------
+// Copyright (c) 2017 Microsoft Corporation
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+// ------------------------------------------------------------------------------
+
+package com.microsoft.graph.core;
+
+import javax.annotation.Nullable;
+import javax.annotation.Nonnull;
+
+import com.google.gson.JsonElement;
+import com.microsoft.graph.content.BatchRequestBuilder;
+import com.microsoft.graph.http.IHttpProvider;
+import com.microsoft.graph.logger.ILogger;
+import com.microsoft.graph.serializer.ISerializer;
+
+/**
+ * A client that communications with an OData service
+ * @param type of a request for the native http client
+ */
+public interface IBaseClient {
+ /**
+ * Gets the service root
+ *
+ * @return the service root
+ */
+ @Nonnull
+ String getServiceRoot();
+
+ /**
+ * Sets the service root
+ *
+ * @param value the service root
+ */
+ void setServiceRoot(@Nonnull final String value);
+
+ /**
+ * Gets the HTTP provider
+ *
+ * @return the HTTP provider
+ */
+ @Nullable
+ IHttpProvider getHttpProvider();
+
+ /**
+ * Gets the logger
+ *
+ * @return the logger
+ */
+ @Nullable
+ ILogger getLogger();
+
+ /**
+ * Gets the serializer
+ *
+ * @return the serializer
+ */
+ @Nullable
+ ISerializer getSerializer();
+
+ /**
+ * Gets a builder to execute a custom request
+ *
+ * @return the custom request builder
+ * @param url the url to send the request to
+ * @param responseType the class to deserialize the response to
+ * @param the type to deserialize the response to
+ */
+ @Nonnull
+ CustomRequestBuilder customRequest(@Nonnull final String url, @Nonnull final Class responseType);
+
+ /**
+ * Gets a builder to execute a custom request with a generic JSONObject response
+ *
+ * @return the custom request builder
+ * @param url the url to send the request to
+ */
+ @Nonnull
+ CustomRequestBuilder customRequest(@Nonnull final String url);
+
+ /**
+ * Get the batch request builder.
+ * @return a request builder to execute a batch.
+ */
+ @Nonnull
+ BatchRequestBuilder batch();
+
+ /**
+ * Gets the service SDK version if the service SDK is in use, null otherwise
+ * @return the service SDK version if the service SDK is in use, null otherwise
+ */
+ @Nullable
+ String getServiceSDKVersion();
+}
diff --git a/src/main/java/com/microsoft/graph/core/Multipart.java b/src/main/java/com/microsoft/graph/core/Multipart.java
new file mode 100644
index 000000000..878f3e84c
--- /dev/null
+++ b/src/main/java/com/microsoft/graph/core/Multipart.java
@@ -0,0 +1,268 @@
+package com.microsoft.graph.core;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+import java.security.SecureRandom;
+import java.util.Map;
+import java.util.Objects;
+
+import javax.annotation.Nullable;
+import javax.annotation.Nonnull;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.microsoft.graph.options.HeaderOption;
+
+/**
+ * Helper for submitting multipart data
+ *
+ * This follows the Network Working Group's RFC
+ * on multipart/form-data posting format:
+ * https://www.ietf.org/rfc/rfc2388.txt
+ */
+public class Multipart {
+ private String boundary;
+ private static final String RETURN = "\r\n";
+ private ByteArrayOutputStream out;
+ /** Default encoding for multi-part requests */
+ public static final String MULTIPART_ENCODING = "US-ASCII";
+ private String contentType = "multipart/form-data";
+
+ /**
+ * Create a new multipart object
+ */
+ public Multipart() {
+ out = new ByteArrayOutputStream();
+ boundary = "part_" + new BigInteger(130, new SecureRandom());
+ }
+
+ /**
+ * Get the multipart boundary for use in the request header
+ * @return the multipart boundary
+ */
+ @Nonnull
+ public String getBoundary() {
+ return boundary;
+ }
+
+ /**
+ * Set the multipart boundary for use in the request header
+ * @param boundary The multipart boundary
+ */
+ public void setBoundary(@Nonnull final String boundary) {
+ this.boundary = Objects.requireNonNull(boundary, "parameter boundary cannot be null");
+ }
+
+ /**
+ * Get the contentType for use in the request header
+ * @return the multipart Content-Type
+ */
+ @Nonnull
+ public String getContentType() {
+ return contentType;
+ }
+
+ /**
+ * Set the contentType for use in the request header
+ * @param contentType The multipart Content-Type
+ */
+ public void setContentType(@Nonnull final String contentType) {
+ this.contentType = Objects.requireNonNull(contentType, "parameter contentType cannot be null");
+ }
+
+ /**
+ * Get the Content-Type header to send the multipart request
+ * @return the multipart header option
+ */
+ @Nonnull
+ public HeaderOption header() {
+ return new HeaderOption("Content-Type", contentType + "; boundary=\"" + boundary + "\"");
+ }
+
+ private void writePartData(String partContent, byte[] byteArray) throws IOException{
+ out.write(partContent.getBytes(MULTIPART_ENCODING));
+ out.write(byteArray);
+ String returnContent = RETURN + RETURN;
+ out.write(returnContent.getBytes(MULTIPART_ENCODING));
+ }
+
+ /**
+ * Create content headers value and parameter
+ * @param name The content header name
+ * @param contentType The content header Content-Type
+ * @param filename The content header filename
+ * @return content header value and parameter string
+ */
+ @VisibleForTesting
+ String createPartHeader(String name, String contentType, String filename) {
+ StringBuilder partContent = new StringBuilder(addBoundary());
+ partContent.append("Content-Disposition: form-data");
+ if(filename != null) {
+ if(name != null)
+ partContent.append("; name=\"").append(name).append("\"; filename=\"").append(filename).append("\"");
+ else
+ partContent.append("; filename=\"").append(filename).append("\"");
+ }
+ else if(name != null)
+ partContent.append("; name=\"").append(name).append("\"");
+ if(contentType != null)
+ partContent.append(RETURN).append("Content-Type: ").append(contentType);
+ partContent.append(RETURN).append(RETURN);
+ return partContent.toString();
+ }
+
+ /**
+ * Create content headers value and parameter
+ * @param contentValue The content header value
+ * @param contentDispParameter Map containing content paramter's key and value pair
+ * @return content header value and parameter string
+ */
+ @Nonnull
+ public static String createContentHeaderValue(@Nonnull final String contentValue, @Nullable final Map contentDispParameter) {
+ final StringBuilder builder = new StringBuilder(contentValue);
+
+ if(contentDispParameter != null) {
+ for(Map.Entry entry : contentDispParameter.entrySet())
+ builder.append(";").append(entry.getKey()).append("=\"").append(entry.getValue()).append("\"");
+ }
+ return builder.toString();
+ }
+
+ /**
+ * Create content headers header-name, value and parameter string
+ * @param headers Map containing Header-name and header-value pair
+ */
+ private String createPartHeader(Map headers) {
+ final StringBuilder builder = new StringBuilder(addBoundary());
+ final String defaultPartContent = "Content-Disposition: form-data;" + RETURN + "Content-Type: " + contentType + RETURN + RETURN;
+
+ if(headers != null) {
+ for(Map.Entry entry : headers.entrySet())
+ builder.append(entry.getKey()).append(": ").append(entry.getValue()).append(RETURN);
+ builder.append(RETURN);
+ } else
+ builder.append(defaultPartContent);
+ return builder.toString();
+ }
+
+ /**
+ * Add multipart content headers and byte content
+ * @param name The multipart content name
+ * @param contentType The multipart Content-Type
+ * @param filename The multipart content file name
+ * @param byteArray The multipart byte content
+ * @throws IOException
+ */
+ private void addData(String name, String contentType, String filename, byte[] byteArray) throws IOException {
+ String partContent = createPartHeader(name, contentType, filename);
+ writePartData(partContent, byteArray);
+ }
+
+ /**
+ * Add a part to the multipart body
+ * @param name The name of the part
+ * @param contentType The MIME type (text/html, video/mp4, etc.)
+ * @param byteArray The byte[] contents of the resource
+ * @throws IOException Throws an exception if the output stream cannot be written to
+ */
+ public void addFormData(@Nonnull final String name, @Nonnull final String contentType, @Nonnull final byte[] byteArray) throws IOException {
+ addData(name, contentType, null, byteArray);
+ }
+
+ /**
+ * Add a part to the multipart body
+ * @param contentType The MIME type (text/html, video/mp4, etc.)
+ * @param byteArray The byte[] contents of the resource
+ * @throws IOException Throws an exception if the output stream cannot be written to
+ */
+ public void addPart(@Nonnull final String contentType, @Nonnull final byte[] byteArray) throws IOException {
+ addData(null, contentType, null, byteArray);
+ }
+
+ /**
+ * Add a part to the multipart body
+ * @param headers Map containing Header's header-name(eg: Content-Disposition, Content-Type, etc..) and header's value-parameter string
+ * @param content The byte[] contents of the resource
+ * @throws IOException Throws an exception if the output stream cannot be written to
+ */
+ public void addPart(@Nonnull final Map headers, @Nonnull final byte[] content) throws IOException{
+ final String partContent = createPartHeader(headers);
+ writePartData(partContent, content);
+ }
+
+ /**
+ * Add an HTML part to the multipart body
+ * @param name The name of the part
+ * @param content The HTML body for the part
+ * @throws IOException Throws an exception if the output stream cannot be written to
+ */
+ public void addHtmlPart(@Nonnull final String name, @Nonnull final byte[] content) throws IOException {
+ addFormData(name, "text/html", content);
+ }
+
+ /**
+ * Add a file part to the multipart body
+ * @param name The name of the part
+ * @param contentType The MIME type of the file (application/pdf, video/mp4, etc.)
+ * @param file The file
+ * @throws IOException Throws an exception if the output stream cannot be written to
+ */
+ public void addFilePart(@Nonnull final String name, @Nonnull final String contentType, @Nonnull final java.io.File file) throws IOException {
+ try(final InputStream fileStream = new FileInputStream(file)) {
+ final byte[] fileBytes = getByteArray(fileStream);
+ addData(name, contentType, file.getName(), fileBytes);
+ }
+ }
+
+ /**
+ * Adds a boundary at the beginning of a new part
+ * @return The boundary
+ */
+ private String addBoundary() {
+ return "--" + boundary + RETURN;
+ }
+
+ /**
+ * Adds a boundary at the end of the multipart body
+ * @return The boundary
+ */
+ private String addEnding() {
+ return "--" + boundary + "--";
+ }
+
+ /**
+ * Returns a full multipart body byte array
+ * @return The byte[] representation of the multipart object
+ * @throws IOException Throws an exception if the output stream cannot be written to
+ */
+ @Nullable
+ public byte[] content() throws IOException {
+ ByteArrayOutputStream finalStream = out;
+ finalStream.write(addEnding().getBytes(MULTIPART_ENCODING));
+ return finalStream.toByteArray();
+ }
+
+ /**
+ * Helper method to convert an InputStream to a byte[]
+ * @param in The input stream to convert
+ * @return The byte[]
+ * @throws IOException Throws an exception if the output stream cannot be written to
+ */
+ private byte[] getByteArray(final InputStream in) throws IOException {
+ try (final ByteArrayOutputStream buffer = new ByteArrayOutputStream()){
+ int nRead;
+ byte[] data = new byte[16384];
+ try {
+ while ((nRead = in.read(data, 0, data.length)) != -1) {
+ buffer.write(data, 0, nRead);
+ }
+ } finally {
+ in.close();
+ }
+ buffer.flush();
+ return buffer.toByteArray();
+ }
+ }
+}
diff --git a/src/main/java/com/microsoft/graph/core/TimeOfDay.java b/src/main/java/com/microsoft/graph/core/TimeOfDay.java
new file mode 100644
index 000000000..bc87efe02
--- /dev/null
+++ b/src/main/java/com/microsoft/graph/core/TimeOfDay.java
@@ -0,0 +1,114 @@
+package com.microsoft.graph.core;
+
+
+import javax.annotation.Nullable;
+import javax.annotation.Nonnull;
+
+import java.math.BigDecimal;
+import java.text.ParseException;
+import java.util.Locale;
+import java.util.Objects;
+
+/**
+ * The time of day
+ */
+public class TimeOfDay {
+
+ /**
+ * The hour
+ */
+ private final int mHour;
+
+ /**
+ * The minute
+ */
+ private final int mMinute;
+
+ /**
+ * The second
+ */
+ private final int mSecond;
+
+ /**
+ * Constructs a timezone-nonspecific DateOnly
+ *
+ * @param timeStr time string of the form HH:mm:ss
+ * @return the parsed DateOnly instance
+ * @exception ParseException If there was a failure parsing the dateStr
+ */
+ @Nullable
+ public static TimeOfDay parse(@Nonnull final String timeStr) throws ParseException {
+ Objects.requireNonNull(timeStr, "parameter timeStr cannot be null");
+ // break the date up into its constituent parts
+ final String[] timeInfo = timeStr.split(":");
+
+ // validate the split date string
+ final int expectedLength = 3;
+ if (timeInfo.length != expectedLength) {
+ throw new ParseException(
+ "Expected time string format 'HH:mm:ss' but found: " + timeStr, 0
+ );
+ }
+
+ // array indices for date parsing
+ final int indHour = 0;
+ final int indMinute = 1;
+ final int indSecond = 2;
+
+ // unpack this array
+ final int hour = Integer.parseInt(timeInfo[indHour]);
+ final int minute = Integer.parseInt(timeInfo[indMinute]);
+ final int second = new BigDecimal(timeInfo[indSecond]).intValue();
+
+ return new TimeOfDay(hour, minute, second);
+ }
+
+ /**
+ * Constructs a TimeOfDay object
+ *
+ * @param hour the hour
+ * @param minute the minute
+ * @param second the second
+ */
+ public TimeOfDay(final int hour, final int minute, final int second) {
+ mHour = hour;
+ mMinute = minute;
+ mSecond = second;
+ }
+
+ /**
+ * Gets the hour
+ *
+ * @return the hour
+ */
+ public int getHour() {
+ return mHour;
+ }
+
+ /**
+ * Gets the minute
+ *
+ * @return the minute
+ */
+ public int getMinute() {
+ return mMinute;
+ }
+
+ /**
+ * Gets the second
+ *
+ * @return the second
+ */
+ public int getSecond() {
+ return mSecond;
+ }
+
+ @Override
+ @Nonnull
+ public String toString() {
+ return String.format(
+ Locale.ROOT,
+ "%02d:%02d:%02d", mHour, mMinute, mSecond
+ );
+ }
+}
diff --git a/src/main/java/com/microsoft/graph/http/BaseActionCollectionRequest.java b/src/main/java/com/microsoft/graph/http/BaseActionCollectionRequest.java
new file mode 100644
index 000000000..caff0a918
--- /dev/null
+++ b/src/main/java/com/microsoft/graph/http/BaseActionCollectionRequest.java
@@ -0,0 +1,107 @@
+// ------------------------------------------------------------------------------
+// Copyright (c) 2020 Microsoft Corporation
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+// ------------------------------------------------------------------------------
+
+package com.microsoft.graph.http;
+
+import java.lang.NoSuchFieldException;
+import java.lang.IllegalAccessException;
+import java.util.List;
+import javax.annotation.Nullable;
+import javax.annotation.Nonnull;
+
+import com.microsoft.graph.core.ClientException;
+import com.microsoft.graph.core.IBaseClient;
+import com.microsoft.graph.options.Option;
+
+/**
+ * A request against a collection bound action
+ *
+ * @param the type of the object in the collection
+ * @param the response collection type
+ * @param the collection page type
+ */
+public abstract class BaseActionCollectionRequest,
+ T3 extends BaseCollectionPage>> extends BaseCollectionRequest {
+
+
+ /**
+ * Create the collection request
+ *
+ * @param requestUrl the URL to make the request against
+ * @param client the client which can issue the request
+ * @param options the options for this request
+ * @param responseCollectionClass the class for the response collection
+ * @param collectionPageClass the class for the collection page
+ * @param collectionRequestBuilderClass the class for the collection request builder
+ */
+ public BaseActionCollectionRequest(@Nonnull final String requestUrl,
+ @Nonnull final IBaseClient> client,
+ @Nullable final List extends Option> options,
+ @Nonnull final Class responseCollectionClass,
+ @Nonnull final Class collectionPageClass,
+ @Nonnull final Class extends BaseCollectionRequestBuilder, T2, T3, ? extends BaseCollectionRequest>> collectionRequestBuilderClass) {
+ super(requestUrl, client, options, responseCollectionClass, collectionPageClass, collectionRequestBuilderClass);
+ }
+
+ /**
+ * Invokes the method and calls the callback with the resulting collection of objects
+ * @return a future with the result
+ */
+ @Nonnull
+ public java.util.concurrent.CompletableFuture postAsync() {
+ getBaseRequest().setHttpMethod(HttpMethod.POST);
+ Object bodyToSend = null;
+ try {
+ bodyToSend = this.getClass().getField("body").get(this);
+ } catch (NoSuchFieldException | IllegalAccessException ex) {
+ // this action doesn't body arguments, expected, no-op
+ }
+ return getBaseRequest()
+ .getClient()
+ .getHttpProvider()
+ .sendAsync(this,
+ responseCollectionClass,
+ bodyToSend)
+ .thenApply(this::buildFromResponse);
+ }
+ /**
+ * Invokes the method and returns the resulting collection of objects
+ * @return a collection of objects returned by the method
+ */
+ @Nullable
+ public T3 post() throws ClientException {
+ getBaseRequest().setHttpMethod(HttpMethod.POST);
+ Object bodyToSend = null;
+ try {
+ bodyToSend = this.getClass().getField("body").get(this);
+ } catch (NoSuchFieldException | IllegalAccessException ex) {
+ // this action doesn't body arguments, expected, no-op
+ }
+ return buildFromResponse(getBaseRequest()
+ .getClient()
+ .getHttpProvider()
+ .send(this,
+ responseCollectionClass,
+ bodyToSend)
+ );
+ }
+}
diff --git a/src/main/java/com/microsoft/graph/http/BaseActionCollectionRequestBuilder.java b/src/main/java/com/microsoft/graph/http/BaseActionCollectionRequestBuilder.java
new file mode 100644
index 000000000..000cac4cd
--- /dev/null
+++ b/src/main/java/com/microsoft/graph/http/BaseActionCollectionRequestBuilder.java
@@ -0,0 +1,41 @@
+// ------------------------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information.
+// ------------------------------------------------------------------------------
+
+package com.microsoft.graph.http;
+
+import com.microsoft.graph.core.IBaseClient;
+import com.microsoft.graph.options.Option;
+
+import java.util.List;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+/**
+ * The base method request builder class used for POST actions
+ */
+public class BaseActionCollectionRequestBuilder,
+ T3 extends ICollectionResponse,
+ T4 extends BaseCollectionPage>,
+ T5 extends BaseCollectionRequest> extends BaseCollectionRequestBuilder {
+
+ /**
+ * Constructs a new {@link BaseActionCollectionRequestBuilder}
+ *
+ * @param requestUrl the URL for the request
+ * @param client the {@link IBaseClient} for handling requests
+ * @param options {@link List} of {@link Option}s to add to this request
+ * @param requestBuilderClass the class for the request builder
+ * @param collectionRequestClass the class for the collection request
+ */
+ public BaseActionCollectionRequestBuilder(
+ @Nonnull final String requestUrl,
+ @Nonnull final IBaseClient> client,
+ @Nullable final List extends Option> options,
+ @Nonnull final Class requestBuilderClass,
+ @Nonnull final Class collectionRequestClass
+ ) {
+ super(requestUrl, client, options, requestBuilderClass, collectionRequestClass);
+ }
+}
diff --git a/src/main/java/com/microsoft/graph/http/BaseActionRequestBuilder.java b/src/main/java/com/microsoft/graph/http/BaseActionRequestBuilder.java
new file mode 100644
index 000000000..17aff1f98
--- /dev/null
+++ b/src/main/java/com/microsoft/graph/http/BaseActionRequestBuilder.java
@@ -0,0 +1,33 @@
+// ------------------------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information.
+// ------------------------------------------------------------------------------
+
+package com.microsoft.graph.http;
+
+import com.microsoft.graph.core.IBaseClient;
+import com.microsoft.graph.options.Option;
+
+import java.util.List;
+
+import javax.annotation.Nullable;
+import javax.annotation.Nonnull;
+
+/**
+ * The base method request builder class used for POST actions
+ */
+public class BaseActionRequestBuilder extends BaseRequestBuilder {
+ /**
+ * Constructs a new {@link BaseActionRequestBuilder}
+ *
+ * @param requestUrl the URL for the request
+ * @param client the {@link IBaseClient} for handling requests
+ * @param options {@link List} of {@link Option}s to add to this request
+ */
+ public BaseActionRequestBuilder(
+ @Nonnull final String requestUrl,
+ @Nonnull final IBaseClient> client,
+ @Nullable final List extends Option> options
+ ) {
+ super(requestUrl, client, options);
+ }
+}
diff --git a/src/main/java/com/microsoft/graph/http/BaseCollectionPage.java b/src/main/java/com/microsoft/graph/http/BaseCollectionPage.java
new file mode 100644
index 000000000..4d72bf0a1
--- /dev/null
+++ b/src/main/java/com/microsoft/graph/http/BaseCollectionPage.java
@@ -0,0 +1,139 @@
+// ------------------------------------------------------------------------------
+// Copyright (c) 2017 Microsoft Corporation
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+// ------------------------------------------------------------------------------
+
+package com.microsoft.graph.http;
+
+import com.google.gson.JsonObject;
+import com.microsoft.graph.serializer.AdditionalDataManager;
+import com.microsoft.graph.serializer.ISerializer;
+import com.microsoft.graph.serializer.IJsonBackedObject;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.annotation.Nullable;
+import javax.annotation.Nonnull;
+
+/**
+ * A page of results from a collection
+ *
+ * @param the type of the item contained within the collection
+ */
+public class BaseCollectionPage> implements IJsonBackedObject {
+
+ private AdditionalDataManager additionalDataManager = new AdditionalDataManager(this);
+
+ /**
+ * The contents of this page
+ */
+ private final List pageContents;
+
+ /**
+ * The request builder for the next page
+ */
+ private final T2 requestBuilder;
+
+ /**
+ * A collection page for WorkforceIntegration
+ *
+ * @param response the serialized WorkforceIntegrationCollectionResponse from the service
+ * @param builder the request builder for the next collection page
+ */
+ public BaseCollectionPage(@Nonnull final ICollectionResponse response, @Nullable final T2 builder) {
+ this(response.values(), builder, response.additionalDataManager());
+ }
+
+ /**
+ * Creates the collection page
+ *
+ * @param pageContents the contents of this page
+ * @param nextRequestBuilder the request builder for the next page
+ */
+ public BaseCollectionPage(@Nonnull final List pageContents, @Nullable final T2 nextRequestBuilder) {
+ // CollectionPages are never directly modifiable, either 'update'/'delete' the specific child or 'add' the new
+ // object to the 'children' of the collection.
+ this.pageContents = Collections.unmodifiableList(pageContents == null ? new ArrayList<>() : pageContents);
+ requestBuilder = nextRequestBuilder;
+ }
+
+ /**
+ * Creates the collection page
+ *
+ * @param pageContents the contents of this page
+ * @param nextRequestBuilder the request builder for the next page
+ * @param responseAdditionalData the additional data returned by the response
+ */
+ public BaseCollectionPage(@Nonnull final List pageContents, @Nullable final T2 nextRequestBuilder, @Nonnull final AdditionalDataManager responseAdditionalData) {
+ this(pageContents, nextRequestBuilder);
+ this.additionalDataManager().putAll(responseAdditionalData);
+ }
+
+ /**
+ * Gets the next page request builder
+ *
+ * @return the next page request builder
+ */
+ @Nullable
+ public T2 getNextPage() {
+ return requestBuilder;
+ }
+
+ /**
+ * Gets the current page
+ *
+ * @return the current page
+ */
+ @Nonnull
+ public List getCurrentPage() {
+ return new ArrayList<>(pageContents);
+ }
+
+ /**
+ * Sets the raw JSON object
+ *
+ * @param serializer the serializer
+ * @param json the JSON object to set this object to
+ */
+ public void setRawObject(@Nonnull final ISerializer serializer, @Nonnull final JsonObject json) {
+ }
+
+ @Override
+ @Nullable
+ @SuppressFBWarnings
+ public final AdditionalDataManager additionalDataManager() {
+ return additionalDataManager;
+ }
+
+ private final static String odataCountKey = "@odata.count";
+ /**
+ * Returns the odata count value if it has been requested, null otherwise
+ * @return the odata count value if it has been requested, null otherwise
+ */
+ @Nullable
+ public final Long getCount() {
+ return additionalDataManager == null || !additionalDataManager.containsKey(odataCountKey) || !additionalDataManager.get(odataCountKey).isJsonPrimitive() ?
+ null :
+ additionalDataManager.get(odataCountKey).getAsLong();
+ }
+}
diff --git a/src/main/java/com/microsoft/graph/http/BaseCollectionReferenceRequestBuilder.java b/src/main/java/com/microsoft/graph/http/BaseCollectionReferenceRequestBuilder.java
new file mode 100644
index 000000000..ae833394c
--- /dev/null
+++ b/src/main/java/com/microsoft/graph/http/BaseCollectionReferenceRequestBuilder.java
@@ -0,0 +1,51 @@
+// ------------------------------------------------------------------------------
+// Copyright (c) 2017 Microsoft Corporation
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+// ------------------------------------------------------------------------------
+
+package com.microsoft.graph.http;
+
+import com.microsoft.graph.core.IBaseClient;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+/**
+ * A request builder
+ */
+public class BaseCollectionReferenceRequestBuilder,
+ T3 extends ICollectionResponse,
+ T4 extends BaseCollectionPage>,
+ T5 extends BaseEntityCollectionRequest> extends BaseCollectionRequestBuilder {
+ /**
+ * The request builder for this collection
+ *
+ * @param requestUrl the request URL
+ * @param client the service client
+ * @param requestOptions the options for this request
+ * @param requestBuilderClass the class for the request builder
+ * @param collectionRequestClass the class for the collection request
+ */
+ public BaseCollectionReferenceRequestBuilder(@Nonnull final String requestUrl, @Nonnull final IBaseClient> client, @Nullable final java.util.List extends com.microsoft.graph.options.Option> requestOptions,
+ @Nonnull final Class requestBuilderClass,
+ @Nonnull final Class collectionRequestClass) {
+ super(requestUrl, client, requestOptions, requestBuilderClass, collectionRequestClass);
+ }
+}
diff --git a/src/main/java/com/microsoft/graph/http/BaseCollectionRequest.java b/src/main/java/com/microsoft/graph/http/BaseCollectionRequest.java
new file mode 100644
index 000000000..090a2ab89
--- /dev/null
+++ b/src/main/java/com/microsoft/graph/http/BaseCollectionRequest.java
@@ -0,0 +1,449 @@
+// ------------------------------------------------------------------------------
+// Copyright (c) 2017 Microsoft Corporation
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+// ------------------------------------------------------------------------------
+
+package com.microsoft.graph.http;
+
+import java.net.URL;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+import javax.annotation.Nullable;
+import javax.annotation.Nonnull;
+
+import com.microsoft.graph.core.ClientException;
+import com.microsoft.graph.core.IBaseClient;
+import com.microsoft.graph.httpcore.middlewareoption.IShouldRedirect;
+import com.microsoft.graph.httpcore.middlewareoption.IShouldRetry;
+import com.microsoft.graph.options.FunctionOption;
+import com.microsoft.graph.options.HeaderOption;
+import com.microsoft.graph.options.Option;
+import com.microsoft.graph.options.QueryOption;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+
+/**
+ * A request against a collection
+ *
+ * @param the type of the object in the collection
+ * @param the response collection type
+ * @param the collection page type
+ */
+public abstract class BaseCollectionRequest,
+ T3 extends BaseCollectionPage>> implements IHttpRequest {
+
+ /**
+ * The base request for this collection request
+ */
+ private final BaseRequest baseRequest;
+
+ /**
+ * The class for the response collection
+ */
+ protected final Class responseCollectionClass;
+
+ /**
+ * The class for the collection page
+ */
+ private final Class collectionPageClass;
+
+ private final Class extends BaseCollectionRequestBuilder, T2, T3, ? extends BaseCollectionRequest>> collRequestBuilderClass;
+
+
+ /**
+ * Create the collection request
+ *
+ * @param requestUrl the URL to make the request against
+ * @param client the client which can issue the request
+ * @param options the options for this request
+ * @param responseCollectionClass the class for the response collection
+ * @param collectionPageClass the class for the collection page
+ * @param collectionRequestBuilderClass the class for the collection request builder
+ */
+ public BaseCollectionRequest(@Nonnull final String requestUrl,
+ @Nonnull final IBaseClient> client,
+ @Nullable final List extends Option> options,
+ @Nonnull final Class responseCollectionClass,
+ @Nonnull final Class collectionPageClass,
+ @Nonnull final Class extends BaseCollectionRequestBuilder, T2, T3, ? extends BaseCollectionRequest>> collectionRequestBuilderClass) {
+ this.responseCollectionClass = Objects.requireNonNull(responseCollectionClass, "parameter responseCollectionClass cannot be null");
+ this.collectionPageClass = Objects.requireNonNull(collectionPageClass, "parameter collectionPageClass cannot be null");
+ this.collRequestBuilderClass = Objects.requireNonNull(collectionRequestBuilderClass, "parameter collectionRequestBuilderClass cannot be null");
+ baseRequest = new BaseRequest(requestUrl, client, options, responseCollectionClass) {};
+ }
+
+ /**
+ * Send this request
+ *
+ * @return the response object
+ * @throws ClientException an exception occurs if there was an error while the request was sent
+ */
+ @Nullable
+ protected T2 send() throws ClientException {
+ baseRequest.setHttpMethod(HttpMethod.GET);
+ return baseRequest.getClient().getHttpProvider().send(this, responseCollectionClass, null);
+ }
+ /**
+ * Send this request
+ *
+ * @return the response object
+ * @throws ClientException an exception occurs if there was an error while the request was sent
+ */
+ @Nullable
+ protected java.util.concurrent.CompletableFuture sendAsync() throws ClientException {
+ baseRequest.setHttpMethod(HttpMethod.GET);
+ return baseRequest.getClient().getHttpProvider().sendAsync(this, responseCollectionClass, null);
+ }
+
+ /**
+ * Deserializes the collection from the response object
+ *
+ * @param response the collection response
+ * @return the collection page
+ */
+ @Nullable
+ public T3 buildFromResponse(@Nonnull final T2 response) {
+ Objects.requireNonNull(response, "parameter response cannot be null");
+ try {
+ final Object builder = response.nextLink() == null ? null : this.collRequestBuilderClass
+ .getConstructor(String.class, IBaseClient.class, java.util.List.class)
+ .newInstance(response.nextLink(), getBaseRequest().getClient(), Collections.emptyList());
+ final T3 page = (T3)this.collectionPageClass.getConstructor(response.getClass(), this.collRequestBuilderClass).newInstance(response, builder);
+ return page;
+ } catch(IllegalArgumentException | InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException | SecurityException ex) {
+ throw new ClientException("Could not find the required class", ex);
+ }
+ }
+
+ /**
+ * Gets the request URL
+ *
+ * @return the request URL
+ */
+ @Override
+ @Nonnull
+ public URL getRequestUrl() {
+ return baseRequest.getRequestUrl();
+ }
+
+ /**
+ * Gets the HTTP method
+ *
+ * @return the HTTP method
+ */
+ @Override
+ @Nullable
+ public HttpMethod getHttpMethod() {
+ return baseRequest.getHttpMethod();
+ }
+
+ /**
+ * Gets the headers
+ *
+ * @return the headers
+ */
+ @Override
+ @Nullable
+ public List getHeaders() {
+ return baseRequest.getHeaders();
+ }
+
+ /**
+ * Adds a header to this request
+ *
+ * @param header the name of the header
+ * @param value the value of the header
+ */
+ @Override
+ public void addHeader(@Nonnull final String header, @Nullable final String value) {
+ Objects.requireNonNull(header, "parameter header cannot be null");
+ baseRequest.addHeader(header, value);
+ }
+
+ /**
+ * Sets useCaches parameter to cache the response
+ *
+ * @param useCaches the value of useCaches
+ */
+ @Override
+ public void setUseCaches(boolean useCaches) {
+ baseRequest.setUseCaches(useCaches);
+ }
+
+ /**
+ * Gets useCaches parameter
+ *
+ * @return the value of useCaches
+ */
+ @Override
+ public boolean getUseCaches() {
+ return baseRequest.getUseCaches();
+ }
+
+ /**
+ * Gets the full list of options for this request
+ *
+ * @return the full list of options for this request
+ */
+ @Nullable
+ public List