Documentation
¶
Index ¶
- Constants
- Variables
- func ConstructQuery(request *Request) (string, *framework.Error)
- func ConvertEntitiesToObjects(entities json.RawMessage) (objects []map[string]any, unmarshalErr *framework.Error)
- func ConvertOVDEAttribute(objects *[]map[string]any)
- func GetAttributePath(input string) []string
- func GetCollectionEntityInterface(entities json.RawMessage, entityName string, collections *ContainerLayers) (any, *framework.Error)
- func GetPageInfoAfter(pageInfo *PageInfo, n int, orgListProvided *bool) *string
- func GetValueFromPath(object map[string]any, path []string) (string, error)
- func InjectCommonFields(objects *[]map[string]any, container *ContainerLayers, externalID string) *framework.Error
- func InjectUniqueID(objects *[]map[string]any, index int, externalID string, attrA string, ...) *framework.Error
- func NewAdapter(client Client) framework.Adapter[Config]
- func ParseAndAssignCollection[T any](entities json.RawMessage, entityName string, collectionField **T) (any, *framework.Error)
- func ParseContainerEntity[T any](entities json.RawMessage, entityName string) (*T, *framework.Error)
- func ParseEntities[T any](entity T, entityName string) (T, *framework.Error)
- func ParseErrors(errors []ErrorInfo) *framework.Error
- func ParseGraphQLResponse(body []byte, externalID string, ...) (objects []map[string]any, nextCursor *pagination.CompositeCursor[string], ...)
- func ParseGraphQLResponseForOrganization(body []byte, currentCursor *pagination.CompositeCursor[string], orgCount int) (objects []map[string]any, nextCursor *pagination.CompositeCursor[string], ...)
- func ParseRESTResponse(body []byte, links []string, currentOrganizationOffset int, numberOfOrgs int) (objects []map[string]any, nextCursor *pagination.CompositeCursor[string], ...)
- func PopulateOrganizationCollectionConfig() *framework.EntityConfig
- func ProcessContainer(isStart bool, containerName string, data *Data, collections *ContainerLayers, ...) (collection any, err *framework.Error)
- func ProcessEntityData(rawEntityFieldName string, firstContainerEntry *EntitiesInfo) (json.RawMessage, *framework.Error)
- func SetAfterParameter(value *string) string
- type Adapter
- func (a *Adapter) GetPage(ctx context.Context, request *framework.Request[Config]) framework.Response
- func (a *Adapter) RequestPageFromDatasource(ctx context.Context, request *framework.Request[Config]) framework.Response
- func (a *Adapter) ValidateGetPageRequest(ctx context.Context, request *framework.Request[Config]) *framework.Error
- type AttributeNode
- type Client
- type CollaboratorQueryBuilder
- type Config
- type ContainerLayers
- type Data
- type Datasource
- type DatasourceResponse
- type DeploymentInfo
- type EnterpriseInfo
- type EnterpriseQueryInfo
- type EntitiesInfo
- type Entity
- type ErrorInfo
- type GraphQLPayload
- type IssueAssigneeQueryBuilder
- type IssueInfo
- type IssueLabelQueryBuilder
- type IssueParticipantQueryBuilder
- type IssueQueryBuilder
- type LabelInfo
- type LabelQueryBuilder
- type OrganizationData
- type OrganizationDatasourceResponse
- type OrganizationInfo
- type OrganizationQueryBuilder
- type OrganizationUserQueryBuilder
- type PageInfo
- func AddPageInfoLayerToLeaf(parentPageInfo *PageInfo, leafPageInfo *PageInfo) (*PageInfo, *framework.Error)
- func DataToObjects(data *Data, externalID string, hasEnterpriseSlug bool) (objects []map[string]any, pageInfo *PageInfo, err *framework.Error)
- func DecodePageInfo(cursor *string) (*PageInfo, *framework.Error)
- func UpdatePageInfo(currentPageInfo, newPageInfo *PageInfo) (bool, *PageInfo)
- type PullRequestAssigneeQueryBuilder
- type PullRequestChangedFileQueryBuilder
- type PullRequestCommitQueryBuilder
- type PullRequestInfo
- type PullRequestLabelQueryBuilder
- type PullRequestParticipantQueryBuilder
- type PullRequestQueryBuilder
- type PullRequestReviewQueryBuilder
- type QueryBuilder
- type RepositoryInfo
- type RepositoryQueryBuilder
- type Request
- type RequestInfo
- type Response
- type TeamQueryBuilder
- type UserQueryBuilder
Constants ¶
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" )
const ( EnterpriseCloud string = "EnterpriseCloud" EnterpriseServer string = "EnterpriseServer" )
const (
CollectionPageSize = 1
)
Variables ¶
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", }, }, }, } )
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 ConvertOVDEAttribute ¶
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 ¶
[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 ¶
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 ¶
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 ¶
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 ¶
ParseEntities will validate that the entity is non-nil.
func ParseErrors ¶
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 ParseRESTResponse ¶ added in v1.58.0
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 ¶
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.
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.
type CollaboratorQueryBuilder ¶
type CollaboratorQueryBuilder struct {
RepositoryQueryBuilder
CollabAfter *string
}
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"
}
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 ¶
Datasource directly implements a Client interface to allow querying an external datasource.
type DatasourceResponse ¶
type DeploymentInfo ¶
type EnterpriseInfo ¶
type EnterpriseInfo struct {
ID *string `json:"id"`
Organizations *EntitiesInfo `json:"organizations"`
}
type EnterpriseQueryInfo ¶
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 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
}
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
}
type IssueParticipantQueryBuilder ¶
type IssueParticipantQueryBuilder struct {
IssueQueryBuilder
ParticipantAfter *string
}
type IssueQueryBuilder ¶
type IssueQueryBuilder struct {
RepositoryQueryBuilder
IssueAfter *string
}
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
}
type OrganizationData ¶
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
}
type OrganizationUserQueryBuilder ¶
OrganizationUser is retrieved through the GitHub Organization query.
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 UpdatePageInfo ¶
* 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
}
type PullRequestChangedFileQueryBuilder ¶
type PullRequestChangedFileQueryBuilder struct {
PullRequestQueryBuilder
ChangedFileAfter *string
}
type PullRequestCommitQueryBuilder ¶
type PullRequestCommitQueryBuilder struct {
PullRequestQueryBuilder
CommitAfter *string
}
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
}
type PullRequestParticipantQueryBuilder ¶
type PullRequestParticipantQueryBuilder struct {
PullRequestQueryBuilder
ParticipantAfter *string
}
type PullRequestQueryBuilder ¶
type PullRequestQueryBuilder struct {
RepositoryQueryBuilder
PullRequestAfter *string
}
type PullRequestReviewQueryBuilder ¶
type PullRequestReviewQueryBuilder struct {
PullRequestQueryBuilder
ReviewAfter *string
}
type QueryBuilder ¶
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
}
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 ¶
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
}
type UserQueryBuilder ¶
type UserQueryBuilder struct {
OrganizationQueryBuilder
UserAfter *string
}