Add InterfaceObjectAttribute - #10371
Conversation
Adds InterfaceObjectAttribute and the matching InterfaceObject() descriptor extension to HotChocolate.Types.Composite, mirroring the sibling composite directive attributes. The attribute implements ITypeAttribute so a class annotated only with [InterfaceObject] and registered via AddType<T>() becomes an object type stand-in with the @interfaceObject directive applied, with no extra [ObjectType] needed.
…<T>] in the source generator Adds the generic InterfaceObjectAttribute<T> marker next to the existing reflection attribute so a static partial resolver class can declare an @interfaceObject stand-in without also needing [ObjectType<T>]. The generator now emits the @interfaceObject directive for the generic form and auto-registers a bare runtime class carrying the non-generic attribute through the generated module's AddTypes(), matching how [ObjectType] already self-registers. The non-generic attribute combined with [ObjectType<T>] on a resolver class keeps flowing through as a plain descriptor attribute, so that existing usage is unaffected.
…leak Wraps three lines that exceeded 120 characters in the InterfaceObject descriptor extension, the analyzer inspector, and the integration test method name. Also turns the ProgrammeLookupType XML doc comment into a plain comment so it no longer leaks into the shared Query type description in the integration test schema snapshots.
…ceObject<T>] Reporting HC0134 and skipping generation when both attributes are present on the same class, regardless of declaration order.
…e descriptor extensions Adds a generic IObjectTypeDescriptor<T> overload for each existing composite descriptor extension declared on the non-generic IObjectTypeDescriptor (InterfaceObject, EntityKey, Inaccessible, Shareable, Internal). Each overload delegates to the existing non-generic implementation via a cast, since the concrete ObjectTypeDescriptorBase<T> already implements both interfaces. This lets ObjectType<T>.Configure(IObjectTypeDescriptor<T>) call these fluent methods without an explicit cast.
Adds one ObjectType<T>.Configure snapshot test per generic overload (InterfaceObject, EntityKey, Inaccessible, Shareable, Internal), exercising each through IObjectTypeDescriptor<T> without a cast.
…fixture InterfaceObject() and EntityKey() now have IObjectTypeDescriptor<T> overloads, so the fluent MediaType fixture no longer needs to cast to the non-generic IObjectTypeDescriptor to call them.
There was a problem hiding this comment.
🟡 Changes recommended
There are a few doc-comment issues in newly added/modified regions (including malformed XML) that should be fixed to avoid documentation build breaks and incorrect API docs.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Adds first-class support for @interfaceObject in code-first scenarios by introducing InterfaceObjectAttribute (runtime + source-generator forms), wiring it into the source generator/analyzers, and expanding the fluent descriptor API to work cleanly with generic IObjectTypeDescriptor<T>.
Changes:
- Introduce
InterfaceObjectAttribute/InterfaceObjectAttribute<T>plus fluentInterfaceObject()extensions and generator/analyzer support (including a conflict diagnostic with[ObjectType<T>]). - Add generic fluent descriptor overloads for composite directives (
Shareable,Internal,Inaccessible,EntityKey) and corresponding tests/snapshots. - Add Fusion gateway coverage for interface-object stand-ins declared via attribute and via fluent descriptor.
File summaries
| File | Description |
|---|---|
| src/HotChocolate/Primitives/src/Primitives/ErrorCodes.cs | Adds analyzer error code for ObjectType/InterfaceObject attribute conflicts. |
| src/HotChocolate/Fusion/test/Fusion.AspNetCore.Tests/InterfaceObjectTests.cs | Adds Fusion integration tests covering attribute- and fluent-declared interface-object stand-ins. |
| src/HotChocolate/Fusion/test/Fusion.AspNetCore.Tests/snapshots/InterfaceObjectTests.CodeFirst_StandIn_With_Fluent_Descriptor_Resolves_Lookup_Through_Covering_Schema.yaml | Snapshot for fluent stand-in lookup resolution through covering schema. |
| src/HotChocolate/Fusion/test/Fusion.AspNetCore.Tests/snapshots/InterfaceObjectTests.CodeFirst_StandIn_With_Fluent_Descriptor_Recovers_TypeName_Through_Covering_Lookup.yaml | Snapshot for fluent stand-in __typename recovery through covering lookup. |
| src/HotChocolate/Fusion/test/Fusion.AspNetCore.Tests/snapshots/InterfaceObjectTests.CodeFirst_StandIn_With_Attribute_Resolves_Lookup_Through_Covering_Schema.yaml | Snapshot for attribute stand-in lookup resolution through covering schema. |
| src/HotChocolate/Fusion/test/Fusion.AspNetCore.Tests/snapshots/InterfaceObjectTests.CodeFirst_StandIn_With_Attribute_Recovers_TypeName_Through_Covering_Lookup.yaml | Snapshot for attribute stand-in __typename recovery through covering lookup. |
| src/HotChocolate/Fusion/test/Fusion.AspNetCore.Tests/snapshots/InterfaceObjectTests.CodeFirst_StandIn_Declared_With_InterfaceObjectAttribute_Resolves.yaml | Snapshot for a stand-in declared solely via [InterfaceObject]. |
| src/HotChocolate/Core/test/Types.Tests/Types/Composite/ShareableTests.cs | Adds test ensuring descriptor.Shareable() works on IObjectTypeDescriptor<T> without casting. |
| src/HotChocolate/Core/test/Types.Tests/Types/Composite/InternalTests.cs | New test ensuring descriptor.Internal() works on IObjectTypeDescriptor<T>. |
| src/HotChocolate/Core/test/Types.Tests/Types/Composite/InterfaceObjectTests.cs | New tests for [InterfaceObject] and fluent InterfaceObject() APIs (including generic descriptor). |
| src/HotChocolate/Core/test/Types.Tests/Types/Composite/InaccessibleTests.cs | New test ensuring descriptor.Inaccessible() works on IObjectTypeDescriptor<T>. |
| src/HotChocolate/Core/test/Types.Tests/Types/Composite/snapshots/ShareableTests.GenericShareableType_Is_Shareable_Fluent_Generic.graphql | Snapshot for generic shareable fluent test schema. |
| src/HotChocolate/Core/test/Types.Tests/Types/Composite/snapshots/InternalTests.Internal_Should_ApplyDirective_When_UsedOnGenericObjectTypeDescriptor.graphql | Snapshot for generic internal fluent test schema. |
| src/HotChocolate/Core/test/Types.Tests/Types/Composite/snapshots/InterfaceObjectTests.Programme_Is_InterfaceObject.graphql | Snapshot for attribute-driven interface object schema. |
| src/HotChocolate/Core/test/Types.Tests/Types/Composite/snapshots/InterfaceObjectTests.Programme_Is_InterfaceObject_Fluent.graphql | Snapshot for non-generic fluent interface object schema. |
| src/HotChocolate/Core/test/Types.Tests/Types/Composite/snapshots/InterfaceObjectTests.Programme_Is_InterfaceObject_Fluent_Generic.graphql | Snapshot for generic fluent interface object schema. |
| src/HotChocolate/Core/test/Types.Tests/Types/Composite/snapshots/InterfaceObjectTests.InterfaceObject_With_Explicit_Name_Renames_Type.graphql | Snapshot for named [InterfaceObject("...")] behavior. |
| src/HotChocolate/Core/test/Types.Tests/Types/Composite/snapshots/InaccessibleTests.Inaccessible_Should_ApplyDirective_When_UsedOnGenericObjectTypeDescriptor.graphql | Snapshot for generic inaccessible fluent test schema. |
| src/HotChocolate/Core/test/Types.Analyzers.Tests/TypeModuleSyntaxGeneratorTests.cs | Adds generator test ensuring non-generic [InterfaceObject] on runtime class is module-registered. |
| src/HotChocolate/Core/test/Types.Analyzers.Tests/InterfaceObjectTests.cs | New analyzer snapshot tests for InterfaceObject attribute handling and error cases. |
| src/HotChocolate/Core/test/Types.Analyzers.Tests/snapshots/TypeModuleSyntaxGeneratorTests.GenerateSource_NonGeneric_InterfaceObject_On_RuntimeClass_RegistersType.md | Snapshot verifying type module registers [InterfaceObject] runtime class. |
| src/HotChocolate/Core/test/Types.Analyzers.Tests/snapshots/InterfaceObjectTests.NonGeneric_Attribute_On_Runtime_Class_MatchesSnapshot.md | Snapshot verifying module registration for runtime [InterfaceObject]. |
| src/HotChocolate/Core/test/Types.Analyzers.Tests/snapshots/InterfaceObjectTests.NonGeneric_Attribute_Combined_With_ObjectType_MatchesSnapshot.md | Snapshot verifying [ObjectType<T>] + non-generic [InterfaceObject] stays descriptor-only. |
| src/HotChocolate/Core/test/Types.Analyzers.Tests/snapshots/InterfaceObjectTests.Generic_ObjectType_And_Generic_InterfaceObject_Combined_RaisesError.md | Snapshot verifying new HC0134 conflict diagnostic. |
| src/HotChocolate/Core/test/Types.Analyzers.Tests/snapshots/InterfaceObjectTests.Generic_InterfaceObject_And_Generic_ObjectType_Combined_RaisesError.md | Snapshot verifying conflict diagnostic regardless of attribute order. |
| src/HotChocolate/Core/test/Types.Analyzers.Tests/snapshots/InterfaceObjectTests.Generic_Attribute_On_Static_Resolver_Class_MatchesSnapshot.md | Snapshot verifying generic [InterfaceObject<T>] is emitted correctly. |
| src/HotChocolate/Core/test/Types.Analyzers.Tests/snapshots/InterfaceObjectTests.Generic_Attribute_On_NonStatic_Class_RaisesError.md | Snapshot verifying error for invalid (non-static) resolver class usage. |
| src/HotChocolate/Core/test/Types.Analyzers.Tests/snapshots/InterfaceObjectTests.Generic_Attribute_On_NonPartial_Class_RaisesError.md | Snapshot verifying error for invalid (non-partial) resolver class usage. |
| src/HotChocolate/Core/test/Types.Analyzers.Integration.Tests/InterfaceObjectTypes.cs | Adds integration types using both generic and non-generic InterfaceObject attribute forms. |
| src/HotChocolate/Core/test/Types.Analyzers.Integration.Tests/InterfaceObjectTests.cs | Adds integration assertions for SDL printing and lookup behavior. |
| src/HotChocolate/Core/test/Types.Analyzers.Integration.Tests/snapshots/IntegrationTests.Schema_Snapshot.snap | Updates integration schema snapshot with interface object additions. |
| src/HotChocolate/Core/test/Types.Analyzers.Integration.Tests/snapshots/IntegrationTests.Schema_Snapshot_Without_ConnectionName_Inference.snap | Updates alternate integration schema snapshot accordingly. |
| src/HotChocolate/Core/src/Types/Types/Composite/Directives/ShareableDirectiveExtensions.cs | Adds generic Shareable<T> fluent overload. |
| src/HotChocolate/Core/src/Types/Types/Composite/Directives/InternalDirectiveExtensions.cs | Adds generic Internal<T> fluent overload. |
| src/HotChocolate/Core/src/Types/Types/Composite/Directives/InterfaceObjectDescriptorExtensions.cs | New fluent InterfaceObject() extensions (generic + non-generic descriptors). |
| src/HotChocolate/Core/src/Types/Types/Composite/Directives/InterfaceObjectAttribute.cs | New attribute(s) enabling runtime and generator-based interface-object declarations. |
| src/HotChocolate/Core/src/Types/Types/Composite/Directives/InaccessibleDescriptorExtensions.cs | Adds generic Inaccessible<T> fluent overload. |
| src/HotChocolate/Core/src/Types/Types/Composite/Directives/EntityKeyDescriptorExtensions.cs | Adds generic EntityKey<T> fluent overload. |
| src/HotChocolate/Core/src/Types.Analyzers/WellKnownTypes.cs | Adds well-known directive type constant for InterfaceObject. |
| src/HotChocolate/Core/src/Types.Analyzers/WellKnownAttributes.cs | Registers InterfaceObjectAttribute as a recognized/bindable attribute for generation. |
| src/HotChocolate/Core/src/Types.Analyzers/Models/ObjectTypeInfo.cs | Tracks whether an object type is an interface object for generation. |
| src/HotChocolate/Core/src/Types.Analyzers/Models/InvalidSyntaxInfo.cs | Adds a syntax-info carrier for diagnostics-only (no generation) cases. |
| src/HotChocolate/Core/src/Types.Analyzers/Inspectors/TypeAttributeInspector.cs | Prevents accidental runtime-type registration for resolver classes with [ObjectType<T>] + [InterfaceObject]. |
| src/HotChocolate/Core/src/Types.Analyzers/Inspectors/ObjectTypeInspector.cs | Adds detection of [InterfaceObject<T>], conflict diagnostic, and propagation of interface-object flag. |
| src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/ObjectTypeFileBuilder.cs | Emits @interfaceObject directive when the inspected object type is marked as interface object. |
| src/HotChocolate/Core/src/Types.Analyzers/Errors.cs | Adds the new diagnostic descriptor for conflicting object-type attributes. |
Review details
- Files reviewed: 46/46 changed files
- Comments generated: 3
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| /// <exception cref="ArgumentNullException"> | ||
| /// The <paramref name="descriptor"/> or the paramref name="fields"/> parameter is <c>null</c>. | ||
| /// </exception> |
| /// <param name="scoped"> | ||
| /// if <c>true</c> and this directive is applied to an object type extension, | ||
| /// it will only be applied to the fields that this object type extension exposes. | ||
| /// if scoped is <c>false</c> the directive will be applied to the type and will thus make all | ||
| /// fields of the type sharable. | ||
| /// </param> |
| /// <param name="scoped"> | ||
| /// if <c>true</c> and this directive is applied to an object type extension, | ||
| /// it will only be applied to the fields that this object type extension exposes. | ||
| /// if scoped is <c>false</c> the directive will be applied to the type and will thus make all | ||
| /// fields of the type sharable. | ||
| /// </param> |
…Provider AppendText now recurses into <para> children instead of using their raw .Value, so a paragraph that only wraps <see href> keeps its link. Blocks (<para>, <code>) are normalized independently and joined by a single blank line, and whitespace-only lines inside a block become fully empty instead of leaking a shorter or longer indent run into the rendered description.
The XmlDocumentationProvider fix changes how <para>/<code> blocks in the composite directive XML docs render: consecutive paragraphs now join with a single blank line, a <see href> wrapped in its own <para> keeps its link, and blank lines inside a <code> block no longer leak leftover indentation. Every refreshed snapshot and inline snapshot below differs only in that description text.
Patch coverage93.1% of changed lines covered (231/248) Uncovered changed lines (JSON){
"sha": "2614e74a48271055b30c91dbc29d425fb92ec6bb",
"files": [
{ "path": "src/HotChocolate/Core/src/Types.Analyzers/Models/InvalidSyntaxInfo.cs", "ranges": [[11, 11], [14, 14], [17, 17], [20, 20]] },
{ "path": "src/HotChocolate/Core/src/Types/Types/Composite/Directives/InterfaceObjectAttribute.cs", "ranges": [[36, 36], [64, 64]] },
{ "path": "src/HotChocolate/Core/src/Types.Analyzers/Inspectors/TypeAttributeInspector.cs", "ranges": [[138, 139]] },
{ "path": "src/HotChocolate/Core/src/Types/Types/Descriptors/Conventions/XmlDocumentationProvider.cs", "ranges": [[231, 233], [242, 243], [269, 270], [289, 290]] }
]
}Project coverage: 57.9% (288471/498098 lines) |
No description provided.