<%= error[:message] %>
84 | <%if error[:debug] %> 85 |<%= error[:debug] %>
86 | <% end %>
87 | ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── ask-a-question.md │ └── bug_report.md ├── dependabot.yml ├── policies │ └── resourceManagement.yml └── workflows │ ├── auto-merge-dependabot.yml │ └── ruby.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── SECURITY.md └── graph-sample ├── .gitattributes ├── .gitignore ├── .rubocop.yml ├── .rubocop_todo.yml ├── .ruby-version ├── Gemfile ├── Gemfile.lock ├── Rakefile ├── app ├── assets │ ├── config │ │ └── manifest.js │ ├── images │ │ ├── .keep │ │ ├── favicon.png │ │ └── no-profile-photo.png │ └── stylesheets │ │ └── application.css ├── channels │ └── application_cable │ │ ├── channel.rb │ │ └── connection.rb ├── controllers │ ├── application_controller.rb │ ├── auth_controller.rb │ ├── calendar_controller.rb │ ├── concerns │ │ └── .keep │ └── home_controller.rb ├── helpers │ ├── application_helper.rb │ ├── auth_helper.rb │ ├── calendar_helper.rb │ ├── graph_helper.rb │ └── home_helper.rb ├── javascript │ ├── application.js │ └── controllers │ │ ├── application.js │ │ ├── hello_controller.js │ │ └── index.js ├── jobs │ └── application_job.rb ├── mailers │ └── application_mailer.rb ├── models │ ├── application_record.rb │ └── concerns │ │ └── .keep └── views │ ├── calendar │ ├── index.html.erb │ └── new.html.erb │ ├── home │ └── index.html.erb │ └── layouts │ ├── application.html.erb │ ├── mailer.html.erb │ └── mailer.text.erb ├── bin ├── bundle ├── importmap ├── rails ├── rake └── setup ├── config.ru ├── config ├── application.rb ├── boot.rb ├── cable.yml ├── credentials.yml.enc ├── database.yml ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ └── test.rb ├── importmap.rb ├── initializers │ ├── assets.rb │ ├── content_security_policy.rb │ ├── filter_parameter_logging.rb │ ├── inflections.rb │ ├── omniauth_graph.rb │ ├── permissions_policy.rb │ └── session_store.rb ├── locales │ └── en.yml ├── oauth_environment_variables.rb.example ├── puma.rb ├── routes.rb └── storage.yml ├── db ├── migrate │ └── 20221004165905_add_sessions_table.rb ├── schema.rb └── seeds.rb ├── lib ├── assets │ └── .keep ├── microsoft_graph_auth.rb └── tasks │ └── .keep ├── log └── .keep ├── public ├── 404.html ├── 422.html ├── 500.html ├── apple-touch-icon-precomposed.png ├── apple-touch-icon.png ├── favicon.ico └── robots.txt ├── storage └── .keep ├── test ├── application_system_test_case.rb ├── channels │ └── application_cable │ │ └── connection_test.rb ├── controllers │ ├── .keep │ ├── auth_controller_test.rb │ ├── calendar_controller_test.rb │ └── home_controller_test.rb ├── fixtures │ └── files │ │ └── .keep ├── helpers │ └── .keep ├── integration │ └── .keep ├── mailers │ └── .keep ├── models │ └── .keep ├── system │ └── .keep └── test_helper.rb ├── tmp ├── .keep ├── pids │ └── .keep └── storage │ └── .keep └── vendor ├── .keep └── javascript └── .keep /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/ask-a-question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Ask a question 3 | about: Ask a question about Graph, adding features to this sample, etc. 4 | title: '' 5 | labels: question, needs triage 6 | assignees: '' 7 | 8 | --- 9 | 10 | Thank you for taking an interest in Microsoft Graph development! Please feel free to ask a question here, but keep in mind the following: 11 | 12 | - This is not an official Microsoft support channel, and our ability to respond to questions here is limited. Questions about Graph, or questions about adding a new feature to the sample, will be answered on a best-effort basis. 13 | - Questions should be asked on [Microsoft Q&A](https://learn.microsoft.com/answers/products/graph). 14 | - Issues with Microsoft Graph itself should be handled through [support](https://developer.microsoft.com/graph/support). 15 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug report, needs triage 6 | assignees: '' 7 | --- 8 | 9 | ### Describe the bug 10 | 11 | A clear and concise description of what the bug is. 12 | 13 | ### To Reproduce 14 | 15 | Steps to reproduce the behavior: 16 | 17 | 1. Go to '...' 18 | 1. Click on '....' 19 | 1. Scroll down to '....' 20 | 1. See error 21 | 22 | ### Expected behavior 23 | 24 | A clear and concise description of what you expected to happen. 25 | 26 | ### Screenshots 27 | 28 | If applicable, add screenshots to help explain your problem. 29 | 30 | ### Desktop 31 | 32 | - OS: [e.g. iOS] 33 | - Browser [e.g. chrome, safari] 34 | - Version [e.g. 22] 35 | 36 | ### Dependency versions 37 | 38 | - Authentication library (MSAL, etc.) version: 39 | - Graph library (Graph SDK, REST library, etc.) version: 40 | 41 | ### Additional context 42 | 43 | Add any other context about the problem here. 44 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: bundler # See documentation for possible values 4 | directory: graph-sample/ # Location of package manifests 5 | schedule: 6 | interval: weekly 7 | - package-ecosystem: github-actions 8 | directory: / 9 | schedule: 10 | interval: weekly 11 | -------------------------------------------------------------------------------- /.github/policies/resourceManagement.yml: -------------------------------------------------------------------------------- 1 | id: 2 | name: GitOps.PullRequestIssueManagement 3 | description: GitOps.PullRequestIssueManagement primitive 4 | owner: 5 | resource: repository 6 | disabled: false 7 | where: 8 | configuration: 9 | resourceManagementConfiguration: 10 | scheduledSearches: 11 | - description: 12 | frequencies: 13 | - hourly: 14 | hour: 6 15 | filters: 16 | - isIssue 17 | - isOpen 18 | - hasLabel: 19 | label: needs author feedback 20 | - hasLabel: 21 | label: no recent activity 22 | - noActivitySince: 23 | days: 3 24 | actions: 25 | - closeIssue 26 | - description: 27 | frequencies: 28 | - hourly: 29 | hour: 6 30 | filters: 31 | - isIssue 32 | - isOpen 33 | - hasLabel: 34 | label: needs author feedback 35 | - noActivitySince: 36 | days: 4 37 | - isNotLabeledWith: 38 | label: no recent activity 39 | actions: 40 | - addLabel: 41 | label: no recent activity 42 | - addReply: 43 | reply: 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**. 44 | - description: 45 | frequencies: 46 | - hourly: 47 | hour: 6 48 | filters: 49 | - isIssue 50 | - isOpen 51 | - hasLabel: 52 | label: duplicate 53 | - noActivitySince: 54 | days: 1 55 | actions: 56 | - addReply: 57 | reply: This issue has been marked as duplicate and has not had any activity for **1 day**. It will be closed for housekeeping purposes. 58 | - closeIssue 59 | - description: 60 | frequencies: 61 | - hourly: 62 | hour: 3 63 | filters: 64 | - isOpen 65 | - isIssue 66 | - hasLabel: 67 | label: graph question 68 | actions: 69 | - removeLabel: 70 | label: 'needs attention :wave:' 71 | - removeLabel: 72 | label: needs author feedback 73 | - removeLabel: 74 | label: 'needs triage :mag:' 75 | - removeLabel: 76 | label: no recent activity 77 | - addLabel: 78 | label: out of scope 79 | - addReply: 80 | reply: >- 81 | It looks like you are asking a question about using Microsoft Graph or one of the Microsoft Graph SDKs that is not directly related to this sample. Unfortunately we are not set up to answer general questions in this repository, so this issue will be closed. 82 | 83 | 84 | Please try asking your question on [Stack Overflow](https://stackoverflow.com/questions/tagged/microsoft-graph-api), tagging your question with `microsoft-graph-api`. 85 | - closeIssue 86 | - description: 87 | frequencies: 88 | - hourly: 89 | hour: 3 90 | filters: 91 | - isOpen 92 | - isIssue 93 | - hasLabel: 94 | label: graph issue 95 | actions: 96 | - removeLabel: 97 | label: 'needs attention :wave:' 98 | - removeLabel: 99 | label: needs author feedback 100 | - removeLabel: 101 | label: 'needs triage :mag:' 102 | - removeLabel: 103 | label: no recent activity 104 | - addLabel: 105 | label: out of scope 106 | - addReply: 107 | reply: >- 108 | It looks like you are reporting an issue with Microsoft Graph or one of the Microsoft Graph SDKs that is not fixable by changing code in this sample. Unfortunately we are not set up to provide product support in this repository, so this issue will be closed. 109 | 110 | 111 | Please visit one of the following links to report your issue. 112 | 113 | 114 | - Issue with Microsoft Graph service: [Microsoft Graph support](https://developer.microsoft.com/graph/support#report-issues-with-the-service), choose one of the options under **Report issues with the service** 115 | 116 | - Issue with a Microsoft Graph SDK: Open an issue in the SDK's GitHub repository. See [microsoftgraph on GitHub](https://github.com/microsoftgraph?q=sdk+in%3Aname&type=public&language=) for a list of SDK repositories. 117 | - closeIssue 118 | eventResponderTasks: 119 | - if: 120 | - payloadType: Issue_Comment 121 | - isAction: 122 | action: Created 123 | - isActivitySender: 124 | issueAuthor: True 125 | - hasLabel: 126 | label: needs author feedback 127 | - isOpen 128 | then: 129 | - addLabel: 130 | label: 'needs attention :wave:' 131 | - removeLabel: 132 | label: needs author feedback 133 | description: 134 | - if: 135 | - payloadType: Issues 136 | - not: 137 | isAction: 138 | action: Closed 139 | - hasLabel: 140 | label: no recent activity 141 | then: 142 | - removeLabel: 143 | label: no recent activity 144 | description: 145 | - if: 146 | - payloadType: Issue_Comment 147 | - hasLabel: 148 | label: no recent activity 149 | then: 150 | - removeLabel: 151 | label: no recent activity 152 | description: 153 | - if: 154 | - payloadType: Pull_Request 155 | then: 156 | - inPrLabel: 157 | label: in pr 158 | description: 159 | onFailure: 160 | onSuccess: 161 | -------------------------------------------------------------------------------- /.github/workflows/auto-merge-dependabot.yml: -------------------------------------------------------------------------------- 1 | name: Auto-merge dependabot updates 2 | 3 | on: 4 | pull_request: 5 | branches: [ main ] 6 | 7 | permissions: 8 | pull-requests: write 9 | contents: write 10 | 11 | jobs: 12 | 13 | dependabot-merge: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | if: ${{ github.actor == 'dependabot[bot]' }} 18 | 19 | steps: 20 | - name: Dependabot metadata 21 | id: metadata 22 | uses: dependabot/fetch-metadata@v2.3.0 23 | with: 24 | github-token: "${{ secrets.GITHUB_TOKEN }}" 25 | 26 | - name: Enable auto-merge for Dependabot PRs 27 | if: ${{steps.metadata.outputs.update-type != 'version-update:semver-major'}} 28 | run: gh pr merge --auto --merge "$PR_URL" 29 | env: 30 | PR_URL: ${{github.event.pull_request.html_url}} 31 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 32 | -------------------------------------------------------------------------------- /.github/workflows/ruby.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | # This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake 6 | # For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby 7 | 8 | name: Ruby 9 | 10 | on: 11 | push: 12 | branches: [ main ] 13 | pull_request: 14 | branches: [ main ] 15 | workflow_dispatch: 16 | 17 | permissions: 18 | contents: read 19 | 20 | jobs: 21 | test: 22 | defaults: 23 | run: 24 | working-directory: graph-sample/ 25 | 26 | runs-on: ubuntu-latest 27 | strategy: 28 | matrix: 29 | ruby-version: ['3.3.5'] 30 | 31 | steps: 32 | - uses: actions/checkout@v4 33 | - name: Set up Ruby 34 | # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby, 35 | # change this to (see https://github.com/ruby/setup-ruby#versioning): 36 | # uses: ruby/setup-ruby@v1 37 | uses: ruby/setup-ruby@354a1ad156761f5ee2b7b13fa8e09943a5e8d252 # v1.229.0 38 | with: 39 | ruby-version: ${{ matrix.ruby-version }} 40 | bundler-cache: true # runs 'bundle install' and caches installed gems automatically 41 | - name: Copy settings file 42 | run: | 43 | cp config/oauth_environment_variables.rb.example config/oauth_environment_variables.rb 44 | - name: Install gems 45 | run: bundle install 46 | - name: Run migrations 47 | run: bundle exec rake db:migrate 48 | - name: Run tests 49 | run: bundle exec rake 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.rbc 2 | capybara-*.html 3 | .rspec 4 | /log 5 | /tmp 6 | /db/*.sqlite3 7 | /db/*.sqlite3-journal 8 | /public/system 9 | /coverage/ 10 | /spec/tmp 11 | *.orig 12 | rerun.txt 13 | pickle-email-*.html 14 | 15 | # TODO Comment out this rule if you are OK with secrets being uploaded to the repo 16 | config/initializers/secret_token.rb 17 | config/master.key 18 | 19 | # Only include if you have production secrets in this file, which is no longer a Rails default 20 | # config/secrets.yml 21 | 22 | # dotenv 23 | # TODO Comment out this rule if environment variables can be committed 24 | .env 25 | 26 | ## Environment normalization: 27 | /.bundle 28 | /vendor/bundle 29 | 30 | # these should all be checked in to normalize the environment: 31 | # Gemfile.lock, .ruby-version, .ruby-gemset 32 | 33 | # unless supporting rvm < 1.11.0 or doing something fancy, ignore this: 34 | .rvmrc 35 | 36 | # if using bower-rails ignore default bower_components path bower.json files 37 | /vendor/assets/bower_components 38 | *.bowerrc 39 | bower.json 40 | 41 | # Ignore pow environment settings 42 | .powenv 43 | 44 | # Ignore Byebug command history file. 45 | .byebug_history 46 | 47 | # Ignore node_modules 48 | node_modules/ 49 | 50 | # Visual Studio Code 51 | .vscode/ 52 | 53 | oauth_environment_variables.rb 54 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 10 | - Employees can reach out at [aka.ms/opensource/moderation-support](https://aka.ms/opensource/moderation-support) 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Microsoft Graph 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_type: sample 3 | description: This sample demonstrates how to use the Microsoft Graph REST API to access data in Office 365 from Ruby on Rails apps. 4 | products: 5 | - ms-graph 6 | - office-exchange-online 7 | languages: 8 | - ruby 9 | --- 10 | 11 | # Microsoft Graph sample Ruby on Rails app 12 | 13 | [](https://github.com/microsoftgraph/msgraph-sample-rubyrailsapp/actions/workflows/ruby.yml)  14 | 15 | This sample demonstrates how to use the Microsoft Graph REST API to access data in Office 365 from Ruby on Rails apps. 16 | 17 | > **NOTE:** This sample was originally built from a tutorial published on the [Microsoft Graph tutorials](https://learn.microsoft.com/graph/tutorials) page. That tutorial has been removed. 18 | 19 | ## Prerequisites 20 | 21 | To run the completed project in this folder, you need the following: 22 | 23 | - [Ruby](https://www.ruby-lang.org/en/downloads/) 24 | - [SQLite3](https://sqlite.org/index.html) 25 | 26 | This sample was written for Ruby 3.1.2. 27 | 28 | ## Register a web application with the Microsoft Entra admin center 29 | 30 | 1. Open a browser and navigate to the [Microsoft Entra admin center](https://entra.microsoft.com). Login using a **Work or School Account**. 31 | 32 | 1. Expand **Identity** in the left-hand navigation, expand **Applications**, then select **App registrations**. 33 | 34 | 1. Select **New registration**. On the **Register an application** page, set the values as follows. 35 | 36 | - Set **Name** to `Ruby Graph Tutorial`. 37 | - Set **Supported account types** to **Accounts in any organizational directory and personal Microsoft accounts**. 38 | - Under **Redirect URI**, set the first drop-down to `Web` and set the value to `http://localhost:3000/auth/microsoft_graph_auth/callback`. 39 | 40 | 1. Choose **Register**. On the **Ruby Graph Tutorial** page, copy the value of the **Application (client) ID** and save it, you will need it in the next step. 41 | 42 | 1. Select **Certificates & secrets** under **Manage**. Select the **New client secret** button. Enter a value in **Description** and select one of the options for **Expires** and choose **Add**. 43 | 44 | 1. Copy the client secret value before you leave this page. You will need it in the next step. 45 | 46 | ## Configure the sample 47 | 48 | 1. Rename the `./graph-sample/config/oauth_environment_variables.rb.example` file to `oauth_environment_variables.rb`. 49 | 50 | 1. Edit the `oauth_environment_variables.rb` file and make the following changes. 51 | 1. Replace `YOUR_APP_ID_HERE` with the **Application Id** you got from the App Registration Portal. 52 | 1. Replace `YOUR_APP_SECRET_HERE` with the secret you got from the App Registration Portal. 53 | 54 | 1. In your command-line interface (CLI), navigate to the `./graph-sample` directory and run the following command to install requirements. 55 | 56 | ```Shell 57 | bundle install 58 | ``` 59 | 60 | 1. In your CLI, run the following command to initialize the app's database. 61 | 62 | ```Shell 63 | rake db:migrate 64 | ``` 65 | 66 | ## Run the sample 67 | 68 | 1. Run the following command in your CLI to start the application. 69 | 70 | ```Shell 71 | rails server 72 | ``` 73 | 74 | 1. Open a browser and browse to `http://localhost:3000`. 75 | 76 | ## Code of conduct 77 | 78 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 79 | 80 | ## Disclaimer 81 | 82 | **THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.** 83 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Security 4 | 5 | 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/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). 14 | 15 | 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://aka.ms/opensource/security/pgpkey). 16 | 17 | 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://aka.ms/opensource/security/msrc). 18 | 19 | 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: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | 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://aka.ms/opensource/security/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /graph-sample/.gitattributes: -------------------------------------------------------------------------------- 1 | # See https://git-scm.com/docs/gitattributes for more about git attribute files. 2 | 3 | # Mark the database schema as having been generated. 4 | db/schema.rb linguist-generated 5 | 6 | # Mark any vendored files as having been vendored. 7 | vendor/* linguist-vendored 8 | -------------------------------------------------------------------------------- /graph-sample/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile '~/.gitignore_global' 6 | 7 | # Ignore bundler config. 8 | /.bundle 9 | 10 | # Ignore the default SQLite database. 11 | /db/*.sqlite3 12 | /db/*.sqlite3-* 13 | 14 | # Ignore all logfiles and tempfiles. 15 | /log/* 16 | /tmp/* 17 | !/log/.keep 18 | !/tmp/.keep 19 | 20 | # Ignore pidfiles, but keep the directory. 21 | /tmp/pids/* 22 | !/tmp/pids/ 23 | !/tmp/pids/.keep 24 | 25 | # Ignore uploaded files in development. 26 | /storage/* 27 | !/storage/.keep 28 | /tmp/storage/* 29 | !/tmp/storage/ 30 | !/tmp/storage/.keep 31 | 32 | /public/assets 33 | 34 | # Ignore master key for decrypting credentials and more. 35 | /config/master.key 36 | -------------------------------------------------------------------------------- /graph-sample/.rubocop.yml: -------------------------------------------------------------------------------- 1 | inherit_from: .rubocop_todo.yml 2 | 3 | AllCops: 4 | NewCops: enable 5 | Exclude: 6 | - 'bin/**/*' 7 | - 'db/**/*' 8 | - 'test/**/*' 9 | 10 | Style/FrozenStringLiteralComment: 11 | Exclude: 12 | - 'Gemfile' 13 | - 'Rakefile' 14 | - 'app/channels/**/*' 15 | - 'app/jobs/**/*' 16 | - 'app/mailers/**/*' 17 | - 'app/models/**/*' 18 | - 'config/environments/*' 19 | - 'config/importmap.rb' 20 | -------------------------------------------------------------------------------- /graph-sample/.rubocop_todo.yml: -------------------------------------------------------------------------------- 1 | # This configuration was generated by 2 | # `rubocop --auto-gen-config` 3 | # on 2022-10-04 20:43:23 UTC using RuboCop version 1.36.0. 4 | # The point is for the user to remove these configuration records 5 | # one by one as the offenses are removed from the code base. 6 | # Note that changes in the inspected code, or installation of new 7 | # versions of RuboCop, may require this file to be generated again. 8 | 9 | # Offense count: 2 10 | # This cop supports safe autocorrection (--autocorrect). 11 | # Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBrackets. 12 | # SupportedStyles: space, no_space, compact 13 | # SupportedStylesForEmptyBrackets: space, no_space 14 | Layout/SpaceInsideArrayLiteralBrackets: 15 | Exclude: 16 | - 'config/environments/production.rb' 17 | 18 | # Offense count: 4 19 | # This cop supports safe autocorrection (--autocorrect). 20 | Layout/SpaceInsidePercentLiteralDelimiters: 21 | Exclude: 22 | - 'Gemfile' 23 | 24 | # Offense count: 5 25 | # Configuration parameters: CountComments, CountAsOne, ExcludedMethods, AllowedMethods, AllowedPatterns, IgnoredMethods. 26 | Metrics/MethodLength: 27 | Max: 30 28 | 29 | # Offense count: 1 30 | # Configuration parameters: CountComments, CountAsOne. 31 | Metrics/ModuleLength: 32 | Max: 212 33 | 34 | # Offense count: 2 35 | # Configuration parameters: CountKeywordArgs, MaxOptionalParameters. 36 | Metrics/ParameterLists: 37 | Max: 7 38 | 39 | # Offense count: 8 40 | # Configuration parameters: AllowedConstants. 41 | Style/Documentation: 42 | Exclude: 43 | - 'spec/**/*' 44 | - 'test/**/*' 45 | - 'app/controllers/application_controller.rb' 46 | - 'app/controllers/auth_controller.rb' 47 | - 'app/controllers/home_controller.rb' 48 | - 'app/helpers/application_helper.rb' 49 | - 'app/helpers/auth_helper.rb' 50 | - 'app/helpers/calendar_helper.rb' 51 | - 'app/helpers/home_helper.rb' 52 | - 'config/application.rb' 53 | 54 | # Offense count: 1 55 | # This cop supports unsafe autocorrection (--autocorrect-all). 56 | Style/GlobalStdStream: 57 | Exclude: 58 | - 'config/environments/production.rb' 59 | 60 | # Offense count: 2 61 | # This cop supports unsafe autocorrection (--autocorrect-all). 62 | # Configuration parameters: SafeForConstants. 63 | Style/RedundantFetchBlock: 64 | Exclude: 65 | - 'config/puma.rb' 66 | 67 | # Offense count: 60 68 | # This cop supports safe autocorrection (--autocorrect). 69 | # Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline. 70 | # SupportedStyles: single_quotes, double_quotes 71 | Style/StringLiterals: 72 | Exclude: 73 | - 'Gemfile' 74 | - 'Rakefile' 75 | - 'app/mailers/application_mailer.rb' 76 | - 'config.ru' 77 | - 'config/application.rb' 78 | - 'config/boot.rb' 79 | - 'config/environment.rb' 80 | - 'config/environments/development.rb' 81 | - 'config/environments/production.rb' 82 | - 'config/environments/test.rb' 83 | - 'config/importmap.rb' 84 | - 'config/initializers/assets.rb' 85 | - 'config/puma.rb' 86 | 87 | # Offense count: 2 88 | # This cop supports safe autocorrection (--autocorrect). 89 | # Configuration parameters: . 90 | # SupportedStyles: percent, brackets 91 | Style/SymbolArray: 92 | EnforcedStyle: percent 93 | MinSize: 10 94 | -------------------------------------------------------------------------------- /graph-sample/.ruby-version: -------------------------------------------------------------------------------- 1 | 3.3.5 2 | -------------------------------------------------------------------------------- /graph-sample/Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | git_source(:github) { |repo| "https://github.com/#{repo}.git" } 3 | 4 | ruby "3.3.5" 5 | 6 | # Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main" 7 | gem "rails", "~> 7.2.2" 8 | 9 | # The original asset pipeline for Rails [https://github.com/rails/sprockets-rails] 10 | gem "sprockets-rails" 11 | 12 | # Use sqlite3 as the database for Active Record 13 | gem "sqlite3", "~> 2.6" 14 | 15 | # Use the Puma web server [https://github.com/puma/puma] 16 | gem "puma", "~> 6.6" 17 | 18 | # Use JavaScript with ESM import maps [https://github.com/rails/importmap-rails] 19 | gem "importmap-rails" 20 | 21 | # Hotwire's SPA-like page accelerator [https://turbo.hotwired.dev] 22 | gem "turbo-rails" 23 | 24 | # Hotwire's modest JavaScript framework [https://stimulus.hotwired.dev] 25 | gem "stimulus-rails" 26 | 27 | # Build JSON APIs with ease [https://github.com/rails/jbuilder] 28 | gem "jbuilder" 29 | 30 | # Use Redis adapter to run Action Cable in production 31 | # gem "redis", "~> 4.0" 32 | 33 | # Use Kredis to get higher-level data types in Redis [https://github.com/rails/kredis] 34 | # gem "kredis" 35 | 36 | # Use Active Model has_secure_password [https://guides.rubyonrails.org/active_model_basics.html#securepassword] 37 | # gem "bcrypt", "~> 3.1.7" 38 | 39 | # Windows does not include zoneinfo files, so bundle the tzinfo-data gem 40 | gem "tzinfo-data", platforms: %i[ mingw mswin x64_mingw jruby ] 41 | 42 | # Reduces boot times through caching; required in config/boot.rb 43 | gem "bootsnap", require: false 44 | 45 | # Use Sass to process CSS 46 | # gem "sassc-rails" 47 | 48 | # Use Active Storage variants [https://guides.rubyonrails.org/active_storage_overview.html#transforming-images] 49 | # gem "image_processing", "~> 1.2" 50 | 51 | group :development, :test do 52 | # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem 53 | gem "debug", platforms: %i[ mri mingw x64_mingw ] 54 | end 55 | 56 | group :development do 57 | # Use console on exceptions pages [https://github.com/rails/web-console] 58 | gem "web-console" 59 | 60 | # Add speed badges [https://github.com/MiniProfiler/rack-mini-profiler] 61 | # gem "rack-mini-profiler" 62 | 63 | # Speed up commands on slow machines / big apps [https://github.com/rails/spring] 64 | # gem "spring" 65 | end 66 | 67 | group :test do 68 | # Use system testing [https://guides.rubyonrails.org/testing.html#system-testing] 69 | gem "capybara" 70 | gem "selenium-webdriver" 71 | gem "webdrivers" 72 | end 73 | 74 | # OAuth 75 | gem 'omniauth-oauth2', '~> 1.8.0' 76 | # OmniAuth CSRF protection 77 | gem 'omniauth-rails_csrf_protection', '~> 1.0.2' 78 | # REST calls to Microsoft Graph 79 | gem 'httparty', '~> 0.23.1' 80 | # Session storage in database 81 | gem 'activerecord-session_store', '~> 2.2.0' 82 | 83 | gem "ostruct", "~> 0.6.1" 84 | -------------------------------------------------------------------------------- /graph-sample/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | actioncable (7.2.2) 5 | actionpack (= 7.2.2) 6 | activesupport (= 7.2.2) 7 | nio4r (~> 2.0) 8 | websocket-driver (>= 0.6.1) 9 | zeitwerk (~> 2.6) 10 | actionmailbox (7.2.2) 11 | actionpack (= 7.2.2) 12 | activejob (= 7.2.2) 13 | activerecord (= 7.2.2) 14 | activestorage (= 7.2.2) 15 | activesupport (= 7.2.2) 16 | mail (>= 2.8.0) 17 | actionmailer (7.2.2) 18 | actionpack (= 7.2.2) 19 | actionview (= 7.2.2) 20 | activejob (= 7.2.2) 21 | activesupport (= 7.2.2) 22 | mail (>= 2.8.0) 23 | rails-dom-testing (~> 2.2) 24 | actionpack (7.2.2) 25 | actionview (= 7.2.2) 26 | activesupport (= 7.2.2) 27 | nokogiri (>= 1.8.5) 28 | racc 29 | rack (>= 2.2.4, < 3.2) 30 | rack-session (>= 1.0.1) 31 | rack-test (>= 0.6.3) 32 | rails-dom-testing (~> 2.2) 33 | rails-html-sanitizer (~> 1.6) 34 | useragent (~> 0.16) 35 | actiontext (7.2.2) 36 | actionpack (= 7.2.2) 37 | activerecord (= 7.2.2) 38 | activestorage (= 7.2.2) 39 | activesupport (= 7.2.2) 40 | globalid (>= 0.6.0) 41 | nokogiri (>= 1.8.5) 42 | actionview (7.2.2) 43 | activesupport (= 7.2.2) 44 | builder (~> 3.1) 45 | erubi (~> 1.11) 46 | rails-dom-testing (~> 2.2) 47 | rails-html-sanitizer (~> 1.6) 48 | activejob (7.2.2) 49 | activesupport (= 7.2.2) 50 | globalid (>= 0.3.6) 51 | activemodel (7.2.2) 52 | activesupport (= 7.2.2) 53 | activerecord (7.2.2) 54 | activemodel (= 7.2.2) 55 | activesupport (= 7.2.2) 56 | timeout (>= 0.4.0) 57 | activerecord-session_store (2.2.0) 58 | actionpack (>= 7.0) 59 | activerecord (>= 7.0) 60 | cgi (>= 0.3.6) 61 | rack (>= 2.0.8, < 4) 62 | railties (>= 7.0) 63 | activestorage (7.2.2) 64 | actionpack (= 7.2.2) 65 | activejob (= 7.2.2) 66 | activerecord (= 7.2.2) 67 | activesupport (= 7.2.2) 68 | marcel (~> 1.0) 69 | activesupport (7.2.2) 70 | base64 71 | benchmark (>= 0.3) 72 | bigdecimal 73 | concurrent-ruby (~> 1.0, >= 1.3.1) 74 | connection_pool (>= 2.2.5) 75 | drb 76 | i18n (>= 1.6, < 2) 77 | logger (>= 1.4.2) 78 | minitest (>= 5.1) 79 | securerandom (>= 0.3) 80 | tzinfo (~> 2.0, >= 2.0.5) 81 | addressable (2.8.7) 82 | public_suffix (>= 2.0.2, < 7.0) 83 | base64 (0.2.0) 84 | benchmark (0.4.0) 85 | bigdecimal (3.1.9) 86 | bindex (0.8.1) 87 | bootsnap (1.18.4) 88 | msgpack (~> 1.2) 89 | builder (3.3.0) 90 | capybara (3.40.0) 91 | addressable 92 | matrix 93 | mini_mime (>= 0.1.3) 94 | nokogiri (~> 1.11) 95 | rack (>= 1.6.0) 96 | rack-test (>= 0.6.3) 97 | regexp_parser (>= 1.5, < 3.0) 98 | xpath (~> 3.2) 99 | cgi (0.4.2) 100 | concurrent-ruby (1.3.5) 101 | connection_pool (2.5.0) 102 | crass (1.0.6) 103 | csv (3.3.3) 104 | date (3.4.1) 105 | debug (1.10.0) 106 | irb (~> 1.10) 107 | reline (>= 0.3.8) 108 | drb (2.2.1) 109 | erubi (1.13.1) 110 | faraday (2.12.0) 111 | faraday-net_http (>= 2.0, < 3.4) 112 | json 113 | logger 114 | faraday-net_http (3.3.0) 115 | net-http 116 | globalid (1.2.1) 117 | activesupport (>= 6.1) 118 | hashie (5.0.0) 119 | httparty (0.23.1) 120 | csv 121 | mini_mime (>= 1.0.0) 122 | multi_xml (>= 0.5.2) 123 | i18n (1.14.7) 124 | concurrent-ruby (~> 1.0) 125 | importmap-rails (2.1.0) 126 | actionpack (>= 6.0.0) 127 | activesupport (>= 6.0.0) 128 | railties (>= 6.0.0) 129 | io-console (0.8.0) 130 | irb (1.15.1) 131 | pp (>= 0.6.0) 132 | rdoc (>= 4.0.0) 133 | reline (>= 0.4.2) 134 | jbuilder (2.13.0) 135 | actionview (>= 5.0.0) 136 | activesupport (>= 5.0.0) 137 | json (2.7.2) 138 | jwt (2.9.3) 139 | base64 140 | logger (1.7.0) 141 | loofah (2.24.0) 142 | crass (~> 1.0.2) 143 | nokogiri (>= 1.12.0) 144 | mail (2.8.1) 145 | mini_mime (>= 0.1.1) 146 | net-imap 147 | net-pop 148 | net-smtp 149 | marcel (1.0.4) 150 | matrix (0.4.2) 151 | mini_mime (1.1.5) 152 | minitest (5.25.5) 153 | msgpack (1.7.3) 154 | multi_xml (0.7.1) 155 | bigdecimal (~> 3.1) 156 | net-http (0.4.1) 157 | uri 158 | net-imap (0.5.0) 159 | date 160 | net-protocol 161 | net-pop (0.1.2) 162 | net-protocol 163 | net-protocol (0.2.2) 164 | timeout 165 | net-smtp (0.5.0) 166 | net-protocol 167 | nio4r (2.7.4) 168 | nokogiri (1.18.6-x86_64-linux-gnu) 169 | racc (~> 1.4) 170 | oauth2 (2.0.9) 171 | faraday (>= 0.17.3, < 3.0) 172 | jwt (>= 1.0, < 3.0) 173 | multi_xml (~> 0.5) 174 | rack (>= 1.2, < 4) 175 | snaky_hash (~> 2.0) 176 | version_gem (~> 1.1) 177 | omniauth (2.1.2) 178 | hashie (>= 3.4.6) 179 | rack (>= 2.2.3) 180 | rack-protection 181 | omniauth-oauth2 (1.8.0) 182 | oauth2 (>= 1.4, < 3) 183 | omniauth (~> 2.0) 184 | omniauth-rails_csrf_protection (1.0.2) 185 | actionpack (>= 4.2) 186 | omniauth (~> 2.0) 187 | ostruct (0.6.1) 188 | pp (0.6.2) 189 | prettyprint 190 | prettyprint (0.2.0) 191 | psych (5.2.3) 192 | date 193 | stringio 194 | public_suffix (6.0.1) 195 | puma (6.6.0) 196 | nio4r (~> 2.0) 197 | racc (1.8.1) 198 | rack (3.1.12) 199 | rack-protection (4.0.0) 200 | base64 (>= 0.1.0) 201 | rack (>= 3.0.0, < 4) 202 | rack-session (2.1.0) 203 | base64 (>= 0.1.0) 204 | rack (>= 3.0.0) 205 | rack-test (2.2.0) 206 | rack (>= 1.3) 207 | rackup (2.2.1) 208 | rack (>= 3) 209 | rails (7.2.2) 210 | actioncable (= 7.2.2) 211 | actionmailbox (= 7.2.2) 212 | actionmailer (= 7.2.2) 213 | actionpack (= 7.2.2) 214 | actiontext (= 7.2.2) 215 | actionview (= 7.2.2) 216 | activejob (= 7.2.2) 217 | activemodel (= 7.2.2) 218 | activerecord (= 7.2.2) 219 | activestorage (= 7.2.2) 220 | activesupport (= 7.2.2) 221 | bundler (>= 1.15.0) 222 | railties (= 7.2.2) 223 | rails-dom-testing (2.2.0) 224 | activesupport (>= 5.0.0) 225 | minitest 226 | nokogiri (>= 1.6) 227 | rails-html-sanitizer (1.6.2) 228 | loofah (~> 2.21) 229 | nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) 230 | railties (7.2.2) 231 | actionpack (= 7.2.2) 232 | activesupport (= 7.2.2) 233 | irb (~> 1.13) 234 | rackup (>= 1.0.0) 235 | rake (>= 12.2) 236 | thor (~> 1.0, >= 1.2.2) 237 | zeitwerk (~> 2.6) 238 | rake (13.2.1) 239 | rdoc (6.13.1) 240 | psych (>= 4.0.0) 241 | regexp_parser (2.9.2) 242 | reline (0.6.0) 243 | io-console (~> 0.5) 244 | rexml (3.3.8) 245 | rubyzip (2.3.2) 246 | securerandom (0.4.1) 247 | selenium-webdriver (4.10.0) 248 | rexml (~> 3.2, >= 3.2.5) 249 | rubyzip (>= 1.2.2, < 3.0) 250 | websocket (~> 1.0) 251 | snaky_hash (2.0.1) 252 | hashie 253 | version_gem (~> 1.1, >= 1.1.1) 254 | sprockets (4.2.1) 255 | concurrent-ruby (~> 1.0) 256 | rack (>= 2.2.4, < 4) 257 | sprockets-rails (3.5.2) 258 | actionpack (>= 6.1) 259 | activesupport (>= 6.1) 260 | sprockets (>= 3.0.0) 261 | sqlite3 (2.6.0-x86_64-linux-gnu) 262 | stimulus-rails (1.3.4) 263 | railties (>= 6.0.0) 264 | stringio (3.1.6) 265 | thor (1.3.2) 266 | timeout (0.4.3) 267 | turbo-rails (2.0.13) 268 | actionpack (>= 7.1.0) 269 | railties (>= 7.1.0) 270 | tzinfo (2.0.6) 271 | concurrent-ruby (~> 1.0) 272 | uri (0.13.1) 273 | useragent (0.16.11) 274 | version_gem (1.1.4) 275 | web-console (4.2.1) 276 | actionview (>= 6.0.0) 277 | activemodel (>= 6.0.0) 278 | bindex (>= 0.4.0) 279 | railties (>= 6.0.0) 280 | webdrivers (5.3.1) 281 | nokogiri (~> 1.6) 282 | rubyzip (>= 1.3.0) 283 | selenium-webdriver (~> 4.0, < 4.11) 284 | websocket (1.2.11) 285 | websocket-driver (0.7.6) 286 | websocket-extensions (>= 0.1.0) 287 | websocket-extensions (0.1.5) 288 | xpath (3.2.0) 289 | nokogiri (~> 1.8) 290 | zeitwerk (2.7.2) 291 | 292 | PLATFORMS 293 | x86_64-linux 294 | 295 | DEPENDENCIES 296 | activerecord-session_store (~> 2.2.0) 297 | bootsnap 298 | capybara 299 | debug 300 | httparty (~> 0.23.1) 301 | importmap-rails 302 | jbuilder 303 | omniauth-oauth2 (~> 1.8.0) 304 | omniauth-rails_csrf_protection (~> 1.0.2) 305 | ostruct (~> 0.6.1) 306 | puma (~> 6.6) 307 | rails (~> 7.2.2) 308 | selenium-webdriver 309 | sprockets-rails 310 | sqlite3 (~> 2.6) 311 | stimulus-rails 312 | turbo-rails 313 | tzinfo-data 314 | web-console 315 | webdrivers 316 | 317 | RUBY VERSION 318 | ruby 3.3.5p100 319 | 320 | BUNDLED WITH 321 | 2.5.21 322 | -------------------------------------------------------------------------------- /graph-sample/Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require_relative "config/application" 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /graph-sample/app/assets/config/manifest.js: -------------------------------------------------------------------------------- 1 | //= link_tree ../images 2 | //= link_directory ../stylesheets .css 3 | //= link_tree ../../javascript .js 4 | //= link_tree ../../../vendor/javascript .js 5 | -------------------------------------------------------------------------------- /graph-sample/app/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/msgraph-sample-rubyrailsapp/0b75127a806e3be4bcab1832cad5b90bf3739f32/graph-sample/app/assets/images/.keep -------------------------------------------------------------------------------- /graph-sample/app/assets/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/msgraph-sample-rubyrailsapp/0b75127a806e3be4bcab1832cad5b90bf3739f32/graph-sample/app/assets/images/favicon.png -------------------------------------------------------------------------------- /graph-sample/app/assets/images/no-profile-photo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/msgraph-sample-rubyrailsapp/0b75127a806e3be4bcab1832cad5b90bf3739f32/graph-sample/app/assets/images/no-profile-photo.png -------------------------------------------------------------------------------- /graph-sample/app/assets/stylesheets/application.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a manifest file that'll be compiled into application.css, which will include all the files 3 | * listed below. 4 | * 5 | * Any CSS (and SCSS, if configured) file within this directory, lib/assets/stylesheets, or any plugin's 6 | * vendor/assets/stylesheets directory can be referenced here using a relative path. 7 | * 8 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the 9 | * compiled file so the styles you add here take precedence over styles defined in any other CSS 10 | * files in this directory. Styles in this file should be added after the last require_* statement. 11 | * It is generally better to create a new file per style scope. 12 | * 13 | *= require_tree . 14 | *= require_self 15 | */ 16 | 17 | .alert-pre { 18 | word-wrap: break-word; 19 | word-break: break-all; 20 | white-space: pre-wrap; 21 | } 22 | 23 | .external-link { 24 | padding-top: 6px; 25 | } 26 | 27 | .avatar-link { 28 | padding-top: 4px; 29 | padding-bottom: 4px; 30 | } 31 | 32 | .profile-photo { 33 | width: 32px; 34 | } 35 | -------------------------------------------------------------------------------- /graph-sample/app/channels/application_cable/channel.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Channel < ActionCable::Channel::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /graph-sample/app/channels/application_cable/connection.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Connection < ActionCable::Connection::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /graph-sample/app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | # frozen_string_literal: true 4 | 5 | require 'microsoft_graph_auth' 6 | require 'oauth2' 7 | 8 | class ApplicationController < ActionController::Base 9 | before_action :set_user 10 | 11 | def set_user 12 | @user_name = user_name 13 | @user_email = user_email 14 | end 15 | 16 | def save_in_session(auth_hash) 17 | # Save the token info 18 | session[:graph_token_hash] = auth_hash[:credentials] 19 | # Save the user's display name 20 | session[:user_name] = auth_hash.dig(:extra, :raw_info, :displayName) 21 | # Save the user's email address 22 | # Use the mail field first. If that's empty, fall back on 23 | # userPrincipalName 24 | session[:user_email] = auth_hash.dig(:extra, :raw_info, :mail) || 25 | auth_hash.dig(:extra, :raw_info, :userPrincipalName) 26 | # Save the user's time zone 27 | session[:user_timezone] = auth_hash.dig(:extra, :raw_info, :mailboxSettings, :timeZone) 28 | end 29 | 30 | def user_name 31 | session[:user_name] 32 | end 33 | 34 | def user_email 35 | session[:user_email] 36 | end 37 | 38 | def user_timezone 39 | session[:user_timezone] 40 | end 41 | 42 | def access_token 43 | token_hash = session[:graph_token_hash] 44 | return if token_hash.nil? 45 | 46 | # Get the expiry time - 5 minutes 47 | expiry = Time.at(token_hash[:expires_at] - 300) 48 | 49 | if Time.now > expiry 50 | # Token expired, refresh 51 | new_hash = refresh_tokens token_hash 52 | new_hash[:token] 53 | else 54 | token_hash[:token] 55 | end 56 | end 57 | 58 | def refresh_tokens(token_hash) 59 | oauth_strategy = OmniAuth::Strategies::MicrosoftGraphAuth.new( 60 | nil, ENV.fetch('AZURE_APP_ID'), ENV.fetch('AZURE_APP_SECRET') 61 | ) 62 | 63 | token = OAuth2::AccessToken.new( 64 | oauth_strategy.client, token_hash[:token], 65 | refresh_token: token_hash[:refresh_token] 66 | ) 67 | 68 | # Refresh the tokens 69 | new_tokens = token.refresh!.to_hash.slice(:access_token, :refresh_token, :expires_at) 70 | 71 | # Rename token key 72 | new_tokens[:token] = new_tokens.delete :access_token 73 | 74 | # Store the new hash 75 | session[:graph_token_hash] = new_tokens 76 | end 77 | end 78 | -------------------------------------------------------------------------------- /graph-sample/app/controllers/auth_controller.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | # frozen_string_literal: true 4 | 5 | class AuthController < ApplicationController 6 | skip_before_action :set_user 7 | 8 | def callback 9 | # Access the authentication hash for omniauth 10 | data = request.env['omniauth.auth'] 11 | 12 | # Save the data in the session 13 | save_in_session data 14 | 15 | redirect_to root_url 16 | end 17 | 18 | def signout 19 | reset_session 20 | redirect_to root_url 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /graph-sample/app/controllers/calendar_controller.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | # frozen_string_literal: true 4 | 5 | # Calendar controller 6 | class CalendarController < ApplicationController 7 | include GraphHelper 8 | 9 | def index 10 | # Get the IANA identifier of the user's time zone 11 | time_zone = get_iana_from_windows(user_timezone) 12 | 13 | # Calculate the start and end of week in the user's time zone 14 | start_datetime = Date.today.beginning_of_week(:sunday).in_time_zone(time_zone).to_time 15 | end_datetime = start_datetime.advance(days: 7) 16 | 17 | @events = get_calendar_view access_token, start_datetime, end_datetime, user_timezone || [] 18 | rescue RuntimeError => e 19 | @events = [] 20 | @errors = [ 21 | { 22 | message: 'Microsoft Graph returned an error getting events.', 23 | debug: e 24 | } 25 | ] 26 | end 27 | 28 | def create 29 | # Semicolon-delimited list, split to an array 30 | attendees = params[:ev_attendees].split(';') 31 | 32 | # Create the event 33 | create_event access_token, 34 | user_timezone, 35 | params[:ev_subject], 36 | params[:ev_start], 37 | params[:ev_end], 38 | attendees, 39 | params[:ev_body] 40 | 41 | # Redirect back to the calendar list 42 | redirect_to({ action: 'index' }) 43 | rescue RuntimeError => e 44 | @errors = [ 45 | { 46 | message: 'Microsoft Graph returned an error creating the event.', 47 | debug: e 48 | } 49 | ] 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /graph-sample/app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/msgraph-sample-rubyrailsapp/0b75127a806e3be4bcab1832cad5b90bf3739f32/graph-sample/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /graph-sample/app/controllers/home_controller.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | # frozen_string_literal: true 4 | 5 | class HomeController < ApplicationController 6 | def index; end 7 | end 8 | -------------------------------------------------------------------------------- /graph-sample/app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | # frozen_string_literal: true 4 | 5 | module ApplicationHelper 6 | end 7 | -------------------------------------------------------------------------------- /graph-sample/app/helpers/auth_helper.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | # frozen_string_literal: true 4 | 5 | module AuthHelper 6 | end 7 | -------------------------------------------------------------------------------- /graph-sample/app/helpers/calendar_helper.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | # frozen_string_literal: true 4 | 5 | module CalendarHelper 6 | end 7 | -------------------------------------------------------------------------------- /graph-sample/app/helpers/graph_helper.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | # frozen_string_literal: true 4 | 5 | require 'httparty' 6 | 7 | # Graph API helper methods 8 | module GraphHelper 9 | GRAPH_HOST = 'https://graph.microsoft.com' 10 | 11 | def make_api_call(method, endpoint, token, headervals = nil, params = nil, payload = nil) 12 | headervals ||= {} 13 | headervals[:Authorization] = "Bearer #{token}" 14 | headervals[:Accept] = 'application/json' 15 | 16 | params ||= {} 17 | 18 | case method.upcase 19 | when 'GET' 20 | HTTParty.get "#{GRAPH_HOST}#{endpoint}", 21 | headers: headervals, 22 | query: params 23 | when 'POST' 24 | headervals['Content-Type'] = 'application/json' 25 | HTTParty.post "#{GRAPH_HOST}#{endpoint}", 26 | headers: headervals, 27 | query: params, 28 | body: payload ? payload.to_json : nil 29 | else 30 | raise "HTTP method #{method.upcase} not implemented" 31 | end 32 | end 33 | 34 | def get_calendar_view(token, start_datetime, end_datetime, timezone) 35 | get_events_url = '/v1.0/me/calendarview' 36 | 37 | headers = { 38 | 'Prefer' => "outlook.timezone=\"#{timezone}\"" 39 | } 40 | 41 | query = { 42 | 'startDateTime' => start_datetime.iso8601, 43 | 'endDateTime' => end_datetime.iso8601, 44 | '$select' => 'subject,organizer,start,end', 45 | '$orderby' => 'start/dateTime', 46 | '$top' => 50 47 | } 48 | 49 | response = make_api_call 'GET', get_events_url, token, headers, query 50 | 51 | raise response.parsed_response.to_s || "Request returned #{response.code}" unless response.code == 200 52 | 53 | response.parsed_response['value'] 54 | end 55 | 56 | def create_event(token, timezone, subject, start_datetime, end_datetime, attendees, body) 57 | create_event_url = '/v1.0/me/events' 58 | 59 | # Create an event object 60 | # https://docs.microsoft.com/graph/api/resources/event?view=graph-rest-1.0 61 | new_event = { 62 | 'subject' => subject, 63 | 'start' => { 64 | 'dateTime' => start_datetime, 65 | 'timeZone' => timezone 66 | }, 67 | 'end' => { 68 | 'dateTime' => end_datetime, 69 | 'timeZone' => timezone 70 | } 71 | } 72 | 73 | unless attendees.empty? 74 | attendee_array = [] 75 | # Create an attendee object 76 | # https://docs.microsoft.com/graph/api/resources/attendee?view=graph-rest-1.0 77 | attendees.each { |email| attendee_array.push({ 'type' => 'required', 'emailAddress' => { 'address' => email } }) } 78 | new_event['attendees'] = attendee_array 79 | end 80 | 81 | unless body.empty? 82 | # Create an itemBody object 83 | # https://docs.microsoft.com/graph/api/resources/itembody?view=graph-rest-1.0 84 | new_event['body'] = { 85 | 'contentType' => 'text', 86 | 'content' => body 87 | } 88 | end 89 | 90 | response = make_api_call 'POST', 91 | create_event_url, 92 | token, 93 | nil, 94 | nil, 95 | new_event 96 | 97 | raise response.parsed_response.to_s || "Request returned #{response.code}" unless response.code == 201 98 | end 99 | 100 | TIME_ZONE_MAP = { 101 | 'Dateline Standard Time' => 'Etc/GMT+12', 102 | 'UTC-11' => 'Etc/GMT+11', 103 | 'Aleutian Standard Time' => 'America/Adak', 104 | 'Hawaiian Standard Time' => 'Pacific/Honolulu', 105 | 'Marquesas Standard Time' => 'Pacific/Marquesas', 106 | 'Alaskan Standard Time' => 'America/Anchorage', 107 | 'UTC-09' => 'Etc/GMT+9', 108 | 'Pacific Standard Time (Mexico)' => 'America/Tijuana', 109 | 'UTC-08' => 'Etc/GMT+8', 110 | 'Pacific Standard Time' => 'America/Los_Angeles', 111 | 'US Mountain Standard Time' => 'America/Phoenix', 112 | 'Mountain Standard Time (Mexico)' => 'America/Chihuahua', 113 | 'Mountain Standard Time' => 'America/Denver', 114 | 'Central America Standard Time' => 'America/Guatemala', 115 | 'Central Standard Time' => 'America/Chicago', 116 | 'Easter Island Standard Time' => 'Pacific/Easter', 117 | 'Central Standard Time (Mexico)' => 'America/Mexico_City', 118 | 'Canada Central Standard Time' => 'America/Regina', 119 | 'SA Pacific Standard Time' => 'America/Bogota', 120 | 'Eastern Standard Time (Mexico)' => 'America/Cancun', 121 | 'Eastern Standard Time' => 'America/New_York', 122 | 'Haiti Standard Time' => 'America/Port-au-Prince', 123 | 'Cuba Standard Time' => 'America/Havana', 124 | 'US Eastern Standard Time' => 'America/Indianapolis', 125 | 'Turks And Caicos Standard Time' => 'America/Grand_Turk', 126 | 'Paraguay Standard Time' => 'America/Asuncion', 127 | 'Atlantic Standard Time' => 'America/Halifax', 128 | 'Venezuela Standard Time' => 'America/Caracas', 129 | 'Central Brazilian Standard Time' => 'America/Cuiaba', 130 | 'SA Western Standard Time' => 'America/La_Paz', 131 | 'Pacific SA Standard Time' => 'America/Santiago', 132 | 'Newfoundland Standard Time' => 'America/St_Johns', 133 | 'Tocantins Standard Time' => 'America/Araguaina', 134 | 'E. South America Standard Time' => 'America/Sao_Paulo', 135 | 'SA Eastern Standard Time' => 'America/Cayenne', 136 | 'Argentina Standard Time' => 'America/Buenos_Aires', 137 | 'Greenland Standard Time' => 'America/Godthab', 138 | 'Montevideo Standard Time' => 'America/Montevideo', 139 | 'Magallanes Standard Time' => 'America/Punta_Arenas', 140 | 'Saint Pierre Standard Time' => 'America/Miquelon', 141 | 'Bahia Standard Time' => 'America/Bahia', 142 | 'UTC-02' => 'Etc/GMT+2', 143 | 'Azores Standard Time' => 'Atlantic/Azores', 144 | 'Cape Verde Standard Time' => 'Atlantic/Cape_Verde', 145 | 'UTC' => 'Etc/GMT', 146 | 'GMT Standard Time' => 'Europe/London', 147 | 'Greenwich Standard Time' => 'Atlantic/Reykjavik', 148 | 'Sao Tome Standard Time' => 'Africa/Sao_Tome', 149 | 'Morocco Standard Time' => 'Africa/Casablanca', 150 | 'W. Europe Standard Time' => 'Europe/Berlin', 151 | 'Central Europe Standard Time' => 'Europe/Budapest', 152 | 'Romance Standard Time' => 'Europe/Paris', 153 | 'Central European Standard Time' => 'Europe/Warsaw', 154 | 'W. Central Africa Standard Time' => 'Africa/Lagos', 155 | 'Jordan Standard Time' => 'Asia/Amman', 156 | 'GTB Standard Time' => 'Europe/Bucharest', 157 | 'Middle East Standard Time' => 'Asia/Beirut', 158 | 'Egypt Standard Time' => 'Africa/Cairo', 159 | 'E. Europe Standard Time' => 'Europe/Chisinau', 160 | 'Syria Standard Time' => 'Asia/Damascus', 161 | 'West Bank Standard Time' => 'Asia/Hebron', 162 | 'South Africa Standard Time' => 'Africa/Johannesburg', 163 | 'FLE Standard Time' => 'Europe/Kiev', 164 | 'Israel Standard Time' => 'Asia/Jerusalem', 165 | 'Kaliningrad Standard Time' => 'Europe/Kaliningrad', 166 | 'Sudan Standard Time' => 'Africa/Khartoum', 167 | 'Libya Standard Time' => 'Africa/Tripoli', 168 | 'Namibia Standard Time' => 'Africa/Windhoek', 169 | 'Arabic Standard Time' => 'Asia/Baghdad', 170 | 'Turkey Standard Time' => 'Europe/Istanbul', 171 | 'Arab Standard Time' => 'Asia/Riyadh', 172 | 'Belarus Standard Time' => 'Europe/Minsk', 173 | 'Russian Standard Time' => 'Europe/Moscow', 174 | 'E. Africa Standard Time' => 'Africa/Nairobi', 175 | 'Iran Standard Time' => 'Asia/Tehran', 176 | 'Arabian Standard Time' => 'Asia/Dubai', 177 | 'Astrakhan Standard Time' => 'Europe/Astrakhan', 178 | 'Azerbaijan Standard Time' => 'Asia/Baku', 179 | 'Russia Time Zone 3' => 'Europe/Samara', 180 | 'Mauritius Standard Time' => 'Indian/Mauritius', 181 | 'Saratov Standard Time' => 'Europe/Saratov', 182 | 'Georgian Standard Time' => 'Asia/Tbilisi', 183 | 'Volgograd Standard Time' => 'Europe/Volgograd', 184 | 'Caucasus Standard Time' => 'Asia/Yerevan', 185 | 'Afghanistan Standard Time' => 'Asia/Kabul', 186 | 'West Asia Standard Time' => 'Asia/Tashkent', 187 | 'Ekaterinburg Standard Time' => 'Asia/Yekaterinburg', 188 | 'Pakistan Standard Time' => 'Asia/Karachi', 189 | 'Qyzylorda Standard Time' => 'Asia/Qyzylorda', 190 | 'India Standard Time' => 'Asia/Calcutta', 191 | 'Sri Lanka Standard Time' => 'Asia/Colombo', 192 | 'Nepal Standard Time' => 'Asia/Katmandu', 193 | 'Central Asia Standard Time' => 'Asia/Almaty', 194 | 'Bangladesh Standard Time' => 'Asia/Dhaka', 195 | 'Omsk Standard Time' => 'Asia/Omsk', 196 | 'Myanmar Standard Time' => 'Asia/Rangoon', 197 | 'SE Asia Standard Time' => 'Asia/Bangkok', 198 | 'Altai Standard Time' => 'Asia/Barnaul', 199 | 'W. Mongolia Standard Time' => 'Asia/Hovd', 200 | 'North Asia Standard Time' => 'Asia/Krasnoyarsk', 201 | 'N. Central Asia Standard Time' => 'Asia/Novosibirsk', 202 | 'Tomsk Standard Time' => 'Asia/Tomsk', 203 | 'China Standard Time' => 'Asia/Shanghai', 204 | 'North Asia East Standard Time' => 'Asia/Irkutsk', 205 | 'Singapore Standard Time' => 'Asia/Singapore', 206 | 'W. Australia Standard Time' => 'Australia/Perth', 207 | 'Taipei Standard Time' => 'Asia/Taipei', 208 | 'Ulaanbaatar Standard Time' => 'Asia/Ulaanbaatar', 209 | 'Aus Central W. Standard Time' => 'Australia/Eucla', 210 | 'Transbaikal Standard Time' => 'Asia/Chita', 211 | 'Tokyo Standard Time' => 'Asia/Tokyo', 212 | 'North Korea Standard Time' => 'Asia/Pyongyang', 213 | 'Korea Standard Time' => 'Asia/Seoul', 214 | 'Yakutsk Standard Time' => 'Asia/Yakutsk', 215 | 'Cen. Australia Standard Time' => 'Australia/Adelaide', 216 | 'AUS Central Standard Time' => 'Australia/Darwin', 217 | 'E. Australia Standard Time' => 'Australia/Brisbane', 218 | 'AUS Eastern Standard Time' => 'Australia/Sydney', 219 | 'West Pacific Standard Time' => 'Pacific/Port_Moresby', 220 | 'Tasmania Standard Time' => 'Australia/Hobart', 221 | 'Vladivostok Standard Time' => 'Asia/Vladivostok', 222 | 'Lord Howe Standard Time' => 'Australia/Lord_Howe', 223 | 'Bougainville Standard Time' => 'Pacific/Bougainville', 224 | 'Russia Time Zone 10' => 'Asia/Srednekolymsk', 225 | 'Magadan Standard Time' => 'Asia/Magadan', 226 | 'Norfolk Standard Time' => 'Pacific/Norfolk', 227 | 'Sakhalin Standard Time' => 'Asia/Sakhalin', 228 | 'Central Pacific Standard Time' => 'Pacific/Guadalcanal', 229 | 'Russia Time Zone 11' => 'Asia/Kamchatka', 230 | 'New Zealand Standard Time' => 'Pacific/Auckland', 231 | 'UTC+12' => 'Etc/GMT-12', 232 | 'Fiji Standard Time' => 'Pacific/Fiji', 233 | 'Chatham Islands Standard Time' => 'Pacific/Chatham', 234 | 'UTC+13' => 'Etc/GMT-13', 235 | 'Tonga Standard Time' => 'Pacific/Tongatapu', 236 | 'Samoa Standard Time' => 'Pacific/Apia', 237 | 'Line Islands Standard Time' => 'Pacific/Kiritimati' 238 | }.freeze 239 | 240 | def get_iana_from_windows(windows_tz_name) 241 | iana = TIME_ZONE_MAP[windows_tz_name] 242 | # If no mapping found, assume the supplied 243 | # value was already an IANA identifier 244 | iana || windows_tz_name 245 | end 246 | end 247 | -------------------------------------------------------------------------------- /graph-sample/app/helpers/home_helper.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | # frozen_string_literal: true 4 | 5 | module HomeHelper 6 | end 7 | -------------------------------------------------------------------------------- /graph-sample/app/javascript/application.js: -------------------------------------------------------------------------------- 1 | // Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails 2 | import "@hotwired/turbo-rails" 3 | import "controllers" 4 | -------------------------------------------------------------------------------- /graph-sample/app/javascript/controllers/application.js: -------------------------------------------------------------------------------- 1 | import { Application } from "@hotwired/stimulus" 2 | 3 | const application = Application.start() 4 | 5 | // Configure Stimulus development experience 6 | application.debug = false 7 | window.Stimulus = application 8 | 9 | export { application } 10 | -------------------------------------------------------------------------------- /graph-sample/app/javascript/controllers/hello_controller.js: -------------------------------------------------------------------------------- 1 | import { Controller } from "@hotwired/stimulus" 2 | 3 | export default class extends Controller { 4 | connect() { 5 | this.element.textContent = "Hello World!" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /graph-sample/app/javascript/controllers/index.js: -------------------------------------------------------------------------------- 1 | // Import and register all your controllers from the importmap under controllers/* 2 | 3 | import { application } from "controllers/application" 4 | 5 | // Eager load all controllers defined in the import map under controllers/**/*_controller 6 | import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading" 7 | eagerLoadControllersFrom("controllers", application) 8 | 9 | // Lazy load controllers as they appear in the DOM (remember not to preload controllers in import map!) 10 | // import { lazyLoadControllersFrom } from "@hotwired/stimulus-loading" 11 | // lazyLoadControllersFrom("controllers", application) 12 | -------------------------------------------------------------------------------- /graph-sample/app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | class ApplicationJob < ActiveJob::Base 2 | # Automatically retry jobs that encountered a deadlock 3 | # retry_on ActiveRecord::Deadlocked 4 | 5 | # Most jobs are safe to ignore if the underlying records are no longer available 6 | # discard_on ActiveJob::DeserializationError 7 | end 8 | -------------------------------------------------------------------------------- /graph-sample/app/mailers/application_mailer.rb: -------------------------------------------------------------------------------- 1 | class ApplicationMailer < ActionMailer::Base 2 | default from: "from@example.com" 3 | layout "mailer" 4 | end 5 | -------------------------------------------------------------------------------- /graph-sample/app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | class ApplicationRecord < ActiveRecord::Base 2 | primary_abstract_class 3 | end 4 | -------------------------------------------------------------------------------- /graph-sample/app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/msgraph-sample-rubyrailsapp/0b75127a806e3be4bcab1832cad5b90bf3739f32/graph-sample/app/models/concerns/.keep -------------------------------------------------------------------------------- /graph-sample/app/views/calendar/index.html.erb: -------------------------------------------------------------------------------- 1 | 3 | 4 |
Organizer | 10 |Subject | 11 |Start | 12 |End | 13 |
---|---|---|---|
<%= event['organizer']['emailAddress']['name'] %> | 19 |<%= event['subject'] %> | 20 |<%= event['start']['dateTime'].to_time(:utc).strftime('%-m/%-d/%Y %l:%M %p') %> | 21 |<%= event['end']['dateTime'].to_time(:utc).strftime('%-m/%-d/%Y %l:%M %p') %> | 22 |
This sample app shows how to use the Microsoft Graph API to access a user's data from Ruby
8 | <% if @user_name %> 9 |Use the navigation bar at the top of the page to get started.
11 | <% else %> 12 |<%= error[:message] %>
84 | <%if error[:debug] %> 85 |<%= error[:debug] %>
86 | <% end %>
87 | You may have mistyped the address or the page may have moved.
63 |If you are the application owner check the logs for more information.
65 |Maybe you tried to change something you didn't have access to.
63 |If you are the application owner check the logs for more information.
65 |If you are the application owner check the logs for more information.
64 |