|
| 1 | +/* |
| 2 | +Utility types definitions |
| 3 | +*/ |
1 | 4 | package utils |
2 | 5 |
|
3 | 6 | import ( |
| 7 | + "bytes" |
| 8 | + "context" |
4 | 9 | "encoding/json" |
5 | 10 | "fmt" |
| 11 | + "net/http" |
6 | 12 | "os" |
| 13 | + "strings" |
| 14 | + "sync" |
7 | 15 | ) |
8 | 16 |
|
| 17 | +// ProjectMirrorOptions defines how the project should be mirrored |
| 18 | +// to the destination GitLab instance |
| 19 | +// - destination_url: the URL of the destination GitLab instance |
| 20 | +// - ci_cd_catalog: whether to add the project to the CI/CD catalog |
| 21 | +// - issues: whether to mirror the issues |
9 | 22 | type ProjectMirroringOptions struct { |
10 | 23 | DestinationURL string `json:"destination_url"` |
11 | 24 | CI_CD_Catalog bool `json:"ci_cd_catalog"` |
12 | 25 | Issues bool `json:"issues"` |
13 | 26 | } |
14 | 27 |
|
| 28 | +// GroupMirrorOptions defines how the group should be mirrored |
| 29 | +// to the destination GitLab instance |
| 30 | +// - destination_url: the URL of the destination GitLab instance |
| 31 | +// - ci_cd_catalog: whether to add the group to the CI/CD catalog |
| 32 | +// - issues: whether to mirror the issues |
15 | 33 | type GroupMirroringOptions struct { |
16 | 34 | DestinationURL string `json:"destination_url"` |
17 | 35 | CI_CD_Catalog bool `json:"ci_cd_catalog"` |
18 | 36 | Issues bool `json:"issues"` |
19 | 37 | } |
20 | 38 |
|
| 39 | +// MirrorMapping defines the mapping of projects and groups |
| 40 | +// to the destination GitLab instance |
| 41 | +// It is used to parse the JSON file that contains the mapping |
| 42 | +// - projects: a map of project names to their mirroring options |
| 43 | +// - groups: a map of group names to their mirroring options |
21 | 44 | type MirrorMapping struct { |
22 | | - Projects map[string]ProjectMirroringOptions `json:"projects"` |
23 | | - Groups map[string]GroupMirroringOptions `json:"groups"` |
| 45 | + Projects map[string]*ProjectMirroringOptions `json:"projects"` |
| 46 | + Groups map[string]*GroupMirroringOptions `json:"groups"` |
| 47 | + muProjects sync.Mutex |
| 48 | + muGroups sync.Mutex |
24 | 49 | } |
25 | 50 |
|
| 51 | +func (m *MirrorMapping) AddProject(project string, options *ProjectMirroringOptions) { |
| 52 | + m.muProjects.Lock() |
| 53 | + defer m.muProjects.Unlock() |
| 54 | + m.Projects[project] = options |
| 55 | +} |
| 56 | + |
| 57 | +func (m *MirrorMapping) AddGroup(group string, options *GroupMirroringOptions) { |
| 58 | + m.muGroups.Lock() |
| 59 | + defer m.muGroups.Unlock() |
| 60 | + m.Groups[group] = options |
| 61 | +} |
| 62 | + |
| 63 | +// OpenMirrorMapping opens the JSON file that contains the mapping |
| 64 | +// and parses it into a MirrorMapping struct |
| 65 | +// It returns the mapping and an error if any |
26 | 66 | func OpenMirrorMapping(path string) (*MirrorMapping, error) { |
27 | 67 | mapping := &MirrorMapping{ |
28 | | - Projects: make(map[string]ProjectMirroringOptions), |
29 | | - Groups: make(map[string]GroupMirroringOptions), |
| 68 | + Projects: make(map[string]*ProjectMirroringOptions), |
| 69 | + Groups: make(map[string]*GroupMirroringOptions), |
30 | 70 | } |
31 | 71 |
|
32 | 72 | // Read the file |
@@ -80,3 +120,59 @@ func (m *MirrorMapping) check() error { |
80 | 120 |
|
81 | 121 | return err |
82 | 122 | } |
| 123 | + |
| 124 | +// GraphQLClient is a client for sending GraphQL requests to GitLab |
| 125 | +type GraphQLClient struct { |
| 126 | + token string |
| 127 | + URL string |
| 128 | +} |
| 129 | + |
| 130 | +// GraphQLRequest is a struct that represents a GraphQL request |
| 131 | +// It contains the query and the variables |
| 132 | +type GraphQLRequest struct { |
| 133 | + Query string `json:"query"` |
| 134 | + Variables string `json:"variables,omitempty"` |
| 135 | +} |
| 136 | + |
| 137 | +// NewGitlabGraphQLClient creates a new GraphQL client for GitLab |
| 138 | +// It takes the token and the GitLab URL as arguments |
| 139 | +// It returns a pointer to the GraphQLClient struct |
| 140 | +func NewGitlabGraphQLClient(token, gitlabUrl string) *GraphQLClient { |
| 141 | + return &GraphQLClient{ |
| 142 | + token: token, |
| 143 | + URL: strings.TrimSuffix(gitlabUrl, "/") + "/api/graphql", |
| 144 | + } |
| 145 | +} |
| 146 | + |
| 147 | +// SendRequest sends a GraphQL request to GitLab |
| 148 | +// It takes a GraphQLRequest struct and the HTTP method as arguments |
| 149 | +// It returns the response body as a string and an error if any |
| 150 | +func (g *GraphQLClient) SendRequest(request *GraphQLRequest, method string) (string, error) { |
| 151 | + requestBody, err := json.Marshal(request) |
| 152 | + if err != nil { |
| 153 | + return "", err |
| 154 | + } |
| 155 | + LogVerbosef("Sending GraphQL request to %s with body: %s", g.URL, string(requestBody)) |
| 156 | + req, err := http.NewRequestWithContext(context.Background(), method, g.URL, bytes.NewBuffer(requestBody)) |
| 157 | + if err != nil { |
| 158 | + return "", err |
| 159 | + } |
| 160 | + |
| 161 | + req.Header.Set("Content-Type", "application/json") |
| 162 | + req.Header.Set("Authorization", "Bearer "+g.token) |
| 163 | + |
| 164 | + client := &http.Client{} |
| 165 | + resp, err := client.Do(req) |
| 166 | + if err != nil { |
| 167 | + return "", err |
| 168 | + } |
| 169 | + defer resp.Body.Close() |
| 170 | + if resp.StatusCode != http.StatusOK { |
| 171 | + return "", fmt.Errorf("GraphQL request failed with status: %s", resp.Status) |
| 172 | + } |
| 173 | + var responseBody bytes.Buffer |
| 174 | + if _, err := responseBody.ReadFrom(resp.Body); err != nil { |
| 175 | + return "", err |
| 176 | + } |
| 177 | + return responseBody.String(), nil |
| 178 | +} |
0 commit comments