├── config ├── CSSStub.ts └── jest │ ├── cssTransform.js │ └── fileTransform.js ├── .release-please-manifest.json ├── .eslintignore ├── .github ├── CODEOWNERS ├── dependabot.yml ├── pull_request_template.md ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md └── workflows │ └── english-file-transfer.yml ├── babel.config.json ├── src ├── app │ ├── services │ │ ├── slices │ │ │ ├── index.ts │ │ │ ├── theme.slice.ts │ │ │ ├── terms-of-use.slice.ts │ │ │ ├── response-area-expanded.slice.ts │ │ │ ├── explorer-mode.slice.ts │ │ │ ├── devxapi.slice.ts │ │ │ ├── sample-query.slice.ts │ │ │ ├── dimensions.slice.ts │ │ │ ├── query-status.slice.ts │ │ │ ├── history.slice.ts │ │ │ └── proxy.slice.ts │ │ ├── hooks │ │ │ ├── index.ts │ │ │ ├── useCollectionPermissions.ts │ │ │ └── usePopups.ts │ │ ├── variant-constants.ts │ │ ├── graph-client │ │ │ ├── index.ts │ │ │ ├── GraphAuthenticationProvider.ts │ │ │ └── graph-client.ts │ │ ├── actions │ │ │ ├── mockThunkMiddleware.ts │ │ │ ├── theme-action-creator.spec.ts │ │ │ ├── response-expanded-action-creator.spec.ts │ │ │ ├── devxApi-action-creators.spec.ts │ │ │ ├── terms-of-use-action-creator.spec.ts │ │ │ ├── explorer-mode-action-creator.spec.ts │ │ │ ├── toggle-sidebar-action-creator.spec.ts │ │ │ ├── profile-action-creators.spec.ts │ │ │ ├── query-status-action-creator.spec.ts │ │ │ ├── query-input-action-creators.spec.ts │ │ │ ├── dimensions-action-creator.spec.ts │ │ │ └── samples-action-creators.spec.ts │ │ ├── context │ │ │ ├── validation-context │ │ │ │ └── ValidationContext.tsx │ │ │ ├── collection-permissions │ │ │ │ └── CollectionPermissionsContext.ts │ │ │ └── popups-context │ │ │ │ ├── reducedPopups.ts │ │ │ │ └── PopupsContext.tsx │ │ └── reducers │ │ │ └── collections-reducer.util.ts │ ├── views │ │ ├── sidebar │ │ │ ├── history │ │ │ │ ├── index.ts │ │ │ │ └── History.styles.ts │ │ │ ├── resource-explorer │ │ │ │ ├── index.ts │ │ │ │ ├── resourceLinkStyles.ts │ │ │ │ └── collection │ │ │ │ │ ├── collection.util.ts │ │ │ │ │ └── Paths.styles.ts │ │ │ ├── sample-queries │ │ │ │ ├── tokens.spec.ts │ │ │ │ └── sample-query-utils.spec.ts │ │ │ ├── sidebar-utils │ │ │ │ └── SidebarUtils.tsx │ │ │ └── Sidebar.styles.tsx │ │ ├── common │ │ │ ├── copy-button │ │ │ │ └── index.ts │ │ │ ├── error-boundary │ │ │ │ ├── index.ts │ │ │ │ └── ErrorBoundary.tsx │ │ │ ├── banners │ │ │ │ ├── index.ts │ │ │ │ └── Notification.styles.ts │ │ │ ├── index.ts │ │ │ ├── popups │ │ │ │ └── popups.types.ts │ │ │ ├── download.spec.ts │ │ │ ├── monaco │ │ │ │ ├── monaco.scss │ │ │ │ └── util │ │ │ │ │ ├── format-xml.ts │ │ │ │ │ ├── format-json.ts │ │ │ │ │ └── format-json.spec.ts │ │ │ ├── copy.spec.ts │ │ │ ├── lazy-loader │ │ │ │ ├── suspense-loader │ │ │ │ │ └── SuspenseLoader.tsx │ │ │ │ └── component-registry │ │ │ │ │ └── popups.tsx │ │ │ ├── image │ │ │ │ └── Image.tsx │ │ │ ├── dimensions │ │ │ │ ├── dimension-adjustment.spec.ts │ │ │ │ └── dimensions-adjustment.ts │ │ │ ├── download.ts │ │ │ ├── message-display │ │ │ │ └── JSXBuilder.tsx │ │ │ └── copy.ts │ │ ├── query-runner │ │ │ ├── request │ │ │ │ ├── auth │ │ │ │ │ └── index.ts │ │ │ │ ├── body │ │ │ │ │ ├── index.ts │ │ │ │ │ └── RequestBody.tsx │ │ │ │ ├── headers │ │ │ │ │ ├── index.ts │ │ │ │ │ └── Headers.styles.ts │ │ │ │ ├── permissions │ │ │ │ │ ├── index.ts │ │ │ │ │ └── util.ts │ │ │ │ ├── feedback │ │ │ │ │ └── uiStrings.ts │ │ │ │ └── request.scss │ │ │ ├── index.ts │ │ │ ├── query-input │ │ │ │ ├── index.ts │ │ │ │ ├── auto-complete │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── use-previous.ts │ │ │ │ │ └── suggestion-list │ │ │ │ │ │ └── SuggestionsList.styles.ts │ │ │ │ └── share-query │ │ │ │ │ ├── index.ts │ │ │ │ │ └── ShareQuery.styles.ts │ │ │ └── query-runner.scss │ │ ├── query-response │ │ │ ├── response │ │ │ │ └── index.ts │ │ │ ├── index.ts │ │ │ ├── snippets │ │ │ │ └── index.tsx │ │ │ ├── headers │ │ │ │ ├── index.ts │ │ │ │ └── ResponseHeaders.tsx │ │ │ └── query-response.scss │ │ ├── authentication │ │ │ ├── auth-util-components │ │ │ │ ├── index.tsx │ │ │ │ └── ProfileButton.tsx │ │ │ └── index.ts │ │ ├── app-sections │ │ │ └── index.ts │ │ └── main-header │ │ │ ├── utils.tsx │ │ │ ├── settings │ │ │ └── ThemeChooser.styles.ts │ │ │ └── Tenant.tsx │ └── utils │ │ ├── get-messages.ts │ │ ├── version.ts │ │ ├── ClientError.ts │ │ ├── error-utils │ │ ├── ClientError.ts │ │ ├── ValidationError.ts │ │ ├── ScopesError.ts │ │ └── RevokeScopesError.ts │ │ ├── searchbox.styles.ts │ │ ├── getPermissionsScopeType.ts │ │ ├── status-message.spec.ts │ │ ├── hash-string.ts │ │ ├── local-storage.ts │ │ ├── hash-string.spec.ts │ │ ├── translate-messages.ts │ │ ├── useDetectMobileScreen.ts │ │ ├── open-api-parser.spec.ts │ │ ├── device-characteristics-telemetry.ts │ │ ├── string-operations.ts │ │ ├── dynamic-sort.ts │ │ ├── device-characteristics-telemetry.spec.ts │ │ ├── fetch-retry-handler.ts │ │ ├── snippet.utils.ts │ │ ├── token-helpers.spec.ts │ │ ├── graph-toolkit-lookup.ts │ │ ├── graph-toolkit-lookup.spec.ts │ │ ├── http-methods.utils.ts │ │ ├── dynamic-sort.spec.ts │ │ ├── generate-groups.ts │ │ ├── external-link-validation.ts │ │ └── adaptive-cards-lookup.ts ├── modules │ ├── cache │ │ ├── index.ts │ │ ├── samples.cache.ts │ │ └── samples.cache.spec.ts │ ├── authentication │ │ ├── index.ts │ │ ├── interfaces │ │ │ ├── IClaimsChallenge.ts │ │ │ └── IAuthenticationWrapper.ts │ │ ├── authUtils.spec.ts │ │ └── authUtils.ts │ ├── suggestions │ │ ├── utilities │ │ │ └── index.ts │ │ └── index.ts │ └── validation │ │ └── abnf.ts ├── types │ ├── devx-api.ts │ ├── root.ts │ ├── settings.ts │ ├── status.ts │ ├── image.ts │ ├── adaptivecard.ts │ ├── submit-button.ts │ ├── share-query.ts │ ├── action.ts │ ├── dimensions.ts │ ├── index.d.ts │ ├── snippets.ts │ ├── authentication.ts │ ├── profile.ts │ ├── postman-collection.ts │ ├── enums.ts │ ├── history.ts │ ├── auto-complete.ts │ ├── query-response.ts │ ├── request.ts │ └── sidebar.ts ├── graph-toolkit-examples │ ├── index.js │ └── templates.json ├── custom.d.ts ├── themes │ ├── theme-context.ts │ ├── theme-utils.spec.ts │ ├── light.ts │ ├── high-contrast.ts │ └── dark.ts ├── messages │ └── index.js ├── styles │ ├── index.scss │ ├── _variables.scss │ └── _reset.scss ├── tests │ ├── ui │ │ ├── anonymous-experiences │ │ │ ├── request.spec.ts-snapshots │ │ │ │ ├── Run-query-user-can-run-query-1-Chrome-win32.png │ │ │ │ ├── Run-query-user-can-run-query-2-Chrome-win32.png │ │ │ │ ├── Run-query-user-can-run-query-1-Ms-Edge-win32.png │ │ │ │ ├── Run-query-user-can-run-query-2-Ms-Edge-win32.png │ │ │ │ ├── Run-query-should-change-version-options-1-Chrome-win32.png │ │ │ │ ├── Run-query-should-change-version-options-1-Ms-Edge-win32.png │ │ │ │ ├── Run-query-should-change-version-options-2-Chrome-win32.png │ │ │ │ ├── Run-query-should-change-version-options-2-Ms-Edge-win32.png │ │ │ │ ├── Run-query-should-change-version-options-3-Chrome-win32.png │ │ │ │ ├── Run-query-should-change-version-options-3-Ms-Edge-win32.png │ │ │ │ ├── Run-query-should-change-version-options-4-Chrome-win32.png │ │ │ │ ├── Run-query-should-change-version-options-4-Ms-Edge-win32.png │ │ │ │ ├── Run-query-should-change-version-options-5-Chrome-win32.png │ │ │ │ ├── Run-query-should-change-version-options-5-Ms-Edge-win32.png │ │ │ │ ├── Request-section-should-add-request-headers-1-Chrome-win32.png │ │ │ │ ├── Request-section-should-add-request-headers-1-Ms-Edge-win32.png │ │ │ │ ├── Request-section-should-add-request-headers-2-Chrome-win32.png │ │ │ │ ├── Request-section-should-add-request-headers-2-Ms-Edge-win32.png │ │ │ │ ├── Request-section-should-edit-request-headers-1-Chrome-win32.png │ │ │ │ ├── Request-section-should-edit-request-headers-2-Chrome-win32.png │ │ │ │ ├── Request-section-should-edit-request-headers-1-Ms-Edge-win32.png │ │ │ │ ├── Request-section-should-edit-request-headers-2-Ms-Edge-win32.png │ │ │ │ ├── Permissions-should-show-permissions-for-me-endpoint-1-Chrome-win32.png │ │ │ │ ├── Permissions-should-show-permissions-for-me-endpoint-1-Ms-Edge-win32.png │ │ │ │ ├── Run-query-Adds-message-and-gets-autocomplete-options-1-Chrome-win32.png │ │ │ │ ├── Run-query-Adds-message-and-gets-autocomplete-options-1-Ms-Edge-win32.png │ │ │ │ ├── Run-query-Adds-message-and-gets-autocomplete-options-2-Chrome-win32.png │ │ │ │ ├── Run-query-Adds-message-and-gets-autocomplete-options-2-Ms-Edge-win32.png │ │ │ │ ├── Run-query-Adds-message-and-gets-autocomplete-options-3-Chrome-win32.png │ │ │ │ ├── Run-query-Adds-message-and-gets-autocomplete-options-3-Ms-Edge-win32.png │ │ │ │ ├── Run-query-Adds-message-and-gets-autocomplete-options-4-Chrome-win32.png │ │ │ │ ├── Run-query-Adds-message-and-gets-autocomplete-options-4-Ms-Edge-win32.png │ │ │ │ ├── Access-token-tab-should-show-access-token-message-when-not-authenticated-1-Chrome-win32.png │ │ │ │ ├── Access-token-tab-should-show-access-token-message-when-not-authenticated-1-Ms-Edge-win32.png │ │ │ │ ├── Run-query-should-launch-the-share-query-dialog-when-share-query-button-is-clicked-1-Chrome-win32.png │ │ │ │ ├── Run-query-should-launch-the-share-query-dialog-when-share-query-button-is-clicked-1-Ms-Edge-win32.png │ │ │ │ ├── Run-query-should-launch-the-share-query-dialog-when-share-query-button-is-clicked-2-Chrome-win32.png │ │ │ │ ├── Run-query-should-launch-the-share-query-dialog-when-share-query-button-is-clicked-2-Ms-Edge-win32.png │ │ │ │ ├── Run-query-Changing-the-version-via-the-request-URL-field-changes-the-version-in-the-dropdown-1-Chrome-win32.png │ │ │ │ ├── Run-query-Changing-the-version-via-the-request-URL-field-changes-the-version-in-the-dropdown-1-Ms-Edge-win32.png │ │ │ │ ├── Run-query-Changing-the-version-via-the-request-URL-field-changes-the-version-in-the-dropdown-2-Chrome-win32.png │ │ │ │ ├── Run-query-Changing-the-version-via-the-request-URL-field-changes-the-version-in-the-dropdown-2-Ms-Edge-win32.png │ │ │ │ ├── Run-query-Changing-the-version-via-the-dropdown-menu-changes-the-version-in-the-request-URL-field-1-Chrome-win32.png │ │ │ │ ├── Run-query-Changing-the-version-via-the-dropdown-menu-changes-the-version-in-the-request-URL-field-1-Ms-Edge-win32.png │ │ │ │ ├── Run-query-Changing-the-version-via-the-dropdown-menu-changes-the-version-in-the-request-URL-field-2-Chrome-win32.png │ │ │ │ ├── Run-query-Changing-the-version-via-the-dropdown-menu-changes-the-version-in-the-request-URL-field-2-Ms-Edge-win32.png │ │ │ │ ├── Run-query-Changing-the-version-via-the-dropdown-menu-changes-the-version-in-the-request-URL-field-3-Chrome-win32.png │ │ │ │ ├── Run-query-Changing-the-version-via-the-dropdown-menu-changes-the-version-in-the-request-URL-field-3-Ms-Edge-win32.png │ │ │ │ ├── Run-query-Changing-the-version-via-the-dropdown-menu-changes-the-version-in-the-request-URL-field-4-Chrome-win32.png │ │ │ │ └── Run-query-Changing-the-version-via-the-dropdown-menu-changes-the-version-in-the-request-URL-field-4-Ms-Edge-win32.png │ │ │ ├── header.spec.ts-snapshots │ │ │ │ ├── Feedback-button-should-provide-feedback-1-Chrome-win32.png │ │ │ │ ├── Feedback-button-should-provide-feedback-2-Chrome-win32.png │ │ │ │ ├── Feedback-button-should-provide-feedback-3-Chrome-win32.png │ │ │ │ ├── Feedback-button-should-provide-feedback-1-Ms-Edge-win32.png │ │ │ │ ├── Feedback-button-should-provide-feedback-2-Ms-Edge-win32.png │ │ │ │ ├── Feedback-button-should-provide-feedback-3-Ms-Edge-win32.png │ │ │ │ ├── Settings-button-should-change-theme-settings-1-Chrome-win32.png │ │ │ │ ├── Settings-button-should-change-theme-settings-2-Chrome-win32.png │ │ │ │ ├── Settings-button-should-change-theme-settings-3-Chrome-win32.png │ │ │ │ ├── Settings-button-should-change-theme-settings-1-Ms-Edge-win32.png │ │ │ │ ├── Settings-button-should-change-theme-settings-2-Ms-Edge-win32.png │ │ │ │ └── Settings-button-should-change-theme-settings-3-Ms-Edge-win32.png │ │ │ ├── sidebar.spec.ts-snapshots │ │ │ │ ├── Sample-Query-tab-should-search-for-a-sample-query-1-Chrome-win32.png │ │ │ │ ├── Sample-Query-tab-should-search-for-a-sample-query-1-Ms-Edge-win32.png │ │ │ │ ├── Sample-Query-tab-should-search-for-a-sample-query-2-Chrome-win32.png │ │ │ │ ├── Sample-Query-tab-should-search-for-a-sample-query-2-Ms-Edge-win32.png │ │ │ │ ├── Sample-Query-tab-should-search-for-a-sample-query-3-Chrome-win32.png │ │ │ │ ├── Sample-Query-tab-should-search-for-a-sample-query-3-Ms-Edge-win32.png │ │ │ │ ├── Sample-Query-tab-should-search-for-a-sample-query-4-Chrome-win32.png │ │ │ │ ├── Sample-Query-tab-should-search-for-a-sample-query-4-Ms-Edge-win32.png │ │ │ │ ├── Sample-Query-tab-should-search-for-a-sample-query-5-Chrome-win32.png │ │ │ │ ├── Sample-Query-tab-should-search-for-a-sample-query-5-Ms-Edge-win32.png │ │ │ │ ├── Sample-Query-tab-should-select-a-sample-query-when-clicked-1-Chrome-win32.png │ │ │ │ ├── Sample-Query-tab-should-select-a-sample-query-when-clicked-2-Chrome-win32.png │ │ │ │ ├── Sample-Query-tab-should-select-a-sample-query-when-clicked-3-Chrome-win32.png │ │ │ │ ├── Sample-Query-tab-should-select-a-sample-query-when-clicked-1-Ms-Edge-win32.png │ │ │ │ ├── Sample-Query-tab-should-select-a-sample-query-when-clicked-2-Ms-Edge-win32.png │ │ │ │ ├── Sample-Query-tab-should-select-a-sample-query-when-clicked-3-Ms-Edge-win32.png │ │ │ │ ├── History-tab-should-show-added-history-items-to-Today-s-collection-1-Chrome-win32.png │ │ │ │ ├── History-tab-should-show-added-history-items-to-Today-s-collection-2-Chrome-win32.png │ │ │ │ ├── History-tab-should-show-added-history-items-to-Today-s-collection-1-Ms-Edge-win32.png │ │ │ │ ├── History-tab-should-show-added-history-items-to-Today-s-collection-2-Ms-Edge-win32.png │ │ │ │ ├── History-tab-should-show-added-history-items-to-Today-s-collection-3-Ms-Edge-win32.png │ │ │ │ ├── Resources-Explorer-should-switch-between-v1-and-beta-versions-of-resources-1-Chrome-win32.png │ │ │ │ ├── Resources-Explorer-should-switch-between-v1-and-beta-versions-of-resources-1-Ms-Edge-win32.png │ │ │ │ ├── Resources-Explorer-should-switch-between-v1-and-beta-versions-of-resources-2-Chrome-win32.png │ │ │ │ ├── Resources-Explorer-should-switch-between-v1-and-beta-versions-of-resources-2-Ms-Edge-win32.png │ │ │ │ ├── Resources-Explorer-should-open-the-resources-e-32f57-when-the-resources-explorer-button-is-clicked-1-Chrome-win32.png │ │ │ │ ├── Resources-Explorer-should-open-the-resources-e-a7aea-when-the-resources-explorer-button-is-clicked-2-Chrome-win32.png │ │ │ │ ├── Resources-Explorer-should-open-the-resources-e-a813e-when-the-resources-explorer-button-is-clicked-3-Chrome-win32.png │ │ │ │ ├── Resources-Explorer-should-open-the-resources-e-32f57-when-the-resources-explorer-button-is-clicked-1-Ms-Edge-win32.png │ │ │ │ ├── Resources-Explorer-should-open-the-resources-e-a7aea-when-the-resources-explorer-button-is-clicked-2-Ms-Edge-win32.png │ │ │ │ └── Resources-Explorer-should-open-the-resources-e-a813e-when-the-resources-explorer-button-is-clicked-3-Ms-Edge-win32.png │ │ │ └── response.spec.ts-snapshots │ │ │ │ ├── Response-section-should-show-toolkit-component-for-a-valid-url-1-Chrome-win32.png │ │ │ │ ├── Response-section-should-show-toolkit-component-for-a-valid-url-2-Chrome-win32.png │ │ │ │ ├── Response-section-should-show-a-response-for-a-successful-request-1-Chrome-win32.png │ │ │ │ ├── Response-section-should-show-an-error-message-for-an-invalid-url-1-Chrome-win32.png │ │ │ │ ├── Response-section-should-show-an-error-message-for-an-invalid-url-2-Chrome-win32.png │ │ │ │ ├── Response-section-should-show-toolkit-component-for-a-valid-url-1-Ms-Edge-win32.png │ │ │ │ ├── Response-section-should-show-toolkit-component-for-a-valid-url-2-Ms-Edge-win32.png │ │ │ │ ├── Response-section-should-show-a-response-for-a-successful-request-1-Ms-Edge-win32.png │ │ │ │ ├── Response-section-should-show-an-error-message-for-an-invalid-url-1-Ms-Edge-win32.png │ │ │ │ ├── Response-section-should-show-an-error-message-for-an-invalid-url-2-Ms-Edge-win32.png │ │ │ │ ├── Response-section-should-open-an-expanded-modal-with-response-tabs-when-Expand-is-clicked-1-Chrome-win32.png │ │ │ │ ├── Response-section-should-open-an-expanded-modal-with-response-tabs-when-Expand-is-clicked-1-Ms-Edge-win32.png │ │ │ │ ├── Response-section-should-open-an-expanded-modal-with-response-tabs-when-Expand-is-clicked-2-Chrome-win32.png │ │ │ │ └── Response-section-should-open-an-expanded-modal-with-response-tabs-when-Expand-is-clicked-2-Ms-Edge-win32.png │ │ ├── responsiveness │ │ │ └── mobile-responsive.spec.ts-snapshots │ │ │ │ ├── Sidebar-navigation-should-have-history-tab-1-Chrome-win32.png │ │ │ │ ├── Sidebar-navigation-should-have-history-tab-1-Ms-Edge-win32.png │ │ │ │ ├── Sidebar-navigation-should-have-resources-tab-1-Chrome-win32.png │ │ │ │ ├── Sidebar-navigation-should-have-resources-tab-1-Ms-Edge-win32.png │ │ │ │ ├── Sidebar-navigation-should-have-sample-queries-tab-1-Chrome-win32.png │ │ │ │ ├── Sidebar-navigation-should-have-sample-queries-tab-1-Ms-Edge-win32.png │ │ │ │ ├── Request-area-navigation-should-have-access-token-tab-in-overflow-menu-1-Chrome-win32.png │ │ │ │ ├── Request-area-navigation-should-have-access-token-tab-in-overflow-menu-1-Ms-Edge-win32.png │ │ │ │ ├── Request-area-navigation-should-have-permissions-tab-in-overflow-menu-1-Chrome-win32.png │ │ │ │ ├── Request-area-navigation-should-have-permissions-tab-in-overflow-menu-1-Ms-Edge-win32.png │ │ │ │ ├── Response-area-navigation-should-have-adaptive-card-tab-in-overflow-menu-1-Chrome-win32.png │ │ │ │ ├── Response-area-navigation-should-have-adaptive-card-tab-in-overflow-menu-1-Ms-Edge-win32.png │ │ │ │ ├── Response-area-navigation-should-have-adaptive-card-tab-in-overflow-menu-2-Chrome-win32.png │ │ │ │ ├── Response-area-navigation-should-have-adaptive-card-tab-in-overflow-menu-2-Ms-Edge-win32.png │ │ │ │ ├── Response-area-navigation-should-have-code-snippets-tab-in-overflow-menu-1-Chrome-win32.png │ │ │ │ ├── Response-area-navigation-should-have-code-snippets-tab-in-overflow-menu-1-Ms-Edge-win32.png │ │ │ │ ├── Response-area-navigation-should-have-expand-component-tab-in-overflow-menu-1-Chrome-win32.png │ │ │ │ ├── Response-area-navigation-should-have-expand-component-tab-in-overflow-menu-1-Ms-Edge-win32.png │ │ │ │ ├── Response-area-navigation-should-have-toolkit-component-tab-in-overflow-menu-1-Chrome-win32.png │ │ │ │ └── Response-area-navigation-should-have-toolkit-component-tab-in-overflow-menu-1-Ms-Edge-win32.png │ │ ├── authenticated-experiences │ │ │ └── authenticated-experiences.spec.ts-snapshots │ │ │ │ ├── Run-query-user-can-run-query-1-Chrome-win32.png │ │ │ │ ├── Run-query-user-can-run-query-1-Ms-Edge-win32.png │ │ │ │ ├── Profile-should-open-the-permissions-panel-1-Chrome-win32.png │ │ │ │ └── Profile-should-open-the-permissions-panel-1-Ms-Edge-win32.png │ │ ├── global-setup.ts │ │ └── login.ts │ ├── accessibility │ │ └── accessibility.spec.ts │ └── common │ │ └── resolver.js ├── telemetry │ ├── index.ts │ ├── error-types.ts │ ├── event-types.ts │ └── ITelemetry.ts ├── setupTests.ts └── adaptivecards-templates │ ├── index.ts │ ├── Insights.Trending.json │ ├── Profile.json │ └── Site.json ├── .devcontainer └── devcontainer.json ├── public ├── favicon.ico └── manifest.json ├── .prettierrc ├── __mocks__ ├── react-monaco-editor.js └── @azure │ └── msal-browser ├── packages ├── types-core-2.16.189.tgz ├── expvariantassignmentsdk-1.0.0.tgz ├── expvariantassignmentsdk-1.0.1.tgz └── officebrowserfeedbacknpm-1.6.6.tgz ├── versioned-build.js ├── CODE_OF_CONDUCT.md ├── .gitignore ├── sonar-project.properties ├── release-please-config.json ├── tsconfig.json ├── .vscode ├── settings.json └── extensions.json ├── LICENSE └── playwright.config.ts /config/CSSStub.ts: -------------------------------------------------------------------------------- 1 | module.exports = null; -------------------------------------------------------------------------------- /.release-please-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | ".": "11.1.5" 3 | } -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | build 2 | eslintrc.js 3 | public 4 | scripts 5 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @microsoftgraph/microsoft-graph-explorer-v4-write 2 | -------------------------------------------------------------------------------- /babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-env" 4 | ] 5 | } -------------------------------------------------------------------------------- /src/app/services/slices/index.ts: -------------------------------------------------------------------------------- 1 | export type Status = 'idle' | 'loading' | 'succeeded' | 'failed'; 2 | -------------------------------------------------------------------------------- /src/modules/cache/index.ts: -------------------------------------------------------------------------------- 1 | export interface ICacheData { 2 | data: T, 3 | expiry: number 4 | } -------------------------------------------------------------------------------- /src/types/devx-api.ts: -------------------------------------------------------------------------------- 1 | export interface IDevxAPI { 2 | baseUrl: string; 3 | parameters: string; 4 | } -------------------------------------------------------------------------------- /src/app/views/sidebar/history/index.ts: -------------------------------------------------------------------------------- 1 | import History from './History'; 2 | 3 | export { 4 | History 5 | } -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "image": "mcr.microsoft.com/devcontainers/typescript-node:0-18" 3 | } -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/public/favicon.ico -------------------------------------------------------------------------------- /src/app/services/hooks/index.ts: -------------------------------------------------------------------------------- 1 | import { usePopups } from './usePopups'; 2 | 3 | export { 4 | usePopups 5 | } -------------------------------------------------------------------------------- /src/app/views/common/copy-button/index.ts: -------------------------------------------------------------------------------- 1 | import CopyButton from './CopyButton'; 2 | export { CopyButton }; 3 | -------------------------------------------------------------------------------- /src/app/views/query-runner/request/auth/index.ts: -------------------------------------------------------------------------------- 1 | import { Auth } from './Auth'; 2 | 3 | export default Auth; 4 | -------------------------------------------------------------------------------- /src/app/views/query-response/response/index.ts: -------------------------------------------------------------------------------- 1 | import Response from './Response'; 2 | export { Response }; 3 | 4 | -------------------------------------------------------------------------------- /src/graph-toolkit-examples/index.js: -------------------------------------------------------------------------------- 1 | import templates from './templates.json'; 2 | 3 | export default templates; 4 | -------------------------------------------------------------------------------- /src/app/utils/get-messages.ts: -------------------------------------------------------------------------------- 1 | import messages from '../../../src/messages'; 2 | 3 | export const messages_ = messages; 4 | -------------------------------------------------------------------------------- /src/app/views/common/error-boundary/index.ts: -------------------------------------------------------------------------------- 1 | import ErrorBoundary from './ErrorBoundary'; 2 | 3 | export { ErrorBoundary }; -------------------------------------------------------------------------------- /src/app/views/query-response/index.ts: -------------------------------------------------------------------------------- 1 | import QueryResponse from './QueryResponse'; 2 | 3 | export { QueryResponse }; 4 | -------------------------------------------------------------------------------- /src/app/views/query-response/snippets/index.tsx: -------------------------------------------------------------------------------- 1 | import Snippets from './Snippets'; 2 | export default Snippets; 3 | 4 | -------------------------------------------------------------------------------- /src/app/views/query-runner/index.ts: -------------------------------------------------------------------------------- 1 | import QueryRunner from './QueryRunner'; 2 | 3 | export { 4 | QueryRunner 5 | }; 6 | -------------------------------------------------------------------------------- /src/app/views/query-runner/query-input/index.ts: -------------------------------------------------------------------------------- 1 | import QueryInput from './QueryInput'; 2 | 3 | export { QueryInput }; 4 | -------------------------------------------------------------------------------- /src/app/views/authentication/auth-util-components/index.tsx: -------------------------------------------------------------------------------- 1 | export { showSignInButtonOrProfile } from './ProfileButton'; 2 | -------------------------------------------------------------------------------- /src/app/views/authentication/index.ts: -------------------------------------------------------------------------------- 1 | import Authentication from './Authentication'; 2 | 3 | export { Authentication }; 4 | -------------------------------------------------------------------------------- /src/app/views/query-runner/request/body/index.ts: -------------------------------------------------------------------------------- 1 | import RequestBody from './RequestBody'; 2 | export { 3 | RequestBody 4 | }; -------------------------------------------------------------------------------- /src/custom.d.ts: -------------------------------------------------------------------------------- 1 | // custom.d.ts 2 | declare module '*.svg' { 3 | const content: any; 4 | export default content; 5 | } 6 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "semi": true, 4 | "jsxSingleQuote": true, 5 | "trailingComma": "none" 6 | } 7 | -------------------------------------------------------------------------------- /src/app/views/common/banners/index.ts: -------------------------------------------------------------------------------- 1 | import Notification from './Notification' 2 | 3 | export default { 4 | Notification 5 | } -------------------------------------------------------------------------------- /src/app/views/query-runner/query-input/auto-complete/index.ts: -------------------------------------------------------------------------------- 1 | import AutoComplete from './AutoComplete'; 2 | export { AutoComplete }; -------------------------------------------------------------------------------- /src/themes/theme-context.ts: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | 3 | export const ThemeContext = createContext('light'); 4 | -------------------------------------------------------------------------------- /__mocks__/react-monaco-editor.js: -------------------------------------------------------------------------------- 1 | function MonacoEditor() { 2 | return (
); 3 | } 4 | 5 | module.exports.default = MonacoEditor; -------------------------------------------------------------------------------- /src/app/views/query-runner/query-input/share-query/index.ts: -------------------------------------------------------------------------------- 1 | import ShareButton from './ShareButton'; 2 | 3 | export { ShareButton }; 4 | -------------------------------------------------------------------------------- /src/app/views/query-runner/request/headers/index.ts: -------------------------------------------------------------------------------- 1 | import RequestHeaders from './RequestHeaders'; 2 | 3 | export default RequestHeaders; -------------------------------------------------------------------------------- /src/app/views/sidebar/resource-explorer/index.ts: -------------------------------------------------------------------------------- 1 | import ResourceExplorer from './ResourceExplorer'; 2 | export default ResourceExplorer; -------------------------------------------------------------------------------- /src/messages/index.js: -------------------------------------------------------------------------------- 1 | import enUS from './GE'; 2 | 3 | const messages = { 4 | 'en-US': enUS 5 | }; 6 | 7 | export default messages; -------------------------------------------------------------------------------- /packages/types-core-2.16.189.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/packages/types-core-2.16.189.tgz -------------------------------------------------------------------------------- /src/app/views/query-response/headers/index.ts: -------------------------------------------------------------------------------- 1 | import ResponseHeaders from './ResponseHeaders'; 2 | 3 | export default ResponseHeaders; 4 | -------------------------------------------------------------------------------- /src/app/views/query-runner/request/permissions/index.ts: -------------------------------------------------------------------------------- 1 | import { Permissions } from './Permissions.Query'; 2 | 3 | export default Permissions; -------------------------------------------------------------------------------- /src/app/services/variant-constants.ts: -------------------------------------------------------------------------------- 1 | export const ALWAYSSHOWBUTTONS = 'alwaysShowButtons'; 2 | export const SAFEROLLOUTACTIVE = 'safeRolloutActive'; -------------------------------------------------------------------------------- /src/types/root.ts: -------------------------------------------------------------------------------- 1 | export interface IApiFetch { 2 | pending: boolean; 3 | data: any[] | object | null | any; 4 | error: any | null; 5 | } 6 | -------------------------------------------------------------------------------- /src/types/settings.ts: -------------------------------------------------------------------------------- 1 | export interface ISettingsProps { 2 | actions?: { 3 | changeTheme: Function; 4 | consentToScopes: Function; 5 | }; 6 | } 7 | -------------------------------------------------------------------------------- /packages/expvariantassignmentsdk-1.0.0.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/packages/expvariantassignmentsdk-1.0.0.tgz -------------------------------------------------------------------------------- /packages/expvariantassignmentsdk-1.0.1.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/packages/expvariantassignmentsdk-1.0.1.tgz -------------------------------------------------------------------------------- /packages/officebrowserfeedbacknpm-1.6.6.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/packages/officebrowserfeedbacknpm-1.6.6.tgz -------------------------------------------------------------------------------- /src/app/utils/version.ts: -------------------------------------------------------------------------------- 1 | import packageJsonFile from '../../../package.json'; 2 | 3 | export function getVersion() { 4 | return packageJsonFile.version || 0; 5 | } -------------------------------------------------------------------------------- /src/app/views/common/index.ts: -------------------------------------------------------------------------------- 1 | import { Image } from './image/Image'; 2 | import { Monaco } from './monaco/Monaco'; 3 | 4 | export { 5 | Image, Monaco 6 | }; 7 | 8 | -------------------------------------------------------------------------------- /src/modules/authentication/index.ts: -------------------------------------------------------------------------------- 1 | import { AuthenticationWrapper } from './AuthenticationWrapper'; 2 | 3 | export const authenticationWrapper = AuthenticationWrapper.getInstance(); 4 | 5 | -------------------------------------------------------------------------------- /src/app/views/query-runner/query-runner.scss: -------------------------------------------------------------------------------- 1 | @use "../../../styles/variables"; 2 | 3 | .ms-Dropdown:focus { 4 | outline-style:double !important; 5 | outline-width:thick !important ; 6 | } -------------------------------------------------------------------------------- /src/types/status.ts: -------------------------------------------------------------------------------- 1 | export interface IStatus { 2 | messageBarType: string; 3 | ok?: boolean; 4 | status: number | string; 5 | statusText: string; 6 | duration?: number; 7 | hint?: string; 8 | } -------------------------------------------------------------------------------- /src/app/views/app-sections/index.ts: -------------------------------------------------------------------------------- 1 | import StatusMessages from './StatusMessages'; 2 | import TermsOfUseMessage from './TermsOfUseMessage'; 3 | 4 | export { 5 | StatusMessages, 6 | TermsOfUseMessage 7 | }; -------------------------------------------------------------------------------- /src/types/image.ts: -------------------------------------------------------------------------------- 1 | export interface IImageComponentProps { 2 | body: any; 3 | styles: object; 4 | alt: string; 5 | } 6 | 7 | export interface IImageComponentState { 8 | imageUrl: string; 9 | } 10 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: "/" 5 | target-branch: "dependabot-upgrades" 6 | schedule: 7 | interval: monthly 8 | open-pull-requests-limit: 20 9 | -------------------------------------------------------------------------------- /src/app/services/graph-client/index.ts: -------------------------------------------------------------------------------- 1 | import { GraphAuthenticationProvider } from './GraphAuthenticationProvider'; 2 | 3 | export { GraphClient } from './graph-client'; 4 | export const authProvider = new GraphAuthenticationProvider(); -------------------------------------------------------------------------------- /src/modules/authentication/interfaces/IClaimsChallenge.ts: -------------------------------------------------------------------------------- 1 | import { IQuery } from '../../../types/query-runner'; 2 | 3 | export default interface IClaimsChallenge { 4 | handle(responseHeaders: Headers, sampleQuery: IQuery): void; 5 | } -------------------------------------------------------------------------------- /src/types/adaptivecard.ts: -------------------------------------------------------------------------------- 1 | import * as AdaptiveCardsTemplateAPI from 'adaptivecards-templating'; 2 | 3 | export interface IAdaptiveCardContent { 4 | card?: AdaptiveCardsTemplateAPI.Template | object; 5 | template: object; 6 | } 7 | -------------------------------------------------------------------------------- /src/modules/suggestions/utilities/index.ts: -------------------------------------------------------------------------------- 1 | import { delimiters, getLastDelimiterInUrl } from './delimiters'; 2 | import { getSuggestions } from './suggestions-filter'; 3 | 4 | export { delimiters, getSuggestions, getLastDelimiterInUrl }; 5 | -------------------------------------------------------------------------------- /src/styles/index.scss: -------------------------------------------------------------------------------- 1 | @use "reset"; 2 | 3 | .ms-Pivot button{ 4 | margin: 5px !important; 5 | } 6 | 7 | .ms-DetailsHeader { 8 | padding-top: 0px !important; 9 | } 10 | 11 | iframe#webpack-dev-server-client-overlay{display:none!important} 12 | -------------------------------------------------------------------------------- /src/types/submit-button.ts: -------------------------------------------------------------------------------- 1 | export interface ISubmitButton { 2 | handleOnClick: Function; 3 | submitting: boolean; 4 | text: string; 5 | ariaLabel?: string; 6 | role?: string; 7 | disabled?: boolean; 8 | allowDisabledFocus?: boolean; 9 | } 10 | -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-user-can-run-query-1-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-user-can-run-query-1-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-user-can-run-query-2-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-user-can-run-query-2-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-user-can-run-query-1-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-user-can-run-query-1-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-user-can-run-query-2-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-user-can-run-query-2-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/types/share-query.ts: -------------------------------------------------------------------------------- 1 | export interface ISharedQueryParams { 2 | request: string | null; 3 | method: string | null; 4 | version: string | null; 5 | requestBody?: string | null; 6 | graphUrl: string | null; 7 | sid?: string | null; 8 | headers?: string | null; 9 | } 10 | -------------------------------------------------------------------------------- /src/app/views/query-runner/query-input/share-query/ShareQuery.styles.ts: -------------------------------------------------------------------------------- 1 | export const shareQueryStyles = () => { 2 | return { 3 | iconButton: { 4 | root: { 5 | float: 'right', 6 | border: '1px solid', 7 | width: '100%' 8 | } 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /src/telemetry/index.ts: -------------------------------------------------------------------------------- 1 | import { telemetry } from './telemetry'; 2 | import * as eventTypes from './event-types'; 3 | import * as componentNames from './component-names'; 4 | import * as errorTypes from './error-types' 5 | 6 | export { 7 | telemetry, eventTypes, componentNames, errorTypes 8 | }; 9 | -------------------------------------------------------------------------------- /src/types/action.ts: -------------------------------------------------------------------------------- 1 | export interface AppAction { 2 | type: string; 3 | payload: object | string | any | undefined; 4 | } 5 | 6 | export interface IApiResponse { 7 | error: object | string | null; 8 | data: object | string | null | undefined; 9 | pending: boolean; 10 | } 11 | 12 | -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/header.spec.ts-snapshots/Feedback-button-should-provide-feedback-1-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/header.spec.ts-snapshots/Feedback-button-should-provide-feedback-1-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/header.spec.ts-snapshots/Feedback-button-should-provide-feedback-2-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/header.spec.ts-snapshots/Feedback-button-should-provide-feedback-2-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/header.spec.ts-snapshots/Feedback-button-should-provide-feedback-3-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/header.spec.ts-snapshots/Feedback-button-should-provide-feedback-3-Chrome-win32.png -------------------------------------------------------------------------------- /versioned-build.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const thisApp = require('./package'); 3 | 4 | const dataToAppend = '' + 5 | ` 6 | !function() { return window['appVersion'] = '${thisApp.version}' }() 7 | `; 8 | 9 | fs.appendFileSync('build/static/js/graph-explorer-v2.js', dataToAppend); 10 | -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/header.spec.ts-snapshots/Feedback-button-should-provide-feedback-1-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/header.spec.ts-snapshots/Feedback-button-should-provide-feedback-1-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/header.spec.ts-snapshots/Feedback-button-should-provide-feedback-2-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/header.spec.ts-snapshots/Feedback-button-should-provide-feedback-2-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/header.spec.ts-snapshots/Feedback-button-should-provide-feedback-3-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/header.spec.ts-snapshots/Feedback-button-should-provide-feedback-3-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-should-change-version-options-1-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-should-change-version-options-1-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-should-change-version-options-1-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-should-change-version-options-1-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-should-change-version-options-2-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-should-change-version-options-2-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-should-change-version-options-2-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-should-change-version-options-2-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-should-change-version-options-3-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-should-change-version-options-3-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-should-change-version-options-3-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-should-change-version-options-3-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-should-change-version-options-4-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-should-change-version-options-4-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-should-change-version-options-4-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-should-change-version-options-4-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-should-change-version-options-5-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-should-change-version-options-5-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-should-change-version-options-5-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-should-change-version-options-5-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/types/dimensions.ts: -------------------------------------------------------------------------------- 1 | export interface IDimensionProperties { 2 | width: string; 3 | height: string; 4 | } 5 | 6 | export interface IDimensions { 7 | request: IDimensionProperties; 8 | response: IDimensionProperties; 9 | sidebar: IDimensionProperties; 10 | content: IDimensionProperties; 11 | } -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/header.spec.ts-snapshots/Settings-button-should-change-theme-settings-1-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/header.spec.ts-snapshots/Settings-button-should-change-theme-settings-1-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/header.spec.ts-snapshots/Settings-button-should-change-theme-settings-2-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/header.spec.ts-snapshots/Settings-button-should-change-theme-settings-2-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/header.spec.ts-snapshots/Settings-button-should-change-theme-settings-3-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/header.spec.ts-snapshots/Settings-button-should-change-theme-settings-3-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Request-section-should-add-request-headers-1-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Request-section-should-add-request-headers-1-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Request-section-should-add-request-headers-1-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Request-section-should-add-request-headers-1-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Request-section-should-add-request-headers-2-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Request-section-should-add-request-headers-2-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Request-section-should-add-request-headers-2-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Request-section-should-add-request-headers-2-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Request-section-should-edit-request-headers-1-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Request-section-should-edit-request-headers-1-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Request-section-should-edit-request-headers-2-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Request-section-should-edit-request-headers-2-Chrome-win32.png -------------------------------------------------------------------------------- /src/types/index.d.ts: -------------------------------------------------------------------------------- 1 | import { Environment } from 'monaco-editor/esm/vs/editor/editor.api'; 2 | 3 | export {}; 4 | declare global { 5 | interface Window { 6 | ClientId: string | undefined 7 | MonacoEnvironment: Environment; 8 | } 9 | } 10 | 11 | window.ClientId = window.ClientId || undefined; 12 | -------------------------------------------------------------------------------- /src/app/services/hooks/useCollectionPermissions.ts: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react'; 2 | 3 | import { CollectionPermissionsContext } from '../context/collection-permissions/CollectionPermissionsContext'; 4 | 5 | export const useCollectionPermissions = () => { 6 | return useContext(CollectionPermissionsContext); 7 | }; -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/header.spec.ts-snapshots/Settings-button-should-change-theme-settings-1-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/header.spec.ts-snapshots/Settings-button-should-change-theme-settings-1-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/header.spec.ts-snapshots/Settings-button-should-change-theme-settings-2-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/header.spec.ts-snapshots/Settings-button-should-change-theme-settings-2-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/header.spec.ts-snapshots/Settings-button-should-change-theme-settings-3-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/header.spec.ts-snapshots/Settings-button-should-change-theme-settings-3-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Request-section-should-edit-request-headers-1-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Request-section-should-edit-request-headers-1-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Request-section-should-edit-request-headers-2-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Request-section-should-edit-request-headers-2-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/responsiveness/mobile-responsive.spec.ts-snapshots/Sidebar-navigation-should-have-history-tab-1-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/responsiveness/mobile-responsive.spec.ts-snapshots/Sidebar-navigation-should-have-history-tab-1-Chrome-win32.png -------------------------------------------------------------------------------- /src/app/views/sidebar/sample-queries/tokens.spec.ts: -------------------------------------------------------------------------------- 1 | import { getTokens } from './tokens'; 2 | 3 | describe('Tests getTokens function', () => { 4 | it('should return an array of IToken objects', () => { 5 | // Arrange, Act and Assert 6 | const tokens = getTokens(); 7 | expect(tokens.length).toBe(34); 8 | }) 9 | }) -------------------------------------------------------------------------------- /src/tests/ui/responsiveness/mobile-responsive.spec.ts-snapshots/Sidebar-navigation-should-have-history-tab-1-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/responsiveness/mobile-responsive.spec.ts-snapshots/Sidebar-navigation-should-have-history-tab-1-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/responsiveness/mobile-responsive.spec.ts-snapshots/Sidebar-navigation-should-have-resources-tab-1-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/responsiveness/mobile-responsive.spec.ts-snapshots/Sidebar-navigation-should-have-resources-tab-1-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/responsiveness/mobile-responsive.spec.ts-snapshots/Sidebar-navigation-should-have-resources-tab-1-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/responsiveness/mobile-responsive.spec.ts-snapshots/Sidebar-navigation-should-have-resources-tab-1-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/app/utils/ClientError.ts: -------------------------------------------------------------------------------- 1 | interface IMetaInfo { 2 | error: string; 3 | } 4 | 5 | export class ClientError extends Error { 6 | constructor(meta: IMetaInfo = { error: '' }) { 7 | super(meta.error); 8 | Object.assign(this, meta); 9 | this.name = 'Client Error'; 10 | this.message = meta.error; 11 | } 12 | } -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Sample-Query-tab-should-search-for-a-sample-query-1-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Sample-Query-tab-should-search-for-a-sample-query-1-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Sample-Query-tab-should-search-for-a-sample-query-1-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Sample-Query-tab-should-search-for-a-sample-query-1-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Sample-Query-tab-should-search-for-a-sample-query-2-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Sample-Query-tab-should-search-for-a-sample-query-2-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Sample-Query-tab-should-search-for-a-sample-query-2-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Sample-Query-tab-should-search-for-a-sample-query-2-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Sample-Query-tab-should-search-for-a-sample-query-3-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Sample-Query-tab-should-search-for-a-sample-query-3-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Sample-Query-tab-should-search-for-a-sample-query-3-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Sample-Query-tab-should-search-for-a-sample-query-3-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Sample-Query-tab-should-search-for-a-sample-query-4-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Sample-Query-tab-should-search-for-a-sample-query-4-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Sample-Query-tab-should-search-for-a-sample-query-4-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Sample-Query-tab-should-search-for-a-sample-query-4-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Sample-Query-tab-should-search-for-a-sample-query-5-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Sample-Query-tab-should-search-for-a-sample-query-5-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Sample-Query-tab-should-search-for-a-sample-query-5-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Sample-Query-tab-should-search-for-a-sample-query-5-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/authenticated-experiences/authenticated-experiences.spec.ts-snapshots/Run-query-user-can-run-query-1-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/authenticated-experiences/authenticated-experiences.spec.ts-snapshots/Run-query-user-can-run-query-1-Chrome-win32.png -------------------------------------------------------------------------------- /src/telemetry/error-types.ts: -------------------------------------------------------------------------------- 1 | // Error types MUST end with the word ERROR 2 | 3 | export const NETWORK_ERROR = 'NETWORK_ERROR'; 4 | export const LINK_ERROR = 'LINK_ERROR'; 5 | export const COMPONENT_ERROR = 'COMPONENT_ERROR'; 6 | export const OPERATIONAL_ERROR = 'OPERATIONAL_ERROR'; 7 | export const UNHANDLED_ERROR = 'UNHANDLED_ERROR'; 8 | -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Permissions-should-show-permissions-for-me-endpoint-1-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Permissions-should-show-permissions-for-me-endpoint-1-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Permissions-should-show-permissions-for-me-endpoint-1-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Permissions-should-show-permissions-for-me-endpoint-1-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-Adds-message-and-gets-autocomplete-options-1-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-Adds-message-and-gets-autocomplete-options-1-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-Adds-message-and-gets-autocomplete-options-1-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-Adds-message-and-gets-autocomplete-options-1-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-Adds-message-and-gets-autocomplete-options-2-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-Adds-message-and-gets-autocomplete-options-2-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-Adds-message-and-gets-autocomplete-options-2-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-Adds-message-and-gets-autocomplete-options-2-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-Adds-message-and-gets-autocomplete-options-3-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-Adds-message-and-gets-autocomplete-options-3-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-Adds-message-and-gets-autocomplete-options-3-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-Adds-message-and-gets-autocomplete-options-3-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-Adds-message-and-gets-autocomplete-options-4-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-Adds-message-and-gets-autocomplete-options-4-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-Adds-message-and-gets-autocomplete-options-4-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-Adds-message-and-gets-autocomplete-options-4-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/authenticated-experiences/authenticated-experiences.spec.ts-snapshots/Run-query-user-can-run-query-1-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/authenticated-experiences/authenticated-experiences.spec.ts-snapshots/Run-query-user-can-run-query-1-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/responsiveness/mobile-responsive.spec.ts-snapshots/Sidebar-navigation-should-have-sample-queries-tab-1-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/responsiveness/mobile-responsive.spec.ts-snapshots/Sidebar-navigation-should-have-sample-queries-tab-1-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/responsiveness/mobile-responsive.spec.ts-snapshots/Sidebar-navigation-should-have-sample-queries-tab-1-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/responsiveness/mobile-responsive.spec.ts-snapshots/Sidebar-navigation-should-have-sample-queries-tab-1-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/app/utils/error-utils/ClientError.ts: -------------------------------------------------------------------------------- 1 | interface IMetaInfo { 2 | error: string; 3 | } 4 | 5 | export class ClientError extends Error { 6 | constructor(meta: IMetaInfo = { error: '' }) { 7 | super(meta.error); 8 | Object.assign(this, meta); 9 | this.name = 'Client Error'; 10 | this.message = meta.error; 11 | } 12 | } -------------------------------------------------------------------------------- /src/app/utils/searchbox.styles.ts: -------------------------------------------------------------------------------- 1 | export const searchBoxStyles: any = () => ({ 2 | root: { 3 | width: '100%' 4 | }, 5 | field: [ 6 | { 7 | paddingLeft: 10 8 | }, 9 | { 10 | selectors: { 11 | ':focus': { 12 | outline: 'none !important' 13 | } 14 | } 15 | } 16 | ] 17 | }) -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Sample-Query-tab-should-select-a-sample-query-when-clicked-1-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Sample-Query-tab-should-select-a-sample-query-when-clicked-1-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Sample-Query-tab-should-select-a-sample-query-when-clicked-2-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Sample-Query-tab-should-select-a-sample-query-when-clicked-2-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Sample-Query-tab-should-select-a-sample-query-when-clicked-3-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Sample-Query-tab-should-select-a-sample-query-when-clicked-3-Chrome-win32.png -------------------------------------------------------------------------------- /src/app/views/query-runner/query-input/auto-complete/use-previous.ts: -------------------------------------------------------------------------------- 1 | import { useRef, useEffect } from 'react'; 2 | 3 | const usePrevious = (value: string) => { 4 | const reference = useRef(null); 5 | useEffect(() => { 6 | reference.current = value; 7 | }); 8 | return reference.current; 9 | } 10 | 11 | export { usePrevious } -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Sample-Query-tab-should-select-a-sample-query-when-clicked-1-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Sample-Query-tab-should-select-a-sample-query-when-clicked-1-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Sample-Query-tab-should-select-a-sample-query-when-clicked-2-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Sample-Query-tab-should-select-a-sample-query-when-clicked-2-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Sample-Query-tab-should-select-a-sample-query-when-clicked-3-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Sample-Query-tab-should-select-a-sample-query-when-clicked-3-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/response.spec.ts-snapshots/Response-section-should-show-toolkit-component-for-a-valid-url-1-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/response.spec.ts-snapshots/Response-section-should-show-toolkit-component-for-a-valid-url-1-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/response.spec.ts-snapshots/Response-section-should-show-toolkit-component-for-a-valid-url-2-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/response.spec.ts-snapshots/Response-section-should-show-toolkit-component-for-a-valid-url-2-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/authenticated-experiences/authenticated-experiences.spec.ts-snapshots/Profile-should-open-the-permissions-panel-1-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/authenticated-experiences/authenticated-experiences.spec.ts-snapshots/Profile-should-open-the-permissions-panel-1-Chrome-win32.png -------------------------------------------------------------------------------- /src/app/views/common/popups/popups.types.ts: -------------------------------------------------------------------------------- 1 | import { PopupsComponent, PopupsProps } from '../../../services/context/popups-context'; 2 | 3 | export interface WrapperProps { 4 | isOpen: boolean; 5 | dismissPopup: Function; 6 | Component: React.ElementType>; 7 | popupsProps: PopupsProps; 8 | closePopup: Function; 9 | } -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/response.spec.ts-snapshots/Response-section-should-show-a-response-for-a-successful-request-1-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/response.spec.ts-snapshots/Response-section-should-show-a-response-for-a-successful-request-1-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/response.spec.ts-snapshots/Response-section-should-show-an-error-message-for-an-invalid-url-1-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/response.spec.ts-snapshots/Response-section-should-show-an-error-message-for-an-invalid-url-1-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/response.spec.ts-snapshots/Response-section-should-show-an-error-message-for-an-invalid-url-2-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/response.spec.ts-snapshots/Response-section-should-show-an-error-message-for-an-invalid-url-2-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/response.spec.ts-snapshots/Response-section-should-show-toolkit-component-for-a-valid-url-1-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/response.spec.ts-snapshots/Response-section-should-show-toolkit-component-for-a-valid-url-1-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/response.spec.ts-snapshots/Response-section-should-show-toolkit-component-for-a-valid-url-2-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/response.spec.ts-snapshots/Response-section-should-show-toolkit-component-for-a-valid-url-2-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/History-tab-should-show-added-history-items-to-Today-s-collection-1-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/History-tab-should-show-added-history-items-to-Today-s-collection-1-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/History-tab-should-show-added-history-items-to-Today-s-collection-2-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/History-tab-should-show-added-history-items-to-Today-s-collection-2-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/authenticated-experiences/authenticated-experiences.spec.ts-snapshots/Profile-should-open-the-permissions-panel-1-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/authenticated-experiences/authenticated-experiences.spec.ts-snapshots/Profile-should-open-the-permissions-panel-1-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/response.spec.ts-snapshots/Response-section-should-show-a-response-for-a-successful-request-1-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/response.spec.ts-snapshots/Response-section-should-show-a-response-for-a-successful-request-1-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/response.spec.ts-snapshots/Response-section-should-show-an-error-message-for-an-invalid-url-1-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/response.spec.ts-snapshots/Response-section-should-show-an-error-message-for-an-invalid-url-1-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/response.spec.ts-snapshots/Response-section-should-show-an-error-message-for-an-invalid-url-2-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/response.spec.ts-snapshots/Response-section-should-show-an-error-message-for-an-invalid-url-2-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/History-tab-should-show-added-history-items-to-Today-s-collection-1-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/History-tab-should-show-added-history-items-to-Today-s-collection-1-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/History-tab-should-show-added-history-items-to-Today-s-collection-2-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/History-tab-should-show-added-history-items-to-Today-s-collection-2-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/History-tab-should-show-added-history-items-to-Today-s-collection-3-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/History-tab-should-show-added-history-items-to-Today-s-collection-3-Ms-Edge-win32.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "GE", 3 | "name": "Graph Explorer", 4 | "icons": [ 5 | { 6 | "src": "", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /src/app/services/actions/mockThunkMiddleware.ts: -------------------------------------------------------------------------------- 1 | // This is a simple custom middleware that allows us to dispatch functions (like redux-thunk) 2 | export const mockThunkMiddleware = (store: any) => (next: any) => (action: any) => { 3 | if (typeof action === 'function') { 4 | return action(store.dispatch, store.getState); 5 | } 6 | 7 | return next(action); 8 | }; 9 | -------------------------------------------------------------------------------- /src/app/services/context/validation-context/ValidationContext.tsx: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | 3 | interface ValidationContext { 4 | isValid: boolean; 5 | validate: (queryUrl: string) => void; 6 | query: string; 7 | error: string; 8 | } 9 | 10 | export const ValidationContext = createContext( 11 | {} as ValidationContext 12 | ); -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Access-token-tab-should-show-access-token-message-when-not-authenticated-1-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Access-token-tab-should-show-access-token-message-when-not-authenticated-1-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Access-token-tab-should-show-access-token-message-when-not-authenticated-1-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Access-token-tab-should-show-access-token-message-when-not-authenticated-1-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/global-setup.ts: -------------------------------------------------------------------------------- 1 | import { chromium, FullConfig } from '@playwright/test'; 2 | 3 | async function globalSetup(config: FullConfig) { 4 | const baseURL = config.projects[0].use.baseURL as string; 5 | const browser = await chromium.launch(); 6 | const page = await browser.newPage(); 7 | await page.goto(baseURL); 8 | } 9 | 10 | export default globalSetup; -------------------------------------------------------------------------------- /src/tests/ui/responsiveness/mobile-responsive.spec.ts-snapshots/Request-area-navigation-should-have-access-token-tab-in-overflow-menu-1-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/responsiveness/mobile-responsive.spec.ts-snapshots/Request-area-navigation-should-have-access-token-tab-in-overflow-menu-1-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/responsiveness/mobile-responsive.spec.ts-snapshots/Request-area-navigation-should-have-access-token-tab-in-overflow-menu-1-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/responsiveness/mobile-responsive.spec.ts-snapshots/Request-area-navigation-should-have-access-token-tab-in-overflow-menu-1-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/responsiveness/mobile-responsive.spec.ts-snapshots/Request-area-navigation-should-have-permissions-tab-in-overflow-menu-1-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/responsiveness/mobile-responsive.spec.ts-snapshots/Request-area-navigation-should-have-permissions-tab-in-overflow-menu-1-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/responsiveness/mobile-responsive.spec.ts-snapshots/Request-area-navigation-should-have-permissions-tab-in-overflow-menu-1-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/responsiveness/mobile-responsive.spec.ts-snapshots/Request-area-navigation-should-have-permissions-tab-in-overflow-menu-1-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Resources-Explorer-should-switch-between-v1-and-beta-versions-of-resources-1-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Resources-Explorer-should-switch-between-v1-and-beta-versions-of-resources-1-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Resources-Explorer-should-switch-between-v1-and-beta-versions-of-resources-1-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Resources-Explorer-should-switch-between-v1-and-beta-versions-of-resources-1-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Resources-Explorer-should-switch-between-v1-and-beta-versions-of-resources-2-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Resources-Explorer-should-switch-between-v1-and-beta-versions-of-resources-2-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Resources-Explorer-should-switch-between-v1-and-beta-versions-of-resources-2-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Resources-Explorer-should-switch-between-v1-and-beta-versions-of-resources-2-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/responsiveness/mobile-responsive.spec.ts-snapshots/Response-area-navigation-should-have-adaptive-card-tab-in-overflow-menu-1-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/responsiveness/mobile-responsive.spec.ts-snapshots/Response-area-navigation-should-have-adaptive-card-tab-in-overflow-menu-1-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/responsiveness/mobile-responsive.spec.ts-snapshots/Response-area-navigation-should-have-adaptive-card-tab-in-overflow-menu-1-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/responsiveness/mobile-responsive.spec.ts-snapshots/Response-area-navigation-should-have-adaptive-card-tab-in-overflow-menu-1-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/responsiveness/mobile-responsive.spec.ts-snapshots/Response-area-navigation-should-have-adaptive-card-tab-in-overflow-menu-2-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/responsiveness/mobile-responsive.spec.ts-snapshots/Response-area-navigation-should-have-adaptive-card-tab-in-overflow-menu-2-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/responsiveness/mobile-responsive.spec.ts-snapshots/Response-area-navigation-should-have-adaptive-card-tab-in-overflow-menu-2-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/responsiveness/mobile-responsive.spec.ts-snapshots/Response-area-navigation-should-have-adaptive-card-tab-in-overflow-menu-2-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/responsiveness/mobile-responsive.spec.ts-snapshots/Response-area-navigation-should-have-code-snippets-tab-in-overflow-menu-1-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/responsiveness/mobile-responsive.spec.ts-snapshots/Response-area-navigation-should-have-code-snippets-tab-in-overflow-menu-1-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/responsiveness/mobile-responsive.spec.ts-snapshots/Response-area-navigation-should-have-code-snippets-tab-in-overflow-menu-1-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/responsiveness/mobile-responsive.spec.ts-snapshots/Response-area-navigation-should-have-code-snippets-tab-in-overflow-menu-1-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/app/services/slices/theme.slice.ts: -------------------------------------------------------------------------------- 1 | import { createSlice, PayloadAction } from '@reduxjs/toolkit'; 2 | 3 | const theme = createSlice({ 4 | name: 'theme', 5 | initialState: 'light', 6 | reducers: { 7 | changeTheme: (_, action: PayloadAction)=> action.payload 8 | } 9 | }) 10 | 11 | export const {changeTheme} = theme.actions 12 | export default theme.reducer; -------------------------------------------------------------------------------- /src/tests/ui/responsiveness/mobile-responsive.spec.ts-snapshots/Response-area-navigation-should-have-expand-component-tab-in-overflow-menu-1-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/responsiveness/mobile-responsive.spec.ts-snapshots/Response-area-navigation-should-have-expand-component-tab-in-overflow-menu-1-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/responsiveness/mobile-responsive.spec.ts-snapshots/Response-area-navigation-should-have-expand-component-tab-in-overflow-menu-1-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/responsiveness/mobile-responsive.spec.ts-snapshots/Response-area-navigation-should-have-expand-component-tab-in-overflow-menu-1-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/responsiveness/mobile-responsive.spec.ts-snapshots/Response-area-navigation-should-have-toolkit-component-tab-in-overflow-menu-1-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/responsiveness/mobile-responsive.spec.ts-snapshots/Response-area-navigation-should-have-toolkit-component-tab-in-overflow-menu-1-Chrome-win32.png -------------------------------------------------------------------------------- /src/app/services/slices/terms-of-use.slice.ts: -------------------------------------------------------------------------------- 1 | import { createSlice } from '@reduxjs/toolkit'; 2 | 3 | const termsOfUseSlice = createSlice({ 4 | name: 'termsOfUse', 5 | initialState: true, 6 | reducers: { 7 | clearTermsOfUse: () => { return false } 8 | } 9 | }) 10 | 11 | export const { clearTermsOfUse } = termsOfUseSlice.actions 12 | export default termsOfUseSlice.reducer; -------------------------------------------------------------------------------- /src/tests/ui/responsiveness/mobile-responsive.spec.ts-snapshots/Response-area-navigation-should-have-toolkit-component-tab-in-overflow-menu-1-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/responsiveness/mobile-responsive.spec.ts-snapshots/Response-area-navigation-should-have-toolkit-component-tab-in-overflow-menu-1-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/app/utils/getPermissionsScopeType.ts: -------------------------------------------------------------------------------- 1 | import { IUser } from '../../types/profile'; 2 | import { ACCOUNT_TYPE, PERMS_SCOPE } from '../services/graph-constants'; 3 | 4 | export function getPermissionsScopeType(profile: IUser | null | undefined) { 5 | if (profile?.profileType === ACCOUNT_TYPE.MSA) { 6 | return PERMS_SCOPE.PERSONAL; 7 | } 8 | return PERMS_SCOPE.WORK; 9 | } 10 | -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-should-launch-the-share-query-dialog-when-share-query-button-is-clicked-1-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-should-launch-the-share-query-dialog-when-share-query-button-is-clicked-1-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-should-launch-the-share-query-dialog-when-share-query-button-is-clicked-1-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-should-launch-the-share-query-dialog-when-share-query-button-is-clicked-1-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-should-launch-the-share-query-dialog-when-share-query-button-is-clicked-2-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-should-launch-the-share-query-dialog-when-share-query-button-is-clicked-2-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-should-launch-the-share-query-dialog-when-share-query-button-is-clicked-2-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-should-launch-the-share-query-dialog-when-share-query-button-is-clicked-2-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/types/snippets.ts: -------------------------------------------------------------------------------- 1 | import { IApiResponse } from './action'; 2 | 3 | export interface ISnippet extends IApiResponse { 4 | pending: boolean; 5 | data: Snippet; 6 | error: SnippetError; 7 | snippetTab?: string; 8 | } 9 | 10 | export interface Snippet { 11 | [language: string]: string 12 | } 13 | 14 | export interface SnippetError { 15 | status: number, 16 | error: string 17 | } 18 | -------------------------------------------------------------------------------- /src/app/utils/status-message.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable max-len */ 2 | import { setStatusMessage } from './status-message'; 3 | 4 | describe('status message should', () => { 5 | 6 | it('return a status message given a status code', () => { 7 | const statusCode: number = 200; 8 | const statusMessage = setStatusMessage(statusCode); 9 | expect(statusMessage).toBe('OK'); 10 | }) 11 | 12 | }); -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/response.spec.ts-snapshots/Response-section-should-open-an-expanded-modal-with-response-tabs-when-Expand-is-clicked-1-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/response.spec.ts-snapshots/Response-section-should-open-an-expanded-modal-with-response-tabs-when-Expand-is-clicked-1-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/response.spec.ts-snapshots/Response-section-should-open-an-expanded-modal-with-response-tabs-when-Expand-is-clicked-1-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/response.spec.ts-snapshots/Response-section-should-open-an-expanded-modal-with-response-tabs-when-Expand-is-clicked-1-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/response.spec.ts-snapshots/Response-section-should-open-an-expanded-modal-with-response-tabs-when-Expand-is-clicked-2-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/response.spec.ts-snapshots/Response-section-should-open-an-expanded-modal-with-response-tabs-when-Expand-is-clicked-2-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/response.spec.ts-snapshots/Response-section-should-open-an-expanded-modal-with-response-tabs-when-Expand-is-clicked-2-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/response.spec.ts-snapshots/Response-section-should-open-an-expanded-modal-with-response-tabs-when-Expand-is-clicked-2-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/app/utils/error-utils/ValidationError.ts: -------------------------------------------------------------------------------- 1 | type ErrorType = 'warning' | 'error'; 2 | 3 | class ValidationError extends Error { 4 | type: ErrorType; 5 | 6 | constructor(message: string, type: ErrorType, name: string = 'ValidationError') { 7 | super(message); 8 | this.name = name; 9 | this.type = type; 10 | this.message = message; 11 | } 12 | } 13 | 14 | export { ValidationError }; 15 | -------------------------------------------------------------------------------- /src/app/views/common/download.spec.ts: -------------------------------------------------------------------------------- 1 | import { downloadToLocal } from './download'; 2 | 3 | window.URL.createObjectURL = jest.fn(); 4 | 5 | describe('Tests file downloads on resource collections', () => { 6 | it('Downloads file to local', () => { 7 | const content = { 8 | app: '/me' 9 | } 10 | const fileName = 'TestFile'; 11 | 12 | downloadToLocal(content, fileName); 13 | }) 14 | }) -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-Changing-the-version-via-the-request-URL-field-changes-the-version-in-the-dropdown-1-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-Changing-the-version-via-the-request-URL-field-changes-the-version-in-the-dropdown-1-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-Changing-the-version-via-the-request-URL-field-changes-the-version-in-the-dropdown-1-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-Changing-the-version-via-the-request-URL-field-changes-the-version-in-the-dropdown-1-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-Changing-the-version-via-the-request-URL-field-changes-the-version-in-the-dropdown-2-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-Changing-the-version-via-the-request-URL-field-changes-the-version-in-the-dropdown-2-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-Changing-the-version-via-the-request-URL-field-changes-the-version-in-the-dropdown-2-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-Changing-the-version-via-the-request-URL-field-changes-the-version-in-the-dropdown-2-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/app/views/common/monaco/monaco.scss: -------------------------------------------------------------------------------- 1 | @use "../../../../styles/variables"; 2 | 3 | .monaco-editor { 4 | margin-left: 1px; 5 | margin-top: 5px; 6 | padding-bottom: $gutter; 7 | width: inherit !important; 8 | overflow: hidden; 9 | 10 | .react-monaco-editor-container { 11 | width: inherit !important; 12 | } 13 | 14 | .overflow-guard { 15 | width: inherit !important; 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-Changing-the-version-via-the-dropdown-menu-changes-the-version-in-the-request-URL-field-1-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-Changing-the-version-via-the-dropdown-menu-changes-the-version-in-the-request-URL-field-1-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-Changing-the-version-via-the-dropdown-menu-changes-the-version-in-the-request-URL-field-1-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-Changing-the-version-via-the-dropdown-menu-changes-the-version-in-the-request-URL-field-1-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-Changing-the-version-via-the-dropdown-menu-changes-the-version-in-the-request-URL-field-2-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-Changing-the-version-via-the-dropdown-menu-changes-the-version-in-the-request-URL-field-2-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-Changing-the-version-via-the-dropdown-menu-changes-the-version-in-the-request-URL-field-2-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-Changing-the-version-via-the-dropdown-menu-changes-the-version-in-the-request-URL-field-2-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-Changing-the-version-via-the-dropdown-menu-changes-the-version-in-the-request-URL-field-3-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-Changing-the-version-via-the-dropdown-menu-changes-the-version-in-the-request-URL-field-3-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-Changing-the-version-via-the-dropdown-menu-changes-the-version-in-the-request-URL-field-3-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-Changing-the-version-via-the-dropdown-menu-changes-the-version-in-the-request-URL-field-3-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-Changing-the-version-via-the-dropdown-menu-changes-the-version-in-the-request-URL-field-4-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-Changing-the-version-via-the-dropdown-menu-changes-the-version-in-the-request-URL-field-4-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-Changing-the-version-via-the-dropdown-menu-changes-the-version-in-the-request-URL-field-4-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/request.spec.ts-snapshots/Run-query-Changing-the-version-via-the-dropdown-menu-changes-the-version-in-the-request-URL-field-4-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Resources-Explorer-should-open-the-resources-e-32f57-when-the-resources-explorer-button-is-clicked-1-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Resources-Explorer-should-open-the-resources-e-32f57-when-the-resources-explorer-button-is-clicked-1-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Resources-Explorer-should-open-the-resources-e-a7aea-when-the-resources-explorer-button-is-clicked-2-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Resources-Explorer-should-open-the-resources-e-a7aea-when-the-resources-explorer-button-is-clicked-2-Chrome-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Resources-Explorer-should-open-the-resources-e-a813e-when-the-resources-explorer-button-is-clicked-3-Chrome-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Resources-Explorer-should-open-the-resources-e-a813e-when-the-resources-explorer-button-is-clicked-3-Chrome-win32.png -------------------------------------------------------------------------------- /src/modules/authentication/authUtils.spec.ts: -------------------------------------------------------------------------------- 1 | import { LoginType } from '../../types/enums'; 2 | import { getLoginType, getCurrentUri } from './authUtils'; 3 | 4 | describe('Auth utils should', () => { 5 | it('return valid login type', () => { 6 | expect(getLoginType()).toBe(LoginType.Popup); 7 | }) 8 | it('return valid uri for current app', () => { 9 | expect(getCurrentUri()).toBeDefined(); 10 | }) 11 | }) -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Resources-Explorer-should-open-the-resources-e-32f57-when-the-resources-explorer-button-is-clicked-1-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Resources-Explorer-should-open-the-resources-e-32f57-when-the-resources-explorer-button-is-clicked-1-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Resources-Explorer-should-open-the-resources-e-a7aea-when-the-resources-explorer-button-is-clicked-2-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Resources-Explorer-should-open-the-resources-e-a7aea-when-the-resources-explorer-button-is-clicked-2-Ms-Edge-win32.png -------------------------------------------------------------------------------- /src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Resources-Explorer-should-open-the-resources-e-a813e-when-the-resources-explorer-button-is-clicked-3-Ms-Edge-win32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoftgraph/microsoft-graph-explorer-v4/dev/src/tests/ui/anonymous-experiences/sidebar.spec.ts-snapshots/Resources-Explorer-should-open-the-resources-e-a813e-when-the-resources-explorer-button-is-clicked-3-Ms-Edge-win32.png -------------------------------------------------------------------------------- /config/jest/cssTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // This is a custom Jest transformer turning style imports into empty objects. 4 | // http://facebook.github.io/jest/docs/en/webpack.html 5 | 6 | module.exports = { 7 | process() { 8 | return { 9 | code: 'module.exports = {};' 10 | } 11 | }, 12 | getCacheKey() { 13 | // The output is always the same. 14 | return 'cssTransform'; 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /src/app/views/sidebar/sample-queries/sample-query-utils.spec.ts: -------------------------------------------------------------------------------- 1 | import { isJsonString } from './sample-query-utils'; 2 | 3 | describe('Tests isJsonString should', () => { 4 | it('return true for valid JSON strings', () => { 5 | expect(isJsonString('{"foo": "bar"}')).toBe(true); 6 | }); 7 | 8 | it('return false for invalid JSON strings', () => { 9 | expect(isJsonString('{"foo": "bar"')).toBe(false); 10 | }) 11 | }) -------------------------------------------------------------------------------- /src/app/views/common/copy.spec.ts: -------------------------------------------------------------------------------- 1 | import { genericCopy } from './copy'; 2 | 3 | describe('Tests generic copy.ts', () => { 4 | it('should resolve to \'copied\' when genericCopy is called with a string', () => { 5 | document.execCommand = jest.fn(); 6 | genericCopy('dummy text') 7 | .then((response: any) => { 8 | expect(response).toBe('copied'); 9 | }) 10 | .catch((e: Error) => { throw e }) 11 | }); 12 | }) 13 | -------------------------------------------------------------------------------- /src/app/utils/hash-string.ts: -------------------------------------------------------------------------------- 1 | export function stringToHash(input: string) { 2 | let hash = 0; 3 | if (input.length === 0) { 4 | return hash; 5 | } 6 | 7 | for (let index = 0; index < input.length; index++) { 8 | const char = input.charCodeAt(index); 9 | // eslint-disable-next-line no-bitwise 10 | hash = ((hash << 5) - hash) + char; 11 | // eslint-disable-next-line no-bitwise 12 | hash = hash & hash; 13 | } 14 | return hash; 15 | } -------------------------------------------------------------------------------- /src/modules/authentication/interfaces/IAuthenticationWrapper.ts: -------------------------------------------------------------------------------- 1 | import { AccountInfo, AuthenticationResult } from '@azure/msal-browser'; 2 | 3 | export default interface IAuthenticationWrapper { 4 | getSessionId(): string | null; 5 | logIn(sessionId?: string): Promise; 6 | logOut(): void; 7 | logOutPopUp(): void; 8 | consentToScopes(scopes: string[]): Promise; 9 | getAccount(): AccountInfo | undefined; 10 | } -------------------------------------------------------------------------------- /src/styles/_variables.scss: -------------------------------------------------------------------------------- 1 | $fontFamilyBold: helvetica, sans-serif; 2 | $fontFamilyBoldItalic: helvetica, sans-serif; 3 | $fontFamilyItalic: helvetica, sans-serif; 4 | $fontFamilyMedium: helvetica, sans-serif; 5 | $fontFamilyMediumItalic: helvetica, sans-serif; 6 | $fontFamilyRegular: helvetica, sans-serif; 7 | 8 | $fontSizeLarge: 20px; 9 | $fontSizeRegular: 14px; 10 | $fontSizeSmall: 12px; 11 | $fontSizeExtraSmall: 0.8em; 12 | 13 | $gutter: 10px; 14 | $radius: 3px; 15 | -------------------------------------------------------------------------------- /src/themes/theme-utils.spec.ts: -------------------------------------------------------------------------------- 1 | import { CURRENT_THEME } from '../app/services/graph-constants'; 2 | import { readFromLocalStorage, saveToLocalStorage } from '../app/utils/local-storage'; 3 | 4 | describe('Tests theme utils should', () => { 5 | it('save theme to local storage then retrieve the saved theme', () => { 6 | const theme = 'dark'; 7 | saveToLocalStorage(CURRENT_THEME,theme); 8 | expect(readFromLocalStorage(CURRENT_THEME)).toEqual(theme); 9 | }) 10 | }) -------------------------------------------------------------------------------- /src/app/services/slices/response-area-expanded.slice.ts: -------------------------------------------------------------------------------- 1 | import { createSlice, PayloadAction } from '@reduxjs/toolkit'; 2 | 3 | const responseAreaExpandedSlice = createSlice({ 4 | name: 'responseAreaExpanded', 5 | initialState: false, 6 | reducers: { 7 | expandResponseArea: (_, action: PayloadAction) => action.payload 8 | } 9 | }) 10 | 11 | export const { expandResponseArea } = responseAreaExpandedSlice.actions 12 | export default responseAreaExpandedSlice.reducer; -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /build 6 | /.pnp 7 | .pnp.js 8 | 9 | # testing 10 | /coverage 11 | /reports 12 | 13 | # misc 14 | .DS_Store 15 | .env.local 16 | .env.development.local 17 | .env.test.local 18 | .env.production.local 19 | .env 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | .idea 26 | .vs 27 | *.xml 28 | playwright-report 29 | test-results 30 | 31 | -------------------------------------------------------------------------------- /src/app/services/slices/explorer-mode.slice.ts: -------------------------------------------------------------------------------- 1 | import { createSlice, PayloadAction } from '@reduxjs/toolkit'; 2 | import { Mode } from '../../../types/enums'; 3 | 4 | const graphExplorerMode = createSlice({ 5 | name: 'graphExplorerMode', 6 | initialState: Mode.Complete, 7 | reducers: { 8 | setGraphExplorerMode: (_, action: PayloadAction) => action.payload 9 | } 10 | }) 11 | 12 | export const { setGraphExplorerMode } = graphExplorerMode.actions 13 | export default graphExplorerMode.reducer; -------------------------------------------------------------------------------- /src/app/views/main-header/utils.tsx: -------------------------------------------------------------------------------- 1 | import { makeStyles } from '@fluentui/react-components'; 2 | 3 | export const useHeaderStyles = makeStyles({ 4 | iconButton: { 5 | height: '100%', 6 | minWidth: '48px', 7 | maxWidth: '48px', 8 | '& .fui-Persona__primaryText': { 9 | display: 'none !important' 10 | } 11 | }, 12 | tenantButton: { 13 | height: '100%', 14 | cursor: 'default !important', 15 | '@media (max-width: 600px)': { 16 | display: 'none' 17 | } 18 | } 19 | }) -------------------------------------------------------------------------------- /src/app/utils/local-storage.ts: -------------------------------------------------------------------------------- 1 | export const saveToLocalStorage = (key: string, value: Object|string) => { 2 | if (typeof value === 'string') { 3 | localStorage.setItem(key, value); 4 | } else { 5 | localStorage.setItem(key, JSON.stringify(value)); 6 | } 7 | }; 8 | 9 | export const readFromLocalStorage = (key: string) => { 10 | const value = localStorage.getItem(key); 11 | 12 | if (value && typeof value === 'object') { 13 | return JSON.parse(value); 14 | } else{ 15 | return value; 16 | } 17 | }; -------------------------------------------------------------------------------- /src/app/services/reducers/collections-reducer.util.ts: -------------------------------------------------------------------------------- 1 | import { ResourcePath } from '../../../types/resources'; 2 | 3 | const getUniquePaths = (paths: ResourcePath[], items: ResourcePath[]): ResourcePath[] => { 4 | const content: ResourcePath[] = [...paths]; 5 | items.forEach((element: ResourcePath) => { 6 | const exists = !!content.find(k => k.key === element.key); 7 | if (!exists) { 8 | content.push(element); 9 | } 10 | }); 11 | return content; 12 | } 13 | 14 | export { 15 | getUniquePaths 16 | }; 17 | -------------------------------------------------------------------------------- /src/app/views/sidebar/history/History.styles.ts: -------------------------------------------------------------------------------- 1 | import { makeStyles } from '@fluentui/react-components' 2 | export const useHistoryStyles = makeStyles({ 3 | container: { 4 | display: 'flex', 5 | flexDirection: 'column' 6 | }, 7 | titleAside: { 8 | display: 'flex', 9 | alignItems: 'center', 10 | gap: '2px' 11 | }, 12 | button: { 13 | border: 'none' 14 | }, 15 | badgeContainer: { 16 | minWidth: '50px', 17 | display: 'inline-flex' 18 | }, 19 | badge: { 20 | maxWidth: '50px' 21 | } 22 | }) -------------------------------------------------------------------------------- /src/app/utils/hash-string.spec.ts: -------------------------------------------------------------------------------- 1 | import { stringToHash } from './hash-string'; 2 | 3 | describe('String to hash should', () => { 4 | 5 | it('return 0 if string is empty', async () => { 6 | const result = stringToHash(''); 7 | expect(result).toBe(0); 8 | }); 9 | 10 | it('return consistent values for each hashing', async () => { 11 | const value = 'hello world'; 12 | const result = stringToHash(value); 13 | const result2 = stringToHash(value); 14 | expect(result).toBe(result2); 15 | }); 16 | }); 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/app/views/common/lazy-loader/suspense-loader/SuspenseLoader.tsx: -------------------------------------------------------------------------------- 1 | import { Spinner } from '@fluentui/react-components'; 2 | import { Suspense } from 'react'; 3 | import ErrorBoundary from '../../error-boundary/ErrorBoundary'; 4 | 5 | interface SuspenseChildren{ 6 | children: React.ReactNode; 7 | } 8 | export const SuspenseLoader = ({ children }: SuspenseChildren) => { 9 | return ( 10 | 11 | }> 12 | {children} 13 | 14 | 15 | ); 16 | } -------------------------------------------------------------------------------- /src/telemetry/event-types.ts: -------------------------------------------------------------------------------- 1 | // Event names MUST end with the word EVENT 2 | 3 | export const BUTTON_CLICK_EVENT = 'BUTTON_CLICK_EVENT'; 4 | export const TAB_CLICK_EVENT = 'TAB_CLICK_EVENT'; 5 | export const LINK_CLICK_EVENT = 'LINK_CLICK_EVENT'; 6 | export const LISTITEM_CLICK_EVENT = 'LISTITEM_CLICK_EVENT'; 7 | export const DROPDOWN_CHANGE_EVENT = 'DROPDOWN_CHANGE_EVENT'; 8 | export const WINDOW_OPEN_EVENT = 'WINDOW_OPEN_EVENT'; 9 | export const KEYBOARD_COPY_EVENT = 'KEYBOARD_COPY_EVENT'; 10 | export const AUTH_REQUEST_EVENT = 'AUTH_REQUEST_EVENT'; 11 | -------------------------------------------------------------------------------- /src/types/authentication.ts: -------------------------------------------------------------------------------- 1 | import { Mode } from './enums'; 2 | 3 | export interface IAuthenticationProps { 4 | styles?: object; 5 | actions?: { 6 | signIn: Function; 7 | storeScopes: Function; 8 | setQueryResponseStatus: Function; 9 | }; 10 | tokenPresent: boolean; 11 | inProgress: boolean; 12 | mobileScreen: boolean; 13 | minimised: boolean; 14 | graphExplorerMode: Mode; 15 | } 16 | 17 | export interface AuthenticateResult { 18 | authToken: { 19 | pending: boolean; 20 | token: boolean; 21 | }; 22 | consentedScopes: string[]; 23 | } 24 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 2 | ## Overview 3 | 4 | Brief description of what this PR does, and why it is needed. 5 | 6 | ### Demo 7 | 8 | Optional. Screenshots, `curl` examples, etc. 9 | 10 | ### Notes 11 | 12 | Optional. Ancillary topics, caveats, alternative strategies that didn't work out, anything else. 13 | 14 | ## Testing Instructions 15 | 16 | * How to test this PR 17 | * Prefer bulleted description 18 | * Start after checking out this branch 19 | * Include any setup required, such as bundling scripts, restarting services, etc. 20 | * Include test case, and expected output -------------------------------------------------------------------------------- /src/app/services/slices/devxapi.slice.ts: -------------------------------------------------------------------------------- 1 | import { createSlice, PayloadAction } from '@reduxjs/toolkit'; 2 | import { IDevxAPI } from '../../../types/devx-api'; 3 | 4 | const initialState: IDevxAPI = { 5 | baseUrl: process.env.REACT_APP_DEVX_API_URL || '', 6 | parameters: '' 7 | }; 8 | 9 | const devxApi = createSlice({ 10 | name: 'devxApi', 11 | initialState, 12 | reducers: { 13 | setDevxApiUrl: (state, action: PayloadAction) => state = action.payload 14 | } 15 | }) 16 | 17 | export const { setDevxApiUrl } = devxApi.actions; 18 | export default devxApi.reducer; 19 | -------------------------------------------------------------------------------- /src/app/services/graph-client/GraphAuthenticationProvider.ts: -------------------------------------------------------------------------------- 1 | import { AuthenticationProvider } from '@microsoft/microsoft-graph-client'; 2 | 3 | import { authenticationWrapper } from '../../../modules/authentication'; 4 | 5 | export class GraphAuthenticationProvider implements AuthenticationProvider { 6 | /** 7 | * getAccessToken 8 | */ 9 | public async getAccessToken(): Promise { 10 | try { 11 | const authResult = await authenticationWrapper.getToken(); 12 | return authResult.accessToken; 13 | } catch (error) { 14 | return ''; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/modules/suggestions/index.ts: -------------------------------------------------------------------------------- 1 | import { IParsedOpenApiResponse } from '../../types/open-api'; 2 | import { suggestions } from './suggestions'; 3 | import { delimiters, getLastDelimiterInUrl, getSuggestions } from './utilities'; 4 | 5 | export interface ISuggestions { 6 | getSuggestions(url: string, api: string, version: string, 7 | context: SignContext): Promise; 8 | } 9 | 10 | export type SignContext = 'paths' | 'properties' | 'parameters'; 11 | 12 | export { 13 | suggestions, 14 | delimiters, 15 | getLastDelimiterInUrl, 16 | getSuggestions 17 | }; 18 | -------------------------------------------------------------------------------- /src/setupTests.ts: -------------------------------------------------------------------------------- 1 | import { GlobalWithFetchMock } from 'jest-fetch-mock'; 2 | import 'jest-canvas-mock'; 3 | import crypto from 'crypto'; 4 | 5 | const customGlobal: GlobalWithFetchMock = global as unknown as GlobalWithFetchMock; 6 | customGlobal.fetch = require('jest-fetch-mock'); // tslint:disable-line 7 | customGlobal.fetchMock = customGlobal.fetch; 8 | 9 | Object.defineProperty(global, 'crypto', { 10 | writable: true, 11 | value: { 12 | getRandomValues: (arr: Uint8Array) => crypto.getRandomValues(arr), 13 | subtle: {} 14 | } 15 | }); 16 | 17 | // Mock MSAL 18 | jest.mock('@azure/msal-browser'); -------------------------------------------------------------------------------- /src/app/services/context/collection-permissions/CollectionPermissionsContext.ts: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | 3 | import { CollectionPermission, ResourcePath } from '../../../../types/resources'; 4 | 5 | interface CollectionPermissionsContext { 6 | getPermissions: (paths: ResourcePath[]) => Promise; 7 | permissions?: { [key: string]: CollectionPermission[] }; 8 | isFetching?: boolean; 9 | } 10 | 11 | // eslint-disable-next-line @typescript-eslint/no-redeclare 12 | export const CollectionPermissionsContext = createContext( 13 | {} as CollectionPermissionsContext 14 | ); -------------------------------------------------------------------------------- /src/app/views/query-runner/request/headers/Headers.styles.ts: -------------------------------------------------------------------------------- 1 | import { makeStyles } from '@fluentui/react-components'; 2 | 3 | export const useHeaderStyles = makeStyles({ 4 | container: { 5 | padding: '10px', 6 | textAlign: 'start', 7 | maxHeight: '15vh', 8 | minHeight: 0 9 | }, 10 | itemContent: { 11 | marginTop: '2.5%' 12 | }, 13 | rowContainer: { 14 | fontSize: '1rem', 15 | position: 'relative' 16 | }, 17 | detailsRow: { 18 | display: 'flex', 19 | justifyContent: 'center', 20 | alignItems: 'center' 21 | }, 22 | actionButtons: { 23 | display: 'flex' 24 | } 25 | }); -------------------------------------------------------------------------------- /src/graph-toolkit-examples/templates.json: -------------------------------------------------------------------------------- 1 | { 2 | "/me": "https://mgt.dev/iframe.html?id=components-mgt-person-card--person-card&source=ge", 3 | "/me/people": "https://mgt.dev/iframe.html?id=components-mgt-people--people&source=ge", 4 | "/me/planner/tasks": "https://mgt.dev/iframe.html?id=components-mgt-tasks--tasks&source=ge", 5 | "/me/calendarview\\?startdatetime=([^/?]+)&enddatetime=([^/?]+)": "https://mgt.dev/iframe.html?id=components-mgt-agenda-properties--get-events-for-next-week&source=ge", 6 | "/me/photo/\\$value": "https://mgt.dev/iframe.html?id=components-mgt-person-properties--person-photo-only&source=ge" 7 | } 8 | -------------------------------------------------------------------------------- /src/types/profile.ts: -------------------------------------------------------------------------------- 1 | import { ACCOUNT_TYPE } from '../app/services/graph-constants'; 2 | 3 | export interface IProfileProps { 4 | styles?: object; 5 | mobileScreen: boolean; 6 | actions?: { 7 | getProfileInfo: Function; 8 | signOut: Function; 9 | }; 10 | } 11 | 12 | export interface IProfileState { 13 | status: 'unset' | 'success' | 'error', 14 | user?: IUser 15 | error?: Error 16 | } 17 | 18 | export interface IUser { 19 | id: string; 20 | displayName: string; 21 | emailAddress: string; 22 | profileImageUrl: string; 23 | profileType: ACCOUNT_TYPE; 24 | ageGroup: number; 25 | tenant: string; 26 | } 27 | -------------------------------------------------------------------------------- /src/app/services/graph-client/graph-client.ts: -------------------------------------------------------------------------------- 1 | import { Client } from '@microsoft/microsoft-graph-client'; 2 | import 'isomorphic-fetch'; 3 | import { authProvider } from '.'; 4 | 5 | export class GraphClient { 6 | private static client: Client; 7 | 8 | private static createClient(): Client { 9 | 10 | const clientOptions = { 11 | authProvider 12 | }; 13 | 14 | return Client.initWithMiddleware(clientOptions); 15 | } 16 | 17 | public static getInstance(): Client { 18 | if (!GraphClient.client) { 19 | GraphClient.client = this.createClient(); 20 | } 21 | return GraphClient.client; 22 | } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /src/app/services/actions/theme-action-creator.spec.ts: -------------------------------------------------------------------------------- 1 | import configureMockStore from 'redux-mock-store'; 2 | 3 | import { changeTheme } from '../slices/theme.slice'; 4 | 5 | const mockStore = configureMockStore(); 6 | 7 | describe('Change theme action creator', () => { 8 | it('Changes theme to dark', () => { 9 | const expectedActions = [ 10 | { 11 | type: 'theme/changeTheme', 12 | payload: 'dark' 13 | } 14 | ]; 15 | 16 | const store = mockStore({ theme: '' }); 17 | 18 | // @ts-ignore 19 | store.dispatch(changeTheme('dark')); 20 | expect(store.getActions()).toEqual(expectedActions); 21 | }) 22 | }) -------------------------------------------------------------------------------- /src/app/utils/translate-messages.ts: -------------------------------------------------------------------------------- 1 | import messages from '../../messages'; 2 | 3 | export function translateMessage(messageId: string): string { 4 | return getTranslation(messageId, 'en-US') || messageId; 5 | } 6 | 7 | function getTranslation(messageId: string, locale: string) { 8 | const localeMessages: object = (messages as { [key: string]: object })[locale]; 9 | if (localeMessages) { 10 | const key: any = Object.keys(localeMessages).find(k => k === messageId); 11 | const message = (localeMessages as { [key: string]: object })[key]; 12 | if (message) { 13 | return message.toString(); 14 | } 15 | } 16 | return null; 17 | } -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | sonar.projectKey=microsoftgraph_microsoft-graph-explorer-v4 2 | sonar.organization=microsoftgraph2 3 | sonar.projectName=microsoft-graph-explorer-v4 4 | // x-release-please-start-version 5 | sonar.projectVersion=11.1.5 6 | // x-release-please-end 7 | sonar.host.url=https://sonarcloud.io 8 | 9 | 10 | sonar.sources=src 11 | sonar.tests=src/tests 12 | sonar.test.inclusions=src/**/*.spec.ts,src/**/*.spec.tsx 13 | sonar.exclusions=node_modules 14 | sonar.sourceEncoding=UTF-8 15 | 16 | sonar.javascript.lcov.reportPaths=coverage/lcov.info 17 | sonar.testExecutionReportPaths=coverage/1.xml,coverage/2.xml,coverage/3.xml,coverage/4.xml,coverage/5.xml,coverage/6.xml, -------------------------------------------------------------------------------- /src/types/postman-collection.ts: -------------------------------------------------------------------------------- 1 | export interface Info { 2 | _postman_id: string; 3 | name: string; 4 | schema: string; 5 | } 6 | 7 | export interface Query { 8 | key: string; 9 | value: string; 10 | } 11 | 12 | export interface Url { 13 | raw: string; 14 | protocol: string; 15 | host: string[]; 16 | path: string[]; 17 | query?: Query[]; 18 | } 19 | 20 | export interface Request { 21 | method: string; 22 | header?: any[]; 23 | url: Url; 24 | } 25 | 26 | export interface Item { 27 | name: string; 28 | request?: Request; 29 | response?: any[]; 30 | } 31 | 32 | export interface IPostmanCollection { 33 | info: Info; 34 | item: Item[]; 35 | } 36 | -------------------------------------------------------------------------------- /src/types/enums.ts: -------------------------------------------------------------------------------- 1 | export enum Mode { 2 | TryIt = 'TRYIT', 3 | Complete = 'COMPLETE' 4 | } 5 | 6 | export enum LoginType { 7 | Redirect = 'REDIRECT', 8 | Popup = 'POPUP' 9 | } 10 | 11 | export enum ContentType { 12 | XML = 'application/xml', 13 | Json = 'application/json', 14 | Image = 'image/jpeg', 15 | TextPlain = 'text/plain', 16 | HTML = 'text/html', 17 | BinaryResponse = 'application/octet-stream', 18 | TextCsv = 'text/csv', 19 | OneNote = 'application/onenote', 20 | Pdf = 'application/pdf' 21 | } 22 | 23 | export enum AppTheme { 24 | Dark = 'dark', 25 | Light = 'light', 26 | } 27 | 28 | export enum SortOrder { 29 | ASC = 'ASC', 30 | DESC = 'DESC' 31 | } 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /release-please-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "bootstrap-sha": "439726b8e93371c61bb3ad76836603b1e822481f", 3 | "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json", 4 | "pull-request-header": ":rocket: A new Graph Explorer release is coming up!", 5 | "include-component-in-tag": false, 6 | "packages": { 7 | ".": { 8 | "changelog-path": "CHANGELOG.md", 9 | "release-type": "node", 10 | "bump-minor-pre-major": false, 11 | "bump-patch-for-minor-pre-major": false, 12 | "include-v-in-tag": true, 13 | "draft": false, 14 | "prerelease": false 15 | } 16 | }, 17 | "extra-files": [ "package.json", "sonar-project.properties" ] 18 | } 19 | -------------------------------------------------------------------------------- /src/app/utils/error-utils/ScopesError.ts: -------------------------------------------------------------------------------- 1 | import { ClientError } from './ClientError'; 2 | 3 | interface IScopesError { 4 | url: string; 5 | message: string; 6 | messageType: number; 7 | status: number; 8 | } 9 | 10 | export class ScopesError extends ClientError { 11 | message: string; 12 | messageType: number; 13 | status: number; 14 | url: string; 15 | 16 | constructor(error: IScopesError = { url: '', message: '', messageType: 0, status: 0 }) { 17 | super(); 18 | Object.assign(this, error); 19 | this.name = 'ScopesError'; 20 | this.url = error.url; 21 | this.message = error.message; 22 | this.messageType = error.messageType; 23 | this.status = error.status; 24 | } 25 | } -------------------------------------------------------------------------------- /src/telemetry/ITelemetry.ts: -------------------------------------------------------------------------------- 1 | import { SeverityLevel } from '@microsoft/applicationinsights-web'; 2 | import { ComponentType } from 'react'; 3 | import { IQuery } from '../types/query-runner'; 4 | 5 | export default interface ITelemetry { 6 | initialize(): void; 7 | trackEvent(name: string, properties: {}): void; 8 | trackReactComponent( 9 | Component: ComponentType, 10 | componentName?: string 11 | ): ComponentType; 12 | trackTabClickEvent(tabKey: string, sampleQuery?: IQuery): void; 13 | trackLinkClickEvent(url: string, componentName: string): void; 14 | trackException( 15 | error: Error, 16 | severityLevel: SeverityLevel, 17 | properties: {} 18 | ): void; 19 | getUserId():string 20 | } 21 | -------------------------------------------------------------------------------- /src/adaptivecards-templates/index.ts: -------------------------------------------------------------------------------- 1 | import DirectReports from './DirectReports.json'; 2 | import Files from './Files.json'; 3 | import FullPersonCard from './FullPersonCard.json'; 4 | import Groups from './Groups.json'; 5 | import InsightsTrending from './Insights.Trending.json'; 6 | import Messages from './Messages.json'; 7 | import Profile from './Profile.json'; 8 | import Site from './Site.json'; 9 | import Sites from './Sites.json'; 10 | import User from './User.json'; 11 | import Users from './Users.json'; 12 | 13 | 14 | export { 15 | DirectReports, 16 | Files, 17 | FullPersonCard, 18 | Groups, 19 | InsightsTrending, 20 | Messages, 21 | Profile, 22 | Site, 23 | Sites, 24 | User, 25 | Users 26 | } 27 | -------------------------------------------------------------------------------- /src/app/services/slices/sample-query.slice.ts: -------------------------------------------------------------------------------- 1 | import { createSlice, PayloadAction } from '@reduxjs/toolkit'; 2 | import { IQuery } from '../../../types/query-runner'; 3 | 4 | const initialState: IQuery = { 5 | selectedVerb: 'GET', 6 | sampleHeaders: [], 7 | sampleUrl: 'https://graph.microsoft.com/v1.0/me', 8 | sampleBody: undefined, 9 | selectedVersion: 'v1.0' 10 | } 11 | const sampleQuery = createSlice({ 12 | name: 'sampleQuery', 13 | initialState, 14 | reducers: { 15 | setSampleQuery: (state, action: PayloadAction)=>{ 16 | state = action.payload 17 | return state 18 | } 19 | } 20 | }) 21 | 22 | export const {setSampleQuery} = sampleQuery.actions 23 | export default sampleQuery.reducer; 24 | -------------------------------------------------------------------------------- /src/app/utils/useDetectMobileScreen.ts: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react'; 2 | import { useAppDispatch } from '../../store'; 3 | import { toggleSidebar } from '../services/slices/sidebar-properties.slice'; 4 | 5 | 6 | const MOBILE_BREAKPOINT = 768; 7 | 8 | export const useDetectMobileScreen = () => { 9 | const dispatch = useAppDispatch(); 10 | 11 | useEffect(() => { 12 | const handleResize = () => { 13 | const isMobile = window.innerWidth <= MOBILE_BREAKPOINT; 14 | dispatch(toggleSidebar({ showSidebar: !isMobile, mobileScreen: isMobile })); 15 | }; 16 | 17 | handleResize(); 18 | window.addEventListener('resize', handleResize); 19 | return () => window.removeEventListener('resize', handleResize); 20 | }, [dispatch]); 21 | }; -------------------------------------------------------------------------------- /src/app/services/actions/response-expanded-action-creator.spec.ts: -------------------------------------------------------------------------------- 1 | import { PayloadAction } from '@reduxjs/toolkit'; 2 | 3 | import { RESPONSE_EXPANDED } from '../../../app/services/redux-constants'; 4 | import { expandResponseArea } from '../slices/response-area-expanded.slice'; 5 | 6 | describe('Response Area Expansion', () => { 7 | it('should dispatch RESPONSE_EXPANDED when expandResponseArea() is called', () => { 8 | //Arrange 9 | const payload: boolean = true; 10 | 11 | const expectedAction: PayloadAction = { 12 | type: RESPONSE_EXPANDED, 13 | payload 14 | } 15 | 16 | // Act 17 | const action = expandResponseArea(payload); 18 | 19 | // Assert 20 | expect(action).toEqual(expectedAction); 21 | }) 22 | }) -------------------------------------------------------------------------------- /src/app/views/common/image/Image.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | import { IImageComponentProps } from '../../../../types/image'; 3 | 4 | export const Image = ({ styles, alt, body }: IImageComponentProps): JSX.Element => { 5 | const [imageUrl, setImageUrl] = useState(''); 6 | 7 | async function getImageUrl() { 8 | body.clone().arrayBuffer().then((buffer: any) => { 9 | const blob = new Blob([buffer], { type: 'image/jpeg' }); 10 | const url = URL.createObjectURL(blob); 11 | setImageUrl(url); 12 | }); 13 | } 14 | 15 | useEffect(() => { 16 | if (body) { 17 | getImageUrl(); 18 | } 19 | }, []); 20 | 21 | return ( 22 | {alt} 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "module": "esnext", 16 | "moduleResolution": "node", 17 | "resolveJsonModule": true, 18 | "noEmit": true, 19 | "jsx": "react-jsx" 20 | }, 21 | "include": [ 22 | "src", 23 | "src/tests", 24 | "typings.d.ts", 25 | "eslint.config.mjs", 26 | "__mocks__", 27 | "config", 28 | "jest.config.js", 29 | "playwright.config.ts", 30 | "versioned-build.js" 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /src/app/utils/error-utils/RevokeScopesError.ts: -------------------------------------------------------------------------------- 1 | import { ClientError } from './ClientError'; 2 | 3 | interface IRevokeScopesError { 4 | errorText: string; 5 | statusText: string; 6 | messageType: number; 7 | status: string; 8 | } 9 | 10 | export class RevokeScopesError extends ClientError { 11 | statusText: string; 12 | messageType: number; 13 | status: string; 14 | errorText: string; 15 | 16 | constructor(error: IRevokeScopesError = { errorText: '', statusText: '', messageType: 0, status: '' }) { 17 | super(); 18 | Object.assign(this, error); 19 | this.name = 'RevokeScopesError'; 20 | this.errorText = error.errorText; 21 | this.statusText = error.statusText; 22 | this.messageType = error.messageType; 23 | this.status = error.status; 24 | } 25 | } -------------------------------------------------------------------------------- /src/app/views/main-header/settings/ThemeChooser.styles.ts: -------------------------------------------------------------------------------- 1 | import { makeStyles } from '@fluentui/react-components'; 2 | 3 | export const useIconOptionStyles = makeStyles({ 4 | root: { 5 | display: 'block', 6 | alignItems: 'center' 7 | }, 8 | radio: { 9 | display: 'flex', 10 | alignItems: 'center', 11 | '&:checked ~ .fui-Radio__indicator::after': { 12 | borderRadius: '50%' 13 | } 14 | }, 15 | icon : { 16 | display: 'flex', 17 | justifySelf: 'center', 18 | marginBlockEnd: '15px' 19 | } 20 | }); 21 | 22 | export const useRadioGroupStyles = makeStyles({ 23 | root: { 24 | display: 'flex', 25 | alignItems: 'center', 26 | flexWrap: 'wrap', 27 | gap: '20px', 28 | padding: '20px', 29 | justifyContent: 'center' 30 | } 31 | }); -------------------------------------------------------------------------------- /src/app/views/common/banners/Notification.styles.ts: -------------------------------------------------------------------------------- 1 | import { makeStyles } from '@fluentui/react-components'; 2 | import polygons from './bgPolygons.svg'; 3 | 4 | export const useNotificationStyles = makeStyles({ 5 | container: { 6 | padding: '8px', 7 | marginBottom: '8px', 8 | backgroundImage: `url(${polygons})`, 9 | backgroundRepeat: 'no-repeat', 10 | backgroundSize: 'contain', 11 | backgroundPosition: 'right', 12 | whiteSpace: 'wrap', 13 | '&light': { 14 | backgroundColor: '#E8EFFF', 15 | color: '#000000' 16 | }, 17 | '&.dark': { 18 | backgroundColor: '#1D202A', 19 | color: '#ffffff' 20 | } 21 | }, 22 | body: { 23 | width: '100%', 24 | '@media (min-width: 720px)': { 25 | width: '100%' 26 | } 27 | } 28 | }); 29 | -------------------------------------------------------------------------------- /src/app/views/query-runner/request/body/RequestBody.tsx: -------------------------------------------------------------------------------- 1 | import { useAppSelector } from '../../../../../store'; 2 | 3 | import { Monaco } from '../../../common'; 4 | 5 | interface IRequestBodyProps { 6 | handleOnEditorChange: (v: string | undefined) => void; 7 | isVisible: boolean; 8 | } 9 | 10 | const RequestBody = ({ handleOnEditorChange, isVisible }: IRequestBodyProps) => { 11 | const sampleBody = useAppSelector((state) => state.sampleQuery.sampleBody); 12 | 13 | 14 | return ( 15 |
16 | handleOnEditorChange(value)} 19 | height='100%' 20 | isVisible={isVisible} 21 | /> 22 |
23 | ); 24 | }; 25 | 26 | export default RequestBody; 27 | -------------------------------------------------------------------------------- /src/app/utils/open-api-parser.spec.ts: -------------------------------------------------------------------------------- 1 | import { parseOpenApiResponse } from './open-api-parser'; 2 | import { getSample } from './open-api-sample'; 3 | import { parseSampleUrl } from './sample-url-generation'; 4 | 5 | describe('Open api spec parser should', () => { 6 | 7 | it('generate parameters', async () => { 8 | const sampleUrl = 'https://graph.microsoft.com/v1.0/me'; 9 | const { requestUrl } = parseSampleUrl(sampleUrl); 10 | const autoCompleteOptions = getSample(); 11 | 12 | let result = null; 13 | const parameters = { 14 | response: JSON.parse(autoCompleteOptions), 15 | url: requestUrl, 16 | verb: 'GET' 17 | }; 18 | result = parseOpenApiResponse(parameters); 19 | 20 | expect(result.parameters).toBeDefined(); 21 | }); 22 | 23 | }); 24 | 25 | 26 | -------------------------------------------------------------------------------- /__mocks__/@azure/msal-browser: -------------------------------------------------------------------------------- 1 | export class PublicClientApplication { 2 | constructor(configuration: any) { 3 | // Mock constructor 4 | } 5 | 6 | async initialize() { 7 | // Mock implementation of initialize 8 | return Promise.resolve(); 9 | } 10 | 11 | acquireTokenSilent() { 12 | // Mock acquireTokenSilent method 13 | return Promise.resolve({ accessToken: 'mock-access-token' }); 14 | } 15 | 16 | acquireTokenPopup() { 17 | // Mock acquireTokenPopup method 18 | return Promise.resolve({ accessToken: 'mock-access-token' }); 19 | } 20 | 21 | loginPopup() { 22 | // Mock loginPopup method 23 | return Promise.resolve({ account: { username: 'mock-user' } }); 24 | } 25 | 26 | logout() { 27 | // Mock logout method 28 | return Promise.resolve(); 29 | } 30 | } -------------------------------------------------------------------------------- /src/app/services/slices/dimensions.slice.ts: -------------------------------------------------------------------------------- 1 | import { createSlice, PayloadAction } from '@reduxjs/toolkit'; 2 | import { IDimensions } from '../../../types/dimensions'; 3 | 4 | const initialState: IDimensions = { 5 | request: { 6 | width: '100%', 7 | height: '38vh' 8 | }, 9 | response: { 10 | width: '100%', 11 | height: '50vh' 12 | }, 13 | sidebar: { 14 | width: '28%', 15 | height: '' 16 | }, 17 | content: { 18 | width: '72%', 19 | height: '100%' 20 | } 21 | }; 22 | 23 | const dimensions = createSlice({ 24 | name: 'dimensions', 25 | initialState, 26 | reducers: { 27 | setDimensions: (state, action: PayloadAction) => state = action.payload 28 | } 29 | }) 30 | 31 | export const { setDimensions } = dimensions.actions; 32 | export default dimensions.reducer; -------------------------------------------------------------------------------- /src/modules/cache/samples.cache.ts: -------------------------------------------------------------------------------- 1 | import localforage from 'localforage'; 2 | import { ISampleQuery } from '../../types/query-runner'; 3 | 4 | const samplesStorage = localforage.createInstance({ 5 | storeName: 'samples', 6 | name: 'GE_V4' 7 | }); 8 | 9 | const SAMPLE_KEY = 'sample-queries'; 10 | 11 | export const samplesCache = (function () { 12 | 13 | const saveSamples = async (queries: ISampleQuery[]) => { 14 | await samplesStorage.setItem(SAMPLE_KEY, JSON.stringify(queries)); 15 | } 16 | 17 | const readSamples = async (): Promise => { 18 | const items = await samplesStorage.getItem(SAMPLE_KEY) as string; 19 | if (items) { 20 | return JSON.parse(items); 21 | } 22 | return []; 23 | } 24 | 25 | return { 26 | saveSamples, 27 | readSamples 28 | } 29 | })(); -------------------------------------------------------------------------------- /src/app/utils/device-characteristics-telemetry.ts: -------------------------------------------------------------------------------- 1 | export function getBrowserScreenSize(browserWidth: number){ 2 | if (browserWidth >= 2560) { 3 | return 'xxxxl'; 4 | } 5 | if(browserWidth >= 1920){ 6 | return 'xxxl'; 7 | } 8 | else if(browserWidth >= 1366){ 9 | return 'xxl'; 10 | } 11 | else if(browserWidth >= 1024){ 12 | return 'xl'; 13 | } 14 | else if(browserWidth >= 640){ 15 | return 'l'; 16 | } 17 | else if(browserWidth >= 480){ 18 | return 'm'; 19 | } 20 | 21 | return 's'; 22 | } 23 | 24 | export function getDeviceScreenScale(){ 25 | const scaleString = (window.devicePixelRatio * 100).toFixed(0); 26 | 27 | matchMedia(`(resolution: ${scaleString})dppx`).addEventListener('change', 28 | getDeviceScreenScale, {once: true}) 29 | 30 | return `${scaleString}%`; 31 | } -------------------------------------------------------------------------------- /src/app/utils/string-operations.ts: -------------------------------------------------------------------------------- 1 | declare global { 2 | interface String { 3 | toSentenceCase(): string; 4 | contains(searchText: string): boolean; 5 | } 6 | } 7 | 8 | /** 9 | * Converts the first character to uppercase if character is alphanumeric and the rest to lowercase 10 | */ 11 | String.prototype.toSentenceCase = function (): string { 12 | return `${this.charAt(0).toUpperCase()}${this.toLowerCase().slice(1)}`; 13 | }; 14 | 15 | /** 16 | * Performs a case-insenstive search of a substring within a string and 17 | * returns true if searchString appears as a substring of a string 18 | * @param searchString search string 19 | */ 20 | String.prototype.contains = function (searchString: string): boolean { 21 | return this.toLowerCase().includes(searchString.toLowerCase()); 22 | }; 23 | 24 | export {}; 25 | -------------------------------------------------------------------------------- /src/app/views/sidebar/sidebar-utils/SidebarUtils.tsx: -------------------------------------------------------------------------------- 1 | import type { LabelProps } from '@fluentui/react-components'; 2 | import { Label } from '@fluentui/react-components'; 3 | import { translateMessage } from '../../../utils/translate-messages'; 4 | 5 | interface LabelMessage { 6 | message: string 7 | } 8 | export const NoResultsFound = (props: LabelProps & LabelMessage ) => ( 9 | 10 | ); 11 | 12 | export type BadgeColors = 13 | | 'brand' 14 | | 'danger' 15 | | 'important' 16 | | 'informative' 17 | | 'severe' 18 | | 'subtle' 19 | | 'success' 20 | | 'warning'; 21 | 22 | export const METHOD_COLORS: Record = { 23 | GET: 'brand', 24 | POST: 'success', 25 | PATCH: 'severe', 26 | DELETE: 'danger', 27 | PUT: 'warning' 28 | }; -------------------------------------------------------------------------------- /src/app/views/common/dimensions/dimension-adjustment.spec.ts: -------------------------------------------------------------------------------- 1 | import { convertVhToPx, convertPxToVh, getResponseHeight } from './dimensions-adjustment'; 2 | 3 | describe('Tests dimension adjustments', () => { 4 | it('Converts vh to px', () => { 5 | const height = '90vh'; 6 | const adjustment = 10; 7 | const newHeight = convertVhToPx(height, adjustment); 8 | expect(newHeight).toBe('-10px'); 9 | }); 10 | it('Converts px to vh', () => { 11 | const px = 890; 12 | const newHeight = convertPxToVh(px); 13 | expect(newHeight).toBe('115.88541666666667vh'); 14 | }); 15 | it('Gets response height', () => { 16 | const height = '90vh'; 17 | const responseAreaExpanded = true; 18 | const newHeight = getResponseHeight(height, responseAreaExpanded); 19 | expect(newHeight).toBe('90vh'); 20 | }); 21 | }) -------------------------------------------------------------------------------- /src/types/history.ts: -------------------------------------------------------------------------------- 1 | import { Header } from './query-runner'; 2 | 3 | export interface IHistoryItem extends IHistory { 4 | index: number; 5 | } 6 | 7 | interface IHistory { 8 | url: string; 9 | result: object; 10 | method: string; 11 | headers: Header[]; 12 | createdAt: string; 13 | status: number; 14 | statusText: string; 15 | response?: Response; 16 | duration: number; 17 | body?: string; 18 | category?: string; 19 | responseHeaders: { [key: string]: string }; 20 | } 21 | 22 | export interface IHistoryProps { 23 | actions?: { 24 | runQuery: Function; 25 | setSampleQuery: Function; 26 | removeHistoryItem: Function; 27 | viewHistoryItem: Function; 28 | setQueryResponseStatus: Function; 29 | bulkRemoveHistoryItems: Function; 30 | }; 31 | styles?: object; 32 | history: History[]; 33 | } 34 | -------------------------------------------------------------------------------- /src/app/views/common/download.ts: -------------------------------------------------------------------------------- 1 | import { telemetry, eventTypes } from '../../../telemetry'; 2 | 3 | function downloadToLocal(content: any, filename: string) { 4 | const blob = new Blob([JSON.stringify(content, null, 4)], { 5 | type: 'text/json' 6 | }); 7 | download(blob, filename); 8 | } 9 | 10 | function download(blob: Blob, filename: string) { 11 | const elem = window.document.createElement('a'); 12 | elem.href = window.URL.createObjectURL(blob); 13 | elem.download = filename; 14 | document.body.appendChild(elem); 15 | elem.click(); 16 | document.body.removeChild(elem); 17 | } 18 | 19 | function trackDownload(filename: string, componentName: string) { 20 | telemetry.trackEvent(eventTypes.BUTTON_CLICK_EVENT, { 21 | componentName, 22 | filename 23 | }); 24 | } 25 | 26 | export { 27 | downloadToLocal, 28 | trackDownload 29 | }; 30 | -------------------------------------------------------------------------------- /src/app/views/common/monaco/util/format-xml.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-useless-escape */ 2 | /** 3 | * Formats the xml content so that it can be readable. Monaco does not have an inbuilt xml formatter 4 | * @returns string 5 | */ 6 | export function formatXml(xml: string) { 7 | const PADDING = ' '.repeat(2); 8 | const reg = /(>)(<)(\/*)/g; 9 | let pad = 0; 10 | 11 | xml = xml.replace(reg, '$1\r\n$2$3'); 12 | 13 | return xml.split('\r\n').map((node: string) => { 14 | let indent = 0; 15 | if (node.match(/.+<\/\w[^>]*>$/)) { 16 | indent = 0; 17 | } else if (node.match(/^<\/\w/) && pad > 0) { 18 | pad -= 1; 19 | } else if (node.match(/^<\w[^>]*[^\/]>.*$/)) { 20 | indent = 1; 21 | } else { 22 | indent = 0; 23 | } 24 | 25 | pad += indent; 26 | 27 | return PADDING.repeat(pad - indent) + node; 28 | }).join('\r\n'); 29 | } 30 | -------------------------------------------------------------------------------- /src/app/services/actions/devxApi-action-creators.spec.ts: -------------------------------------------------------------------------------- 1 | import { setDevxApiUrl } from '../../../app/services/slices/devxapi.slice'; 2 | import { SET_DEVX_API_URL_SUCCESS } from '../../../app/services/redux-constants'; 3 | import { IDevxAPI } from '../../../types/devx-api'; 4 | 5 | describe('Devx api url', () => { 6 | it('should dispatch SET_DEVX_API_URL_SUCCESS when setDevcApiUrl({IDevxAPI object}) is called', () => { 7 | 8 | // Arrange 9 | const devxApiUrl = new URLSearchParams(location.search).get('devx-api'); 10 | const devxApi: IDevxAPI = { 11 | baseUrl: devxApiUrl!, 12 | parameters: '' 13 | }; 14 | 15 | const expectedActions = 16 | { 17 | type: SET_DEVX_API_URL_SUCCESS, 18 | payload: devxApi 19 | }; 20 | 21 | // Act 22 | const action = setDevxApiUrl(devxApi); 23 | 24 | // Assert 25 | expect(action).toEqual(expectedActions); 26 | }) 27 | }); -------------------------------------------------------------------------------- /src/app/services/actions/terms-of-use-action-creator.spec.ts: -------------------------------------------------------------------------------- 1 | import configureMockStore from 'redux-mock-store'; 2 | 3 | import { PayloadAction } from '@reduxjs/toolkit'; 4 | import { CLEAR_TERMS_OF_USE } from '../../../app/services/redux-constants'; 5 | import { clearTermsOfUse } from '../slices/terms-of-use.slice'; 6 | import { mockThunkMiddleware } from './mockThunkMiddleware'; 7 | 8 | const mockStore = configureMockStore([mockThunkMiddleware]); 9 | 10 | describe('Terms of Use Action Creators', () => { 11 | it('should set terms of use flag to false', () => { 12 | const expectedAction: PayloadAction[] = [ 13 | { 14 | type: CLEAR_TERMS_OF_USE, 15 | payload: undefined 16 | } 17 | ]; 18 | 19 | const store = mockStore({ termsOfUse: {} }); 20 | 21 | store.dispatch(clearTermsOfUse()); 22 | expect(store.getActions()).toEqual(expectedAction); 23 | }); 24 | }); -------------------------------------------------------------------------------- /src/types/auto-complete.ts: -------------------------------------------------------------------------------- 1 | import { IApiResponse } from './action'; 2 | import { IParsedOpenApiResponse } from './open-api'; 3 | 4 | export interface AutoCompleteOption { 5 | url: string; 6 | parameters: any[]; 7 | } 8 | 9 | export interface IAutoCompleteProps { 10 | contentChanged: Function; 11 | runQuery: Function; 12 | } 13 | 14 | export interface IAutoCompleteState { 15 | activeSuggestion: number; 16 | filteredSuggestions: string[]; 17 | suggestions: string[]; 18 | showSuggestions: boolean; 19 | userInput: string; 20 | compare: string; 21 | queryUrl: string; 22 | multiline: boolean; 23 | } 24 | 25 | export interface ISuggestionsList { 26 | activeSuggestion: number; 27 | filteredSuggestions: string[]; 28 | onSuggestionSelected: (suggestion: string) => void; 29 | } 30 | 31 | export interface IAutocompleteResponse extends IApiResponse { 32 | data: IParsedOpenApiResponse | null 33 | } -------------------------------------------------------------------------------- /src/app/services/context/popups-context/reducedPopups.ts: -------------------------------------------------------------------------------- 1 | import { PopupState, PopupAction, POPUPS, initialState } from '.'; 2 | 3 | export function reducedPopups(state: PopupState = initialState, action: PopupAction): PopupState { 4 | const payload = { ...action.payload }; 5 | switch (action.type) { 6 | case POPUPS.ADD_POPUPS: { 7 | let popups = [...state.popups]; 8 | popups = popups.filter(k => k.isOpen); 9 | payload.isOpen = true; 10 | payload.status = 'open'; 11 | return { 12 | ...state, 13 | popups: [...popups, payload] 14 | }; 15 | } 16 | 17 | case POPUPS.DELETE_POPUPS: { 18 | const popups = [...state.popups]; 19 | const index = state.popups.findIndex((e) => e.id === payload.id); 20 | payload.isOpen = false; 21 | popups[index] = payload; 22 | return { 23 | ...state, 24 | popups 25 | }; 26 | } 27 | } 28 | return state; 29 | } 30 | -------------------------------------------------------------------------------- /src/app/services/actions/explorer-mode-action-creator.spec.ts: -------------------------------------------------------------------------------- 1 | import configureMockStore from 'redux-mock-store'; 2 | 3 | import { setGraphExplorerMode } from '../slices/explorer-mode.slice'; 4 | import { SET_GRAPH_EXPLORER_MODE_SUCCESS } from '../redux-constants'; 5 | import { Mode } from '../../../types/enums'; 6 | 7 | const mockStore = configureMockStore(); 8 | 9 | describe('Graph Explorer Mode Action Creators', () => { 10 | it('should dispatch SET_GRAPH_EXPLORER_MODE_SUCCESS when setGraphExplorerMode() is called', () => { 11 | const expectedActions = [ 12 | { 13 | type: SET_GRAPH_EXPLORER_MODE_SUCCESS, 14 | payload: Mode.TryIt 15 | } 16 | ]; 17 | 18 | const store = mockStore({ graphExplorerMode: '' }); 19 | 20 | // @ts-ignore 21 | store.dispatch(setGraphExplorerMode(Mode.TryIt)); 22 | expect(store.getActions()).toEqual(expectedActions); 23 | }); 24 | }); 25 | 26 | -------------------------------------------------------------------------------- /src/app/services/context/popups-context/PopupsContext.tsx: -------------------------------------------------------------------------------- 1 | import { createContext, useContext, useReducer } from 'react'; 2 | 3 | import { PopupState } from '.'; 4 | import { reducedPopups } from './reducedPopups'; 5 | 6 | const PopupStateContext = createContext({ 7 | popups: [] 8 | }); 9 | const PopupDispatchContext = createContext({}); 10 | 11 | export function PopupsProvider({ children }: any) { 12 | const [state, dispatch] = useReducer(reducedPopups, { 13 | popups: [] 14 | }); 15 | 16 | return ( 17 | 18 | 19 | {children} 20 | 21 | 22 | ); 23 | } 24 | 25 | export const usePopupsStateContext = () => useContext(PopupStateContext); 26 | export const usePopupsDispatchContext = () => useContext(PopupDispatchContext); 27 | -------------------------------------------------------------------------------- /src/app/utils/dynamic-sort.ts: -------------------------------------------------------------------------------- 1 | import { SortOrder } from '../../types/enums'; 2 | /** 3 | * Sorts a given array by the passed in property in the direction specified 4 | * @param {string} property the property to sort the array with 5 | * @param {SortOrder} sortOrder the direction to follow Ascending / Descending 6 | * You pass this helper to the array sort function 7 | */ 8 | export function dynamicSort(property: keyof T, sortOrder: SortOrder) { 9 | let order = 1; 10 | if (sortOrder === SortOrder.DESC) { 11 | order = -1; 12 | } 13 | if (property) { 14 | return (first: T, second: T) => { 15 | const result = (first[property] < second[property]) ? -1 : (first[property] > second[property]) ? 1 : 0; 16 | return result * order; 17 | }; 18 | } 19 | return (first: T, second: T) => { 20 | const result = (first < second) ? -1 : (first > second) ? 1 : 0; 21 | return result * order; 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /src/adaptivecards-templates/Insights.Trending.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "AdaptiveCard", 3 | "version": "1.0", 4 | "@odata.type": "#microsoft.graph.trending", 5 | "body": [ 6 | { 7 | "type": "Container", 8 | "$data": "{value}", 9 | "items": [ 10 | { 11 | "type": "TextBlock", 12 | "text": "{resourceVisualization.title}" 13 | }, 14 | { 15 | "type": "TextBlock", 16 | "text": "{resourceVisualization.previewText}" 17 | }, 18 | { 19 | "type": "Image", 20 | "altText": "", 21 | "url": "{resourceVisualization.previewImageUrl}" 22 | } 23 | ], 24 | "separator": true 25 | } 26 | ], 27 | "$schema": "http://adaptivecards.io/schemas/adaptive-card.json" 28 | } -------------------------------------------------------------------------------- /src/app/views/common/monaco/util/format-json.ts: -------------------------------------------------------------------------------- 1 | import { SeverityLevel } from '@microsoft/applicationinsights-web'; 2 | 3 | import { 4 | componentNames, 5 | errorTypes, 6 | telemetry 7 | } from '../../../../../telemetry'; 8 | 9 | export function formatJsonStringForAllBrowsers( 10 | body: string | object | undefined 11 | ) { 12 | /** 13 | * 1. Remove whitespace, tabs or raw string (Safari related issue) 14 | * 2. Convert back to javascript object 15 | * 3. format the string (works for all browsers) 16 | */ 17 | try { 18 | body = JSON.stringify(body); 19 | body = JSON.parse(body); 20 | } catch (error) { 21 | telemetry.trackException( 22 | new Error(errorTypes.OPERATIONAL_ERROR), 23 | SeverityLevel.Error, 24 | { 25 | ComponentName: componentNames.MONACO_EDITOR_FORMAT_JSON_ACTION, 26 | Message: `${error}` 27 | } 28 | ); 29 | } 30 | return JSON.stringify(body, null, 4); 31 | } 32 | -------------------------------------------------------------------------------- /config/jest/fileTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | 5 | // This is a custom Jest transformer turning file imports into filenames. 6 | // http://facebook.github.io/jest/docs/en/webpack.html 7 | 8 | module.exports = { 9 | process(src, filename) { 10 | const assetFilename = JSON.stringify(path.basename(filename)); 11 | 12 | if (filename.match(/\.svg$/)) { 13 | return { 14 | code: `module.exports = { 15 | __esModule: true, 16 | default: ${assetFilename}, 17 | ReactComponent: (props) => ({ 18 | $$typeof: Symbol.for('react.element'), 19 | type: 'svg', 20 | ref: null, 21 | key: null, 22 | props: Object.assign({}, props, { 23 | children: ${assetFilename} 24 | }) 25 | }), 26 | };` 27 | } 28 | } 29 | return { 30 | code: 31 | `module.exports = ${assetFilename};` 32 | }; 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "search.exclude": { 3 | "**/node_modules": true, 4 | "**/all.min.js": true 5 | }, 6 | "files.exclude": { 7 | "**/node_modules": false, 8 | "**/.github": false 9 | }, 10 | "files.trimTrailingWhitespace": true, 11 | "editor.codeActionsOnSave": { 12 | "source.fixAll.eslint": "explicit" 13 | }, 14 | "eslint.format.enable": true, 15 | "editor.formatOnSave": false, 16 | "editor.formatOnPaste": false, 17 | "typescript.updateImportsOnFileMove.enabled": "always", 18 | "eslint.validate": ["typescript", "typescriptreact"], 19 | "explorer.fileNesting.enabled": true, 20 | "explorer.fileNesting.patterns": { 21 | "*.ts": "$(capture).js, $(capture).d.ts, $(capture).spec.ts", 22 | "*.tsx": "$(capture).ts, $(capture).d.ts, $(capture).spec.tsx, $(capture).styles.ts", 23 | "package.json": "package-lock.json, .npmrc" 24 | }, 25 | "cSpell.words": [ 26 | "fluentui", 27 | "noreferrer" 28 | ], 29 | } 30 | -------------------------------------------------------------------------------- /src/app/views/query-response/query-response.scss: -------------------------------------------------------------------------------- 1 | @use "../../../styles/variables"; 2 | 3 | .query-response { 4 | margin-top: $gutter; 5 | border: 1px solid silver; 6 | overflow: hidden; 7 | display: block; 8 | 9 | 10 | .ms-Pivot.root-84 { 11 | width: 100%; 12 | } 13 | 14 | 15 | @media screen and (min-width: 1320px) { 16 | .pivot-response button:nth-last-child(2) { 17 | float: right; 18 | } 19 | .unstyled-pivot button:nth-last-child(2) { 20 | float: none; 21 | } 22 | .adaptive-pivot button:nth-last-child(2) { 23 | float: none; 24 | } 25 | } 26 | 27 | .default-text { 28 | display: flex; 29 | width: 100%; 30 | min-height: 370px; 31 | font-size: 50px; 32 | justify-content: center; 33 | align-items: center; 34 | } 35 | } 36 | 37 | .share-query-params { 38 | font-family: 'monospace', 'courier'; 39 | font-size: 12px; 40 | width: '100%'; 41 | overflow-x: auto; 42 | overflow-y: hidden; 43 | } -------------------------------------------------------------------------------- /src/themes/light.ts: -------------------------------------------------------------------------------- 1 | // Theme generated from https://developer.microsoft.com/en-us/fabric#/styles/themegenerator 2 | 3 | export const light = { 4 | palette: { 5 | themePrimary: '#0078d4', 6 | themeLighterAlt: '#eff6fc', 7 | themeLighter: '#deecf9', 8 | themeLight: '#c7e0f4', 9 | themeTertiary: '#71afe5', 10 | themeSecondary: '#2b88d8', 11 | themeDarkAlt: '#106ebe', 12 | themeDark: '#005a9e', 13 | themeDarker: '#004578', 14 | neutralLighterAlt: '#f8f8f8', 15 | neutralLighter: '#f4f4f4', 16 | neutralLight: '#eaeaea', 17 | neutralQuaternaryAlt: '#dadada', 18 | neutralQuaternary: '#d0d0d0', 19 | neutralTertiaryAlt: '#c8c8c8', 20 | neutralTertiary: '#c2c2c2', 21 | neutralSecondary: '#535353', 22 | neutralPrimaryAlt: '#4b4b4b', 23 | neutralPrimary: '#333333', 24 | neutralDark: '#272727', 25 | black: '#1d1d1d', 26 | white: '#ffffff', 27 | blueMid: '#00188F', 28 | green: '#008000' 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /src/adaptivecards-templates/Profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "AdaptiveCard", 3 | "@odata.type": "#microsoft.graph.user", 4 | "body": [ 5 | { 6 | "type": "TextBlock", 7 | "size": "large", 8 | "weight": "bolder", 9 | "text": "${displayName}" 10 | }, 11 | { 12 | "type": "FactSet", 13 | "facts": [ 14 | { 15 | "title": "Given name", 16 | "value": "${givenName}" 17 | }, 18 | { 19 | "title": "Surname", 20 | "value": "${surname}" 21 | }, 22 | { 23 | "title": "Job title", 24 | "value": "${jobTitle}" 25 | }, 26 | { 27 | "title": "Office location", 28 | "value": "${officeLocation}" 29 | }, 30 | { 31 | "title": "Email", 32 | "value": "${mail}" 33 | } 34 | ] 35 | } 36 | ], 37 | "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", 38 | "version": "1.0" 39 | } 40 | -------------------------------------------------------------------------------- /src/app/services/actions/toggle-sidebar-action-creator.spec.ts: -------------------------------------------------------------------------------- 1 | import configureMockStore from 'redux-mock-store'; 2 | 3 | import { TOGGLE_SIDEBAR_SUCCESS } from '../redux-constants'; 4 | import { toggleSidebar } from '../slices/sidebar-properties.slice'; 5 | 6 | const mockStore = configureMockStore(); 7 | 8 | describe('Toggle Sidebar Action Creators', () => { 9 | it('should dispatch TOGGLE_SIDEBAR_SUCCESS and set sidebar toggle to visible when toggleSidebar() is called', () => { 10 | const expectedActions = [ 11 | { 12 | type: TOGGLE_SIDEBAR_SUCCESS, 13 | payload: { 14 | mobileScreen: true, 15 | showSidebar: false 16 | } 17 | } 18 | ]; 19 | 20 | const store = mockStore({ sidebarProperties: {} }); 21 | 22 | // @ts-ignore 23 | store.dispatch(toggleSidebar({ 24 | mobileScreen: true, 25 | showSidebar: false 26 | })) 27 | expect(store.getActions()).toEqual(expectedActions); 28 | }); 29 | }); 30 | 31 | -------------------------------------------------------------------------------- /src/app/services/slices/query-status.slice.ts: -------------------------------------------------------------------------------- 1 | import { createSlice, PayloadAction } from '@reduxjs/toolkit'; 2 | 3 | import { IStatus } from '../../../types/status'; 4 | import { LOGOUT_SUCCESS, QUERY_GRAPH_RUNNING } from '../redux-constants'; 5 | 6 | const queryRunnerStatusSlice = createSlice({ 7 | name: 'queryRunnerStatus', 8 | initialState: null as IStatus | null, 9 | reducers: { 10 | setQueryResponseStatus: (_state, action: PayloadAction) => { 11 | return action.payload; 12 | }, 13 | clearQueryStatus: () => { 14 | return null; 15 | } 16 | }, 17 | extraReducers: (builder) => { 18 | builder.addCase(QUERY_GRAPH_RUNNING, () => { 19 | return null; 20 | }); 21 | builder.addCase(LOGOUT_SUCCESS, () => { 22 | return null; 23 | }); 24 | } 25 | }); 26 | 27 | export const { setQueryResponseStatus, clearQueryStatus } = queryRunnerStatusSlice.actions; 28 | export default queryRunnerStatusSlice.reducer; 29 | -------------------------------------------------------------------------------- /src/app/views/query-runner/request/feedback/uiStrings.ts: -------------------------------------------------------------------------------- 1 | import { translateMessage } from '../../../../utils/translate-messages'; 2 | 3 | export const uiStringMap: Record = { 4 | Prompt_Title: translateMessage('love your feedback'), 5 | Prompt_Question: translateMessage('We have just two questions.'), 6 | Prompt_YesLabel: translateMessage('Sure'), 7 | Prompt_NoLabel: translateMessage('Sure'), 8 | Graph_Explorer_Rating_Question: translateMessage('Graph Rating Question'), 9 | Rating_Values_0: `0 - ${translateMessage('Not at all likely')}`, 10 | Rating_Values_1: '1', 11 | Rating_Values_2: '2', 12 | Rating_Values_3: '3', 13 | Rating_Values_4: '4', 14 | Rating_Values_5: '5', 15 | Rating_Values_6: '6', 16 | Rating_Values_7: '7', 17 | Rating_Values_8: '8', 18 | Rating_Values_9: '9', 19 | Rating_Values_10: `10 - ${translateMessage('Extremely likely')}`, 20 | Question_Question: translateMessage('Please tell us more. Why did you choose that answer') 21 | } 22 | -------------------------------------------------------------------------------- /src/app/utils/device-characteristics-telemetry.spec.ts: -------------------------------------------------------------------------------- 1 | import { getDeviceScreenScale, getBrowserScreenSize } from './device-characteristics-telemetry' 2 | 3 | Object.defineProperty(window, 'matchMedia', { 4 | writable: true, 5 | value: jest.fn().mockImplementation(query => ({ 6 | matches: false, 7 | media: query, 8 | onchange: null, 9 | addListener: jest.fn(), // Deprecated 10 | removeListener: jest.fn(), // Deprecated 11 | addEventListener: jest.fn(), 12 | removeEventListener: jest.fn(), 13 | dispatchEvent: jest.fn() 14 | })) 15 | }); 16 | describe('Device Telemetry', () => { 17 | it('should get device screen scale', () => { 18 | const percentage = '%'; 19 | expect(getDeviceScreenScale()).toContain(percentage); 20 | }); 21 | 22 | it('should get device screen size', () => { 23 | const deviceWidth = 1367; 24 | const expectedScreenSize = 'xxl'; 25 | expect(getBrowserScreenSize(deviceWidth)).toBe(expectedScreenSize); 26 | }); 27 | }) 28 | 29 | -------------------------------------------------------------------------------- /src/tests/ui/login.ts: -------------------------------------------------------------------------------- 1 | import { expect } from '@playwright/test'; 2 | require('dotenv').config(); 3 | 4 | const PLAYWRIGHT_TESTS_USERNAME = process.env.PLAYWRIGHT_TESTS_USERNAME || ''; 5 | const PLAYWRIGHT_TESTS_PASSWORD = process.env.PLAYWRIGHT_TESTS_PASSWORD || ''; 6 | 7 | export const logIn = async (page: any) => { 8 | 9 | await page.goto('/'); 10 | 11 | const [popup] = await Promise.all([ 12 | page.waitForEvent('popup'), 13 | page.locator('[aria-label="Sign in"]').click() 14 | ]); 15 | 16 | await popup.locator('input[name="loginfmt"]').fill(PLAYWRIGHT_TESTS_USERNAME); 17 | await popup.locator('text=Next').click(); 18 | 19 | await popup.locator('[placeholder="Password"]').fill(PLAYWRIGHT_TESTS_PASSWORD); 20 | await popup.locator('text=Sign in').click(); 21 | 22 | await expect(popup).toHaveURL('https://login.microsoftonline.com/common/login'); 23 | const finalStep = popup.locator('text=Yes'); 24 | if(finalStep.isVisible()) { 25 | await finalStep.click(); 26 | } 27 | }; -------------------------------------------------------------------------------- /src/modules/authentication/authUtils.ts: -------------------------------------------------------------------------------- 1 | import { LoginType } from '../../types/enums'; 2 | 3 | /** 4 | * Returns whether to load the POPUP/REDIRECT interaction 5 | * @returns string 6 | */ 7 | export function getLoginType(): LoginType { 8 | const userAgent = window.navigator.userAgent; 9 | const msie = userAgent.indexOf('MSIE '); 10 | const msie11 = userAgent.indexOf('Trident/'); 11 | const msedge = userAgent.indexOf('Edge/'); 12 | const isIE = msie > 0 || msie11 > 0; 13 | const isEdge = msedge > 0; 14 | return isIE || isEdge ? LoginType.Redirect : LoginType.Popup; 15 | } 16 | 17 | /** 18 | * get current uri for redirect uri purpose 19 | * ref - https://github.com/AzureAD/microsoft-authentication-library-for 20 | * -js/blob/9274fac6d100a6300eb2faa4c94aa2431b1ca4b0/lib/msal-browser/src/utils/BrowserUtils.ts#L49 21 | */ 22 | export function getCurrentUri(): string { 23 | const currentUrl = window.location.href.split('?')[0].split('#')[0]; 24 | return currentUrl.toLowerCase(); 25 | } 26 | -------------------------------------------------------------------------------- /src/app/services/actions/profile-action-creators.spec.ts: -------------------------------------------------------------------------------- 1 | import configureMockStore from 'redux-mock-store'; 2 | 3 | import { PROFILE_REQUEST_ERROR } from '../redux-constants'; 4 | import { mockThunkMiddleware } from './mockThunkMiddleware'; 5 | import { getProfileInfo } from '../slices/profile.slice'; 6 | 7 | const mockStore = configureMockStore([mockThunkMiddleware]); 8 | 9 | describe('Profile action creators', () => { 10 | beforeEach(() => { 11 | fetchMock.resetMocks(); 12 | }); 13 | 14 | it('should dispatch PROFILE_REQUEST_ERROR when getProfileInfo() request fails', () => { 15 | fetchMock.mockResponseOnce(JSON.stringify({ ok: false })); 16 | const store = mockStore({}); 17 | // @ts-ignore 18 | return store.dispatch(getProfileInfo()) 19 | .then(() => { 20 | const includesError = store.getActions().filter(k => k.type === PROFILE_REQUEST_ERROR) 21 | expect(!!includesError).toEqual(true); 22 | }) 23 | .catch((e: Error) => { throw e }) 24 | }); 25 | }); -------------------------------------------------------------------------------- /src/app/utils/fetch-retry-handler.ts: -------------------------------------------------------------------------------- 1 | export async function exponentialFetchRetry( fn: () => Promise, retriesLeft: number, 2 | interval: number, condition?: (result: T, retriesLeft?: number) => Promise 3 | ): Promise { 4 | try { 5 | const result = await fn(); 6 | if (condition) { 7 | const isConditionSatisfied = await condition(result, retriesLeft); 8 | if(isConditionSatisfied){ 9 | throw new Error('An error occurred during the execution of the request'); 10 | } 11 | } 12 | if (result && result instanceof Response && result.status && result.status >= 500){ 13 | throw new Error('Encountered a server error during execution of the request'); 14 | } 15 | return result; 16 | } catch (error: unknown) { 17 | if (retriesLeft === 1) { 18 | throw error; 19 | } 20 | await new Promise((resolve) => setTimeout(resolve, interval)); 21 | return exponentialFetchRetry(fn, retriesLeft - 1, interval * 2, condition); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /.github/workflows/english-file-transfer.yml: -------------------------------------------------------------------------------- 1 | name: English File transfer 2 | 3 | on: 4 | push: 5 | branches: 6 | - dev 7 | paths: 8 | - 'src/messages/GE.json' 9 | 10 | jobs: 11 | copy-file: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v4 16 | 17 | - name: Set current date as env variable 18 | run: echo "today=$(date +%Y%m%d%H%M)" >> $GITHUB_ENV 19 | 20 | - name: Pushes English file 21 | uses: dmnemec/copy_file_to_another_repo_action@main 22 | env: 23 | API_TOKEN_GITHUB: ${{ secrets.API_TOKEN_GITHUB }} 24 | with: 25 | source_file: 'src/messages/GE.json' 26 | destination_repo: 'microsoftgraph/microsoft-graph-devx-content' 27 | destination_folder: 'messages' 28 | destination_branch: 'dev' 29 | user_email: ${{ secrets.ACTION_EMAIL }} 30 | user_name: ${{ secrets.ACTION_USERNAME }} 31 | commit_message: ${{ env.today }} English file transfer 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /src/app/utils/snippet.utils.ts: -------------------------------------------------------------------------------- 1 | import { Header, IQuery } from '../../types/query-runner'; 2 | 3 | function constructHeaderString(sampleQuery: IQuery): string { 4 | const { sampleHeaders, selectedVerb } = sampleQuery; 5 | let headersString = ''; 6 | 7 | const isContentTypeInHeaders: boolean = !!sampleHeaders.find( 8 | (header) => header.name.toLocaleLowerCase() === 'content-type' 9 | ); 10 | 11 | if (sampleHeaders && sampleHeaders.length > 0) { 12 | headersString = getHeaderStringProperties(sampleHeaders); 13 | } 14 | 15 | headersString += 16 | !isContentTypeInHeaders && selectedVerb !== 'GET' 17 | ? 'Content-Type: application/json\r\n' 18 | : ''; 19 | return headersString; 20 | } 21 | 22 | function getHeaderStringProperties(sampleHeaders: Header[]): string { 23 | let constructedHeader = ''; 24 | sampleHeaders.forEach((header: Header) => { 25 | constructedHeader += `${header.name}: ${header.value}\r\n`; 26 | }); 27 | return constructedHeader; 28 | } 29 | 30 | export { constructHeaderString }; -------------------------------------------------------------------------------- /src/types/query-response.ts: -------------------------------------------------------------------------------- 1 | import { Person, ResponseType, User } from '@microsoft/microsoft-graph-types'; 2 | import { ContentType, Mode } from './enums'; 3 | import { IQuery } from './query-runner'; 4 | 5 | export interface IQueryResponseProps { 6 | mode: Mode; 7 | dispatch: Function; 8 | graphResponse?: IGraphResponse; 9 | verb: string; 10 | theme: string; 11 | scopes: string[]; 12 | sampleQuery: IQuery; 13 | actions: { 14 | getConsent: Function; 15 | }; 16 | mobileScreen: boolean; 17 | } 18 | 19 | export interface IGraphResponse { 20 | isLoadingData: boolean; 21 | response: { 22 | body: ResponseBody; 23 | headers: Record; 24 | } 25 | } 26 | 27 | export interface ResponseValue { 28 | id: string 29 | } 30 | 31 | export interface CustomBody { 32 | throwsCorsError: boolean, 33 | contentDownloadUrl: string, 34 | error: Error, 35 | value: Partial[] | undefined 36 | 37 | } 38 | export type ResponseBody = Partial | string | object | null | undefined; 39 | -------------------------------------------------------------------------------- /src/types/request.ts: -------------------------------------------------------------------------------- 1 | import { IDimensions } from './dimensions'; 2 | import { Mode } from './enums'; 3 | import { Header, IQuery } from './query-runner'; 4 | 5 | export interface IHeadersListControl { 6 | handleOnHeaderDelete: Function; 7 | headers?: Header[]; 8 | handleOnHeaderEdit: Function; 9 | } 10 | 11 | export interface IRequestHeadersProps { 12 | sampleQuery: IQuery; 13 | height: string; 14 | actions?: { 15 | setSampleQuery: Function; 16 | }; 17 | } 18 | 19 | export interface IRequestComponent { 20 | sampleQuery: IQuery; 21 | mode: Mode; 22 | handleOnEditorChange: Function; 23 | dimensions: IDimensions; 24 | headers?: Header[]; 25 | actions: { 26 | setDimensions: Function; 27 | }; 28 | officeBrowserFeedback: any; 29 | enableShowSurvey: boolean; 30 | } 31 | 32 | export interface IRequestOptions { 33 | headers?: {}; 34 | method?: string; 35 | body?: string; 36 | } 37 | 38 | export interface IRequestState { 39 | headers: Header[]; 40 | headerName: string; 41 | headerValue: string; 42 | } 43 | -------------------------------------------------------------------------------- /src/app/services/actions/query-status-action-creator.spec.ts: -------------------------------------------------------------------------------- 1 | import configureMockStore from 'redux-mock-store'; 2 | 3 | import { QUERY_GRAPH_STATUS } from '../../../app/services/redux-constants'; 4 | import { setQueryResponseStatus } from '../../../app/services/slices/query-status.slice'; 5 | 6 | const mockStore = configureMockStore([]); 7 | 8 | describe('Query Action Creators', () => { 9 | it('should dispatch QUERY_GRAPH_STATUS when setQueryResponseStatus() is called', () => { 10 | // Arrange 11 | const payload = { 12 | ok: false, 13 | statusText: 'Something worked!', 14 | status: 200, 15 | messageBarType: 'success', 16 | hint: 'Something worked!' 17 | } 18 | 19 | const expectedAction = { 20 | type: QUERY_GRAPH_STATUS, 21 | payload 22 | } 23 | 24 | // Act 25 | const store = mockStore({ queryRunnerStatus: null, auth: {} }); 26 | store.dispatch(setQueryResponseStatus(payload)); 27 | 28 | // Assert 29 | expect(store.getActions()).toEqual([expectedAction]); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /src/app/views/common/monaco/util/format-json.spec.ts: -------------------------------------------------------------------------------- 1 | import { formatJsonStringForAllBrowsers } from './format-json'; 2 | import { formatXml } from './format-xml'; 3 | 4 | describe('Tests json strings formatting in monaco editor ', () => { 5 | it('Tests json strings formatting in monaco editor', () => { 6 | const body = { 7 | name: 'Megan', 8 | surname: 'Bowen' 9 | } 10 | const formattedBody = formatJsonStringForAllBrowsers(body); 11 | expect(isJSON(formattedBody)).toBe(true); 12 | }) 13 | 14 | it('Tests the xml formatter function', () => { 15 | // Arrange 16 | const xml = 'Hello'; 17 | // Act 18 | const formattedXml = formatXml(xml); 19 | // Assert 20 | expect(formattedXml).toBeDefined(); 21 | }) 22 | }) 23 | 24 | const isJSON = (str: string) => { 25 | try { 26 | const json = JSON.parse(str); 27 | if (Object.prototype.toString.call(json).slice(8, -1) !== 'Object') { 28 | return false 29 | } 30 | } catch (e) { 31 | return false 32 | } 33 | return true 34 | } -------------------------------------------------------------------------------- /src/app/views/main-header/Tenant.tsx: -------------------------------------------------------------------------------- 1 | import { CompoundButton, Tooltip } from '@fluentui/react-components' 2 | import { GlobeSurface24Regular } from '@fluentui/react-icons' 3 | import { useAppSelector } from '../../../store' 4 | import { translateMessage } from '../../utils/translate-messages' 5 | import { useHeaderStyles } from './utils' 6 | 7 | const Tenant = ()=>{ 8 | const styles = useHeaderStyles() 9 | const user = useAppSelector(state => state.profile.user) 10 | const secondaryContent = user?.tenant ?? 'Sample' 11 | const tooltipContent = user?.tenant 12 | ?? `${translateMessage('Using demo tenant')} ${translateMessage('To access your own data:')}` 13 | 14 | return ( 15 | 16 | } 20 | secondaryContent={secondaryContent}> 21 | Tenant 22 | 23 | 24 | ) 25 | } 26 | 27 | export { Tenant } 28 | 29 | -------------------------------------------------------------------------------- /src/app/services/actions/query-input-action-creators.spec.ts: -------------------------------------------------------------------------------- 1 | import configureMockStore from 'redux-mock-store'; 2 | 3 | import { setSampleQuery } from '../slices/sample-query.slice'; 4 | import { SET_SAMPLE_QUERY_SUCCESS } from '../redux-constants'; 5 | 6 | const mockStore = configureMockStore(); 7 | 8 | describe('Query input action creators should', () => { 9 | beforeEach(() => { 10 | fetchMock.resetMocks(); 11 | }); 12 | 13 | it('dispatch SET_SAMPLE_QUERY_SUCCESS when setSampleQuery is called', () => { 14 | const expectedActions = [ 15 | { 16 | type: SET_SAMPLE_QUERY_SUCCESS, 17 | payload: { 18 | selectedVerb: 'GET', 19 | sampleUrl: 'https://graph.microsoft.com/v1.0/me/' 20 | } 21 | } 22 | ]; 23 | 24 | const store = mockStore({ sampleQuery: '' }); 25 | 26 | // @ts-ignore 27 | store.dispatch(setSampleQuery({ 28 | selectedVerb: 'GET', 29 | sampleUrl: 'https://graph.microsoft.com/v1.0/me/' 30 | })); 31 | expect(store.getActions()).toEqual(expectedActions); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. 3 | // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp 4 | 5 | // List of extensions which should be recommended for users of this workspace. 6 | "recommendations": [ 7 | "formulahendry.auto-close-tag", 8 | "atishay-jain.all-autocomplete", 9 | "github.vscode-pull-request-github", 10 | "ionutvmi.path-autocomplete", 11 | "dsznajder.es7-react-js-snippets", 12 | "formulahendry.auto-rename-tag", 13 | "dbaeumer.vscode-eslint", 14 | "techer.open-in-browser", 15 | "igor-uzhviev.jwt-decoder", 16 | "kisstkondoros.vscode-codemetrics", 17 | "naumovs.color-highlight", 18 | "ms-vscode.sublime-keybindings", 19 | "redhat.vscode-yaml", 20 | "redhat.vscode-commons", 21 | "eamodio.gitlens", 22 | "ms-playwright.playwright", 23 | "nitayneeman.playwright-snippets" 24 | ], 25 | // List of extensions recommended by VS Code that should not be recommended for users of this workspace. 26 | "unwantedRecommendations": [ 27 | 28 | ] 29 | } -------------------------------------------------------------------------------- /src/app/views/query-response/headers/ResponseHeaders.tsx: -------------------------------------------------------------------------------- 1 | import { RESPONSE_HEADERS_COPY_BUTTON } from '../../../../telemetry/component-names'; 2 | 3 | import { useAppSelector } from '../../../../store'; 4 | import { Monaco } from '../../common'; 5 | import { trackedGenericCopy } from '../../common/copy'; 6 | import { CopyButton } from '../../common/lazy-loader/component-registry'; 7 | 8 | const ResponseHeaders = () => { 9 | const graphResponse = useAppSelector((state) => state.graphResponse); 10 | 11 | const sampleQuery = useAppSelector((state) => state.sampleQuery); 12 | const { headers } = graphResponse.response; 13 | const handleCopy = async () => 14 | trackedGenericCopy( 15 | JSON.stringify(headers), 16 | RESPONSE_HEADERS_COPY_BUTTON, 17 | sampleQuery 18 | ); 19 | 20 | if (headers) { 21 | return ( 22 | <> 23 | 24 | 25 | 26 | ); 27 | } 28 | 29 | return
; 30 | }; 31 | 32 | export default ResponseHeaders; 33 | -------------------------------------------------------------------------------- /src/app/views/sidebar/resource-explorer/resourceLinkStyles.ts: -------------------------------------------------------------------------------- 1 | import { makeStyles } from '@fluentui/react-components'; 2 | import { tokens } from '@fluentui/react-theme'; 3 | 4 | export const useStyles = makeStyles({ 5 | link: { 6 | display: 'flex', 7 | lineHeight: tokens.lineHeightBase300, 8 | justifyContent: 'space-between', 9 | alignItems: 'center', 10 | marginRight: tokens.spacingHorizontalS, 11 | position: 'relative' 12 | }, 13 | resourceLinkNameContainer: { 14 | textAlign: 'left', 15 | flex: '1', 16 | overflow: 'hidden', 17 | display: 'flex', 18 | paddingLeft: tokens.spacingHorizontalXXS 19 | }, 20 | resourceLinkText: { 21 | textOverflow: 'ellipsis', 22 | overflow: 'hidden', 23 | whiteSpace: 'nowrap' 24 | }, 25 | badge: { 26 | maxWidth: '50px' 27 | }, 28 | linkIcon: { 29 | display: 'flex', 30 | '&:hover': { 31 | color: tokens.colorBrandForegroundLinkHover 32 | } 33 | } 34 | }); 35 | 36 | export const useIconButtonStyles = makeStyles({ 37 | root: { 38 | marginRight: tokens.spacingHorizontalXXS 39 | } 40 | }); 41 | -------------------------------------------------------------------------------- /src/app/services/actions/dimensions-action-creator.spec.ts: -------------------------------------------------------------------------------- 1 | import { setDimensions } from '../../../app/services/slices/dimensions.slice'; 2 | import { RESIZE_SUCCESS } from '../../../app/services/redux-constants'; 3 | import { IDimensions } from '../../../types/dimensions'; 4 | 5 | describe('Dimensions setting on GE', () => { 6 | it('should dispatch RESIZE_SUCCESS when setDimensions is called with new dimensions', () => { 7 | // Arrange 8 | const dimensions: IDimensions = { 9 | request: { 10 | width: '100%', 11 | height: '36vh' 12 | }, 13 | response: { 14 | width: '100%', 15 | height: '46vh' 16 | }, 17 | sidebar: { 18 | width: '100%', 19 | height: '46vh' 20 | }, 21 | content: { 22 | width: '100%', 23 | height: '46vh' 24 | } 25 | } 26 | 27 | const expectedActions = { 28 | type: RESIZE_SUCCESS, 29 | payload: dimensions 30 | } 31 | 32 | // Act 33 | const action = setDimensions(dimensions); 34 | 35 | // Assert 36 | expect(action).toEqual(expectedActions); 37 | }) 38 | }); -------------------------------------------------------------------------------- /src/app/services/slices/history.slice.ts: -------------------------------------------------------------------------------- 1 | import { createSlice, PayloadAction } from '@reduxjs/toolkit'; 2 | 3 | import { IHistoryItem } from '../../../types/history'; 4 | 5 | const initialState: IHistoryItem[] = []; 6 | 7 | const historySlice = createSlice({ 8 | name: 'history', 9 | initialState, 10 | reducers: { 11 | addHistoryItem(state, action: PayloadAction) { 12 | state.push(action.payload); 13 | }, 14 | bulkAddHistoryItems(state, action: PayloadAction) { 15 | state.push(...action.payload); 16 | }, 17 | removeHistoryItem(state, action: PayloadAction) { 18 | return state.filter(item => item.createdAt !== action.payload.createdAt); 19 | }, 20 | removeAllHistoryItems(state, action: PayloadAction) { 21 | return state.filter(item => !action.payload.includes(item.createdAt)); 22 | } 23 | } 24 | }); 25 | 26 | export const { 27 | addHistoryItem, 28 | bulkAddHistoryItems, 29 | removeHistoryItem, 30 | removeAllHistoryItems 31 | } = historySlice.actions; 32 | 33 | export default historySlice.reducer; 34 | -------------------------------------------------------------------------------- /src/app/views/common/message-display/JSXBuilder.tsx: -------------------------------------------------------------------------------- 1 | import { Link } from '@fluentui/react-components'; 2 | import { Fragment } from 'react'; 3 | 4 | export class JSXBuilder { 5 | private elements: JSX.Element[] = []; 6 | 7 | addText(text: string) { 8 | this.elements.push( 9 | {text} 10 | ); 11 | return this; 12 | } 13 | 14 | addLink({ label, url, onClick }: { label: string; url: string; onClick?: (url: string) => void; }) { 15 | this.elements.push( 16 | onClick(url) : undefined} 19 | target={!onClick ? '_blank' : undefined} 20 | href={!onClick ? url : undefined} 21 | > 22 | {label} 23 | 24 | ); 25 | return this; 26 | } 27 | 28 | addBoldText(text: string) { 29 | this.elements.push( 30 | 31 | {text} 32 | 33 | ); 34 | return this; 35 | } 36 | 37 | build() { 38 | return <>{this.elements}; 39 | } 40 | } -------------------------------------------------------------------------------- /src/app/views/common/dimensions/dimensions-adjustment.ts: -------------------------------------------------------------------------------- 1 | export function convertVhToPx(height: string, adjustment: number) { 2 | const newHeight = parseFloat(height.replace('vh', '')) / 100 3 | * document.documentElement.clientHeight; 4 | return Math.floor(newHeight - adjustment) + 'px'; 5 | } 6 | 7 | export function convertPxToVh(px: number){ 8 | const innerHeight = window.innerHeight; 9 | const convertedHeight = (100*px)/innerHeight; 10 | return convertedHeight+ 'vh'; 11 | } 12 | 13 | export function getResponseHeight(height: string, responseAreaExpanded: boolean) { 14 | let responseHeight = height; 15 | if (responseAreaExpanded) { 16 | responseHeight = '90vh'; 17 | } 18 | return responseHeight; 19 | } 20 | 21 | export function getResponseEditorHeight(adjustment: number){ 22 | const queryResponseElement = document.getElementsByClassName('query-response')[0]; 23 | let responseHeight = ''; 24 | if(queryResponseElement){ 25 | const queryResponseElementHeight = queryResponseElement?.clientHeight; 26 | responseHeight = `${queryResponseElementHeight-adjustment}px` 27 | } 28 | return responseHeight; 29 | } -------------------------------------------------------------------------------- /src/app/views/common/lazy-loader/component-registry/popups.tsx: -------------------------------------------------------------------------------- 1 | import { lazy } from 'react'; 2 | 3 | export const popups = new Map([ 4 | ['share-query', lazy(() => import('../../../query-runner/query-input/share-query/ShareQuery'))], 5 | ['theme-chooser', lazy(() => import('../../../main-header/settings/ThemeChooser'))], 6 | ['preview-collection', lazy(() => import('../../../sidebar/resource-explorer/collection/APICollection'))], 7 | ['full-permissions', lazy(() => import('../../../query-runner/request/permissions/Permissions.Full'))], 8 | ['collection-permissions', lazy(() => import('../../../sidebar/resource-explorer/collection/CollectionPermissions'))], 9 | ['edit-collection-panel', lazy(() => import('../../../sidebar/resource-explorer/collection/EditCollectionPanel'))], 10 | ['edit-scope-panel', lazy(() => import('../../../sidebar/resource-explorer/collection/EditScopePanel'))] 11 | 12 | ]); 13 | 14 | export type PopupItem = 15 | 'share-query' | 16 | 'theme-chooser' | 17 | 'preview-collection' | 18 | 'full-permissions' | 19 | 'collection-permissions' | 20 | 'edit-collection-panel' | 21 | 'edit-scope-panel' -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 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 | -------------------------------------------------------------------------------- /src/app/views/query-runner/request/permissions/util.ts: -------------------------------------------------------------------------------- 1 | import { IPermission } from '../../../../../types/permissions'; 2 | 3 | export function setConsentedStatus(tokenPresent: boolean, 4 | permissions: IPermission[], consentedScopes: string[]): IPermission[] { 5 | if (tokenPresent && permissions && permissions.length > 0) { 6 | const clonedPermissions = JSON.parse(JSON.stringify(permissions)) as IPermission[]; 7 | clonedPermissions.forEach((permission: IPermission) => { 8 | permission.consented = (consentedScopes && consentedScopes.length > 0 && 9 | consentedScopes.indexOf(permission.value) !== -1); 10 | }); 11 | return clonedPermissions; 12 | } 13 | return permissions; 14 | } 15 | 16 | export function sortPermissionsWithPrivilege(permissionsToSort: IPermission[]) { 17 | const leastPrivileged = permissionsToSort.find(permission => permission.isLeastPrivilege === true); 18 | if (leastPrivileged) { 19 | const permissions: IPermission[] = permissionsToSort.filter(k => k.value !== leastPrivileged.value); 20 | permissions.unshift(leastPrivileged); 21 | return permissions; 22 | } 23 | return permissionsToSort; 24 | } -------------------------------------------------------------------------------- /src/app/utils/token-helpers.spec.ts: -------------------------------------------------------------------------------- 1 | import { getTokenSubstituteValue, substituteTokens } from '../../app/utils/token-helpers'; 2 | import { IQuery } from '../../types/query-runner'; 3 | 4 | describe('Tests token helper utils', () => { 5 | const token = { 6 | placeholder: 'testHolder' 7 | }; 8 | const isAuthenticated = true; 9 | const profile = { 10 | tenant: { 11 | id: 'tenantId', 12 | name: 'tenantName' 13 | }, 14 | user: { 15 | id: 'userId', 16 | name: 'userName' 17 | } 18 | }; 19 | it('should return the value of the token', () => { 20 | const value = getTokenSubstituteValue(token, isAuthenticated); 21 | expect(value).toBeUndefined(); 22 | 23 | const unAuthenticatedValue = getTokenSubstituteValue(token, false); 24 | expect(unAuthenticatedValue).toBeUndefined(); 25 | }); 26 | it('should substitute the token with the correct value', () => { 27 | const query: IQuery = { 28 | selectedVerb: 'GET', 29 | sampleUrl: '/v1.0/me', 30 | selectedVersion: 'v1.0', 31 | sampleBody: '', 32 | sampleHeaders: [] 33 | }; 34 | substituteTokens(query, profile); 35 | }); 36 | }) -------------------------------------------------------------------------------- /src/app/utils/graph-toolkit-lookup.ts: -------------------------------------------------------------------------------- 1 | import templates from '../../graph-toolkit-examples'; 2 | import { IQuery } from '../../types/query-runner'; 3 | import { GRAPH_TOOOLKIT_EXAMPLE_URL } from '../services/graph-constants'; 4 | import { parseSampleUrl } from './sample-url-generation'; 5 | 6 | export function lookupToolkitUrl(sampleQuery: IQuery) { 7 | if (sampleQuery) { 8 | const { requestUrl, search } = parseSampleUrl(sampleQuery.sampleUrl); 9 | const query = '/' + requestUrl + search; 10 | for (const templateMapKey in templates) { 11 | if (Object.prototype.hasOwnProperty.call(templates, templateMapKey)) { 12 | const isMatch = new RegExp(templateMapKey + '$', 'i').test(query); 13 | if (isMatch) { 14 | const url: string = (templates as any)[templateMapKey]; 15 | let { search: componentUrl } = parseSampleUrl(url); 16 | componentUrl = componentUrl.replace('?id=', ''); 17 | return { 18 | exampleUrl: `${GRAPH_TOOOLKIT_EXAMPLE_URL}/${componentUrl}`, 19 | toolkitUrl: url 20 | }; 21 | } 22 | } 23 | } 24 | } 25 | return { toolkitUrl: null, exampleUrl: null }; 26 | } 27 | -------------------------------------------------------------------------------- /playwright.config.ts: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | import { PlaywrightTestConfig } from '@playwright/test'; 3 | 4 | const baseURL = process.env.PLAYWRIGHT_TESTS_BASE_URL!; 5 | 6 | const config: PlaywrightTestConfig = { 7 | globalSetup: require.resolve('./src/tests/ui/global-setup'), 8 | expect: { 9 | toMatchSnapshot: { 10 | threshold: 0.3, 11 | maxDiffPixelRatio: 0.05 12 | } 13 | }, 14 | use: { 15 | baseURL, 16 | trace: 'on-first-retry', 17 | headless: !!process.env.CI, 18 | ignoreHTTPSErrors: true, 19 | screenshot: 'only-on-failure' 20 | }, 21 | testDir: './src/tests', 22 | testIgnore: '**/authenticated-experiences/**', 23 | reporter: [ 24 | [ 25 | 'html', 26 | { outputFolder: 'playwright-report' } 27 | ] 28 | ], 29 | retries: 1, 30 | timeout: 60000, 31 | projects: [ 32 | { 33 | name: 'Ms-Edge', 34 | use: { 35 | channel: 'msedge', 36 | viewport: { width: 1920, height: 1080 }} 37 | }, 38 | { 39 | name: 'Chrome', 40 | use: { 41 | channel: 'chrome', 42 | viewport: { width: 1365, height: 768 }} 43 | } 44 | ] 45 | }; 46 | export default config; -------------------------------------------------------------------------------- /src/app/views/common/error-boundary/ErrorBoundary.tsx: -------------------------------------------------------------------------------- 1 | import { Label } from '@fluentui/react-components'; 2 | import React, { ReactNode, useState, useEffect } from 'react'; 3 | import { translateMessage } from '../../../utils/translate-messages'; 4 | 5 | interface Props { 6 | children: ReactNode; 7 | } 8 | 9 | interface State { 10 | hasError: boolean; 11 | } 12 | 13 | interface ErrorBoundaryChildProps { 14 | onError: () => void; 15 | } 16 | 17 | function ErrorBoundary(props: Props) { 18 | const [state, setState] = useState({ hasError: false }); 19 | 20 | useEffect(() => { 21 | setState({ hasError: false }); 22 | }, [props.children]); 23 | 24 | const handleError = () => { 25 | setState({ hasError: true }); 26 | }; 27 | 28 | if (state.hasError) { 29 | return ; 30 | } else { 31 | return ( 32 | 33 | {React.Children.map(props.children, (child) => { 34 | return React.cloneElement(child as React.ReactElement, { onError: handleError }); 35 | })} 36 | 37 | ); 38 | } 39 | } 40 | 41 | export default ErrorBoundary; 42 | -------------------------------------------------------------------------------- /src/modules/cache/samples.cache.spec.ts: -------------------------------------------------------------------------------- 1 | import { ISampleQuery } from '../../types/query-runner'; 2 | import { samplesCache } from './samples.cache'; 3 | 4 | jest.mock('localforage', () => ({ 5 | // eslint-disable-next-line @typescript-eslint/no-empty-function 6 | config: () => { }, 7 | createInstance: () => ({ 8 | // eslint-disable-next-line @typescript-eslint/no-empty-function 9 | getItem: () => { }, 10 | // eslint-disable-next-line @typescript-eslint/no-empty-function 11 | setItem: () => { } 12 | }) 13 | })); 14 | 15 | const queries: ISampleQuery[] = []; 16 | describe('Samples Cache should', () => { 17 | it('return the same queries after queries added', async () => { 18 | expect(queries.length).toBe(0); 19 | queries.push( 20 | { 21 | category: 'Getting Started', 22 | method: 'GET', 23 | humanName: 'my profile', 24 | requestUrl: '/v1.0/me', 25 | docLink: 'https://learn.microsoft.com/en-us/graph/api/user-get', 26 | skipTest: false 27 | }, 28 | ); 29 | samplesCache.saveSamples(queries); 30 | const samplesData = await samplesCache.readSamples(); 31 | expect(samplesData).not.toBe(queries); 32 | }) 33 | }) 34 | -------------------------------------------------------------------------------- /src/app/utils/graph-toolkit-lookup.spec.ts: -------------------------------------------------------------------------------- 1 | import { lookupToolkitUrl } from './graph-toolkit-lookup'; 2 | 3 | describe('Tests lookToolkitUrl', () => { 4 | it('should return a valid toolkit url depending on sampleQuery passed', () => { 5 | // Arrange 6 | const sampleQuery = { 7 | selectedVerb: 'GET', 8 | sampleUrl: 'https://graph.microsoft.com/v1.0/me/', 9 | selectedVersion: 'v1.0', 10 | sampleBody: '', 11 | sampleHeaders: [] 12 | } 13 | // Act 14 | const result = lookupToolkitUrl(sampleQuery); 15 | 16 | // Assert 17 | expect(result.toolkitUrl). 18 | toBe('https://mgt.dev/iframe.html?id=components-mgt-person-card--person-card&source=ge'); 19 | }); 20 | 21 | it('should return null in toolkiturl property when toolkit url is unavailable', () => { 22 | // Arrange 23 | const sampleQuery = { 24 | selectedVerb: 'GET', 25 | sampleUrl: 'https://graph.microsoft.com/v1.0', 26 | selectedVersion: 'v1.0', 27 | sampleBody: '', 28 | sampleHeaders: [] 29 | } 30 | // Act 31 | const result = lookupToolkitUrl(sampleQuery); 32 | 33 | // Assert 34 | expect(result.toolkitUrl).toBe(null); 35 | }) 36 | } 37 | ) 38 | -------------------------------------------------------------------------------- /src/app/services/hooks/usePopups.ts: -------------------------------------------------------------------------------- 1 | import { PopupItem, popups } from '../../views/common/lazy-loader/component-registry/popups'; 2 | import { 3 | POPUPS, PopupsProps, PopupsType, 4 | UsePopupsResponse, 5 | usePopupsDispatchContext, usePopupsStateContext 6 | } from '../context/popups-context'; 7 | 8 | const usePopups = (item: PopupItem , type: PopupsType, 9 | reference?: string): UsePopupsResponse => { 10 | const dispatch = usePopupsDispatchContext(); 11 | const { popups: popupsState } = usePopupsStateContext(); 12 | const current = popupsState.find(k => k.reference === reference) 13 | 14 | function show(properties: PopupsProps) { 15 | 16 | let component = null; 17 | if (typeof (item) === 'string' && popups.has(item)) { 18 | component = popups.get(item); 19 | } 20 | 21 | const payload = { 22 | component, 23 | popupsProps: properties, 24 | type, 25 | id: new Date().getTime().toString(), 26 | reference 27 | }; 28 | 29 | dispatch({ 30 | type: POPUPS.ADD_POPUPS, 31 | payload 32 | }); 33 | } 34 | return { show, status: current?.status, result: current?.result, reference }; 35 | } 36 | 37 | export { usePopups }; 38 | 39 | -------------------------------------------------------------------------------- /src/tests/accessibility/accessibility.spec.ts: -------------------------------------------------------------------------------- 1 | import AxeBuilder from '@axe-core/playwright'; 2 | import { test, expect, Page } from '@playwright/test'; 3 | 4 | let page: Page; 5 | 6 | test.beforeAll(async ({ browser }) => { 7 | const context = await browser.newContext(); 8 | page = await context.newPage(); 9 | await page.goto('/'); 10 | }); 11 | 12 | test.describe('Accessibility', () => { 13 | test.use({ viewport: { width: 1024, height: 768 }}); 14 | 15 | test('should not have any automatically detectable accessibility issues', async () => { 16 | 17 | await page.locator('[aria-label="Settings"]').isVisible(); 18 | const accessibilityScan = new AxeBuilder({ page }).setLegacyMode(); 19 | const result = await accessibilityScan 20 | .disableRules([ 21 | 'landmark-one-main', 22 | 'region', 23 | 'document-title', 24 | 'html-has-lang', 25 | 'page-has-heading-one', 26 | 'landmark-unique', 27 | 'aria-allowed-attr', 28 | 'aria-required-children', 29 | 'color-contrast', 30 | 'aria-conditional-attr' 31 | ]) 32 | .analyze(); 33 | accessibilityScan.setLegacyMode(false); 34 | 35 | expect(result.violations).toEqual([]); 36 | }); 37 | }) -------------------------------------------------------------------------------- /src/app/views/query-runner/query-input/auto-complete/suggestion-list/SuggestionsList.styles.ts: -------------------------------------------------------------------------------- 1 | import { makeStyles, tokens } from '@fluentui/react-components'; 2 | 3 | export const useSuggestionStyles = makeStyles({ 4 | suggestions: { 5 | maxHeight: '25vh', 6 | overflow: 'auto', 7 | position: 'absolute', 8 | minWidth: '40%', 9 | maxWidth: '50%', 10 | zIndex: 1, 11 | cursor: 'pointer', 12 | color: tokens.colorNeutralForeground1, 13 | '@media (max-width: 480px)': { 14 | minWidth: '100%', 15 | maxWidth: '100%' 16 | } 17 | }, 18 | suggestionOption: { 19 | backgroundColor: tokens.colorNeutralBackground1, 20 | position: 'relative', 21 | whiteSpace: 'nowrap', 22 | textOverflow: 'ellipsis', 23 | border: `1px solid ${tokens.colorNeutralStroke1}`, 24 | overflow: 'hidden', 25 | '&:hover': { 26 | background: tokens.colorNeutralBackground1Hover 27 | } 28 | }, 29 | suggestionActive: { 30 | cursor: 'pointer', 31 | position: 'relative', 32 | whiteSpace: 'nowrap', 33 | textOverflow: 'ellipsis', 34 | border: '1px solid', 35 | overflow: 'hidden', 36 | wordWrap: 'normal', 37 | backgroundColor: tokens.colorNeutralBackground1Selected 38 | } 39 | }); -------------------------------------------------------------------------------- /src/adaptivecards-templates/Site.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "AdaptiveCard", 3 | "version": "1.0", 4 | "@odata.type": "#microsoft.graph.site", 5 | "body": [ 6 | { 7 | "type": "Container", 8 | "$data": "${value}", 9 | "items": [ 10 | { 11 | "type": "TextBlock", 12 | "text": "${displayName}", 13 | "size": "Medium", 14 | "weight": "Bolder" 15 | }, 16 | { 17 | "type": "TextBlock", 18 | "text": "${description}", 19 | "spacing": "Small", 20 | "wrap": true, 21 | "maxLines": 2 22 | }, 23 | { 24 | "type": "ActionSet", 25 | "actions": [ 26 | { 27 | "type": "Action.OpenUrl", 28 | "title": "Open site", 29 | "url": "${webUrl}" 30 | } 31 | ] 32 | } 33 | ], 34 | "separator": true 35 | } 36 | ], 37 | "$schema": "http://adaptivecards.io/schemas/adaptive-card.json" 38 | } -------------------------------------------------------------------------------- /src/app/utils/http-methods.utils.ts: -------------------------------------------------------------------------------- 1 | import { ResponseBody } from '../../types/query-response'; 2 | import { tokens } from '@fluentui/react-components'; 3 | 4 | type BadgeColors = 5 | | 'brand' 6 | | 'danger' 7 | | 'important' 8 | | 'informative' 9 | | 'severe' 10 | | 'subtle' 11 | | 'success' 12 | | 'warning'; 13 | 14 | export const methodColors: Record = { 15 | GET: 'brand', 16 | POST: 'success', 17 | PATCH: 'severe', 18 | DELETE: 'danger', 19 | PUT: 'warning' 20 | }; 21 | 22 | export function getStyleFor(method: string) { 23 | method = method?.toUpperCase(); 24 | const styles: Record = { 25 | GET: tokens.colorBrandBackground, 26 | POST: tokens.colorStatusSuccessForeground1, 27 | PUT: tokens.colorStatusWarningForeground2, 28 | PATCH: tokens.colorStatusWarningForeground3, 29 | DELETE: tokens.colorStatusDangerForeground1 30 | }; 31 | 32 | return styles[method] || tokens.colorNeutralForeground1; 33 | } 34 | 35 | export function getHeaders(response: ResponseBody) { 36 | const headers: Record = {}; 37 | if(response instanceof Response){ 38 | for (const entry of response.headers.entries()) { 39 | headers[entry[0]] = entry[1]; 40 | } 41 | } 42 | return headers; 43 | } 44 | 45 | -------------------------------------------------------------------------------- /src/app/views/sidebar/resource-explorer/collection/collection.util.ts: -------------------------------------------------------------------------------- 1 | import { ResourcePath } from '../../../../../types/resources'; 2 | import { PERMS_SCOPE } from '../../../../services/graph-constants'; 3 | 4 | interface ScopeOption { 5 | key: PERMS_SCOPE; 6 | text: PERMS_SCOPE; 7 | } 8 | 9 | const scopeOptions: ScopeOption[] = Object.entries(PERMS_SCOPE).map(([_key, value]) => ({ 10 | key: value, 11 | text: value as PERMS_SCOPE 12 | })); 13 | 14 | function getScopesFromPaths(paths: ResourcePath[]): string[] { 15 | const scopes = paths.map(path => path.scope ?? scopeOptions[0].key); 16 | return [...new Set(scopes)]; 17 | } 18 | 19 | function getVersionsFromPaths(paths: ResourcePath[]): string[] { 20 | const versions = paths.map(path => path.version!); 21 | return [...new Set(versions)]; 22 | } 23 | 24 | const formatScopeLabel = (scope: PERMS_SCOPE) => { 25 | switch (scope) { 26 | case PERMS_SCOPE.WORK: 27 | return 'Delegated Work'; 28 | case PERMS_SCOPE.APPLICATION: 29 | return 'Application'; 30 | case PERMS_SCOPE.PERSONAL: 31 | return 'Delegated Personal'; 32 | default: 33 | return scope; // fallback in case of an unknown value 34 | } 35 | }; 36 | 37 | export { getVersionsFromPaths, scopeOptions, ScopeOption, getScopesFromPaths, formatScopeLabel }; 38 | -------------------------------------------------------------------------------- /src/types/sidebar.ts: -------------------------------------------------------------------------------- 1 | export interface ISidebarProps { 2 | showSidebar: boolean; 3 | mobileScreen: boolean; 4 | } 5 | 6 | /** 7 | * Tokens are used as placeholder values in sample queries to cover many scenarios: 8 | * - ID tokens for sample tenant nodes like user IDs, file IDs and other string constants 9 | * - Tokens that must be determined at runtime like the current date 10 | * - Tokens that are determined from the authenticated users session 11 | * - Tokens can be in the POST body or part of the URL 12 | * 13 | * The token fields are split into default, demo and authenticated. If neither the demo or 14 | * auth values are supplied, the token falls back to the default value. 15 | * 16 | * Tokens are maintained in tokens.ts. 17 | */ 18 | 19 | export interface IToken { 20 | placeholder: string; 21 | 22 | // Base defaults to replace the placeholder with. Not used if any of the below are defined 23 | defaultValue?: string; 24 | defaultValueFn?: Function; 25 | 26 | // When the user is not authenticated, use these values for the demo tenant 27 | demoTenantValue?: string; 28 | demoTenantValueFn?: Function; 29 | 30 | // When the user is authenticated with MSA or AAD, replace token with these values 31 | authenticatedUserValue?: string; 32 | authenticatedUserValueFn?: Function; 33 | } -------------------------------------------------------------------------------- /src/app/services/actions/samples-action-creators.spec.ts: -------------------------------------------------------------------------------- 1 | import { AnyAction, PayloadAction } from '@reduxjs/toolkit'; 2 | import configureMockStore from 'redux-mock-store'; 3 | import { ISampleQuery } from '../../../types/query-runner'; 4 | import { 5 | SAMPLES_FETCH_PENDING, 6 | SAMPLES_FETCH_SUCCESS 7 | } from '../redux-constants'; 8 | import { fetchSamples } from '../slices/samples.slice'; 9 | import { mockThunkMiddleware } from './mockThunkMiddleware'; 10 | import { queries } from '../../views/sidebar/sample-queries/queries'; 11 | 12 | 13 | const mockStore = configureMockStore([mockThunkMiddleware]); 14 | 15 | describe('Samples action creators', () => { 16 | 17 | it('should dispatch SAMPLES_FETCH_PENDING when fetchSamples() is called', () => { 18 | // Arrange 19 | const expectedActions = [ 20 | { 21 | type: SAMPLES_FETCH_PENDING, 22 | payload: undefined 23 | } 24 | ]; 25 | const store_ = mockStore({}); 26 | fetchMock.mockResponseOnce(JSON.stringify({ queries })); 27 | 28 | // Act 29 | store_.dispatch(fetchSamples() as unknown as AnyAction); 30 | 31 | // Assert 32 | expect(store_.getActions().map(action => { 33 | const { meta, ...rest } = action; 34 | return rest; 35 | })).toEqual(expectedActions); 36 | 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /src/app/utils/dynamic-sort.spec.ts: -------------------------------------------------------------------------------- 1 | import { SortOrder } from '../../types/enums'; 2 | import { dynamicSort } from './dynamic-sort'; 3 | interface INameAge { 4 | name: string 5 | age: number 6 | } 7 | describe('Dynamic Sort', () => { 8 | 9 | it('should sort an array of objects in ascending order', () => { 10 | const arrayToSort = [ 11 | { name: 'Ann', age: 14 }, 12 | { name: 'Lucy', age: 18 }, 13 | { name: 'Diana', age: 11 } 14 | ]; 15 | 16 | const expected = [ 17 | { name: 'Ann', age: 14 }, 18 | { name: 'Diana', age: 11 }, 19 | { name: 'Lucy', age: 18 } 20 | ]; 21 | 22 | const sortedArray = arrayToSort.sort(dynamicSort('name', SortOrder.ASC)); 23 | expect(expected).toEqual(sortedArray); 24 | }); 25 | 26 | it('should sort an array of objects in descending order', () => { 27 | const arrayToSort = [ 28 | { name: 'Ann', age: 14 }, 29 | { name: 'Lucy', age: 18 }, 30 | { name: 'Diana', age: 11 } 31 | ]; 32 | 33 | const expected = [ 34 | { name: 'Lucy', age: 18 }, 35 | { name: 'Diana', age: 11 }, 36 | { name: 'Ann', age: 14 } 37 | ]; 38 | 39 | const sortedArray = arrayToSort.sort(dynamicSort('name', SortOrder.DESC)); 40 | expect(expected).toEqual(sortedArray); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /src/styles/_reset.scss: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v2.0 | 20110126 3 | License: none (public domain) 4 | */ 5 | 6 | html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video { 7 | margin: 0; 8 | padding: 0; 9 | border: 0; 10 | font: inherit; 11 | font-size: 100%; 12 | vertical-align: baseline; } 13 | 14 | /* HTML5 display-role reset for older browsers */ 15 | 16 | article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { 17 | display: block; } 18 | 19 | body { 20 | line-height: 1; } 21 | 22 | ol, ul { 23 | list-style: none; } 24 | 25 | blockquote, q { 26 | quotes: none; } 27 | 28 | blockquote { 29 | &:before, &:after { 30 | content: ''; 31 | content: none; } } 32 | 33 | q { 34 | &:before, &:after { 35 | content: ''; 36 | content: none; } } 37 | 38 | table { 39 | border-collapse: collapse; 40 | border-spacing: 0; } 41 | -------------------------------------------------------------------------------- /src/app/views/sidebar/resource-explorer/collection/Paths.styles.ts: -------------------------------------------------------------------------------- 1 | import { makeStyles, tokens } from '@fluentui/react-components'; 2 | 3 | const pathStyles = makeStyles({ 4 | table: { 5 | tableLayout: 'auto', 6 | width: '100%', 7 | overflow: 'auto', 8 | borderCollapse: 'collapse', 9 | marginInlineStart: tokens.spacingHorizontalL 10 | }, 11 | row: { 12 | borderBottom: 'none' 13 | }, 14 | checkbox: { 15 | width: '10px' 16 | }, 17 | scopeLabel: { 18 | backgroundColor: tokens.colorNeutralForeground3, 19 | color: tokens.colorNeutralBackground1, 20 | padding: '4px 12px', 21 | borderRadius: '16px', 22 | fontSize: '12px', 23 | display: 'inline-block', 24 | textAlign: 'center', 25 | maxWidth: '100%', 26 | whiteSpace: 'nowrap', 27 | overflow: 'hidden', 28 | textOverflow: 'ellipsis' 29 | }, 30 | badge: { 31 | maxWidth: '50%' 32 | }, 33 | badgeContainer: { 34 | display: 'inline-block', 35 | minWidth: '55px' 36 | }, 37 | urlAndMethod: { 38 | display: 'inline-flex', 39 | whiteSpace: 'nowrap', 40 | overflow: 'hidden', 41 | textOverflow: 'ellipsis' 42 | }, 43 | tableHeader: { 44 | fontWeight: 'bold' 45 | }, 46 | rowFocused: { 47 | backgroundColor: tokens.colorNeutralBackground4 48 | } 49 | }); 50 | 51 | export default pathStyles; 52 | -------------------------------------------------------------------------------- /src/app/views/sidebar/Sidebar.styles.tsx: -------------------------------------------------------------------------------- 1 | import { makeStyles, tokens } from '@fluentui/react-components'; 2 | 3 | export const useSidebarStyles = makeStyles({ 4 | container: { 5 | display: 'flex', 6 | flexDirection: 'column', 7 | overflow: 'hidden', 8 | height: '100%', 9 | padding: `0 ${tokens.spacingHorizontalS}`, 10 | backgroundColor: tokens.colorNeutralBackground2, 11 | borderRightWidth: tokens.strokeWidthThin, 12 | borderRight: `1px solid ${tokens.colorNeutralForeground3}`, 13 | '@media (max-width: 768px)': { 14 | height: '100%', 15 | minHeight: '100%' 16 | } 17 | }, 18 | sidebarToggle: { 19 | marginLeft: 'auto', 20 | padding: '10px' 21 | }, 22 | tree: { 23 | flexGrow: 1, 24 | overflowY: 'auto', 25 | padding: '5px', 26 | maxHeight: 'calc(100vh - 400px)' 27 | }, 28 | searchBox: { 29 | width: '100%', 30 | maxWidth: '100%' 31 | }, 32 | activeLeaf: { 33 | backgroundColor: tokens.colorNeutralBackground3Hover, 34 | '@media (forced-colors: active)': { 35 | backgroundColor: 'Highlight', 36 | color: 'HighlightText', 37 | forcedColorAdjust: 'none', 38 | // Adding border for better visibility in high contrast mode 39 | outlineWidth: '2px', 40 | outlineStyle: 'solid', 41 | outlineColor: 'ButtonText' 42 | } 43 | } 44 | }) -------------------------------------------------------------------------------- /src/app/utils/generate-groups.ts: -------------------------------------------------------------------------------- 1 | export interface IGroup { 2 | key: string; 3 | name: string; 4 | startIndex: number; 5 | count: number; 6 | isCollapsed: boolean; 7 | ariaLabel: string; 8 | } 9 | 10 | export function generateGroupsFromList(list: any[], property: string) : IGroup[] { 11 | const map = new Map(); 12 | const groups: IGroup[] = []; 13 | 14 | let isCollapsed = false; 15 | let previousCount = 0; 16 | let count = 0; 17 | 18 | if (!list || list.length === 0 || list.some(e => !e[property])) { 19 | return groups; 20 | } 21 | 22 | for (const listItem of list) { 23 | if (!map.has(listItem[property])) { 24 | map.set(listItem[property], true); 25 | count = list.filter(item => item[property] === listItem[property]).length; 26 | const ariaLabel: string = listItem[property] + ' has ' + count + ' results '; 27 | if (groups.length > 0) { 28 | isCollapsed = true; 29 | } 30 | groups.push({ 31 | name: listItem[property], 32 | key: listItem[property], 33 | startIndex: previousCount, 34 | isCollapsed, 35 | count, 36 | ariaLabel 37 | }); 38 | previousCount += count; 39 | } 40 | } 41 | let i = 1; 42 | groups.forEach(function (group){ 43 | group.ariaLabel += `${i} of ${groups.length}`; 44 | i++; 45 | }); 46 | return groups; 47 | } -------------------------------------------------------------------------------- /src/app/views/common/copy.ts: -------------------------------------------------------------------------------- 1 | import { key } from 'localforage'; 2 | import { telemetry } from '../../../telemetry'; 3 | import { IQuery } from '../../../types/query-runner'; 4 | 5 | export function genericCopy(text: string) { 6 | const element = document.createElement('textarea'); 7 | element.value = text; 8 | document.body.appendChild(element); 9 | element.select(); 10 | 11 | document.execCommand('copy'); 12 | document.body.removeChild(element); 13 | 14 | return Promise.resolve('copied'); 15 | } 16 | 17 | export function copy(id: string) { 18 | const textArea: any = document.getElementById(id); 19 | textArea.focus(); 20 | textArea.select(); 21 | 22 | document.execCommand('copy'); 23 | document.execCommand('unselect'); 24 | 25 | textArea.blur(); 26 | 27 | return Promise.resolve('copied'); 28 | } 29 | 30 | export function trackedGenericCopy( 31 | text: string, 32 | componentName: string, 33 | sampleQuery?: IQuery, 34 | properties?: { [key: string]: string } 35 | ) { 36 | genericCopy(text); 37 | telemetry.trackCopyButtonClickEvent(componentName, sampleQuery, properties); 38 | } 39 | 40 | export function copyAndTrackText( 41 | text: string, 42 | componentName: string, 43 | properties?: { [key: string]: string } 44 | ) { 45 | genericCopy(text); 46 | telemetry.trackCopyButtonClickEvent(componentName, undefined, properties); 47 | } 48 | -------------------------------------------------------------------------------- /src/themes/high-contrast.ts: -------------------------------------------------------------------------------- 1 | export const highContrast = { 2 | palette: { 3 | themePrimary: '#feff00', 4 | themeLighterAlt: '#0a0a00', 5 | themeLighter: '#292900', 6 | themeLight: '#4d4d00', 7 | themeTertiary: '#999900', 8 | themeSecondary: '#e0e000', 9 | themeDarkAlt: '#ffff19', 10 | themeDark: '#ffff3d', 11 | themeDarker: '#ffff70', 12 | neutralLighterAlt: '#000000', 13 | neutralLighter: '#000000', 14 | neutralLight: '#000000', 15 | neutralQuaternaryAlt: '#000000', 16 | neutralQuaternary: '#000000', 17 | neutralTertiaryAlt: '#000000', 18 | neutralTertiary: '#c8c8c8', 19 | neutralSecondary: '#d0d0d0', 20 | neutralPrimaryAlt: '#dadada', 21 | neutralPrimary: '#ffffff', 22 | neutralDark: '#f4f4f4', 23 | black: '#f8f8f8', 24 | white: '#000000', 25 | blueMid: '#6cb8f6', 26 | green: '#92c353' 27 | 28 | }, 29 | semanticColors: { 30 | messageText: '#f4f4f4', 31 | infoBackground: '#1b1a19', 32 | infoIcon: '#c8c6c4', 33 | errorBackground: '#442726', 34 | errorIcon: '#f1707b', 35 | blockingBackground: '#442726', 36 | blockingIcon: '#f1707b', 37 | severeWarningBackground: '#4f2a0f', 38 | severeWarningIcon: '#fce100', 39 | successBackground: '#393d1b', 40 | successIcon: '#92c353', 41 | warningBackground: '#433519', 42 | warningIcon: '#c8c6c4' 43 | } 44 | }; 45 | -------------------------------------------------------------------------------- /src/app/services/slices/proxy.slice.ts: -------------------------------------------------------------------------------- 1 | import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit'; 2 | import { GRAPH_API_SANDBOX_ENDPOINT_URL, GRAPH_API_SANDBOX_URL } from '../graph-constants'; 3 | 4 | export const getGraphProxyUrl = createAsyncThunk( 5 | 'proxyUrl/getGraphProxyUrl', 6 | async (_, { rejectWithValue }) => { 7 | try { 8 | const response = await fetch(GRAPH_API_SANDBOX_ENDPOINT_URL); 9 | if (!response.ok) { 10 | throw new Error('Network response was not ok'); 11 | } 12 | return await response.json(); 13 | } catch (error) { 14 | return rejectWithValue(GRAPH_API_SANDBOX_URL); 15 | } 16 | } 17 | ); 18 | 19 | const proxyUrlSlice = createSlice({ 20 | name: 'proxyUrl', 21 | initialState: GRAPH_API_SANDBOX_URL, 22 | reducers: { 23 | setGraphProxyUrl: (_state, action: PayloadAction) => { 24 | return action.payload; 25 | } 26 | }, 27 | extraReducers: (builder) => { 28 | builder.addCase(getGraphProxyUrl.fulfilled, (_state, action) => { 29 | return action.payload as string; 30 | }); 31 | builder.addCase(getGraphProxyUrl.rejected, (_state, action) => { 32 | if (action.payload) { 33 | return action.payload as string; 34 | } 35 | }); 36 | } 37 | }); 38 | 39 | export const { setGraphProxyUrl } = proxyUrlSlice.actions; 40 | export default proxyUrlSlice.reducer; 41 | -------------------------------------------------------------------------------- /src/app/views/authentication/auth-util-components/ProfileButton.tsx: -------------------------------------------------------------------------------- 1 | import { Button, Persona, PersonaProps, Tooltip } from '@fluentui/react-components'; 2 | import { translateMessage } from '../../../utils/translate-messages'; 3 | import { useHeaderStyles } from '../../main-header/utils'; 4 | import { ProfileV9 } from '../profile/Profile'; 5 | 6 | export const PersonaSignIn = (props: Partial) => { 7 | return ( 8 | 13 | ); 14 | }; 15 | 16 | const SignInButton = ({signIn}: {signIn: ()=> void})=>{ 17 | const styles = useHeaderStyles(); 18 | return ( 19 | 22 |