github.com/ungtb10d/cli/v2@v2.0.0-20221110210412-98537dd9d6a1/pkg/cmd/repo/create/http.go (about) 1 package create 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "net/http" 8 "strings" 9 10 "github.com/ungtb10d/cli/v2/api" 11 ) 12 13 // repoCreateInput is input parameters for the repoCreate method 14 type repoCreateInput struct { 15 Name string 16 HomepageURL string 17 Description string 18 Visibility string 19 OwnerLogin string 20 TeamSlug string 21 TemplateRepositoryID string 22 HasIssuesEnabled bool 23 HasWikiEnabled bool 24 GitIgnoreTemplate string 25 LicenseTemplate string 26 IncludeAllBranches bool 27 InitReadme bool 28 } 29 30 // createRepositoryInputV3 is the payload for the repo create REST API 31 type createRepositoryInputV3 struct { 32 Name string `json:"name"` 33 HomepageURL string `json:"homepage,omitempty"` 34 Description string `json:"description,omitempty"` 35 IsPrivate bool `json:"private"` 36 Visibility string `json:"visibility,omitempty"` 37 TeamID uint64 `json:"team_id,omitempty"` 38 HasIssuesEnabled bool `json:"has_issues"` 39 HasWikiEnabled bool `json:"has_wiki"` 40 GitIgnoreTemplate string `json:"gitignore_template,omitempty"` 41 LicenseTemplate string `json:"license_template,omitempty"` 42 InitReadme bool `json:"auto_init,omitempty"` 43 } 44 45 // createRepositoryInput is the payload for the repo create GraphQL mutation 46 type createRepositoryInput struct { 47 Name string `json:"name"` 48 HomepageURL string `json:"homepageUrl,omitempty"` 49 Description string `json:"description,omitempty"` 50 Visibility string `json:"visibility"` 51 OwnerID string `json:"ownerId,omitempty"` 52 TeamID string `json:"teamId,omitempty"` 53 HasIssuesEnabled bool `json:"hasIssuesEnabled"` 54 HasWikiEnabled bool `json:"hasWikiEnabled"` 55 } 56 57 // cloneTemplateRepositoryInput is the payload for creating a repo from a template using GraphQL 58 type cloneTemplateRepositoryInput struct { 59 Name string `json:"name"` 60 Visibility string `json:"visibility"` 61 Description string `json:"description,omitempty"` 62 OwnerID string `json:"ownerId"` 63 RepositoryID string `json:"repositoryId"` 64 IncludeAllBranches bool `json:"includeAllBranches"` 65 } 66 67 // repoCreate creates a new GitHub repository 68 func repoCreate(client *http.Client, hostname string, input repoCreateInput) (*api.Repository, error) { 69 isOrg := false 70 var ownerID string 71 var teamID string 72 var teamIDv3 uint64 73 74 apiClient := api.NewClientFromHTTP(client) 75 76 if input.TeamSlug != "" { 77 team, err := resolveOrganizationTeam(apiClient, hostname, input.OwnerLogin, input.TeamSlug) 78 if err != nil { 79 return nil, err 80 } 81 teamIDv3 = team.ID 82 teamID = team.NodeID 83 ownerID = team.Organization.NodeID 84 isOrg = true 85 } else if input.OwnerLogin != "" { 86 owner, err := resolveOwner(apiClient, hostname, input.OwnerLogin) 87 if err != nil { 88 return nil, err 89 } 90 ownerID = owner.NodeID 91 isOrg = owner.IsOrganization() 92 } 93 94 if input.TemplateRepositoryID != "" { 95 var response struct { 96 CloneTemplateRepository struct { 97 Repository api.Repository 98 } 99 } 100 101 if ownerID == "" { 102 var err error 103 ownerID, err = api.CurrentUserID(apiClient, hostname) 104 if err != nil { 105 return nil, err 106 } 107 } 108 109 variables := map[string]interface{}{ 110 "input": cloneTemplateRepositoryInput{ 111 Name: input.Name, 112 Description: input.Description, 113 Visibility: strings.ToUpper(input.Visibility), 114 OwnerID: ownerID, 115 RepositoryID: input.TemplateRepositoryID, 116 IncludeAllBranches: input.IncludeAllBranches, 117 }, 118 } 119 120 err := apiClient.GraphQL(hostname, ` 121 mutation CloneTemplateRepository($input: CloneTemplateRepositoryInput!) { 122 cloneTemplateRepository(input: $input) { 123 repository { 124 id 125 name 126 owner { login } 127 url 128 } 129 } 130 } 131 `, variables, &response) 132 if err != nil { 133 return nil, err 134 } 135 136 return api.InitRepoHostname(&response.CloneTemplateRepository.Repository, hostname), nil 137 } 138 139 if input.GitIgnoreTemplate != "" || input.LicenseTemplate != "" || input.InitReadme { 140 inputv3 := createRepositoryInputV3{ 141 Name: input.Name, 142 HomepageURL: input.HomepageURL, 143 Description: input.Description, 144 IsPrivate: strings.EqualFold(input.Visibility, "PRIVATE"), 145 TeamID: teamIDv3, 146 HasIssuesEnabled: input.HasIssuesEnabled, 147 HasWikiEnabled: input.HasWikiEnabled, 148 GitIgnoreTemplate: input.GitIgnoreTemplate, 149 LicenseTemplate: input.LicenseTemplate, 150 InitReadme: input.InitReadme, 151 } 152 153 path := "user/repos" 154 if isOrg { 155 path = fmt.Sprintf("orgs/%s/repos", input.OwnerLogin) 156 inputv3.Visibility = strings.ToLower(input.Visibility) 157 } 158 159 body := &bytes.Buffer{} 160 enc := json.NewEncoder(body) 161 if err := enc.Encode(inputv3); err != nil { 162 return nil, err 163 } 164 165 repo, err := api.CreateRepoTransformToV4(apiClient, hostname, "POST", path, body) 166 if err != nil { 167 return nil, err 168 } 169 return repo, nil 170 } 171 172 var response struct { 173 CreateRepository struct { 174 Repository api.Repository 175 } 176 } 177 178 variables := map[string]interface{}{ 179 "input": createRepositoryInput{ 180 Name: input.Name, 181 Description: input.Description, 182 HomepageURL: input.HomepageURL, 183 Visibility: strings.ToUpper(input.Visibility), 184 OwnerID: ownerID, 185 TeamID: teamID, 186 HasIssuesEnabled: input.HasIssuesEnabled, 187 HasWikiEnabled: input.HasWikiEnabled, 188 }, 189 } 190 191 err := apiClient.GraphQL(hostname, ` 192 mutation RepositoryCreate($input: CreateRepositoryInput!) { 193 createRepository(input: $input) { 194 repository { 195 id 196 name 197 owner { login } 198 url 199 } 200 } 201 } 202 `, variables, &response) 203 if err != nil { 204 return nil, err 205 } 206 207 return api.InitRepoHostname(&response.CreateRepository.Repository, hostname), nil 208 } 209 210 type ownerResponse struct { 211 NodeID string `json:"node_id"` 212 Type string `json:"type"` 213 } 214 215 func (r *ownerResponse) IsOrganization() bool { 216 return r.Type == "Organization" 217 } 218 219 func resolveOwner(client *api.Client, hostname, orgName string) (*ownerResponse, error) { 220 var response ownerResponse 221 err := client.REST(hostname, "GET", fmt.Sprintf("users/%s", orgName), nil, &response) 222 return &response, err 223 } 224 225 type teamResponse struct { 226 ID uint64 `json:"id"` 227 NodeID string `json:"node_id"` 228 Organization struct { 229 NodeID string `json:"node_id"` 230 } 231 } 232 233 func resolveOrganizationTeam(client *api.Client, hostname, orgName, teamSlug string) (*teamResponse, error) { 234 var response teamResponse 235 err := client.REST(hostname, "GET", fmt.Sprintf("orgs/%s/teams/%s", orgName, teamSlug), nil, &response) 236 return &response, err 237 } 238 239 // listGitIgnoreTemplates uses API v3 here because gitignore template isn't supported by GraphQL yet. 240 func listGitIgnoreTemplates(httpClient *http.Client, hostname string) ([]string, error) { 241 var gitIgnoreTemplates []string 242 client := api.NewClientFromHTTP(httpClient) 243 err := client.REST(hostname, "GET", "gitignore/templates", nil, &gitIgnoreTemplates) 244 if err != nil { 245 return []string{}, err 246 } 247 return gitIgnoreTemplates, nil 248 } 249 250 // listLicenseTemplates uses API v3 here because license template isn't supported by GraphQL yet. 251 func listLicenseTemplates(httpClient *http.Client, hostname string) ([]api.License, error) { 252 var licenseTemplates []api.License 253 client := api.NewClientFromHTTP(httpClient) 254 err := client.REST(hostname, "GET", "licenses", nil, &licenseTemplates) 255 if err != nil { 256 return nil, err 257 } 258 return licenseTemplates, nil 259 }