Temporary fix - Member.id is not globally unique (#4790)

This commit is contained in:
Kamil Kisiela 2024-05-21 11:54:07 +02:00 committed by GitHub
parent 3540722f29
commit 8d98266eb6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 294 additions and 87 deletions

View file

@ -78,9 +78,14 @@ module.exports = {
},
{
files: ['packages/web/app/**/*.graphql'],
plugins: ['@graphql-eslint'],
plugins: ['@graphql-eslint', 'hive'],
rules: {
'@graphql-eslint/require-id-when-available': 'error',
// Require temporaryFixId field when available.
// We need it to be able to cache Member objects in the frontend properly.
// Member.id is not unique, so we need to use Member.temporaryFixId instead.
// Once we have a better solution, we can remove this rule.
'hive/graphql-require-selection': ['error'],
},
},
{

View file

@ -59,6 +59,7 @@ export default gql`
type Member {
id: ID!
temporaryFixId: ID!
user: User!
isOwner: Boolean!
organizationAccessScopes: [OrganizationAccessScope!]!

View file

@ -78,6 +78,9 @@ export const resolvers: AuthModule.Resolvers & {
TOKENS_WRITE: TargetAccessScope.TOKENS_WRITE,
},
Member: {
temporaryFixId(member) {
return `${member.organization}:${member.id}`;
},
organizationAccessScopes(member, _, { injector }) {
return injector.get(AuthManager).getMemberOrganizationScopes({
user: member.id,

View file

@ -169,6 +169,7 @@ const AdminStatsQuery = graphql(`
name
owner {
id
temporaryFixId
user {
id
email
@ -177,6 +178,7 @@ const AdminStatsQuery = graphql(`
members {
nodes {
id
temporaryFixId
user {
id
email

View file

@ -33,6 +33,7 @@ const ProjectLayoutQuery = graphql(`
name
me {
id
temporaryFixId
...CanAccessProject_MemberFragment
}
projects {

View file

@ -49,14 +49,6 @@ export function TargetSelector(props: {
const targets = currentProject?.targets.nodes;
const currentTarget = targets?.find(node => node.cleanId === props.currentTargetCleanId);
console.log({
organizations,
props,
currentOrganization,
currentProject,
currentTarget,
});
return (
<>
{currentOrganization ? (

View file

@ -37,6 +37,7 @@ const TargetLayoutQuery = graphql(`
name
me {
id
temporaryFixId
...CanAccessTarget_MemberFragment
}
projects {

View file

@ -264,6 +264,7 @@ const UsePermissionManager_OrganizationFragment = graphql(`
const UsePermissionManager_MemberFragment = graphql(`
fragment UsePermissionManager_MemberFragment on Member {
id
temporaryFixId
targetAccessScopes
projectAccessScopes
organizationAccessScopes

View file

@ -36,6 +36,7 @@ const OrganizationMemberRoleSwitcher_AssignRoleMutation = graphql(`
ok {
updatedMember {
id
temporaryFixId
user {
id
displayName
@ -65,6 +66,7 @@ const OrganizationMemberRoleSwitcher_OrganizationFragment = graphql(`
cleanId
me {
id
temporaryFixId
isAdmin
organizationAccessScopes
projectAccessScopes
@ -72,6 +74,7 @@ const OrganizationMemberRoleSwitcher_OrganizationFragment = graphql(`
}
owner {
id
temporaryFixId
}
memberRoles {
id
@ -95,6 +98,7 @@ const OrganizationMemberRoleSwitcher_OrganizationFragment = graphql(`
const OrganizationMemberRoleSwitcher_MemberFragment = graphql(`
fragment OrganizationMemberRoleSwitcher_MemberFragment on Member {
id
temporaryFixId
organizationAccessScopes
projectAccessScopes
targetAccessScopes
@ -317,6 +321,7 @@ const OrganizationMemberRow_DeleteMember = graphql(`
const OrganizationMemberRow_MemberFragment = graphql(`
fragment OrganizationMemberRow_MemberFragment on Member {
id
temporaryFixId
user {
id
provider

View file

@ -363,6 +363,7 @@ const OrganizationMemberRolesMigrationGroup_Migrate = graphql(`
members {
nodes {
id
temporaryFixId
organizationAccessScopes
projectAccessScopes
targetAccessScopes

View file

@ -13,6 +13,7 @@ const ChangePermissionsModal_OrganizationFragment = graphql(`
export const ChangePermissionsModal_MemberFragment = graphql(`
fragment ChangePermissionsModal_MemberFragment on Member {
temporaryFixId
...UsePermissionManager_MemberFragment
}
`);

View file

@ -34,6 +34,7 @@ const TransferOrganizationOwnership_Members = graphql(`
name
members {
nodes {
temporaryFixId
isOwner
...MemberFields
user {
@ -53,6 +54,7 @@ const TransferOrganizationOwnership_Members = graphql(`
const MemberFields = graphql(`
fragment MemberFields on Member {
id
temporaryFixId
user {
id
fullName

View file

@ -7,6 +7,7 @@ export { OrganizationAccessScope };
const CanAccessOrganization_MemberFragment = graphql(`
fragment CanAccessOrganization_MemberFragment on Member {
id
temporaryFixId
organizationAccessScopes
}
`);

View file

@ -7,6 +7,7 @@ export { TargetAccessScope };
export const CanAccessTarget_MemberFragment = graphql(`
fragment CanAccessTarget_MemberFragment on Member {
id
temporaryFixId
targetAccessScopes
}
`);

View file

@ -9,6 +9,7 @@ import type { CreateOperationMutationType } from '@/components/target/laboratory
import type { DeleteCollectionMutationType } from '@/components/target/laboratory/delete-collection-modal';
import type { DeleteOperationMutationType } from '@/components/target/laboratory/delete-operation-modal';
import type { CreateAccessToken_CreateTokenMutation } from '@/components/v2/modals/create-access-token';
import type { CreateOrganizationMutation } from '@/components/v2/modals/create-organization';
import type { CreateProjectMutation } from '@/components/v2/modals/create-project';
import type { CreateTarget_CreateTargetMutation } from '@/components/v2/modals/create-target';
import type { DeleteOrganizationDocument } from '@/components/v2/modals/delete-organization';
@ -68,6 +69,14 @@ const deleteAlerts: TypedDocumentNodeUpdateResolver<
}
};
const createOrganization: TypedDocumentNodeUpdateResolver<typeof CreateOrganizationMutation> = (
_data,
_args,
cache,
) => {
cache.invalidate('Query', 'organizations');
};
const deleteOrganization: TypedDocumentNodeUpdateResolver<typeof DeleteOrganizationDocument> = (
{ deleteOrganization },
_args,
@ -334,6 +343,7 @@ const createOperationInDocumentCollection: TypedDocumentNodeUpdateResolver<
// UpdateResolver
export const Mutation = {
createOrganization,
deleteOrganization,
createProject,
deleteProject,

View file

@ -35,6 +35,10 @@ export const urqlClient = createClient({
Mutation,
},
keys: {
// Member.id is not globally unique, it's really User.id
// In order to avoid conflicts or cache issues, let's use Member.temporaryFixId.
// This is a temporary solution until we have a better way to handle this.
Member: ({ temporaryFixId }) => `Member:${temporaryFixId}`,
RequestsOverTime: noKey,
FailuresOverTime: noKey,
DurationOverTime: noKey,

View file

@ -74,7 +74,6 @@ function useOidcProviderId() {
}
function SupertokensRoutingComponent() {
console.log('SupertokensRoutingComponent');
const routingComponent = getRoutingComponent([
ThirdPartyEmailPasswordPreBuiltUI,
EmailVerificationPreBuiltUI,
@ -97,7 +96,6 @@ const isOkta = () =>
* Route for showing the SuperTokens login page.
*/
export function AuthPage() {
console.log('AuthPage');
const [error, setError] = useState<string | null>(null);
const oidcProvider = useOidcProviderId();

View file

@ -19,6 +19,7 @@ const OrganizationPolicyPageQuery = graphql(`
organization {
id
me {
temporaryFixId
...CanAccessOrganization_MemberFragment
}
projects {

View file

@ -23,6 +23,7 @@ const OrganizationTransferPage_GetRequest = graphql(`
name
owner {
id
temporaryFixId
user {
id
displayName

View file

@ -18,6 +18,7 @@ const ProjectPolicyPageQuery = graphql(`
id
me {
id
temporaryFixId
...CanAccessProject_MemberFragment
}
}

View file

@ -827,6 +827,7 @@ const TargetLaboratoryPageQuery = graphql(`
id
me {
id
temporaryFixId
...CanAccessTarget_MemberFragment
}
}

View file

@ -1033,6 +1033,7 @@ const TargetSettingsPageQuery = graphql(`
cleanId
...TargetSettingsPage_OrganizationFragment
me {
temporaryFixId
...CDNAccessTokens_MeFragment
}
}

View file

@ -61,7 +61,6 @@ import { TargetLaboratoryPage } from './pages/target-laboratory';
import { TargetSettingsPage } from './pages/target-settings';
if (globalThis.window) {
console.log('Initializing SuperTokens');
SuperTokens.init(frontendConfig());
if (env.sentry) {
init({

View file

@ -67,7 +67,7 @@ importers:
version: 5.0.2(graphql@16.8.1)
'@graphql-codegen/cli':
specifier: 5.0.2
version: 5.0.2(@babel/core@7.24.0)(@types/node@20.12.12)(encoding@0.1.13)(enquirer@2.3.6)(graphql@16.8.1)(typescript@5.4.5)
version: 5.0.2(@babel/core@7.22.9)(@types/node@20.12.12)(encoding@0.1.13)(enquirer@2.3.6)(graphql@16.8.1)(typescript@5.4.5)
'@graphql-codegen/client-preset':
specifier: 4.2.6
version: 4.2.6(encoding@0.1.13)(graphql@16.8.1)
@ -91,7 +91,7 @@ importers:
version: 3.0.0(graphql@16.8.1)
'@graphql-eslint/eslint-plugin':
specifier: 3.20.1
version: 3.20.1(patch_hash=n437g5o7zq7pnxdxldn52uql2q)(@babel/core@7.24.0)(@types/node@20.12.12)(encoding@0.1.13)(graphql@16.8.1)
version: 3.20.1(patch_hash=n437g5o7zq7pnxdxldn52uql2q)(@babel/core@7.22.9)(@types/node@20.12.12)(encoding@0.1.13)(graphql@16.8.1)
'@graphql-inspector/cli':
specifier: 4.0.3
version: 4.0.3(@types/node@20.12.12)(encoding@0.1.13)(graphql@16.8.1)
@ -1982,7 +1982,7 @@ importers:
version: 1.0.7(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@theguild/components':
specifier: 6.5.3
version: 6.5.3(@types/react@18.3.2)(next@14.2.3(@babel/core@7.24.0)(@opentelemetry/api@1.8.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5)(webpack@5.89.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.19.11))
version: 6.5.3(@types/react@18.3.2)(next@14.2.3(@babel/core@7.22.9)(@opentelemetry/api@1.8.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5)(webpack@5.89.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.19.11))
clsx:
specifier: 2.1.1
version: 2.1.1
@ -1994,13 +1994,13 @@ importers:
version: 4.0.3
next:
specifier: 14.2.3
version: 14.2.3(@babel/core@7.24.0)(@opentelemetry/api@1.8.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
version: 14.2.3(@babel/core@7.22.9)(@opentelemetry/api@1.8.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
next-sitemap:
specifier: 4.2.3
version: 4.2.3(next@14.2.3(@babel/core@7.24.0)(@opentelemetry/api@1.8.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))
version: 4.2.3(next@14.2.3(@babel/core@7.22.9)(@opentelemetry/api@1.8.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))
next-themes:
specifier: '*'
version: 0.2.1(next@14.2.3(@babel/core@7.24.0)(@opentelemetry/api@1.8.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
version: 0.2.1(next@14.2.3(@babel/core@7.22.9)(@opentelemetry/api@1.8.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react:
specifier: 18.3.1
version: 18.3.1
@ -19746,7 +19746,7 @@ snapshots:
graphql: 16.8.1
tslib: 2.6.2
'@graphql-codegen/cli@5.0.2(@babel/core@7.24.0)(@types/node@20.12.12)(encoding@0.1.13)(enquirer@2.3.6)(graphql@16.8.1)(typescript@5.4.5)':
'@graphql-codegen/cli@5.0.2(@babel/core@7.22.9)(@types/node@20.12.12)(encoding@0.1.13)(enquirer@2.3.6)(graphql@16.8.1)(typescript@5.4.5)':
dependencies:
'@babel/generator': 7.23.6
'@babel/template': 7.22.15
@ -19756,7 +19756,7 @@ snapshots:
'@graphql-codegen/plugin-helpers': 5.0.3(graphql@16.8.1)
'@graphql-tools/apollo-engine-loader': 8.0.0(encoding@0.1.13)(graphql@16.8.1)
'@graphql-tools/code-file-loader': 8.1.0(graphql@16.8.1)
'@graphql-tools/git-loader': 8.0.1(@babel/core@7.24.0)(graphql@16.8.1)
'@graphql-tools/git-loader': 8.0.1(@babel/core@7.22.9)(graphql@16.8.1)
'@graphql-tools/github-loader': 8.0.0(@types/node@20.12.12)(encoding@0.1.13)(graphql@16.8.1)
'@graphql-tools/graphql-file-loader': 8.0.0(graphql@16.8.1)
'@graphql-tools/json-file-loader': 8.0.0(graphql@16.8.1)
@ -19960,11 +19960,11 @@ snapshots:
- encoding
- supports-color
'@graphql-eslint/eslint-plugin@3.20.1(patch_hash=n437g5o7zq7pnxdxldn52uql2q)(@babel/core@7.24.0)(@types/node@20.12.12)(encoding@0.1.13)(graphql@16.8.1)':
'@graphql-eslint/eslint-plugin@3.20.1(patch_hash=n437g5o7zq7pnxdxldn52uql2q)(@babel/core@7.22.9)(@types/node@20.12.12)(encoding@0.1.13)(graphql@16.8.1)':
dependencies:
'@babel/code-frame': 7.21.4
'@graphql-tools/code-file-loader': 7.3.23(@babel/core@7.24.0)(graphql@16.8.1)
'@graphql-tools/graphql-tag-pluck': 7.5.2(@babel/core@7.24.0)(graphql@16.8.1)
'@graphql-tools/code-file-loader': 7.3.23(@babel/core@7.22.9)(graphql@16.8.1)
'@graphql-tools/graphql-tag-pluck': 7.5.2(@babel/core@7.22.9)(graphql@16.8.1)
'@graphql-tools/utils': 9.2.1(graphql@16.8.1)
chalk: 4.1.2
debug: 4.3.4(supports-color@8.1.1)
@ -20035,7 +20035,7 @@ snapshots:
'@graphql-inspector/graphql-loader': 4.0.2(graphql@16.8.1)
'@graphql-inspector/introspect-command': 4.0.3(@graphql-inspector/config@4.0.2(graphql@16.8.1))(@graphql-inspector/loaders@4.0.3(@babel/core@7.22.9)(@graphql-inspector/config@4.0.2(graphql@16.8.1))(graphql@16.8.1))(graphql@16.8.1)(yargs@17.7.2)
'@graphql-inspector/json-loader': 4.0.2(graphql@16.8.1)
'@graphql-inspector/loaders': 4.0.3(@babel/core@7.24.0)(@graphql-inspector/config@4.0.2(graphql@16.8.1))(graphql@16.8.1)
'@graphql-inspector/loaders': 4.0.3(@babel/core@7.22.9)(@graphql-inspector/config@4.0.2(graphql@16.8.1))(graphql@16.8.1)
'@graphql-inspector/serve-command': 4.0.3(@graphql-inspector/config@4.0.2(graphql@16.8.1))(@graphql-inspector/loaders@4.0.3(@babel/core@7.22.9)(@graphql-inspector/config@4.0.2(graphql@16.8.1))(graphql@16.8.1))(graphql@16.8.1)(yargs@17.7.2)
'@graphql-inspector/similar-command': 4.0.3(@graphql-inspector/config@4.0.2(graphql@16.8.1))(@graphql-inspector/loaders@4.0.3(@babel/core@7.22.9)(@graphql-inspector/config@4.0.2(graphql@16.8.1))(graphql@16.8.1))(graphql@16.8.1)(yargs@17.7.2)
'@graphql-inspector/url-loader': 4.0.2(@types/node@20.12.12)(encoding@0.1.13)(graphql@16.8.1)
@ -20062,7 +20062,7 @@ snapshots:
'@graphql-inspector/commands@4.0.3(@graphql-inspector/config@4.0.2(graphql@16.8.1))(@graphql-inspector/loaders@4.0.3(@babel/core@7.22.9)(@graphql-inspector/config@4.0.2(graphql@16.8.1))(graphql@16.8.1))(graphql@16.8.1)(yargs@17.7.2)':
dependencies:
'@graphql-inspector/config': 4.0.2(graphql@16.8.1)
'@graphql-inspector/loaders': 4.0.3(@babel/core@7.24.0)(@graphql-inspector/config@4.0.2(graphql@16.8.1))(graphql@16.8.1)
'@graphql-inspector/loaders': 4.0.3(@babel/core@7.22.9)(@graphql-inspector/config@4.0.2(graphql@16.8.1))(graphql@16.8.1)
graphql: 16.8.1
tslib: 2.6.2
yargs: 17.7.2
@ -20165,10 +20165,10 @@ snapshots:
graphql: 16.8.1
tslib: 2.6.2
'@graphql-inspector/loaders@4.0.3(@babel/core@7.24.0)(@graphql-inspector/config@4.0.2(graphql@16.8.1))(graphql@16.8.1)':
'@graphql-inspector/loaders@4.0.3(@babel/core@7.22.9)(@graphql-inspector/config@4.0.2(graphql@16.8.1))(graphql@16.8.1)':
dependencies:
'@graphql-inspector/config': 4.0.2(graphql@16.8.1)
'@graphql-tools/code-file-loader': 8.0.1(@babel/core@7.24.0)(graphql@16.8.1)
'@graphql-tools/code-file-loader': 8.0.1(@babel/core@7.22.9)(graphql@16.8.1)
'@graphql-tools/load': 8.0.0(graphql@16.8.1)
'@graphql-tools/utils': 10.0.3(graphql@16.8.1)
graphql: 16.8.1
@ -20277,9 +20277,9 @@ snapshots:
tslib: 2.6.2
value-or-promise: 1.0.12
'@graphql-tools/code-file-loader@7.3.23(@babel/core@7.24.0)(graphql@16.8.1)':
'@graphql-tools/code-file-loader@7.3.23(@babel/core@7.22.9)(graphql@16.8.1)':
dependencies:
'@graphql-tools/graphql-tag-pluck': 7.5.2(@babel/core@7.24.0)(graphql@16.8.1)
'@graphql-tools/graphql-tag-pluck': 7.5.2(@babel/core@7.22.9)(graphql@16.8.1)
'@graphql-tools/utils': 9.2.1(graphql@16.8.1)
globby: 11.1.0
graphql: 16.8.1
@ -20313,18 +20313,6 @@ snapshots:
- '@babel/core'
- supports-color
'@graphql-tools/code-file-loader@8.0.1(@babel/core@7.24.0)(graphql@16.8.1)':
dependencies:
'@graphql-tools/graphql-tag-pluck': 8.0.1(@babel/core@7.24.0)(graphql@16.8.1)
'@graphql-tools/utils': 10.2.0(graphql@16.8.1)
globby: 11.1.0
graphql: 16.8.1
tslib: 2.6.2
unixify: 1.0.0
transitivePeerDependencies:
- '@babel/core'
- supports-color
'@graphql-tools/code-file-loader@8.1.0(graphql@16.8.1)':
dependencies:
'@graphql-tools/graphql-tag-pluck': 8.2.0(graphql@16.8.1)
@ -20504,19 +20492,6 @@ snapshots:
- '@babel/core'
- supports-color
'@graphql-tools/git-loader@8.0.1(@babel/core@7.24.0)(graphql@16.8.1)':
dependencies:
'@graphql-tools/graphql-tag-pluck': 8.0.1(@babel/core@7.24.0)(graphql@16.8.1)
'@graphql-tools/utils': 10.2.0(graphql@16.8.1)
graphql: 16.8.1
is-glob: 4.0.3
micromatch: 4.0.5
tslib: 2.6.2
unixify: 1.0.0
transitivePeerDependencies:
- '@babel/core'
- supports-color
'@graphql-tools/github-loader@8.0.0(@types/node@20.12.12)(encoding@0.1.13)(graphql@16.8.1)':
dependencies:
'@ardatan/sync-fetch': 0.0.1(encoding@0.1.13)
@ -20550,10 +20525,10 @@ snapshots:
tslib: 2.6.2
unixify: 1.0.0
'@graphql-tools/graphql-tag-pluck@7.5.2(@babel/core@7.24.0)(graphql@16.8.1)':
'@graphql-tools/graphql-tag-pluck@7.5.2(@babel/core@7.22.9)(graphql@16.8.1)':
dependencies:
'@babel/parser': 7.24.0
'@babel/plugin-syntax-import-assertions': 7.23.3(@babel/core@7.24.0)
'@babel/plugin-syntax-import-assertions': 7.23.3(@babel/core@7.22.9)
'@babel/traverse': 7.24.0
'@babel/types': 7.24.0
'@graphql-tools/utils': 9.2.1(graphql@16.8.1)
@ -20589,19 +20564,6 @@ snapshots:
- '@babel/core'
- supports-color
'@graphql-tools/graphql-tag-pluck@8.0.1(@babel/core@7.24.0)(graphql@16.8.1)':
dependencies:
'@babel/parser': 7.24.0
'@babel/plugin-syntax-import-assertions': 7.23.3(@babel/core@7.24.0)
'@babel/traverse': 7.24.0
'@babel/types': 7.24.0
'@graphql-tools/utils': 10.2.0(graphql@16.8.1)
graphql: 16.8.1
tslib: 2.6.2
transitivePeerDependencies:
- '@babel/core'
- supports-color
'@graphql-tools/graphql-tag-pluck@8.2.0(graphql@16.8.1)':
dependencies:
'@babel/core': 7.24.0
@ -24973,16 +24935,16 @@ snapshots:
'@theguild/buddy@0.1.0(patch_hash=ryylgra5xglhidfoiaxehn22hq)': {}
'@theguild/components@6.5.3(@types/react@18.3.2)(next@14.2.3(@babel/core@7.24.0)(@opentelemetry/api@1.8.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5)(webpack@5.89.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.19.11))':
'@theguild/components@6.5.3(@types/react@18.3.2)(next@14.2.3(@babel/core@7.22.9)(@opentelemetry/api@1.8.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5)(webpack@5.89.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.19.11))':
dependencies:
'@giscus/react': 3.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@next/bundle-analyzer': 13.4.2
clsx: 2.1.0
fuzzy: 0.1.3
next: 14.2.3(@babel/core@7.24.0)(@opentelemetry/api@1.8.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
next: 14.2.3(@babel/core@7.22.9)(@opentelemetry/api@1.8.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
next-videos: 1.5.0(webpack@5.89.0(@swc/core@1.5.7(@swc/helpers@0.5.5))(esbuild@0.19.11))
nextra: 3.0.0-alpha.22(@types/react@18.3.2)(next@14.2.3(@babel/core@7.24.0)(@opentelemetry/api@1.8.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5)
nextra-theme-docs: 3.0.0-alpha.22(next@14.2.3(@babel/core@7.24.0)(@opentelemetry/api@1.8.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(nextra@3.0.0-alpha.22(@types/react@18.3.2)(next@14.2.3(@babel/core@7.24.0)(@opentelemetry/api@1.8.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
nextra: 3.0.0-alpha.22(@types/react@18.3.2)(next@14.2.3(@babel/core@7.22.9)(@opentelemetry/api@1.8.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5)
nextra-theme-docs: 3.0.0-alpha.22(next@14.2.3(@babel/core@7.22.9)(@opentelemetry/api@1.8.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(nextra@3.0.0-alpha.22(@types/react@18.3.2)(next@14.2.3(@babel/core@7.22.9)(@opentelemetry/api@1.8.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
react-paginate: 8.2.0(react@18.3.1)
@ -32474,17 +32436,17 @@ snapshots:
neo-async@2.6.2: {}
next-sitemap@4.2.3(next@14.2.3(@babel/core@7.24.0)(@opentelemetry/api@1.8.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)):
next-sitemap@4.2.3(next@14.2.3(@babel/core@7.22.9)(@opentelemetry/api@1.8.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)):
dependencies:
'@corex/deepmerge': 4.0.43
'@next/env': 13.5.6
fast-glob: 3.3.2
minimist: 1.2.8
next: 14.2.3(@babel/core@7.24.0)(@opentelemetry/api@1.8.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
next: 14.2.3(@babel/core@7.22.9)(@opentelemetry/api@1.8.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
next-themes@0.2.1(next@14.2.3(@babel/core@7.24.0)(@opentelemetry/api@1.8.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
next-themes@0.2.1(next@14.2.3(@babel/core@7.22.9)(@opentelemetry/api@1.8.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
next: 14.2.3(@babel/core@7.24.0)(@opentelemetry/api@1.8.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
next: 14.2.3(@babel/core@7.22.9)(@opentelemetry/api@1.8.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
@ -32496,7 +32458,7 @@ snapshots:
transitivePeerDependencies:
- webpack
next@14.2.3(@babel/core@7.24.0)(@opentelemetry/api@1.8.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
next@14.2.3(@babel/core@7.22.9)(@opentelemetry/api@1.8.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
'@next/env': 14.2.3
'@swc/helpers': 0.5.5
@ -32506,7 +32468,7 @@ snapshots:
postcss: 8.4.31
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
styled-jsx: 5.1.1(@babel/core@7.24.0)(react@18.3.1)
styled-jsx: 5.1.1(@babel/core@7.22.9)(react@18.3.1)
optionalDependencies:
'@next/swc-darwin-arm64': 14.2.3
'@next/swc-darwin-x64': 14.2.3
@ -32522,7 +32484,7 @@ snapshots:
- '@babel/core'
- babel-plugin-macros
nextra-theme-docs@3.0.0-alpha.22(next@14.2.3(@babel/core@7.24.0)(@opentelemetry/api@1.8.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(nextra@3.0.0-alpha.22(@types/react@18.3.2)(next@14.2.3(@babel/core@7.24.0)(@opentelemetry/api@1.8.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5))(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
nextra-theme-docs@3.0.0-alpha.22(next@14.2.3(@babel/core@7.22.9)(@opentelemetry/api@1.8.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(nextra@3.0.0-alpha.22(@types/react@18.3.2)(next@14.2.3(@babel/core@7.22.9)(@opentelemetry/api@1.8.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5))(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
'@headlessui/react': 1.7.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@popperjs/core': 2.11.8
@ -32531,15 +32493,15 @@ snapshots:
flexsearch: 0.7.43
focus-visible: 5.2.0
intersection-observer: 0.12.2
next: 14.2.3(@babel/core@7.24.0)(@opentelemetry/api@1.8.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
next-themes: 0.2.1(next@14.2.3(@babel/core@7.24.0)(@opentelemetry/api@1.8.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
nextra: 3.0.0-alpha.22(@types/react@18.3.2)(next@14.2.3(@babel/core@7.24.0)(@opentelemetry/api@1.8.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5)
next: 14.2.3(@babel/core@7.22.9)(@opentelemetry/api@1.8.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
next-themes: 0.2.1(next@14.2.3(@babel/core@7.22.9)(@opentelemetry/api@1.8.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
nextra: 3.0.0-alpha.22(@types/react@18.3.2)(next@14.2.3(@babel/core@7.22.9)(@opentelemetry/api@1.8.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5)
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
scroll-into-view-if-needed: 3.1.0
zod: 3.23.8
nextra@3.0.0-alpha.22(@types/react@18.3.2)(next@14.2.3(@babel/core@7.24.0)(@opentelemetry/api@1.8.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5):
nextra@3.0.0-alpha.22(@types/react@18.3.2)(next@14.2.3(@babel/core@7.22.9)(@opentelemetry/api@1.8.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.4.5):
dependencies:
'@headlessui/react': 1.7.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@mdx-js/mdx': 3.0.1
@ -32557,7 +32519,7 @@ snapshots:
gray-matter: 4.0.3
hast-util-to-estree: 3.1.0
katex: 0.16.9
next: 14.2.3(@babel/core@7.24.0)(@opentelemetry/api@1.8.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
next: 14.2.3(@babel/core@7.22.9)(@opentelemetry/api@1.8.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
p-limit: 4.0.0
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
@ -35445,12 +35407,12 @@ snapshots:
hey-listen: 1.0.8
tslib: 2.6.2
styled-jsx@5.1.1(@babel/core@7.24.0)(react@18.3.1):
styled-jsx@5.1.1(@babel/core@7.22.9)(react@18.3.1):
dependencies:
client-only: 0.0.1
react: 18.3.1
optionalDependencies:
'@babel/core': 7.24.0
'@babel/core': 7.22.9
stylehacks@6.1.1(postcss@8.4.38):
dependencies:

View file

@ -0,0 +1,211 @@
/// @ts-check
const {
requireGraphQLSchemaFromContext,
requireSiblingsOperations,
} = require('@graphql-eslint/eslint-plugin');
const {
Kind,
TypeInfo,
visit,
visitWithTypeInfo,
GraphQLObjectType,
GraphQLInterfaceType,
GraphQLUnionType,
isListType,
isNonNullType,
} = require('graphql');
function getBaseType(type) {
if (isNonNullType(type) || isListType(type)) {
return getBaseType(type.ofType);
}
return type;
}
const RULE_ID = 'graphql-require-selections';
const idNames = ['temporaryFixId'];
/// Ported https://github.com/dimaMachina/graphql-eslint/blob/3c1020888472eb6579ffddc1e8e5ec16df8fad74/packages/plugin/src/rules/require-selections.ts
/**
* @type {import('@graphql-eslint/eslint-plugin').GraphQLESLintRule}
*/
const rule = {
meta: {
type: 'problem',
hasSuggestions: true,
messages: {
[RULE_ID]:
"Field{{ pluralSuffix }} {{ fieldName }} must be selected when it's available on a type.\nInclude it in your selection set{{ addition }}.",
},
docs: {
category: 'Operations',
description: 'Enforce selecting specific fields when they are available on the GraphQL type.',
requiresSchema: true,
requiresSiblings: true,
},
schema: [],
},
create(context) {
const schema = requireGraphQLSchemaFromContext(RULE_ID, context);
const siblings = requireSiblingsOperations(RULE_ID, context);
// Check selections only in OperationDefinition,
// skip selections of OperationDefinition and InlineFragment
const selector =
'OperationDefinition SelectionSet[parent.kind!=/(^OperationDefinition|InlineFragment)$/]';
const typeInfo = new TypeInfo(schema);
function checkFragments(node) {
for (const selection of node.selections) {
if (selection.kind !== Kind.FRAGMENT_SPREAD) {
continue;
}
const [foundSpread] = siblings.getFragment(selection.name.value);
if (!foundSpread) {
continue;
}
const checkedFragmentSpreads = new Set();
const visitor = visitWithTypeInfo(typeInfo, {
SelectionSet(node, key, _parent) {
const parent = _parent;
if (parent.kind === Kind.FRAGMENT_DEFINITION) {
checkedFragmentSpreads.add(parent.name.value);
} else if (parent.kind !== Kind.INLINE_FRAGMENT) {
checkSelections(
node,
typeInfo.getType(),
selection.loc.start,
parent,
checkedFragmentSpreads,
);
}
},
});
visit(foundSpread.document, visitor);
}
}
function checkSelections(
node,
type,
// Fragment can be placed in separate file
// Provide actual fragment spread location instead of location in fragment
loc,
// Can't access to node.parent in GraphQL AST.Node, so pass as argument
parent,
checkedFragmentSpreads = new Set(),
) {
const rawType = getBaseType(type);
if (rawType instanceof GraphQLObjectType || rawType instanceof GraphQLInterfaceType) {
checkFields(rawType);
} else if (rawType instanceof GraphQLUnionType) {
for (const selection of node.selections) {
if (selection.kind === Kind.INLINE_FRAGMENT) {
const types = rawType.getTypes();
const t = types.find(t => t.name === selection.typeCondition.name.value);
if (t) {
checkFields(t);
}
}
}
}
function checkFields(rawType) {
const fields = rawType.getFields();
const hasIdFieldInType = idNames.some(name => fields[name]);
if (!hasIdFieldInType) {
return;
}
function hasIdField({ selections }) {
return selections.some(selection => {
if (selection.kind === Kind.FIELD) {
if (selection.alias && idNames.includes(selection.alias.value)) {
return true;
}
return idNames.includes(selection.name.value);
}
if (selection.kind === Kind.INLINE_FRAGMENT) {
return hasIdField(selection.selectionSet);
}
if (selection.kind === Kind.FRAGMENT_SPREAD) {
const [foundSpread] = siblings.getFragment(selection.name.value);
if (foundSpread) {
const fragmentSpread = foundSpread.document;
checkedFragmentSpreads.add(fragmentSpread.name.value);
return hasIdField(fragmentSpread.selectionSet);
}
}
return false;
});
}
const hasId = hasIdField(node);
checkFragments(node);
if (hasId) {
return;
}
const pluralSuffix = idNames.length > 1 ? 's' : '';
const fieldName = idNames.join(',');
const addition =
checkedFragmentSpreads.size === 0
? ''
: ` or add to used fragment${
checkedFragmentSpreads.size > 1 ? 's' : ''
} ${Array.from(checkedFragmentSpreads).join(', ')}`;
const problem = {
loc,
messageId: RULE_ID,
data: {
pluralSuffix,
fieldName,
addition,
},
};
// Don't provide suggestions for selections in fragments as fragment can be in a separate file
if ('type' in node) {
problem.suggest = idNames.map(idName => ({
desc: `Add \`${idName}\` selection`,
fix: fixer => {
let insertNode = node.selections[0];
insertNode =
insertNode.kind === Kind.INLINE_FRAGMENT
? insertNode.selectionSet.selections[0]
: insertNode;
return fixer.insertTextBefore(insertNode, `${idName} `);
},
}));
}
context.report(problem);
}
}
return {
[selector](node) {
const typeInfo = node.typeInfo();
if (typeInfo.gqlType) {
checkSelections(node, typeInfo.gqlType, node.loc.start, node.parent);
}
},
};
},
};
module.exports = rule;

View file

@ -1,5 +1,6 @@
module.exports = {
rules: {
'enforce-deps-in-dev': require('./enforce-deps-in-dev.cjs'),
'graphql-require-selection': require('./graphql-require-selection.cjs'),
},
};