From f195cd3567185a62724950c4460b2fea3a21d63b Mon Sep 17 00:00:00 2001 From: Quentin Gliech Date: Tue, 15 Nov 2022 12:36:44 +0100 Subject: [PATCH] OAuth and browser session lists --- crates/graphql/src/model/users.rs | 4 +- frontend/src/components/BrowserSession.tsx | 54 +++ .../src/components/BrowserSessionList.tsx | 64 ++++ .../src/components/CompatSsoLoginList.tsx | 2 +- frontend/src/components/OAuth2Session.tsx | 58 +++ frontend/src/components/OAuth2SessionList.tsx | 59 +++ .../BrowserSessionListQuery.graphql.ts | 244 +++++++++++++ .../BrowserSessionList_user.graphql.ts | 170 +++++++++ .../BrowserSession_session.graphql.ts | 71 ++++ .../OAuth2SessionListQuery.graphql.ts | 263 ++++++++++++++ .../OAuth2SessionList_user.graphql.ts | 170 +++++++++ .../OAuth2Session_session.graphql.ts | 92 +++++ frontend/src/pages/Home.tsx | 28 +- .../pages/__generated__/HomeQuery.graphql.ts | 336 ++++++++++++++---- 14 files changed, 1529 insertions(+), 86 deletions(-) create mode 100644 frontend/src/components/BrowserSession.tsx create mode 100644 frontend/src/components/BrowserSessionList.tsx create mode 100644 frontend/src/components/OAuth2Session.tsx create mode 100644 frontend/src/components/OAuth2SessionList.tsx create mode 100644 frontend/src/components/__generated__/BrowserSessionListQuery.graphql.ts create mode 100644 frontend/src/components/__generated__/BrowserSessionList_user.graphql.ts create mode 100644 frontend/src/components/__generated__/BrowserSession_session.graphql.ts create mode 100644 frontend/src/components/__generated__/OAuth2SessionListQuery.graphql.ts create mode 100644 frontend/src/components/__generated__/OAuth2SessionList_user.graphql.ts create mode 100644 frontend/src/components/__generated__/OAuth2Session_session.graphql.ts diff --git a/crates/graphql/src/model/users.rs b/crates/graphql/src/model/users.rs index b9aa5283..771d856d 100644 --- a/crates/graphql/src/model/users.rs +++ b/crates/graphql/src/model/users.rs @@ -127,10 +127,10 @@ impl User { |after, before, first, last| async move { let mut conn = database.acquire().await?; let after_id = after - .map(|x: OpaqueCursor| x.extract_for_type(NodeType::UserEmail)) + .map(|x: OpaqueCursor| x.extract_for_type(NodeType::BrowserSession)) .transpose()?; let before_id = before - .map(|x: OpaqueCursor| x.extract_for_type(NodeType::UserEmail)) + .map(|x: OpaqueCursor| x.extract_for_type(NodeType::BrowserSession)) .transpose()?; let (has_previous_page, has_next_page, edges) = diff --git a/frontend/src/components/BrowserSession.tsx b/frontend/src/components/BrowserSession.tsx new file mode 100644 index 00000000..5ce6caff --- /dev/null +++ b/frontend/src/components/BrowserSession.tsx @@ -0,0 +1,54 @@ +// Copyright 2022 The Matrix.org Foundation C.I.C. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import type { BrowserSession_session$key } from "./__generated__/BrowserSession_session.graphql"; +import { graphql, useFragment } from "react-relay"; + +type Props = { + session: BrowserSession_session$key; + isCurrent: boolean; +}; + +const BrowserSession: React.FC = ({ session, isCurrent }) => { + const data = useFragment( + graphql` + fragment BrowserSession_session on BrowserSession { + id + createdAt + lastAuthentication { + id + createdAt + } + } + `, + session + ); + + return ( +
+ {isCurrent &&
Current session
} +
+ Started: {data.createdAt} +
+
+ Last authentication:{" "} + + {data.lastAuthentication?.createdAt || "never"} + +
+
+ ); +}; + +export default BrowserSession; diff --git a/frontend/src/components/BrowserSessionList.tsx b/frontend/src/components/BrowserSessionList.tsx new file mode 100644 index 00000000..8bd2d1f3 --- /dev/null +++ b/frontend/src/components/BrowserSessionList.tsx @@ -0,0 +1,64 @@ +// Copyright 2022 The Matrix.org Foundation C.I.C. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { graphql, usePaginationFragment } from "react-relay"; +import BrowserSession from "./BrowserSession"; + +import { BrowserSessionList_user$key } from "./__generated__/BrowserSessionList_user.graphql"; + +type Props = { + user: BrowserSessionList_user$key; + currentSessionId: string; +}; + +const BrowserSessionList: React.FC = ({ user, currentSessionId }) => { + const { data, loadNext, hasNext } = usePaginationFragment( + graphql` + fragment BrowserSessionList_user on User + @refetchable(queryName: "BrowserSessionListQuery") { + browserSessions(first: $count, after: $cursor) + @connection(key: "BrowserSessionList_user_browserSessions") { + edges { + cursor + node { + id + ...BrowserSession_session + } + } + } + } + `, + user + ); + + return ( +
+

List of browser sessions:

+ {data.browserSessions.edges.map((n) => ( + + ))} + {hasNext ? ( + + ) : null} +
+ ); +}; + +export default BrowserSessionList; diff --git a/frontend/src/components/CompatSsoLoginList.tsx b/frontend/src/components/CompatSsoLoginList.tsx index 106f7654..e68592f4 100644 --- a/frontend/src/components/CompatSsoLoginList.tsx +++ b/frontend/src/components/CompatSsoLoginList.tsx @@ -40,7 +40,7 @@ const CompatSsoLoginList: React.FC = ({ user }) => { ); return ( -
+

List of compatibility sessions:

{data.compatSsoLogins.edges.map((n) => ( diff --git a/frontend/src/components/OAuth2Session.tsx b/frontend/src/components/OAuth2Session.tsx new file mode 100644 index 00000000..1ee29ba1 --- /dev/null +++ b/frontend/src/components/OAuth2Session.tsx @@ -0,0 +1,58 @@ +// Copyright 2022 The Matrix.org Foundation C.I.C. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import type { OAuth2Session_session$key } from "./__generated__/OAuth2Session_session.graphql"; +import { graphql, useFragment } from "react-relay"; + +type Props = { + session: OAuth2Session_session$key; +}; + +const OAuth2Session: React.FC = ({ session }) => { + const data = useFragment( + graphql` + fragment OAuth2Session_session on Oauth2Session { + id + scope + client { + id + clientId + clientName + clientUri + } + } + `, + session + ); + + return ( +
+
+ Client ID:{" "} + {data.client.clientId} +
+ {data.client.clientName && ( +
+ Client name:{" "} + {data.client.clientName} +
+ )} +
+ Scope: {data.scope} +
+
+ ); +}; + +export default OAuth2Session; diff --git a/frontend/src/components/OAuth2SessionList.tsx b/frontend/src/components/OAuth2SessionList.tsx new file mode 100644 index 00000000..d76115ea --- /dev/null +++ b/frontend/src/components/OAuth2SessionList.tsx @@ -0,0 +1,59 @@ +// Copyright 2022 The Matrix.org Foundation C.I.C. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { graphql, usePaginationFragment } from "react-relay"; +import OAuth2Session from "./OAuth2Session"; + +import { OAuth2SessionList_user$key } from "./__generated__/OAuth2SessionList_user.graphql"; + +type Props = { + user: OAuth2SessionList_user$key; +}; + +const OAuth2SessionList: React.FC = ({ user }) => { + const { data, loadNext, hasNext } = usePaginationFragment( + graphql` + fragment OAuth2SessionList_user on User + @refetchable(queryName: "OAuth2SessionListQuery") { + oauth2Sessions(first: $count, after: $cursor) + @connection(key: "OAuth2SessionList_user_oauth2Sessions") { + edges { + cursor + node { + id + ...OAuth2Session_session + } + } + } + } + `, + user + ); + + return ( +
+

List of OAuth 2.0 sessions:

+ {data.oauth2Sessions.edges.map((n) => ( + + ))} + {hasNext ? ( + + ) : null} +
+ ); +}; + +export default OAuth2SessionList; diff --git a/frontend/src/components/__generated__/BrowserSessionListQuery.graphql.ts b/frontend/src/components/__generated__/BrowserSessionListQuery.graphql.ts new file mode 100644 index 00000000..9d42eb97 --- /dev/null +++ b/frontend/src/components/__generated__/BrowserSessionListQuery.graphql.ts @@ -0,0 +1,244 @@ +/** + * @generated SignedSource<<2e2beb7aa6522ccc21b080d150de618a>> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { ConcreteRequest, Query } from 'relay-runtime'; +import { FragmentRefs } from "relay-runtime"; +export type BrowserSessionListQuery$variables = { + count?: number | null; + cursor?: string | null; + id: string; +}; +export type BrowserSessionListQuery$data = { + readonly node: { + readonly " $fragmentSpreads": FragmentRefs<"BrowserSessionList_user">; + } | null; +}; +export type BrowserSessionListQuery = { + response: BrowserSessionListQuery$data; + variables: BrowserSessionListQuery$variables; +}; + +const node: ConcreteRequest = (function(){ +var v0 = [ + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "count" + }, + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "cursor" + }, + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "id" + } +], +v1 = [ + { + "kind": "Variable", + "name": "id", + "variableName": "id" + } +], +v2 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null +}, +v3 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null +}, +v4 = [ + { + "kind": "Variable", + "name": "after", + "variableName": "cursor" + }, + { + "kind": "Variable", + "name": "first", + "variableName": "count" + } +], +v5 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "createdAt", + "storageKey": null +}; +return { + "fragment": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Fragment", + "metadata": null, + "name": "BrowserSessionListQuery", + "selections": [ + { + "alias": null, + "args": (v1/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + { + "args": null, + "kind": "FragmentSpread", + "name": "BrowserSessionList_user" + } + ], + "storageKey": null + } + ], + "type": "RootQuery", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Operation", + "name": "BrowserSessionListQuery", + "selections": [ + { + "alias": null, + "args": (v1/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v2/*: any*/), + (v3/*: any*/), + { + "kind": "InlineFragment", + "selections": [ + { + "alias": null, + "args": (v4/*: any*/), + "concreteType": "BrowserSessionConnection", + "kind": "LinkedField", + "name": "browserSessions", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "BrowserSessionEdge", + "kind": "LinkedField", + "name": "edges", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "cursor", + "storageKey": null + }, + { + "alias": null, + "args": null, + "concreteType": "BrowserSession", + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v3/*: any*/), + (v5/*: any*/), + { + "alias": null, + "args": null, + "concreteType": "Authentication", + "kind": "LinkedField", + "name": "lastAuthentication", + "plural": false, + "selections": [ + (v3/*: any*/), + (v5/*: any*/) + ], + "storageKey": null + }, + (v2/*: any*/) + ], + "storageKey": null + } + ], + "storageKey": null + }, + { + "alias": null, + "args": null, + "concreteType": "PageInfo", + "kind": "LinkedField", + "name": "pageInfo", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "endCursor", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "hasNextPage", + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": null + }, + { + "alias": null, + "args": (v4/*: any*/), + "filters": null, + "handle": "connection", + "key": "BrowserSessionList_user_browserSessions", + "kind": "LinkedHandle", + "name": "browserSessions" + } + ], + "type": "User", + "abstractKey": null + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "c05f614c382cae0bed080006b43f14f3", + "id": null, + "metadata": {}, + "name": "BrowserSessionListQuery", + "operationKind": "query", + "text": "query BrowserSessionListQuery(\n $count: Int\n $cursor: String\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...BrowserSessionList_user\n id\n }\n}\n\nfragment BrowserSessionList_user on User {\n browserSessions(first: $count, after: $cursor) {\n edges {\n cursor\n node {\n id\n ...BrowserSession_session\n __typename\n }\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n id\n}\n\nfragment BrowserSession_session on BrowserSession {\n id\n createdAt\n lastAuthentication {\n id\n createdAt\n }\n}\n" + } +}; +})(); + +(node as any).hash = "5f21c429aa98b854c17d3f9eb83b81d8"; + +export default node; diff --git a/frontend/src/components/__generated__/BrowserSessionList_user.graphql.ts b/frontend/src/components/__generated__/BrowserSessionList_user.graphql.ts new file mode 100644 index 00000000..c060f4d5 --- /dev/null +++ b/frontend/src/components/__generated__/BrowserSessionList_user.graphql.ts @@ -0,0 +1,170 @@ +/** + * @generated SignedSource<<3b7505958556a142e2d9926ae631f0e0>> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { ReaderFragment, RefetchableFragment } from 'relay-runtime'; +import { FragmentRefs } from "relay-runtime"; +export type BrowserSessionList_user$data = { + readonly browserSessions: { + readonly edges: ReadonlyArray<{ + readonly cursor: string; + readonly node: { + readonly id: string; + readonly " $fragmentSpreads": FragmentRefs<"BrowserSession_session">; + }; + }>; + }; + readonly id: string; + readonly " $fragmentType": "BrowserSessionList_user"; +}; +export type BrowserSessionList_user$key = { + readonly " $data"?: BrowserSessionList_user$data; + readonly " $fragmentSpreads": FragmentRefs<"BrowserSessionList_user">; +}; + +import BrowserSessionListQuery_graphql from './BrowserSessionListQuery.graphql'; + +const node: ReaderFragment = (function(){ +var v0 = [ + "browserSessions" +], +v1 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null +}; +return { + "argumentDefinitions": [ + { + "kind": "RootArgument", + "name": "count" + }, + { + "kind": "RootArgument", + "name": "cursor" + } + ], + "kind": "Fragment", + "metadata": { + "connection": [ + { + "count": "count", + "cursor": "cursor", + "direction": "forward", + "path": (v0/*: any*/) + } + ], + "refetch": { + "connection": { + "forward": { + "count": "count", + "cursor": "cursor" + }, + "backward": null, + "path": (v0/*: any*/) + }, + "fragmentPathInResult": [ + "node" + ], + "operation": BrowserSessionListQuery_graphql, + "identifierField": "id" + } + }, + "name": "BrowserSessionList_user", + "selections": [ + { + "alias": "browserSessions", + "args": null, + "concreteType": "BrowserSessionConnection", + "kind": "LinkedField", + "name": "__BrowserSessionList_user_browserSessions_connection", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "BrowserSessionEdge", + "kind": "LinkedField", + "name": "edges", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "cursor", + "storageKey": null + }, + { + "alias": null, + "args": null, + "concreteType": "BrowserSession", + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v1/*: any*/), + { + "args": null, + "kind": "FragmentSpread", + "name": "BrowserSession_session" + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": null + }, + { + "alias": null, + "args": null, + "concreteType": "PageInfo", + "kind": "LinkedField", + "name": "pageInfo", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "endCursor", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "hasNextPage", + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": null + }, + (v1/*: any*/) + ], + "type": "User", + "abstractKey": null +}; +})(); + +(node as any).hash = "5f21c429aa98b854c17d3f9eb83b81d8"; + +export default node; diff --git a/frontend/src/components/__generated__/BrowserSession_session.graphql.ts b/frontend/src/components/__generated__/BrowserSession_session.graphql.ts new file mode 100644 index 00000000..ecfacc69 --- /dev/null +++ b/frontend/src/components/__generated__/BrowserSession_session.graphql.ts @@ -0,0 +1,71 @@ +/** + * @generated SignedSource<> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { Fragment, ReaderFragment } from 'relay-runtime'; +import { FragmentRefs } from "relay-runtime"; +export type BrowserSession_session$data = { + readonly createdAt: any; + readonly id: string; + readonly lastAuthentication: { + readonly createdAt: any; + readonly id: string; + } | null; + readonly " $fragmentType": "BrowserSession_session"; +}; +export type BrowserSession_session$key = { + readonly " $data"?: BrowserSession_session$data; + readonly " $fragmentSpreads": FragmentRefs<"BrowserSession_session">; +}; + +const node: ReaderFragment = (function(){ +var v0 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null +}, +v1 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "createdAt", + "storageKey": null +}; +return { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "BrowserSession_session", + "selections": [ + (v0/*: any*/), + (v1/*: any*/), + { + "alias": null, + "args": null, + "concreteType": "Authentication", + "kind": "LinkedField", + "name": "lastAuthentication", + "plural": false, + "selections": [ + (v0/*: any*/), + (v1/*: any*/) + ], + "storageKey": null + } + ], + "type": "BrowserSession", + "abstractKey": null +}; +})(); + +(node as any).hash = "04d6adf0b2a1bf2098938ef30f195c4a"; + +export default node; diff --git a/frontend/src/components/__generated__/OAuth2SessionListQuery.graphql.ts b/frontend/src/components/__generated__/OAuth2SessionListQuery.graphql.ts new file mode 100644 index 00000000..8b9292f1 --- /dev/null +++ b/frontend/src/components/__generated__/OAuth2SessionListQuery.graphql.ts @@ -0,0 +1,263 @@ +/** + * @generated SignedSource<<816340b05858a7b5a61aca0a72c21c46>> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { ConcreteRequest, Query } from 'relay-runtime'; +import { FragmentRefs } from "relay-runtime"; +export type OAuth2SessionListQuery$variables = { + count?: number | null; + cursor?: string | null; + id: string; +}; +export type OAuth2SessionListQuery$data = { + readonly node: { + readonly " $fragmentSpreads": FragmentRefs<"OAuth2SessionList_user">; + } | null; +}; +export type OAuth2SessionListQuery = { + response: OAuth2SessionListQuery$data; + variables: OAuth2SessionListQuery$variables; +}; + +const node: ConcreteRequest = (function(){ +var v0 = [ + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "count" + }, + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "cursor" + }, + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "id" + } +], +v1 = [ + { + "kind": "Variable", + "name": "id", + "variableName": "id" + } +], +v2 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null +}, +v3 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null +}, +v4 = [ + { + "kind": "Variable", + "name": "after", + "variableName": "cursor" + }, + { + "kind": "Variable", + "name": "first", + "variableName": "count" + } +]; +return { + "fragment": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Fragment", + "metadata": null, + "name": "OAuth2SessionListQuery", + "selections": [ + { + "alias": null, + "args": (v1/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + { + "args": null, + "kind": "FragmentSpread", + "name": "OAuth2SessionList_user" + } + ], + "storageKey": null + } + ], + "type": "RootQuery", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Operation", + "name": "OAuth2SessionListQuery", + "selections": [ + { + "alias": null, + "args": (v1/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v2/*: any*/), + (v3/*: any*/), + { + "kind": "InlineFragment", + "selections": [ + { + "alias": null, + "args": (v4/*: any*/), + "concreteType": "Oauth2SessionConnection", + "kind": "LinkedField", + "name": "oauth2Sessions", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "Oauth2SessionEdge", + "kind": "LinkedField", + "name": "edges", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "cursor", + "storageKey": null + }, + { + "alias": null, + "args": null, + "concreteType": "Oauth2Session", + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v3/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "scope", + "storageKey": null + }, + { + "alias": null, + "args": null, + "concreteType": "Oauth2Client", + "kind": "LinkedField", + "name": "client", + "plural": false, + "selections": [ + (v3/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "clientId", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "clientName", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "clientUri", + "storageKey": null + } + ], + "storageKey": null + }, + (v2/*: any*/) + ], + "storageKey": null + } + ], + "storageKey": null + }, + { + "alias": null, + "args": null, + "concreteType": "PageInfo", + "kind": "LinkedField", + "name": "pageInfo", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "endCursor", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "hasNextPage", + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": null + }, + { + "alias": null, + "args": (v4/*: any*/), + "filters": null, + "handle": "connection", + "key": "OAuth2SessionList_user_oauth2Sessions", + "kind": "LinkedHandle", + "name": "oauth2Sessions" + } + ], + "type": "User", + "abstractKey": null + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "26d1b723940d396f1af608f36ff05ce6", + "id": null, + "metadata": {}, + "name": "OAuth2SessionListQuery", + "operationKind": "query", + "text": "query OAuth2SessionListQuery(\n $count: Int\n $cursor: String\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...OAuth2SessionList_user\n id\n }\n}\n\nfragment OAuth2SessionList_user on User {\n oauth2Sessions(first: $count, after: $cursor) {\n edges {\n cursor\n node {\n id\n ...OAuth2Session_session\n __typename\n }\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n id\n}\n\nfragment OAuth2Session_session on Oauth2Session {\n id\n scope\n client {\n id\n clientId\n clientName\n clientUri\n }\n}\n" + } +}; +})(); + +(node as any).hash = "17ef23458a73705550aa33e7f73e41cf"; + +export default node; diff --git a/frontend/src/components/__generated__/OAuth2SessionList_user.graphql.ts b/frontend/src/components/__generated__/OAuth2SessionList_user.graphql.ts new file mode 100644 index 00000000..8f1d15ba --- /dev/null +++ b/frontend/src/components/__generated__/OAuth2SessionList_user.graphql.ts @@ -0,0 +1,170 @@ +/** + * @generated SignedSource<<8cb936a8cc1a9ab2a877a4ea92622c88>> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { ReaderFragment, RefetchableFragment } from 'relay-runtime'; +import { FragmentRefs } from "relay-runtime"; +export type OAuth2SessionList_user$data = { + readonly id: string; + readonly oauth2Sessions: { + readonly edges: ReadonlyArray<{ + readonly cursor: string; + readonly node: { + readonly id: string; + readonly " $fragmentSpreads": FragmentRefs<"OAuth2Session_session">; + }; + }>; + }; + readonly " $fragmentType": "OAuth2SessionList_user"; +}; +export type OAuth2SessionList_user$key = { + readonly " $data"?: OAuth2SessionList_user$data; + readonly " $fragmentSpreads": FragmentRefs<"OAuth2SessionList_user">; +}; + +import OAuth2SessionListQuery_graphql from './OAuth2SessionListQuery.graphql'; + +const node: ReaderFragment = (function(){ +var v0 = [ + "oauth2Sessions" +], +v1 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null +}; +return { + "argumentDefinitions": [ + { + "kind": "RootArgument", + "name": "count" + }, + { + "kind": "RootArgument", + "name": "cursor" + } + ], + "kind": "Fragment", + "metadata": { + "connection": [ + { + "count": "count", + "cursor": "cursor", + "direction": "forward", + "path": (v0/*: any*/) + } + ], + "refetch": { + "connection": { + "forward": { + "count": "count", + "cursor": "cursor" + }, + "backward": null, + "path": (v0/*: any*/) + }, + "fragmentPathInResult": [ + "node" + ], + "operation": OAuth2SessionListQuery_graphql, + "identifierField": "id" + } + }, + "name": "OAuth2SessionList_user", + "selections": [ + { + "alias": "oauth2Sessions", + "args": null, + "concreteType": "Oauth2SessionConnection", + "kind": "LinkedField", + "name": "__OAuth2SessionList_user_oauth2Sessions_connection", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "Oauth2SessionEdge", + "kind": "LinkedField", + "name": "edges", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "cursor", + "storageKey": null + }, + { + "alias": null, + "args": null, + "concreteType": "Oauth2Session", + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v1/*: any*/), + { + "args": null, + "kind": "FragmentSpread", + "name": "OAuth2Session_session" + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": null + }, + { + "alias": null, + "args": null, + "concreteType": "PageInfo", + "kind": "LinkedField", + "name": "pageInfo", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "endCursor", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "hasNextPage", + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": null + }, + (v1/*: any*/) + ], + "type": "User", + "abstractKey": null +}; +})(); + +(node as any).hash = "17ef23458a73705550aa33e7f73e41cf"; + +export default node; diff --git a/frontend/src/components/__generated__/OAuth2Session_session.graphql.ts b/frontend/src/components/__generated__/OAuth2Session_session.graphql.ts new file mode 100644 index 00000000..efc5daae --- /dev/null +++ b/frontend/src/components/__generated__/OAuth2Session_session.graphql.ts @@ -0,0 +1,92 @@ +/** + * @generated SignedSource<<599452efb8ce96e81a5e9500668ff055>> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { Fragment, ReaderFragment } from 'relay-runtime'; +import { FragmentRefs } from "relay-runtime"; +export type OAuth2Session_session$data = { + readonly client: { + readonly clientId: string; + readonly clientName: string | null; + readonly clientUri: any | null; + readonly id: string; + }; + readonly id: string; + readonly scope: string; + readonly " $fragmentType": "OAuth2Session_session"; +}; +export type OAuth2Session_session$key = { + readonly " $data"?: OAuth2Session_session$data; + readonly " $fragmentSpreads": FragmentRefs<"OAuth2Session_session">; +}; + +const node: ReaderFragment = (function(){ +var v0 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null +}; +return { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "OAuth2Session_session", + "selections": [ + (v0/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "scope", + "storageKey": null + }, + { + "alias": null, + "args": null, + "concreteType": "Oauth2Client", + "kind": "LinkedField", + "name": "client", + "plural": false, + "selections": [ + (v0/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "clientId", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "clientName", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "clientUri", + "storageKey": null + } + ], + "storageKey": null + } + ], + "type": "Oauth2Session", + "abstractKey": null +}; +})(); + +(node as any).hash = "d9fa36c7f93b7cef4d5a038d19f768b1"; + +export default node; diff --git a/frontend/src/pages/Home.tsx b/frontend/src/pages/Home.tsx index 2d620437..0a1f795f 100644 --- a/frontend/src/pages/Home.tsx +++ b/frontend/src/pages/Home.tsx @@ -13,32 +13,44 @@ // limitations under the License. import { graphql, useLazyLoadQuery } from "react-relay"; +import BrowserSessionList from "../components/BrowserSessionList"; import CompatSsoLoginList from "../components/CompatSsoLoginList"; +import OAuth2SessionList from "../components/OAuth2SessionList"; import type { HomeQuery } from "./__generated__/HomeQuery.graphql"; const Home: React.FC = () => { const data = useLazyLoadQuery( graphql` query HomeQuery($count: Int!, $cursor: String) { - currentUser { + currentBrowserSession { id - username + user { + id + username - ...CompatSsoLoginList_user + ...CompatSsoLoginList_user + ...BrowserSessionList_user + ...OAuth2SessionList_user + } } } `, { count: 2 } ); - if (data.currentUser) { + if (data.currentBrowserSession) { + const session = data.currentBrowserSession; + const user = session.user; + return ( <> -

- Hello {data.currentUser.username}! -

- +

Hello {user.username}!

+
+ + + +
); } else { diff --git a/frontend/src/pages/__generated__/HomeQuery.graphql.ts b/frontend/src/pages/__generated__/HomeQuery.graphql.ts index b48ff3b2..e9a07024 100644 --- a/frontend/src/pages/__generated__/HomeQuery.graphql.ts +++ b/frontend/src/pages/__generated__/HomeQuery.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<874ab84c1095ab907f12b91b55f4bf2c>> + * @generated SignedSource<> * @lightSyntaxTransform * @nogrep */ @@ -15,10 +15,13 @@ export type HomeQuery$variables = { cursor?: string | null; }; export type HomeQuery$data = { - readonly currentUser: { + readonly currentBrowserSession: { readonly id: string; - readonly username: string; - readonly " $fragmentSpreads": FragmentRefs<"CompatSsoLoginList_user">; + readonly user: { + readonly id: string; + readonly username: string; + readonly " $fragmentSpreads": FragmentRefs<"BrowserSessionList_user" | "CompatSsoLoginList_user" | "OAuth2SessionList_user">; + }; } | null; }; export type HomeQuery = { @@ -71,6 +74,45 @@ v4 = { "kind": "ScalarField", "name": "createdAt", "storageKey": null +}, +v5 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null +}, +v6 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "cursor", + "storageKey": null +}, +v7 = { + "alias": null, + "args": null, + "concreteType": "PageInfo", + "kind": "LinkedField", + "name": "pageInfo", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "endCursor", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "hasNextPage", + "storageKey": null + } + ], + "storageKey": null }; return { "fragment": { @@ -82,17 +124,39 @@ return { { "alias": null, "args": null, - "concreteType": "User", + "concreteType": "BrowserSession", "kind": "LinkedField", - "name": "currentUser", + "name": "currentBrowserSession", "plural": false, "selections": [ (v1/*: any*/), - (v2/*: any*/), { + "alias": null, "args": null, - "kind": "FragmentSpread", - "name": "CompatSsoLoginList_user" + "concreteType": "User", + "kind": "LinkedField", + "name": "user", + "plural": false, + "selections": [ + (v1/*: any*/), + (v2/*: any*/), + { + "args": null, + "kind": "FragmentSpread", + "name": "CompatSsoLoginList_user" + }, + { + "args": null, + "kind": "FragmentSpread", + "name": "BrowserSessionList_user" + }, + { + "args": null, + "kind": "FragmentSpread", + "name": "OAuth2SessionList_user" + } + ], + "storageKey": null } ], "storageKey": null @@ -110,52 +174,126 @@ return { { "alias": null, "args": null, - "concreteType": "User", + "concreteType": "BrowserSession", "kind": "LinkedField", - "name": "currentUser", + "name": "currentBrowserSession", "plural": false, "selections": [ (v1/*: any*/), - (v2/*: any*/), { "alias": null, - "args": (v3/*: any*/), - "concreteType": "CompatSsoLoginConnection", + "args": null, + "concreteType": "User", "kind": "LinkedField", - "name": "compatSsoLogins", + "name": "user", "plural": false, "selections": [ + (v1/*: any*/), + (v2/*: any*/), { "alias": null, - "args": null, - "concreteType": "CompatSsoLoginEdge", + "args": (v3/*: any*/), + "concreteType": "CompatSsoLoginConnection", "kind": "LinkedField", - "name": "edges", - "plural": true, + "name": "compatSsoLogins", + "plural": false, "selections": [ { "alias": null, "args": null, - "concreteType": "CompatSsoLogin", + "concreteType": "CompatSsoLoginEdge", "kind": "LinkedField", - "name": "node", - "plural": false, + "name": "edges", + "plural": true, "selections": [ - (v1/*: any*/), { "alias": null, "args": null, - "kind": "ScalarField", - "name": "redirectUri", + "concreteType": "CompatSsoLogin", + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v1/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "redirectUri", + "storageKey": null + }, + (v4/*: any*/), + { + "alias": null, + "args": null, + "concreteType": "CompatSession", + "kind": "LinkedField", + "name": "session", + "plural": false, + "selections": [ + (v1/*: any*/), + (v4/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "deviceId", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "finishedAt", + "storageKey": null + } + ], + "storageKey": null + }, + (v5/*: any*/) + ], "storageKey": null }, - (v4/*: any*/), + (v6/*: any*/) + ], + "storageKey": null + }, + (v7/*: any*/) + ], + "storageKey": null + }, + { + "alias": null, + "args": (v3/*: any*/), + "filters": null, + "handle": "connection", + "key": "CompatSsoLoginList_user_compatSsoLogins", + "kind": "LinkedHandle", + "name": "compatSsoLogins" + }, + { + "alias": null, + "args": (v3/*: any*/), + "concreteType": "BrowserSessionConnection", + "kind": "LinkedField", + "name": "browserSessions", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "BrowserSessionEdge", + "kind": "LinkedField", + "name": "edges", + "plural": true, + "selections": [ + (v6/*: any*/), { "alias": null, "args": null, - "concreteType": "CompatSession", + "concreteType": "BrowserSession", "kind": "LinkedField", - "name": "session", + "name": "node", "plural": false, "selections": [ (v1/*: any*/), @@ -163,76 +301,124 @@ return { { "alias": null, "args": null, - "kind": "ScalarField", - "name": "deviceId", + "concreteType": "Authentication", + "kind": "LinkedField", + "name": "lastAuthentication", + "plural": false, + "selections": [ + (v1/*: any*/), + (v4/*: any*/) + ], "storageKey": null }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "finishedAt", - "storageKey": null - } + (v5/*: any*/) ], "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "__typename", - "storageKey": null } ], "storageKey": null }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "cursor", - "storageKey": null - } + (v7/*: any*/) ], "storageKey": null }, { "alias": null, - "args": null, - "concreteType": "PageInfo", + "args": (v3/*: any*/), + "filters": null, + "handle": "connection", + "key": "BrowserSessionList_user_browserSessions", + "kind": "LinkedHandle", + "name": "browserSessions" + }, + { + "alias": null, + "args": (v3/*: any*/), + "concreteType": "Oauth2SessionConnection", "kind": "LinkedField", - "name": "pageInfo", + "name": "oauth2Sessions", "plural": false, "selections": [ { "alias": null, "args": null, - "kind": "ScalarField", - "name": "endCursor", + "concreteType": "Oauth2SessionEdge", + "kind": "LinkedField", + "name": "edges", + "plural": true, + "selections": [ + (v6/*: any*/), + { + "alias": null, + "args": null, + "concreteType": "Oauth2Session", + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v1/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "scope", + "storageKey": null + }, + { + "alias": null, + "args": null, + "concreteType": "Oauth2Client", + "kind": "LinkedField", + "name": "client", + "plural": false, + "selections": [ + (v1/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "clientId", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "clientName", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "clientUri", + "storageKey": null + } + ], + "storageKey": null + }, + (v5/*: any*/) + ], + "storageKey": null + } + ], "storageKey": null }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "hasNextPage", - "storageKey": null - } + (v7/*: any*/) ], "storageKey": null + }, + { + "alias": null, + "args": (v3/*: any*/), + "filters": null, + "handle": "connection", + "key": "OAuth2SessionList_user_oauth2Sessions", + "kind": "LinkedHandle", + "name": "oauth2Sessions" } ], "storageKey": null - }, - { - "alias": null, - "args": (v3/*: any*/), - "filters": null, - "handle": "connection", - "key": "CompatSsoLoginList_user_compatSsoLogins", - "kind": "LinkedHandle", - "name": "compatSsoLogins" } ], "storageKey": null @@ -240,16 +426,16 @@ return { ] }, "params": { - "cacheID": "3543c7ada63831383f66dbeed4b1648e", + "cacheID": "c2c8afebb1acbce26f8d164d911c5833", "id": null, "metadata": {}, "name": "HomeQuery", "operationKind": "query", - "text": "query HomeQuery(\n $count: Int!\n $cursor: String\n) {\n currentUser {\n id\n username\n ...CompatSsoLoginList_user\n }\n}\n\nfragment CompatSsoLoginList_user on User {\n compatSsoLogins(first: $count, after: $cursor) {\n edges {\n node {\n id\n ...CompatSsoLogin_login\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n id\n}\n\nfragment CompatSsoLogin_login on CompatSsoLogin {\n id\n redirectUri\n createdAt\n session {\n id\n createdAt\n deviceId\n finishedAt\n }\n}\n" + "text": "query HomeQuery(\n $count: Int!\n $cursor: String\n) {\n currentBrowserSession {\n id\n user {\n id\n username\n ...CompatSsoLoginList_user\n ...BrowserSessionList_user\n ...OAuth2SessionList_user\n }\n }\n}\n\nfragment BrowserSessionList_user on User {\n browserSessions(first: $count, after: $cursor) {\n edges {\n cursor\n node {\n id\n ...BrowserSession_session\n __typename\n }\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n id\n}\n\nfragment BrowserSession_session on BrowserSession {\n id\n createdAt\n lastAuthentication {\n id\n createdAt\n }\n}\n\nfragment CompatSsoLoginList_user on User {\n compatSsoLogins(first: $count, after: $cursor) {\n edges {\n node {\n id\n ...CompatSsoLogin_login\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n id\n}\n\nfragment CompatSsoLogin_login on CompatSsoLogin {\n id\n redirectUri\n createdAt\n session {\n id\n createdAt\n deviceId\n finishedAt\n }\n}\n\nfragment OAuth2SessionList_user on User {\n oauth2Sessions(first: $count, after: $cursor) {\n edges {\n cursor\n node {\n id\n ...OAuth2Session_session\n __typename\n }\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n id\n}\n\nfragment OAuth2Session_session on Oauth2Session {\n id\n scope\n client {\n id\n clientId\n clientName\n clientUri\n }\n}\n" } }; })(); -(node as any).hash = "1cd5ce8ae5a912b3d7ecf0c6e3cd8469"; +(node as any).hash = "f26e9c3756edccc8584de5f359fd96bd"; export default node;