Create separate GraphQL type, resolver and interface for Group.dependencies

Context

Currently, Project.dependencies and Group.dependencies both share the same GraphQL type (and hence resolver).

In this discussion: #513524 (comment 2417628391) it was decided to create a new, separate type for the Group.dependencies (DependencyAggregation).

Since both the Dependency- and DependencyAggreation-type share data, and to DRY up the FE Queries, we can introduce an interface type and share some of the data.

classDiagram
    %% Interface
    class DependencyEntityType {
        <<interface>>
        +id: GlobalIDType
        +name: String
        +version: String
        +packager: PackageManagerEnum
        +location: LocationType
        +licenses: [LicenseType]
        +reachability: ReachabilityEnum
        +vulnerability_count: Int
    }
    
    %% Concrete Types
    class DependencyType {
        +implements DependencyEntityType
        +authorize :read_dependency
    }
    
    class DependencyAggregationType {
        +implements DependencyEntityType
        +occurrence_count: Int
    }
    
    %% Relationships
    DependencyEntityType <|.. DependencyType
    DependencyEntityType <|.. DependencyAggregationType
    
    %% Used in
    class ProjectType {
        +dependencies: DependencyType.connection_type
    }
    
    class GroupType {
        +dependencies: DependencyAggregationType.connection_type
    }
    
    ProjectType --> DependencyType : exposes
    GroupType --> DependencyAggregationType : exposes

See this POC for details: !186099

Note: This will allow us to create a query like the below example:

query GetDependencies($fullPath: ID!, $first: Int = 20) {
  namespace(fullPath: $fullPath) {
    id
    name
    
    # This will work for both project and group namespaces
    dependencies(first: $first) {
      pageInfo {
        hasNextPage
        endCursor
      }
      nodes {
        ...DependencyFields
        
        # Fields specific to DependencyAggregationType (only available for groups)
        ... on DependencyAggregation {
          occurrenceCount
        }
      }
    }
  }
}

# Common fields defined by the DependencyEntityType interface
fragment DependencyFields on DependencyEntity {
  id
  name
  version
  packager
  location {
    path
    lineNumber
  }
  licenses {
    name
    url
  }
  reachability
  vulnerabilityCount
}

Implementation Details

GraphQL Types:
  • Create DependencyEntityType interface with all common fields between group and project level
  • Keep the existing DependencyType for project dependencies and refer to DependencyEntityType
  • Create DependencyAggregationType for group dependencies with the occurrence_count field
Resolvers:
  • Split existing Resolvers::Sbom::DependenciesResolver into:
    • Update for individual dependencies (project context)
    • Create new for aggregated dependencies (group context)
Tests
  • Update ee/spec/graphql/types/sbom/dependency_type_spec.rb
  • Create specs for DependencyEntityType interface
  • Create specs for DependencyAggregationType
Edited by Charlie Kroon