github

package
v1.45.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 17, 2025 License: BSD-3-Clause Imports: 18 Imported by: 0

Documentation

Overview

Copyright 2025 SGNL.ai, Inc.

Copyright 2025 SGNL.ai, Inc.

Copyright 2025 SGNL.ai, Inc.

Copyright 2025 SGNL.ai, Inc.

Copyright 2025 SGNL.ai, Inc.

Copyright 2025 SGNL.ai, Inc.

Copyright 2025 SGNL.ai, Inc.

Index

Constants

View Source
const (
	Organization           string = "Organization"
	OrganizationUser       string = "OrganizationUser"
	User                   string = "User"
	OVDE                   string = "$.node.organizationVerifiedDomainEmails"
	Team                   string = "Team"
	TeamMember             string = "$.members.edges"
	TeamRepository         string = "$.repositories.edges"
	Repository             string = "Repository"
	RepositoryCollaborator string = "$.collaborators.edges"
	Collaborator           string = "Collaborator"
	Label                  string = "Label"
	IssueLabel             string = "IssueLabel"
	PullRequestLabel       string = "PullRequestLabel"
	Issue                  string = "Issue"
	IssueAssignee          string = "IssueAssignee"
	IssueParticipant       string = "IssueParticipant"
	PullRequest            string = "PullRequest"
	PullRequestChangedFile string = "PullRequestChangedFile"
	PullRequestReview      string = "PullRequestReview"
	PullRequestCommit      string = "PullRequestCommit"
	PullRequestAssignee    string = "PullRequestAssignee"
	PullRequestParticipant string = "PullRequestParticipant"
	SecretScanningAlert    string = "SecretScanningAlert"
)
View Source
const (
	EnterpriseCloud  string = "EnterpriseCloud"
	EnterpriseServer string = "EnterpriseServer"
)
View Source
const (
	CollectionPageSize = 1
)

Variables

View Source
var (
	EndpointMappings = map[string]DeploymentInfo{
		EnterpriseCloud: {
			GraphQLBasePath: "/graphql",
			RESTBasePath:    "",
			RESTEndpoints: map[string]map[string]string{
				SecretScanningAlert: {
					"enterprise":   "/enterprises/%s/secret-scanning/alerts",
					"organization": "/orgs/%s/secret-scanning/alerts",
				},
			},
		},
		EnterpriseServer: {
			GraphQLBasePath: "/api/graphql",
			RESTBasePath:    "/api/%s",
			RESTEndpoints: map[string]map[string]string{
				SecretScanningAlert: {
					"enterprise":   "/enterprises/%s/secret-scanning/alerts",
					"organization": "/orgs/%s/secret-scanning/alerts",
				},
			},
		},
	}
)
View Source
var (
	// ValidEntityExternalIDs is a set of valid external IDs of entities that can be queried.
	// The map value is the Entity struct which contains the unique ID attribute.
	ValidEntityExternalIDs = map[string]Entity{
		Organization: {
			UniqueExternalIDAttribute: "id",
			CollectionEntityConfig:    PopulateOrganizationCollectionConfig(),
			ParsePath:                 []string{"Enterprise", "Organizations", "Nodes"},
		},
		OrganizationUser: {
			UniqueExternalIDAttribute: "uniqueId",
			RequiredAttributes:        []string{"$.node.id", "orgId"},
			CollectionAttribute:       "login",
			MemberOf: func() *string {
				s := Organization

				return &s
			}(),
			ParsePath: []string{"Organization", "Users", "Edges"},
		},
		User: {
			UniqueExternalIDAttribute: "id",
			ParsePath:                 []string{"Enterprise", "Organizations", "Nodes", "Organization", "Users", "Nodes"},
		},
		Team: {
			UniqueExternalIDAttribute: "id",
			ParsePath:                 []string{"Enterprise", "Organizations", "Nodes", "Organization", "Teams", "Nodes"},
		},

		TeamMember: {
			UniqueExternalIDAttribute: "$.node.id",
		},

		TeamRepository: {
			UniqueExternalIDAttribute: "$.node.id",
		},
		Repository: {
			UniqueExternalIDAttribute: "id",
			ParsePath:                 []string{"Enterprise", "Organizations", "Nodes", "Organization", "Repositories", "Nodes"},
		},

		RepositoryCollaborator: {
			UniqueExternalIDAttribute: "id",
		},
		Collaborator: {
			UniqueExternalIDAttribute: "id",
			ParsePath: []string{"Enterprise", "Organizations", "Nodes", "Organization",
				"Repositories", "Nodes", "Repository", "Collaborators", "Nodes"},
		},
		Label: {
			UniqueExternalIDAttribute: "id",
			ParsePath: []string{"Enterprise", "Organizations", "Nodes", "Organization",
				"Repositories", "Nodes", "Repository", "Labels", "Nodes"},
		},
		IssueLabel: {
			UniqueExternalIDAttribute: "uniqueId",
			RequiredAttributes:        []string{"labelId", "id"},
			ParsePath: []string{"Enterprise", "Organizations", "Nodes", "Organization",
				"Repositories", "Nodes", "Repository", "Labels", "Nodes", "Label", "Issues", "Nodes"},
		},
		PullRequestLabel: {
			UniqueExternalIDAttribute: "uniqueId",
			RequiredAttributes:        []string{"labelId", "id"},
			ParsePath: []string{"Enterprise", "Organizations", "Nodes", "Organization",
				"Repositories", "Nodes", "Repository", "Labels", "Nodes", "Label", "PullRequests", "Nodes"},
		},
		Issue: {
			UniqueExternalIDAttribute: "id",
			ParsePath: []string{"Enterprise", "Organizations", "Nodes", "Organization",
				"Repositories", "Nodes", "Repository", "Issues", "Nodes"},
		},
		IssueAssignee: {
			UniqueExternalIDAttribute: "uniqueId",
			RequiredAttributes:        []string{"issueId", "id"},
			ParsePath: []string{"Enterprise", "Organizations", "Nodes", "Organization",
				"Repositories", "Nodes", "Repository", "Issues", "Nodes", "Issue", "Assignees", "Nodes"},
		},
		IssueParticipant: {
			UniqueExternalIDAttribute: "uniqueId",
			RequiredAttributes:        []string{"issueId", "id"},
			ParsePath: []string{"Enterprise", "Organizations", "Nodes", "Organization",
				"Repositories", "Nodes", "Repository", "Issues", "Nodes", "Issue", "Participants", "Nodes"},
		},
		PullRequest: {
			UniqueExternalIDAttribute: "id",
			ParsePath: []string{"Enterprise", "Organizations", "Nodes", "Organization",
				"Repositories", "Nodes", "Repository", "PullRequests", "Nodes"},
		},
		PullRequestChangedFile: {
			UniqueExternalIDAttribute: "uniqueId",
			RequiredAttributes:        []string{"pullRequestId", "path"},
			ParsePath: []string{"Enterprise", "Organizations", "Nodes", "Organization",
				"Repositories", "Nodes", "Repository", "PullRequests", "Nodes", "PullRequest", "ChangedFiles", "Nodes"},
		},
		PullRequestReview: {
			UniqueExternalIDAttribute: "id",
			ParsePath: []string{"Enterprise", "Organizations", "Nodes", "Organization",
				"Repositories", "Nodes", "Repository", "PullRequests", "Nodes", "PullRequest", "Reviews", "Nodes"},
		},
		PullRequestCommit: {
			UniqueExternalIDAttribute: "id",
			ParsePath: []string{"Enterprise", "Organizations", "Nodes", "Organization",
				"Repositories", "Nodes", "Repository", "PullRequests", "Nodes", "PullRequest", "Commits", "Nodes"},
		},
		PullRequestAssignee: {
			UniqueExternalIDAttribute: "uniqueId",
			RequiredAttributes:        []string{"pullRequestId", "id"},
			ParsePath: []string{"Enterprise", "Organizations", "Nodes", "Organization",
				"Repositories", "Nodes", "Repository", "PullRequests", "Nodes", "PullRequest", "Assignees", "Nodes"},
		},
		PullRequestParticipant: {
			UniqueExternalIDAttribute: "uniqueId",
			RequiredAttributes:        []string{"pullRequestId", "id"},
			ParsePath: []string{"Enterprise", "Organizations", "Nodes", "Organization",
				"Repositories", "Nodes", "Repository", "PullRequests", "Nodes", "PullRequest", "Participants", "Nodes"},
		},
		SecretScanningAlert: {
			UniqueExternalIDAttribute: "number",
			// contains filtered or unexported fields
		},
	}
)

Functions

func ConstructQuery

func ConstructQuery(request *Request) (string, *framework.Error)

func ConvertEntitiesToObjects

func ConvertEntitiesToObjects(entities json.RawMessage) (objects []map[string]any, unmarshalErr *framework.Error)

func ConvertOVDEAttribute

func ConvertOVDEAttribute(objects *[]map[string]any)

OVDE is originally a list of strings, so we need to convert it to a list of JSON objects to allow it to be parsed as a child entity.

func GetAttributePath

func GetAttributePath(input string) []string

[sc-22880] TODO: Change this to use extractor package. GetAttributePath extracts the attribute name from a simple JSON path string. It is designed to work with basic JSON path expressions that start with '$' and are followed by direct attribute names (e.g., `$.name`). This function does not support complex JSON path queries (like `$..["$ref"]`), and it is important to note this limitation when extending or using this adapter.

The function assumes the JSON path is simple and directly references an attribute without nested or recursive structures. For example, it can handle `$.name` but not `$..["$ref"]`. This limitation stems from the current design, where the function is used to dynamically construct GraphQL queries based on the requested attributes.

func GetCollectionEntityInterface

func GetCollectionEntityInterface(
	entities json.RawMessage,
	entityName string,
	collections *ContainerLayers,
) (
	any,
	*framework.Error,
)

GetCollectionEntityInterface returns uses the entityName to retrieve the appropriate collection struct and returns it as an interface.

func GetPageInfoAfter

func GetPageInfoAfter(pageInfo *PageInfo, n int, orgListProvided *bool) *string

This returns the PageInfo struct of the n deep layer, where 0 is the outermost layer. If n > number of layers or the pageInfo is nil, the function returns nil. If n < 0, the function returns the PageInfo of the outermost layer. The 3rd argument if the value of `n` is decremented by 1 or not. If the request is for an enterprise, keep the value of `n` as-is. If the request is for an organization, decrement `n` by 1. If an organization is passed in, the query does not need to worry about fetching the next organization for the enterprise and will not need to populate the `OrgAfter` field. This will reduce the number of `PageInfo` objects that need to be passed around by 1.

func GetValueFromPath

func GetValueFromPath(object map[string]any, path []string) (string, error)

GetValueFromPath retrieves the value of a nested attribute from an object. This function assume that the nested attribute is a string, and throws an error if it is not.

func InjectCommonFields

func InjectCommonFields(
	objects *[]map[string]any,
	container *ContainerLayers,
	externalID string,
) *framework.Error

InjectCommonFields adds common fields to objects for building relationships based on externalID. It appends 'id' attributes for specific entities and handles post-processing tasks like creating uniqueId. The function ensures that required fields are present and forms relationships between objects.

func InjectUniqueID

func InjectUniqueID(
	objects *[]map[string]any,
	index int,
	externalID string,
	attrA string,
	attrB string,
) *framework.Error

InjectUniqueID adds the uniqueId field to objects in the format of 'attrA-attrB'.

func NewAdapter

func NewAdapter(client Client) framework.Adapter[Config]

NewAdapter instantiates a new Adapter.

func ParseAndAssignCollection

func ParseAndAssignCollection[T any](
	entities json.RawMessage,
	entityName string,
	collectionField **T,
) (
	any,
	*framework.Error,
)

ParseAndAssignCollection is a helper function for GetCollectionEntityInterface function that retrieves the collection from the ParseContainerEntity function and assigns it to the appropriate collection field.

func ParseContainerEntity

func ParseContainerEntity[T any](entities json.RawMessage, entityName string) (*T, *framework.Error)
ParseContainerEntity will attempt to parse the entities json into an array of T.

* Case 1: If the unmarshaling fails, an error is returned. * Case 2: If the container is empty, nil is returned to indicate that the sub-entities should * not be parsed. Empty containers are expected for certain entity types. The caller can determine * if an empty container is an error for type T. * Case 3: The container should never have a length greater than 1, as it is expected to * be a single entity. The function will return an error if the container length is not 1. * Case 4: The function will return the first element of the container as a pointer to T.

func ParseEntities

func ParseEntities[T any](entity T, entityName string) (T, *framework.Error)

ParseEntities will validate that the entity is non-nil.

func ParseErrors

func ParseErrors(
	errors []ErrorInfo,
) *framework.Error

func ParseGraphQLResponse

func ParseGraphQLResponse(
	body []byte,
	externalID string,
	currentCursor *pagination.CompositeCursor[string],
	orgCount int,
) (
	objects []map[string]any,
	nextCursor *pagination.CompositeCursor[string],
	err *framework.Error,
)

func ParseGraphQLResponseForOrganization

func ParseGraphQLResponseForOrganization(
	body []byte,
	currentCursor *pagination.CompositeCursor[string],
	orgCount int,
) (
	objects []map[string]any,
	nextCursor *pagination.CompositeCursor[string],
	err *framework.Error,
)

ParseGraphQLResponseForOrganization parses the GraphQL response for a singular organization. This function is invoked when a list of organizations are passed to the request and data for just one organization is expected. This is a sample response for a singular organization:

{
	"data": {
	  	"organization": {
			"id": "O_kgDOCzkBcw",
			"login": "dh-test-org-2",
			"email": null,
			"url": "https://github.com/dh-test-org-2"
	  	}
	}
}

The above response is not handled by the ParseGraphQLResponse function and hence this function is used to parse the response.

func ParseRESTReponse

func ParseRESTReponse(
	body []byte,
	links []string,
	currentOrganizationOffset int,
	numberOfOrgs int,
) (
	objects []map[string]any,
	nextCursor *pagination.CompositeCursor[string],
	err *framework.Error,
)

func PopulateOrganizationCollectionConfig

func PopulateOrganizationCollectionConfig() *framework.EntityConfig

func ProcessContainer

func ProcessContainer(
	isStart bool,
	containerName string,
	data *Data,
	collections *ContainerLayers,
	rawEntityData json.RawMessage,
) (
	collection any,
	err *framework.Error,
)

* ProcessContainer retrieves the collection from the data and returns it as an interface. * The 'isStart' parameter is used to determine if the collection is the first layer of the response. * There are two cases: * 1. If we are the start of the parsing, we will directly use the data struct to get the Enterprise or Organization. * a. If the collection is nil or the casting to the appropriate collection struct fails, an error is returned. * 2. If we are not the start of the parsing, we will use the rawEntityData to get the next collection. * a. If GetCollectionEntityInterface returns an error, it will be returned. * b. If the collection is nil, we will return nil to indicate that the sub-entities should not be parsed. * c. If the collection is nil, and the containerName is Organization, an error is returned.

func ProcessEntityData

func ProcessEntityData(
	rawEntityFieldName string,
	firstContainerEntry *EntitiesInfo,
) (
	json.RawMessage,
	*framework.Error,
)

func SetAfterParameter

func SetAfterParameter(value *string) string

Types

type Adapter

type Adapter struct {
	GithubClient Client
}

Adapter implements the framework.Adapter interface to query pages of objects from datasources.

func (*Adapter) GetPage

func (a *Adapter) GetPage(ctx context.Context, request *framework.Request[Config]) framework.Response

GetPage is called by SGNL's ingestion service to query a page of objects from a datasource.

func (*Adapter) RequestPageFromDatasource

func (a *Adapter) RequestPageFromDatasource(
	ctx context.Context, request *framework.Request[Config],
) framework.Response

RequestPageFromDatasource requests a page of objects from a datasource.

func (*Adapter) ValidateGetPageRequest

func (a *Adapter) ValidateGetPageRequest(ctx context.Context, request *framework.Request[Config]) *framework.Error

ValidateGetPageRequest validates the fields of the GetPage Request.

type AttributeNode

type AttributeNode struct {
	Name     string
	Children map[string]*AttributeNode
}

AttributeNode stores the metadata required to build the inner part of the query for an entity.

func AttributeQueryBuilder

func AttributeQueryBuilder(
	entityConfig *framework.EntityConfig,
	login *string,
	rootName string,
) (*AttributeNode, *framework.Error)

func (*AttributeNode) AddChild

func (node *AttributeNode) AddChild(path []string) *AttributeNode

AddChild adds a child to the current node and returns the child node.

func (*AttributeNode) BuildQuery

func (node *AttributeNode) BuildQuery() string

type Client

type Client interface {
	GetPage(ctx context.Context, request *Request) (*Response, *framework.Error)
}

Client is a client that allows querying the datasource which contains JSON objects.

func NewClient

func NewClient(client *http.Client) Client

NewClient returns a Client to query the datasource.

type CollaboratorQueryBuilder

type CollaboratorQueryBuilder struct {
	RepositoryQueryBuilder
	CollabAfter *string
}

func (*CollaboratorQueryBuilder) Build

func (b *CollaboratorQueryBuilder) Build(request *Request) (string, *framework.Error)

type Config

type Config struct {
	// Common configuration
	*config.CommonConfig

	// EnterpriseSlug is the enterprise slug to query. This is the top level entity for every Github query.
	EnterpriseSlug *string `json:"enterpriseSlug,omitempty"`

	// Organizations is the list of organizations to query. Either this field or EnterpriseSlug must be set (but not both).
	Organizations []string `json:"organizations,omitempty"`

	// isEnterpriseCloud is a boolean that indicates whether the deployment is GitHub Enterprise Cloud.
	// This is used to determine the base URL to use.
	// If true, the deployment type is Enterprise Cloud. If false, the deployment type is Enterprise Server.
	IsEnterpriseCloud bool `json:"isEnterpriseCloud"`

	// APIVersion is the version of the GitHub API to use.
	// This is only used when constructing REST endpoints.
	APIVersion *string `json:"apiVersion"`
}

Config is the configuration passed in each GetPage calls to the adapter. Adapter configuration example: nolint: godot

{
	"enterpriseSlug": "SGNL_ENTERPRISE",
	"organizations": [
		"sgnl-demos",
		"wholesalechips"
	],
	"isEnterpriseCloud": true,
	"apiVersion": "v3"
}

func (*Config) Validate

func (c *Config) Validate(_ context.Context, isRestAPI bool) error

ValidateConfig validates that a Config received in a GetPage call is valid.

type ContainerLayers

type ContainerLayers struct {
	Enterprise   *EnterpriseInfo
	Organization *OrganizationInfo
	Repository   *RepositoryInfo
	Label        *LabelInfo
	Issue        *IssueInfo
	PullRequest  *PullRequestInfo
}

type Data

type Data struct {
	Enterprise   *EnterpriseInfo   `json:"enterprise"`
	Organization *OrganizationInfo `json:"organization"`
}

type Datasource

type Datasource struct {
	Client *http.Client
}

Datasource directly implements a Client interface to allow querying an external datasource.

func (*Datasource) GetPage

func (d *Datasource) GetPage(ctx context.Context, request *Request) (*Response, *framework.Error)

type DatasourceResponse

type DatasourceResponse struct {
	Data   *Data       `json:"data"`
	Errors []ErrorInfo `json:"errors"`
}

type DeploymentInfo

type DeploymentInfo struct {
	GraphQLBasePath string
	RESTBasePath    string
	RESTEndpoints   map[string]map[string]string
}

type EnterpriseInfo

type EnterpriseInfo struct {
	ID            *string       `json:"id"`
	Organizations *EntitiesInfo `json:"organizations"`
}

type EnterpriseQueryInfo

type EnterpriseQueryInfo struct {
	EnterpriseSlug string
	PageSize       int64
}

EnterpriseQueryInfo stores the metadata used when making a GitHub enterprise query.

type EntitiesInfo

type EntitiesInfo struct {
	PageInfo *PageInfo       `json:"pageInfo"`
	Edges    json.RawMessage `json:"edges"`
	Nodes    json.RawMessage `json:"nodes"`
}

This is the generic representation of a single layer of GitHub GraphQL response.

func ProcessEntries

func ProcessEntries(
	container any,
	containerName string,
	entriesName string,
) (
	*EntitiesInfo,
	*framework.Error,
)

ProcessEntries processes the entries of a container using reflection and validates the following assumption: 'container' is expected to a be a struct pointer with a field named 'entriesName' of type *EntitiesInfo.

type Entity

type Entity struct {
	// The RequiredAttributes array specifies attributes necessary to create the uniqueID in the response.
	// In OrganizationUser, $.node.id field is the userId. To create the uniqueId, both userId and orgId are needed.
	// orgId is required for establishing relationships between OrganizationUser and Organization.
	RequiredAttributes []string
	// UniqueExternalIDAttribute is the unique attribute of the entity.
	UniqueExternalIDAttribute string
	// ParsePath is the path to the entity in the GraphQL response. This will be used to unmarshal the response.
	// The length of the ParsePath should always be a multiple of 3. Refer to the DataToObjects function for more details.
	ParsePath []string
	// MemberOf is the external ID of the collection entity that the entity belongs to.
	MemberOf *string
	// CollectionEntityConfig contains the required collection attributes to request for the entity.
	// This matches the default entities to support the test case matching.
	CollectionEntityConfig *framework.EntityConfig
	// CollectionAttribute is the attribute of the collection that needs to be used as the collectionID
	CollectionAttribute string
	// contains filtered or unexported fields
}

type ErrorInfo

type ErrorInfo struct {
	Message string `json:"message"`
}

type GraphQLPayload

type GraphQLPayload struct {
	Query string `json:"query"`
}

GraphQLPayload is used as a wrapper to construct the query.

type IssueAssigneeQueryBuilder

type IssueAssigneeQueryBuilder struct {
	IssueQueryBuilder
	AssigneeAfter *string
}

func (*IssueAssigneeQueryBuilder) Build

func (b *IssueAssigneeQueryBuilder) Build(request *Request) (string, *framework.Error)

type IssueInfo

type IssueInfo struct {
	ID           *string       `json:"id"`
	Assignees    *EntitiesInfo `json:"assignees"`
	Participants *EntitiesInfo `json:"participants"`
	Labels       *EntitiesInfo `json:"labels"`
}

type IssueLabelQueryBuilder

type IssueLabelQueryBuilder struct {
	LabelQueryBuilder
	IssueAfter *string
}

func (*IssueLabelQueryBuilder) Build

func (b *IssueLabelQueryBuilder) Build(request *Request) (string, *framework.Error)

type IssueParticipantQueryBuilder

type IssueParticipantQueryBuilder struct {
	IssueQueryBuilder
	ParticipantAfter *string
}

func (*IssueParticipantQueryBuilder) Build

type IssueQueryBuilder

type IssueQueryBuilder struct {
	RepositoryQueryBuilder
	IssueAfter *string
}

func (*IssueQueryBuilder) Build

func (b *IssueQueryBuilder) Build(request *Request) (string, *framework.Error)

type LabelInfo

type LabelInfo struct {
	ID           *string       `json:"id"`
	Issues       *EntitiesInfo `json:"issues"`
	PullRequests *EntitiesInfo `json:"pullRequests"`
}

type LabelQueryBuilder

type LabelQueryBuilder struct {
	RepositoryQueryBuilder
	LabelAfter *string
}

func (*LabelQueryBuilder) Build

func (b *LabelQueryBuilder) Build(request *Request) (string, *framework.Error)

type OrganizationData

type OrganizationData struct {
	Organization *map[string]any `json:"organization"`
}

type OrganizationDatasourceResponse

type OrganizationDatasourceResponse struct {
	Data   *OrganizationData `json:"data"`
	Errors []ErrorInfo       `json:"errors"`
}

type OrganizationInfo

type OrganizationInfo struct {
	ID           *string       `json:"id"`
	Users        *EntitiesInfo `json:"membersWithRole"`
	Repositories *EntitiesInfo `json:"repositories"`
	Teams        *EntitiesInfo `json:"teams"`
}

type OrganizationQueryBuilder

type OrganizationQueryBuilder struct {
	EnterpriseQueryInfo *EnterpriseQueryInfo
	OrgAfter            *string
	Organizations       []string
	OrganizationOffset  int
}

func (*OrganizationQueryBuilder) Build

func (b *OrganizationQueryBuilder) Build(request *Request) (string, *framework.Error)

type OrganizationUserQueryBuilder

type OrganizationUserQueryBuilder struct {
	OrgLogin  string
	PageSize  int64
	UserAfter *string
}

OrganizationUser is retrieved through the GitHub Organization query.

func (*OrganizationUserQueryBuilder) Build

type PageInfo

type PageInfo struct {
	HasNextPage bool `json:"hasNextPage"`

	// EndCursor represents the next cursor information.
	EndCursor *string `json:"endCursor"`

	// OrganizationOffset represents the offset within request.Organizations slice.
	OrganizationOffset int `json:"organizationOffset"`

	// InnerPageInfo represents the paging details for the next nested level. (1 layer deeper)
	InnerPageInfo *PageInfo
}

This entity represents the pagination metadata for each layer of the GraphQL response.

func AddPageInfoLayerToLeaf

func AddPageInfoLayerToLeaf(parentPageInfo *PageInfo, leafPageInfo *PageInfo) (*PageInfo, *framework.Error)

Helper function to add new layer as a leaf of the deepest layer of the parentPageInfo. The leafPageInfo will become the new deepest layer in the hierarchy.

func DataToObjects

func DataToObjects(
	data *Data,
	externalID string,
	hasEnterpriseSlug bool,
) (
	objects []map[string]any,
	pageInfo *PageInfo,
	err *framework.Error,
)

* DataToObjects will attempt to convert the entities in the response data to objects. * It also populates the PageInfoLayers to create the next cursor. * The conversion is done using each entity's ParsePath. The ParsePath is a list of strings * that represent the path to the entity in the GraphQL response. The ParsePath is also always a * multiple of three because each 'phase' of the parsing can be divided into three steps: * 1. Get the container entity. * a. If this is the first phase, we will get the Enterprise or Organization entity from the 'data'. * b. If this is not the first phase, we will use the json saved from the last phase. * results to retrieve the next container entity. * 2. Get the first entry from the container and update the PageInfo. * 3. Get the raw entity data from the first entry and save it for the next phase. * * Ex. ParsePath for User: ["Enterprise", "Organizations", "Nodes", "Organization", "Users", "Nodes"] * * Phase 1: ["Enterprise", "Organizations", "Nodes"] * 1. We start by getting the EnterpriseInfo struct from the 'Data' struct through ProcessContainer(). * 2. We then pass this EnterpriseInfo struct to ProcessEntries() to get the * Organizations container, which is an EntitiesInfo struct. The PageInfo field from the EntitiesInfo * is then added to the return page struct 'pageInfo'. * 3. We then get the raw data of the Organizations struct through the "Nodes" field in ProcessEntityData(). * * Phase 2: ["Organization", "Users", "Nodes"] * 1. We use the raw Organizations array data from the last phase to get the first Organization * using ProcessContainer(). * 2. We then pass this OrganizationInfo struct to ProcessEntries() to get the Users container, * which is an EntitiesInfo struct. The PageInfo field from the EntitiesInfo * is then added to the return page struct 'pageInfo'. * 3. We then get the raw data of the Users struct through the "Nodes" field in ProcessEntityData(). * * The raw entity data is then converted to objects using ConvertEntitiesToObjects(). * The objects are then injected with common fields using InjectCommonFields().

func DecodePageInfo

func DecodePageInfo(cursor *string) (*PageInfo, *framework.Error)

func UpdatePageInfo

func UpdatePageInfo(currentPageInfo, newPageInfo *PageInfo) (bool, *PageInfo)

* PageInfo Update Rules: (Multiple Layer GraphQL Pagination) * 1. We will initialize our new PageInfo struct by using the currentPageInfo. * a. This number of layers in currentPageInfo will always be less than or equal to the * number of layers in the newPageInfo. This is because newPageInfo will always contain the * maximum number of layers for each entity type. However, currentPageInfo could be nil at * the start of the sync and also gets rid of all the deepest layers that have nil endCursors. * 2. We will iterate through the layers starting from the deepest layer and work our way out. * a. If the new PageInfo is nil, we will return false indicating that we've reached the deepest layer. * b. If the current PageInfo is nil, we will initialize it with the new PageInfo to match * the layer of the newPageInfo. * 3. If the recursive call returns false, this indicates that all deeper layers have nil endCursors and * therefore we can set the innerPageInfo reference to nil. * 4. If the current layer has a next page, we will update the endCursor for the current layer and * return true to indicate that an update has been made at this layer. * 4. If the current layer does not have a next page, we will return false and nil which will set the * innerPageInfo reference for the outer layer to nil. * 5. If we iterate through all layers and none of them have a next page, false is returned indicating no * updates have been made. This return parameter is used to determine if the nextCursor should be set to nil.

type PullRequestAssigneeQueryBuilder

type PullRequestAssigneeQueryBuilder struct {
	PullRequestQueryBuilder
	AssigneeAfter *string
}

func (*PullRequestAssigneeQueryBuilder) Build

type PullRequestChangedFileQueryBuilder

type PullRequestChangedFileQueryBuilder struct {
	PullRequestQueryBuilder
	ChangedFileAfter *string
}

func (*PullRequestChangedFileQueryBuilder) Build

type PullRequestCommitQueryBuilder

type PullRequestCommitQueryBuilder struct {
	PullRequestQueryBuilder
	CommitAfter *string
}

func (*PullRequestCommitQueryBuilder) Build

type PullRequestInfo

type PullRequestInfo struct {
	ID           *string       `json:"id"`
	ChangedFiles *EntitiesInfo `json:"files"`
	Reviews      *EntitiesInfo `json:"latestOpinionatedReviews"`
	Commits      *EntitiesInfo `json:"commits"`
	Assignees    *EntitiesInfo `json:"assignees"`
	Participants *EntitiesInfo `json:"participants"`
}

type PullRequestLabelQueryBuilder

type PullRequestLabelQueryBuilder struct {
	LabelQueryBuilder
	PullRequestAfter *string
}

func (*PullRequestLabelQueryBuilder) Build

type PullRequestParticipantQueryBuilder

type PullRequestParticipantQueryBuilder struct {
	PullRequestQueryBuilder
	ParticipantAfter *string
}

func (*PullRequestParticipantQueryBuilder) Build

type PullRequestQueryBuilder

type PullRequestQueryBuilder struct {
	RepositoryQueryBuilder
	PullRequestAfter *string
}

func (*PullRequestQueryBuilder) Build

func (b *PullRequestQueryBuilder) Build(request *Request) (string, *framework.Error)

type PullRequestReviewQueryBuilder

type PullRequestReviewQueryBuilder struct {
	PullRequestQueryBuilder
	ReviewAfter *string
}

func (*PullRequestReviewQueryBuilder) Build

type QueryBuilder

type QueryBuilder interface {
	Build(*Request) (string, *framework.Error)
}

QueryBuilder is an interface that defines the method for building a query. Each entity has its own builder struct that contains the query parameters required to retrieve the entity.

func ConstructQueryBuilder

func ConstructQueryBuilder(request *Request, pageInfo *PageInfo) (QueryBuilder, *framework.Error)

type RepositoryInfo

type RepositoryInfo struct {
	ID            *string       `json:"id"`
	Collaborators *EntitiesInfo `json:"collaborators"`
	Labels        *EntitiesInfo `json:"labels"`
	Issues        *EntitiesInfo `json:"issues"`
	PullRequests  *EntitiesInfo `json:"pullRequests"`
}

type RepositoryQueryBuilder

type RepositoryQueryBuilder struct {
	OrganizationQueryBuilder
	RepoAfter *string
}

func (*RepositoryQueryBuilder) Build

func (b *RepositoryQueryBuilder) Build(request *Request) (string, *framework.Error)

type Request

type Request struct {
	// BaseURL is the Base URL of the datasource to query.
	BaseURL string

	// Token is the Bearer API token to authenticate a request.
	Token string

	// PageSize is the maximum number of objects to return from the entity.
	PageSize int64

	// EntityExternalID is the external ID of the entity.
	// The external ID should match the API's resource name.
	EntityExternalID string

	// Cursor identifies the first object of the page to return, as returned by
	// the last request for the entity.
	// nil in the request for the first page.
	Cursor *pagination.CompositeCursor[string]

	// EnterpriseSlug is the slug attribute belonging to a GitHub Enterprise entity.
	// Either this field or Organizations must be set (but not both).
	EnterpriseSlug *string

	// Organizations is the list of organizations to query. Either this field or EnterpriseSlug must be set (but not both).
	Organizations []string

	// APIVersion is the version of the GitHub API that is used for constructing REST endpoints.
	APIVersion *string

	// IsEnterpriseCloud is a boolean that indicates whether the deployment is GitHub Enterprise Cloud.
	IsEnterpriseCloud bool

	// Attributes contains the list of attributes to request along with the current request.
	EntityConfig *framework.EntityConfig

	// RequestTimeoutSeconds is the timeout duration for requests made to datasources.
	// This should be set to the number of seconds to wait before timing out.
	RequestTimeoutSeconds int
}

Request is a request to the datasource.

type RequestInfo

type RequestInfo struct {
	Endpoint           string
	HTTPMethod         string
	Query              string
	OrganizationOffset int
}

func PopulateRequestInfo

func PopulateRequestInfo(request *Request) (*RequestInfo, *framework.Error)

PopulateRequestInfo populates the RequestInfo struct with the necessary information to make a request to the datasource.

type Response

type Response struct {
	// StatusCode is an HTTP status code.
	StatusCode int

	// RetryAfterHeader is the Retry-After response HTTP header, if set.
	RetryAfterHeader string

	// Objects is the list of
	// May be empty.
	Objects []map[string]any

	// NextCursor is the cursor that identifies the first object of the next page.
	// nil if this is the last page in this full sync.
	NextCursor *pagination.CompositeCursor[string]
}

Response is a response returned by the datasource.

type TeamQueryBuilder

type TeamQueryBuilder struct {
	OrganizationQueryBuilder
	TeamAfter *string
}

func (*TeamQueryBuilder) Build

func (b *TeamQueryBuilder) Build(request *Request) (string, *framework.Error)

type UserQueryBuilder

type UserQueryBuilder struct {
	OrganizationQueryBuilder
	UserAfter *string
}

func (*UserQueryBuilder) Build

func (b *UserQueryBuilder) Build(request *Request) (string, *framework.Error)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL