8000 ENG-1365 - Adamk/roblox connection for students by adamkecskes · Pull Request #7831 · codecombat/codecombat · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

ENG-1365 - Adamk/roblox connection for students #7831

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already 8000 on GitHub? Sign in to your account

Merged
merged 3 commits into from
Nov 19, 2024

Conversation

adamkecskes
Copy link
Collaborator
@adamkecskes adamkecskes commented Nov 15, 2024
  • Roblox connection works by ID instead of OAuth2 in classroom mode.
  • Added to /roblox, settings and Teacher Dashboard

backend PR: https://github.com/codecombat/codecombat-server/pull/1106

settingsrobloxteacher
settingsroblox

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced RobloxIdentityField component for managing Roblox identity connections.
    • Added LoadingSpinner for visual feedback during asynchronous operations.
    • Enhanced localization with new and updated strings for better user guidance and clarity.
  • Improvements

    • Updated the UI to provide clearer feedback and more tailored interactions regarding Roblox connections.
    • Improved API interactions with new methods for handling OAuth2 identity.
  • Bug Fixes

    • Resolved formatting issues in various components and methods for better consistency.
  • Documentation

    • Updated documentation to reflect new features and changes in user experience.

Copy link
Contributor
coderabbitai bot commented Nov 15, 2024

Walkthrough

The pull request introduces several significant changes across multiple files. A new oauth2identity module is added, providing functions for managing OAuth2 identity connections. Additionally, a new Vue component, RobloxIdentityField, is created to facilitate user interactions with Roblox identities. Existing components are updated to integrate this new functionality, enhancing the user interface and experience. Localization strings are also modified to accommodate these new features, ensuring clarity and usability. Overall, the changes aim to improve the integration of Roblox accounts within the application.

Changes

File Path Change Summary
app/core/api/index.js Added export for oauth2identity module.
app/core/api/oauth2identity.js Introduced new module with post(options) and fetchForProviderAndUser(provider, userId) methods.
app/locale/en.js Extensive updates to localization strings, including additions and modifications for clarity.
app/models/User.js Syntax improvements, including trailing commas in method signatures and object literals.
app/views/account/RobloxIdentityField.vue New component for managing Roblox identity connections, including form and methods for API interaction.
app/views/account/robloxButton.vue Integrated RobloxIdentityField and LoadingSpinner, updated props and methods for user interaction.
app/views/landing-pages/roblox/components/AccountLinkingRewards.vue Updated template logic and added RobloxIdentityField component for conditional rendering.
ozaria/site/components/teacher-dashboard/modals/ModalEditStudent.vue Added RobloxButton component and made minor formatting adjustments.

Assessment against linked issues

Objective Addressed Explanation
Implement student connection via Roblox ID entry in settings (ENG-1365)
Allow teachers to connect student accounts to Roblox accounts (ENG-1365)
Add option for students to disconnect their Roblox account (ENG-1365) No functionality for disconnecting added.
Enhance user interface for Roblox connections (ENG-1365)

Possibly related PRs

  • ENG-692 - Don't automatically show Roblox modal on homepage #7484: The changes in the main PR introduce a new module related to OAuth2 identity management, which is relevant to the modifications in the CampaignView.js that involve user interactions and modals related to Roblox.
  • ENG-1139 - Fix map if first levels are skipped #7695: The changes in the main PR regarding the oauth2identity module could relate to the user management and identity handling in the Classroom.js modifications, which also involve user-specific data handling.
  • ENG-1177 - Update legend for CodeCombat #7746: The updates to localization strings in app/locale/en.js in this PR may connect with the new functionalities introduced in the main PR, as both involve enhancing user experience and clarity in the application.
  • classroom modal changes  #7799: The classroom modal changes may relate to the new oauth2identity module, as both involve user identity and management features within the application.

Suggested reviewers

  • mrfinch

🐰 In the meadow where rabbits play,
A new module hops in, brightening the day!
With Roblox connections now in sight,
Students can link with all their might.
So let’s celebrate this joyful feat,
For coding and gaming, oh what a treat! 🐇✨

Warning

There were issues while running some tools. Please review the errors and either fix the tool’s configuration or disable the tool if it’s a critical failure.

🔧 eslint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

warning eslint@8.57.1: This version is no longer supported. Please see https://eslint.org/version-support for other options.
warning eslint > @humanwhocodes/config-array@0.13.0: Use @eslint/config-array instead
warning eslint > @humanwhocodes/config-array > @humanwhocodes/object-schema@2.0.3: Use @eslint/object-schema instead
warning eslint > file-entry-cache > flat-cache > rimraf@3.0.2: Rimraf versions prior to v4 are no longer supported
warning eslint > file-entry-cache > flat-cache > rimraf > glob@7.2.3: Glob versions prior to v9 are no longer supported
warning eslint > file-entry-cache > flat-cache > rimraf > glob > inflight@1.0.6: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
warning eslint-plugin-script-tags@0.5.0: Now published as @mapbox/eslint-plugin-script-tags
warning blockly > jsdom > abab@2.0.6: Use your platform's native atob() and btoa() methods instead
warning blockly > jsdom > data-urls > abab@2.0.6: Use your platform's native atob() and btoa() methods instead
warning blockly > jsdom > domexception@4.0.0: Use your platform's native DOMException instead
warning treema > express > mkdirp@0.3.3: Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)
warning treema > express > connect@2.7.2: connect 2.x series is deprecated
warning treema > express > connect > formidable@1.0.11: Please upgrade to latest, formidable@v2 or formidable@v3! Check these notes: https://bit.ly/2ZEqIau
warning vue@2.7.10: Vue 2 has reached EOL and is no longer actively maintained. See https://v2.vuejs.org/eol/ for more details.
warning jasmine > glob@3.2.11: Glob versions prior to v9 are no longer supported
warning jasmine > glob > minimatch@0.3.0: Please update to minimatch 3.0.2 or higher to avoid a RegExp DoS issue
warning karma > glob@7.2.3: Glob versions prior to v9 are no longer supported
warning karma > rimraf@3.0.2: Rimraf versions prior to v4 are no longer supported
warning karma > tmp > rimraf@3.0.2: Rimraf versions prior to v4 are no longer supported
warning karma > socket.io > debug@4.1.1: Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (debug-js/debug#797)
warning karma > socket.io > engine.io > debug@4.1.1: Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (debug-js/debug#797)
warning karma > socket.io > socket.io-parser > debug@4.1.1: Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (debug-js/debug#797)
warning pug > pug-code-gen > constantinople > babel-types > babel-runtime > core-js@2.6.12: core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.
warning standard > eslint@8.57.1: This version is no longer supported. Please see https://eslint.org/version-support for other options.
warning @babel/plugin-proposal-optional-chaining@7.21.0: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-chaining instead.
error treema@0.0.0: The engine "node" is incompatible with this module. Expected version "0.8.x". Got "22.9.0"
error Found incompatible module.


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor
@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

🧹 Outside diff range and nitpick comments (11)
app/core/api/oauth2identity.js (1)

1-16: Clarify architectural direction: OAuth2 vs Profile ID approach

The PR objectives specifically mention using a "profile ID method instead of OAuth2 in classroom mode", but this file implements OAuth2-based identity management. We should clarify:

  1. Is OAuth2 still needed for some use cases?
  2. Should this implementation be modified to use profile IDs instead?
  3. Are we maintaining both approaches in parallel?

This architectural decision should be documented to prevent confusion and ensure maintainability.

app/core/api/index.js (1)

Line range hint 1-2: Remove the bulk-decaffeinate TODO comment.

The file appears to be working correctly after the CoffeeScript to JavaScript conversion. This comment can be safely removed.

-// TODO: This file was created by bulk-decaffeinate.
-// Sanity-check the conversion and remove this comment.
app/views/account/RobloxIdentityField.vue (3)

1-7: Consider local component registration and add missing Vue import.

The current implementation globally registers VueConfirmDialog which could unnecessarily increase the bundle size for routes that don't use it. Also, Vue is used but not imported.

Consider this refactoring:

 <script>
+import Vue from 'vue'
 import VueConfirmDialog from 'vue-confirm-dialog'
 import oauth2identity from 'core/api/oauth2identity'

-Vue.use(VueConfirmDialog)
-Vue.component('VueConfirmDialog', VueConfirmDialog.default)
+export default Vue.extend({
+  components: {
+    VueConfirmDialog: VueConfirmDialog.default
+  },

9-14: Consider adding props for teacher functionality.

Based on the PR objectives, teachers need to connect student accounts. Consider adding props to support this use case:

 props: {
   userId: {
     type: String,
     required: true,
   },
+  isTeacherMode: {
+    type: Boolean,
+    default: false
+  },
+  studentName: {
+    type: String,
+    default: ''
+  }
 },

77-88: Add responsive design considerations.

The current styles work well but could be improved for different screen sizes.

 <style lang="scss" scoped>
 .roblox-id {
   width: 100%;
   .form-group {
     display: flex;
+    flex-direction: row;
     gap: 15px;
+    @media (max-width: 576px) {
+      flex-direction: column;
+    }
     .roblox-id__input {
       width: 100%;
     }
+    .invalid-feedback {
+      display: block;
+      margin-top: 0.25rem;
+    }
   }
 }
 </style>
ozaria/site/components/teacher-dashboard/modals/ModalEditStudent.vue (1)

Line range hint 1-255: Implementation approach aligns with requirements

The integration of Roblox connectivity through profile ID instead of OAuth2 is well-implemented in the student edit modal. The approach:

  1. Maintains consistency with existing UI patterns
  2. Provides a straightforward way for teachers to manage student Roblox connections
  3. Follows the requirements specified in ENG-1365

Consider documenting this implementation choice in the codebase to explain why profile ID was chosen over OAuth2 for classroom mode.

app/views/landing-pages/roblox/components/AccountLinkingRewards.vue (2)

211-221: Fix indentation in the confirmation dialog callback.

The code logic is correct, but the indentation is inconsistent with the rest of the file.

 button: {
   no: $.i18n.t('modal.cancel'),
-          yes: $.i18n.t('modal.okay'),
+   yes: $.i18n.t('modal.okay'),
 },
 callback: async confirm => {
   if (confirm) {
     await identity.destroy()
     this.checkRobloxConnectionStatus()
   }
-        },
+   },

85-105: Consider enhancing error handling and user feedback.

The component would benefit from:

  1. Error handling for failed connections and invalid profile IDs
  2. Loading states during async operations (connecting/disconnecting)
  3. Success notifications after successful connections/disconnections

This would improve the user experience for both teachers and students.

Also applies to: 174-220

app/views/account/robloxButton.vue (1)

69-71: Potential Optimization of Computed Property isOtherUser

The computed property isOtherUser compares this.userId with me.id. Since me.id is unlikely to change during the session, consider caching this value or ensuring reactivity is necessary.

app/locale/en.js (2)

2017-2018: Inconsistent indentation.

The indentation is inconsistent for these two lines compared to the surrounding code. They should be indented one level more to match.

Apply this diff to fix the indentation:

-      roblox_connected_other_user: 'The student\'s account is connected to  <strong>__username__</strong> Roblox account.',
-      roblox_not_connected: 'Connect your CodeCombat and Roblox accounts.',
+      roblox_connected_other_user: 'The student\'s account is connected to  <strong>__username__</strong> Roblox account.',
+      roblox_not_connected: 'Connect your CodeCombat and Roblox accounts.',

2019-2020: Inconsistent indentation.

Similar to the previous comment, the indentation is off for these lines and should be fixed.

Apply a similar indentation fix as shown in the previous comment.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 0adb356 and 3d1ffbc.

📒 Files selected for processing (8)
  • app/core/api/index.js (1 hunks)
  • app/core/api/oauth2identity.js (1 hunks)
  • app/locale/en.js (1 hunks)
  • app/models/User.js (16 hunks)
  • app/views/account/RobloxIdentityField.vue (1 hunks)
  • app/views/account/robloxButton.vue (9 hunks)
  • app/views/landing-pages/roblox/components/AccountLinkingRewards.vue (6 hunks)
  • ozaria/site/components/teacher-dashboard/modals/ModalEditStudent.vue (8 hunks)
🔇 Additional comments (12)
app/core/api/oauth2identity.js (1)

13-15: 🛠️ Refactor suggestion

Add parameter validation and verify architectural alignment

The function needs parameter validation and error handling. Additionally, there seems to be a discrepancy between the PR objectives (which mention using profile ID method instead of OAuth2) and this OAuth2-based implementation.

  1. Add parameter validation:
-  fetchForProviderAndUser (provider, userId) {
-    return fetchJson('/db/oauth2identity/by-user', { data: { userId, provider } })
+  fetchForProviderAndUser (provider, userId) {
+    if (!provider || typeof provider !== 'string') {
+      throw new TypeError('Provider must be a non-empty string')
+    }
+    if (!userId) {
+      throw new TypeError('UserId is required')
+    }
+    return fetchJson('/db/oauth2identity/by-user', { 
+      data: { userId, provider } 
+    }).catch(error => {
+      console.error('Failed to fetch OAuth2 identity:', error)
+      throw error
+    })
   },
  1. Let's verify the architectural approach:
app/core/api/index.js (2)

24-24: LGTM! Module addition follows the established pattern.

The new oauth2identity module is correctly integrated into the API index, maintaining alphabetical ordering.


24-24: Verify the module naming convention.

The PR objectives mention using "profile ID instead of OAuth2 in classroom mode", but the module is named oauth2identity. Could you clarify if this naming accurately reflects its intended functionality?

✅ Verification successful

The module name oauth2identity is appropriate and should be retained

The module name accurately reflects its functionality as it handles OAuth2 identity management, which is broader than just profile IDs. The schema and implementation show it's a general-purpose OAuth2 identity system that:

  • Manages OAuth2 tokens and authentication data
  • Stores provider-specific profile information
  • Supports multiple providers (not just classroom mode)
  • Handles essential OAuth2 fields like access_tokens, refresh_tokens, and token expiration

The PR's objective of "using profile ID instead of OAuth2 in classroom mode" refers to a specific usage pattern, not the underlying identity management system itself.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check the implementation details of oauth2identity module
# and its usage to understand if the naming is appropriate

# Check the oauth2identity module implementation
echo "Checking oauth2identity module implementation:"
cat app/core/api/oauth2identity.js || echo "Module file not found"

# Search for references to profile ID in the codebase
echo -e "\nChecking profile ID references:"
rg -i "profile.*id" --type js --type vue

# Look for OAuth2-related functionality
echo -e "\nChecking OAuth2 references:"
rg -i "oauth2" --type js --type vue

Length of output: 800


Script:

#!/bin/bash
# Let's try a different approach using fd to find files and then examine them

# First, let's find the oauth2identity module and related files
echo "Finding oauth2identity related files:"
fd -e js -e jsx oauth2 

# Let's examine the content of any files that mention oauth2identity
echo -e "\nChecking files containing oauth2identity references:"
rg -l "oauth2identity"

# Let's look for profile ID related code with proper file types
echo -e "\nChecking profile ID references:"
rg -t js "profile.*id"

# Let's check OAuth2 references with proper file type
echo -e "\nChecking OAuth2 references:"
rg -t js "oauth2"

# Let's also check the git history for context about this change
echo -e "\nChecking recent commits mentioning oauth2 or profile:"
git log -n 5 --grep="oauth2\|profile" --pretty=format:"%h %s"

Length of output: 2972


Script:

#!/bin/bash
# Let's examine the specific implementation and schema to understand the module's purpose

# Check the schema definition
echo "Checking OAuth2Identity schema:"
cat app/schemas/models/oauth2identity.schema.js

# Check the model implementation
echo -e "\nChecking OAuth2Identity model:"
cat app/models/OAuth2Identity.js

# Let's also check the Roblox-related files since they seem to be the main consumers
echo -e "\nChecking Roblox integration files:"
cat app/views/account/RobloxIdentityField.vue
cat app/core/api/roblox.js

Length of output: 4100

ozaria/site/components/teacher-dashboard/modals/ModalEditStudent.vue (1)

8-8: LGTM: RobloxButton integration

The import and component registration for RobloxButton aligns with the PR objectives for implementing Roblox connection functionality.

Also applies to: 15-16

app/views/landing-pages/roblox/components/AccountLinkingRewards.vue (2)

85-105: LGTM! Well-structured conditional rendering for different connection methods.

The template changes effectively implement the dual connection approach (OAuth2 and Profile ID) as specified in the PR objectives. The code properly handles different user scenarios with appropriate UI elements and warnings.


116-116: LGTM! Clean component integration.

The RobloxIdentityField component is properly imported and registered.

Also applies to: 129-129

app/models/User.js (1)

284-287: Implementation aligns with requirements

The method correctly implements the requirement to disable Roblox OAuth connections for teachers and students (classroom mode), while enabling it for other users.

Let's verify the usage of this method:

✅ Verification successful

Method is correctly integrated and used consistently

The verification shows the method is properly used across the codebase:

  • In AccountLinkingRewards.vue, it controls the visibility of OAuth-related UI elements and warnings
  • In robloxButton.vue, it determines the authentication mode through boolean props

The implementation is correctly integrated and follows the classroom mode restrictions as intended.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find where canUseRobloxOauthConnection is used to ensure proper integration
rg -A 3 "canUseRobloxOauthConnection"

Length of output: 2059

app/views/account/robloxButton.vue (4)

258-260: Ensure Safe String Interpolation in Localization

When using string interpolation within the $t() function, confirm that interpolated variables are properly sanitized to prevent XSS attacks. Although $t() handles escaping by default, it's important to ensure that isOtherUser and other variables do not introduce unsafe content.


4-5: Confirm Import Paths and Component Availability

Verify that the components RobloxIdentityField and LoadingSpinner are correctly imported and that the file paths are accurate. Incorrect import paths can lead to module not found errors.


284-287: Ensure RobloxIdentityField Component Handles userId Prop Correctly

When using <RobloxIdentityField :user-id="userId" @saved="checkRobloxConnectionStatus" />, confirm that the RobloxIdentityField component accepts userId as a prop and properly emits the saved event.


83-83: Verify Method Existence and Correct Usage

Ensure that the method fetchForProviderAndUser exists in the oauth2identity module and that it accepts the parameters 'roblox' and this.userId. This is crucial for fetching the correct identities.

Run the following script to confirm the method's existence:

✅ Verification successful

Method exists and is being used correctly

The fetchForProviderAndUser method exists in app/core/api/oauth2identity.js and accepts exactly two parameters: provider and userId. The usage in robloxButton.vue matches this signature by passing 'roblox' as the provider and this.userId as the user ID parameter.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify 'fetchForProviderAndUser' method in 'oauth2identity.js'

# Test: Search for the method definition
rg 'fetchForProviderAndUser' core/api/oauth2identity.js

# Expected: Should find the method definition

Length of output: 122


Script:

#!/bin/bash
# Let's first find the correct location of oauth2identity related files
fd -t f oauth2identity

# Then search for fetchForProviderAndUser method across the codebase
rg -l "fetchForProviderAndUser"

# Search for any file containing both oauth2 and identity
rg -l "oauth2.*identity|identity.*oauth2" 

Length of output: 532


Script:

#!/bin/bash
# Let's check the implementation of fetchForProviderAndUser in the correct file
rg -A 5 "fetchForProviderAndUser" app/core/api/oauth2identity.js

# Also check the method usage in the vue file for context
rg -A 5 "fetchForProviderAndUser" app/views/account/robloxButton.vue

Length of output: 540

app/locale/en.js (1)

Line range hint 1-2016: LGTM!

The English locale file looks good overall. It contains a comprehensive set of translations for various parts of the application.

Comment on lines +1 to +4
const fetchJson = require('./fetch-json')

module.exports = {

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add missing lodash import

The code uses _.assign but lodash is not imported. This will cause a runtime error.

Add the following import at the top of the file:

const fetchJson = require('./fetch-json')
+const _ = require('lodash')
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const fetchJson = require('./fetch-json')
module.exports = {
const fetchJson = require('./fetch-json')
const _ = require('lodash')
module.exports = {

Comment on lines +5 to +11
post (options) {
if (options == null) { options = {} }
return fetchJson('/db/oauth2identity', _.assign({}, {
method: 'POST',
json: options,
}))
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance error handling and parameter validation

The current implementation could benefit from several improvements:

  1. Use ES6 default parameters instead of null check
  2. Add type validation for options
  3. Add error handling for the fetch operation

Consider this implementation:

-  post (options) {
-    if (options == null) { options = {} }
-    return fetchJson('/db/oauth2identity', _.assign({}, {
-      method: 'POST',
-      json: options,
-    }))
+  post (options = {}) {
+    if (typeof options !== 'object') {
+      throw new TypeError('Options must be an object')
+    }
+    return fetchJson('/db/oauth2identity', {
+      method: 'POST',
+      json: options,
+    }).catch(error => {
+      console.error('Failed to post OAuth2 identity:', error)
+      throw error
+    })
+  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
post (options) {
if (options == null) { options = {} }
return fetchJson('/db/oauth2identity', _.assign({}, {
method: 'POST',
json: options,
}))
},
post (options = {}) {
if (typeof options !== 'object') {
throw new TypeError('Options must be an object')
}
return fetchJson('/db/oauth2identity', {
method: 'POST',
json: options,
}).catch(error => {
console.error('Failed to post OAuth2 identity:', error)
throw error
})
}

Comment on lines +15 to +20
data () {
return {
isAnonymous: me.isAnonymous(),
robloxID: '',
}
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add missing import and Roblox ID validation.

The component uses me.isAnonymous() without importing it. Additionally, consider adding validation for the Roblox ID format.

  1. Add the missing import:
 import Vue from 'vue'
 import VueConfirmDialog from 'vue-confirm-dialog'
 import oauth2identity from 'core/api/oauth2identity'
+import { me } from 'core/utils'
  1. Add validation computed property:
+  computed: {
+    isValidRobloxID() {
+      return /^\d+$/.test(this.robloxID.trim())
+    }
+  },

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +56 to +75
<template>
<div class="roblox-id form-inline">
<div class="form-group">
<input
ref="robloxID"
v-model="robloxID"
type="text"
class="roblox-id__input form-control"
placeholder="Roblox ID"
>
<button
ref="connectRobloxButton"
class="btn form-control btn-primary"
@click="onSave"
>
{{ $t('account_settings.connect_roblox_button') }}
</button>
</div>
</div>
</template>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance template with validation feedback and accessibility.

The template needs improvements for better user experience and accessibility.

 <template>
   <div class="roblox-id form-inline">
     <div class="form-group">
       <input
         ref="robloxID"
         v-model="robloxID"
         type="text"
         class="roblox-id__input form-control"
+        :class="{ 'is-invalid': robloxID && !isValidRobloxID }"
         placeholder="Roblox ID"
+        aria-label="Roblox ID"
+        :aria-invalid="!isValidRobloxID"
       >
+      <div v-if="robloxID && !isValidRobloxID" class="invalid-feedback">
+        {{ $t('account_settings.invalid_roblox_id') }}
+      </div>
       <button
         ref="connectRobloxButton"
-        class="btn form-control btn-primary"
+        class="btn form-control btn-primary" 
+        :disabled="isLoading || !isValidRobloxID"
         @click="onSave"
+        aria-label="Connect Roblox Account"
       >
-        {{ $t('account_settings.connect_roblox_button') }}
+        <span v-if="isLoading">
+          <i class="fa fa-spinner fa-spin"></i>
+        </span>
+        <span v-else>
+          {{ $t('account_settings.connect_roblox_button') }}
+        </span>
       </button>
     </div>
   </div>
 
8000
</template>

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +22 to +52
methods: {
async createNewIdentity () {
await oauth2identity.post({
ownerID: this.userId,
provider: 'roblox',
profile: {
sub: this.robloxID,
},
})
},
async onSave () {
if (this.robloxID) {
this.$refs.connectRobloxButton.disabled = true
try {
await this.createNewIdentity()
this.$emit('saved')
} catch (error) {
noty({
text: error.message,
type: 'error',
timeout: 5000,
})
this.$refs.robloxID.select()
} finally {
if (this.$refs.connectRobloxButton) {
this.$refs.connectRobloxButton.disabled = false
}
}
}
},
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Enhance security and robustness of the save operation.

Several improvements can be made to the methods implementation:

  1. Sanitize input before API call
  2. Handle specific error types
  3. Improve button state management
  4. Add loading state

Apply these changes:

 methods: {
   async createNewIdentity () {
+    const sanitizedID = this.robloxID.trim()
+    if (!this.isValidRobloxID) {
+      throw new Error(this.$t('account_settings.invalid_roblox_id'))
+    }
     await oauth2identity.post({
       ownerID: this.userId,
       provider: 'roblox',
       profile: {
-        sub: this.robloxID,
+        sub: sanitizedID,
       },
     })
   },
   async onSave () {
-    if (this.robloxID) {
-      this.$refs.connectRobloxButton.disabled = true
+    if (this.robloxID.trim()) {
+      this.isLoading = true
       try {
         await this.createNewIdentity()
         this.$emit('saved')
       } catch (error) {
+        const errorMessage = error.response?.status === 409
+          ? this.$t('account_settings.roblox_id_already_connected')
+          : this.$t('account_settings.connection_error')
         noty({
-          text: error.message,
+          text: errorMessage,
           type: 'error',
           timeout: 5000,
         })
         this.$refs.robloxID.select()
       } finally {
-        if (this.$refs.connectRobloxButton) {
-          this.$refs.connectRobloxButton.disabled = false
-        }
+        this.isLoading = false
       }
     }
   },
 },

Don't forget to add the loading state to the data:

 data () {
   return {
     isAnonymous: me.isAnonymous(),
     robloxID: '',
+    isLoading: false,
   }
 },

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +199 to +204
<roblox-button
size="small"
:user-id="studentId"
:use-oauth="false"
:use-roblox-id="true"
/>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

RobloxButton implementation needs error handling improvements

The component has proper loading states but lacks error handling for API calls. Specific improvements needed:

  • Add error state in the component's data
  • Implement try-catch blocks in checkRobloxConnectionStatus and getRobloxIdentities methods
  • Add error message display in the template when API calls fail

The useRobloxId prop is properly implemented with a default value based on user permissions, but the error handling is missing for a complete user experience.

🔗 Analysis chain

Verify RobloxButton implementation and add error handling

The RobloxButton integration looks good, but consider the following improvements:

  1. The use of profile ID instead of OAuth2 aligns with PR objectives, but we should verify the implementation.
  2. Consider adding loading and error states for better user experience.

Let me gather more information about the RobloxButton implementation, particularly focusing on the useRobloxId prop and error handling.


Let me check one more aspect to understand how the RobloxButton handles the profile ID functionality.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify RobloxButton implementation and its props

# Check RobloxButton implementation
echo "Checking RobloxButton implementation..."
ast-grep --pattern 'export default {
  props: {
    $$$
    useRobloxId: {
      $$$
    }
    $$$
  }
  $$$
}' app/views/account/robloxButton.vue

# Check for loading/error handling
echo "Checking for loading/error handling..."
rg -A 5 'loading|error' app/views/account/robloxButton.vue

Length of output: 830


Script:

#!/bin/bash
# Check for useRobloxId prop definition and usage
echo "Checking useRobloxId prop definition..."
rg -A 10 'useRobloxId:' app/views/account/robloxButton.vue

# Check for error handling patterns
echo "Checking error handling..."
rg -A 5 'catch|error|fail' app/views/account/robloxButton.vue

# Check for methods that might use the useRobloxId prop
echo "Checking methods using useRobloxId..."
ast-grep --pattern 'methods: {
  $$$
  getRobloxIdentities() {
    $$$
  }
  $$$
}'

Length of output: 701


Script:

#!/bin/bash
# Check for profile ID related methods and OAuth handling
echo "Checking profile ID and OAuth related code..."
rg -A 10 'getRobloxIdentities|roblox\.connect|roblox\.profile' app/views/account/robloxButton.vue

# Check for any error state in the template
echo "Checking template error states..."
ast-grep --pattern 'template: `
  $$$
  <div $$$>
    $$$
    <div v-if="error" $$$>
      $$$
    </div>
    $$$
  </div>
`'

Length of output: 1013

@@ -142,14 +154,17 @@ export default {
robloxIdentities: [],
progress: 3800 / 10000,
nextURL: window.location.href,
isAnonymous: me.isAnonymous()
isAnonymous: me.isAnonymous(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add defensive checks for 'me' object.

The code assumes 'me' is always defined, which could lead to runtime errors. Consider adding defensive checks:

-      isAnonymous: me.isAnonymous(),
+      isAnonymous: me?.isAnonymous() ?? false,

 computed: {
   me () {
-      return me
+      return me || {
67E6
}
   },

Also applies to: 161-163

Comment on lines +275 to +277
class="age-restriciton-warning"
>
{{ $t('roblox_landing.age_restriction') }}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix Typographical Error in Class Name

The class name age-restriciton-warning contains a typo. It should be corrected to age-restriction-warning to ensure that the styling is applied correctly.

Apply this diff to fix the typo in both the template and style sections:

- class="age-restriciton-warning"
+ class="age-restriction-warning"

And in the style section:

-.age-restriciton-warning {
+.age-restriction-warning {

Also applies to: 386-386

Comment on lines +243 to +245
`account_settings.roblox_connected${isOtherUser ? '_other_user' : ''}`,
{ username: identity.get('profile').preferred_username }
)"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Avoid Potential XSS Vulnerability with v-html

Using v-html with dynamic content can introduce Cross-Site Scripting (XSS) vulnerabilities, especially if the content includes user-generated data. It's safer to use text interpolation {{ }} which automatically escapes HTML.

Update the code to use {{ }} instead of v-html:

-<p
-  v-if="size === SMALL"
-  v-html="$t(
-    `account_settings.roblox_connected${isOtherUser ? '_other_user' : ''}`,
-    { username: identity.get('profile').preferred_username }
-  )"
-/>

+<p
+  v-if="size === SMALL"
+>
+  {{ $t(
+    `account_settings.roblox_connected${isOtherUser ? '_other_user' : ''}`,
+    { username: identity.get('profile').preferred_username }
+  ) }}
+</p>

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +86 to +88
this.loading = true
this.robloxIdentities = new OAuth2Identities(await this.getRobloxIdentities()).models
this.loading = false
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add Error Handling to Asynchronous Methods

The checkRobloxConnectionStatus method lacks error handling. If getRobloxIdentities() fails due to a network error or server issue, it could cause unhandled exceptions and leave loading indefinitely true.

Modify the method to include a try...catch block to handle potential errors:

async checkRobloxConnectionStatus () {
  this.loading = true
+ try {
    const identities = await this.getRobloxIdentities()
    this.robloxIdentities = new OAuth2Identities(identities).models
+ } catch (error) {
+   console.error('Failed to fetch Roblox identities:', error)
+   this.robloxIdentities = []
+ } finally {
    this.loading = false
+ }
}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
this.loading = true
this.robloxIdentities = new OAuth2Identities(await this.getRobloxIdentities()).models
this.loading = false
this.loading = true
try {
const identities = await this.getRobloxIdentities()
this.robloxIdentities = new OAuth2Identities(identities).models
} catch (error) {
console.error('Failed to fetch Roblox identities:', error)
this.robloxIdentities = []
} finally {
this.loading = false
}

userId: {
type: String,
required: true,
default: () => me.id,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

required and default not a good combination 😅

Copy link
Contributor
@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (2)
app/views/account/robloxButton.vue (2)

4-7: Standardize import statements

Consider using consistent ES6 import syntax instead of mixing with CommonJS require:

import RobloxIdentityField from './RobloxIdentityField.vue'
import LoadingSpinner from 'app/components/common/elements/LoadingSpinner'
-const oauth2identity = require('core/api/oauth2identity')
-const OAuth2Identities = require('collections/OAuth2Identities')
+import oauth2identity from 'core/api/oauth2identity'
+import OAuth2Identities from 'collections/OAuth2Identities'

31-45: Simplify prop declarations

The userId, useOauth, and useRobloxId props have redundant configurations. When a prop has a default value, it's implicitly optional, making required: false unnecessary.

userId: {
  type: String,
- required: false,
  default: () => me.id,
},
useOauth: {
  type: Boolean,
- required: false,
  default: () => me.canUseRobloxOauthConnection(),
},
useRobloxId: {
  type: Boolean,
- required: false,
  default: () => !me.canUseRobloxOauthConnection(),
},
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 3d1ffbc and 09835ac.

📒 Files selected for processing (1)
  • app/views/account/robloxButton.vue (9 hunks)
🔇 Additional comments (3)
app/views/account/robloxButton.vue (3)

53-53: LGTM!

The addition of the loading state and isOtherUser computed property is well-implemented.

Also applies to: 71-73


286-289: LGTM!

The RobloxIdentityField component integration is well-implemented with proper prop passing and event handling.


307-311: LGTM!

The spinner styling is properly scoped and uses the correct deep selector syntax.

Comment on lines +85 to +90
return oauth2identity.fetchForProviderAndUser('roblox', this.userId)
},
async checkRobloxConnectionStatus () {
this.robloxIdentities = await this.getRobloxIdentities()
this.loading = true
this.robloxIdentities = new OAuth2Identities(await this.getRobloxIdentities()).models
this.loading = false
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add error handling to async operations

The async methods lack error handling, which could leave the component in an inconsistent state if API calls fail.

async getRobloxIdentities () {
- return oauth2identity.fetchForProviderAndUser('roblox', this.userId)
+ try {
+   return await oauth2identity.fetchForProviderAndUser('roblox', this.userId)
+ } catch (error) {
+   console.error('Failed to fetch Roblox identities:', error)
+   return []
+ }
},
async checkRobloxConnectionStatus () {
  this.loading = true
+ try {
    this.robloxIdentities = new OAuth2Identities(await this.getRobloxIdentities()).models
+ } catch (error) {
+   console.error('Failed to check Roblox connection status:', error)
+   this.robloxIdentities = []
+ } finally {
    this.loading = false
+ }
}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return oauth2identity.fetchForProviderAndUser('roblox', this.userId)
},
async checkRobloxConnectionStatus () {
this.robloxIdentities = await this.getRobloxIdentities()
this.loading = true
this.robloxIdentities = new OAuth2Identities(await this.getRobloxIdentities()).models
this.loading = false
async getRobloxIdentities () {
try {
return await oauth2identity.fetchForProviderAndUser('roblox', this.userId)
} catch (error) {
console.error('Failed to fetch Roblox identities:', error)
return []
}
},
async checkRobloxConnectionStatus () {
this.loading = true
try {
this.robloxIdentities = new OAuth2Identities(await this.getRobloxIdentities()).models
} catch (error) {
console.error('Failed to check Roblox connection status:', error)
this.robloxIdentities = []
} finally {
this.loading = false
}

@mrfinch mrfinch merged commit 68e7e6e into master Nov 19, 2024
2 checks passed
@mrfinch mrfinch deleted the adamk/roblox-connection-for-students branch November 19, 2024 06:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants
0