GPAL - Generally Positive Automation Library v1.0
GPAL The Fluent Automation LIbrary
Loading...
Searching...
No Matches
GoogleCloud.cs
1// =============================================================================
2// GPAL - Generally Positive Automation Library
3// Copyright © 2026 Software Decisions, Inc. All rights reserved.
4//
5// This file is part of GPAL.
6// Licensed under the Business Source License 1.1
7//
8// Primary development, architecture, and vision by Michael B. Vederman,
9// CEO of Software Decisions, Inc., Texas.
10//
11// Internal development maintained privately.
12// Public releases appear on GitHub: https://github.com/SoftwareDecisionsInc/GPAL.
13//
14// See LICENSE for full terms, including Additional Use Grant.
15// =============================================================================
16
17using System;
18using System.Collections.Generic;
19using System.IO;
20using System.Text.Json;
21using System.Threading;
22using static GenerallyPositive.Enums;
23
24namespace GenerallyPositive
25{
29 public class GoogleCloud
30 {
31 private readonly ICredentials _credentials;
32
33 public GoogleCloud(ICredentials credentials)
34 {
35 if (null == credentials)
36 {
37 GPAL.PublishSimpleEvent(GPALEventType.ERROR, "No credentials provided for GoogleCloud operations.", null, GPALObjectType.None);
38 throw new ArgumentNullException(nameof(credentials));
39 }
40 _credentials = credentials;
41 if (CredentialServiceType.Google != ((Credentials)_credentials).ServiceType)
42 {
43 GPAL.PublishSimpleEvent(GPALEventType.ERROR, "Credentials must be for Google service.", null, GPALObjectType.None);
44 throw new ArgumentException("Credentials must be for Google service.");
45 }
46 }
47
48 public string GetProjectName(string projectId)
49 {
50 if (true == string.IsNullOrEmpty(projectId))
51 {
52 GPAL.PublishSimpleEvent(GPALEventType.ERROR, "Project ID cannot be empty.", null, GPALObjectType.None);
53 return "Unknown";
54 }
55
56 var client = GPAL.RESTClient
57 .WithAPIBase("https://cloudresourcemanager.googleapis.com")
58 .WithEndpoint($"/v1/projects/{projectId}")
59 .WithHeader("Authorization", $"Bearer {((Credentials)_credentials).AccessToken}")
60 .Execute();
61 var projectData = JsonSerializer.Deserialize<Dictionary<string, object>>(client);
62 return true == projectData.ContainsKey("name") ? projectData["name"].ToString() : "Unknown";
63 }
64
65 public string CreateProject(string projectName, string projectId = null)
66 {
67 string googleProjectId = "";
68 int timeout = 30000;
69 int elapsed = 0;
70 string operationName;
71
72 if (true == string.IsNullOrEmpty(projectId))
73 {
74 projectId = $"myproject{DateTime.Now.Year}{DateTime.Now.Ticks}".Substring(0, 30).ToLower();
75 if (false == string.IsNullOrEmpty(projectName))
76 {
77 projectId = $"{projectName}{DateTime.Now.Year}{DateTime.Now.Ticks}".Substring(0, 29).ToLower();
78 }
79 }
80 string effectiveProjectName = projectName ?? projectId;
81 string googleProjectName = GetProjectName(projectId);
82
83 if (true == "Unknown".Equals(googleProjectName))
84 {
85 var projectPayload = JsonSerializer.Serialize(new { projectId, name = effectiveProjectName });
86 var projectClient = GPAL.RESTClient
87 .WithAPIBase("https://cloudresourcemanager.googleapis.com")
88 .WithEndpoint("/v1/projects")
89 .WithParameters(projectPayload)
90 .WithHeader("Authorization", $"Bearer {((Credentials)_credentials).AccessToken}")
91 .Execute();
92
93 var projectData = JsonSerializer.Deserialize<Dictionary<string, object>>(projectClient);
94 if (true == projectData.ContainsKey("projectId"))
95 {
96 googleProjectId = projectData["projectId"].ToString();
97 }
98 else if (true == projectData.ContainsKey("name"))
99 {
100 operationName = projectData["name"].ToString();
101 elapsed = 0;
102 while (timeout > elapsed)
103 {
104 var opClient = GPAL.RESTClient
105 .WithAPIBase("https://cloudresourcemanager.googleapis.com")
106 .WithEndpoint($"/v1/{operationName}")
107 .WithHeader("Authorization", $"Bearer {((Credentials)_credentials).AccessToken}")
108 .Execute();
109 var opData = JsonSerializer.Deserialize<Dictionary<string, object>>(opClient);
110 if (true == opData.ContainsKey("error"))
111 {
112 GPAL.PublishSimpleEvent(GPALEventType.ERROR, $"Project creation failed: [{opData["error"]}]", null, GPALObjectType.None);
113 return "";
114 }
115 if (true == opData.ContainsKey("done") && true == ((JsonElement)opData["done"]).GetBoolean())
116 {
117 var responseJson = opData["response"].ToString();
118 var response = JsonSerializer.Deserialize<Dictionary<string, object>>(responseJson);
119 googleProjectId = response["projectId"].ToString();
120 break;
121 }
122 Thread.Sleep(1000);
123 elapsed += 1000;
124 }
125 if (timeout <= elapsed || true == string.IsNullOrEmpty(googleProjectId))
126 {
127 GPAL.PublishSimpleEvent(GPALEventType.ERROR, "Project creation timeout or no projectId.", null, GPALObjectType.None);
128 return "";
129 }
130 }
131 else
132 {
133 GPAL.PublishSimpleEvent(GPALEventType.ERROR, "No projectId or operation name returned.", null, GPALObjectType.None);
134 return "";
135 }
136 }
137 else
138 {
139 googleProjectId = projectId;
140 GPAL.PublishSimpleEvent(GPALEventType.INFO, $"Using existing project: ProjectId [{googleProjectId}], Name [{googleProjectName}]", null, GPALObjectType.None);
141 }
142
143 return googleProjectId;
144 }
145
146 public bool EnableApi(string googleProjectId, string serviceId)
147 {
148 if (true == string.IsNullOrEmpty(googleProjectId) || true == string.IsNullOrEmpty(serviceId))
149 {
150 GPAL.PublishSimpleEvent(GPALEventType.ERROR, "Project ID or service ID cannot be empty.", null, GPALObjectType.None);
151 return false;
152 }
153
154 int timeout = 30000;
155 int elapsed = 0;
156 var servicePayload = JsonSerializer.Serialize(new { serviceIds = new[] { serviceId } });
157 var client = GPAL.RESTClient
158 .WithAPIBase("https://serviceusage.googleapis.com")
159 .WithEndpoint($"/v1/projects/{googleProjectId}/services:batchEnable")
160 .WithParameters(servicePayload)
161 .WithHeader("Authorization", $"Bearer {((Credentials)_credentials).AccessToken}")
162 .Execute();
163
164 var opData = JsonSerializer.Deserialize<Dictionary<string, object>>(client);
165 if (true == opData.ContainsKey("done") && true == ((JsonElement)opData["done"]).GetBoolean())
166 {
167 return true;
168 }
169 else if (true == opData.ContainsKey("name"))
170 {
171 var operationName = opData["name"].ToString();
172 elapsed = 0;
173 while (timeout > elapsed)
174 {
175 var opClient = GPAL.RESTClient
176 .WithAPIBase("https://serviceusage.googleapis.com")
177 .WithEndpoint($"/v1/{operationName}")
178 .WithHeader("Authorization", $"Bearer {((Credentials)_credentials).AccessToken}")
179 .Execute();
180 var data = JsonSerializer.Deserialize<Dictionary<string, object>>(opClient);
181 if (true == data.ContainsKey("error"))
182 {
183 GPAL.PublishSimpleEvent(GPALEventType.ERROR, $"[{serviceId}] enable failed: [{data["error"]}]", null, GPALObjectType.None);
184 return false;
185 }
186 if (true == data.ContainsKey("done") && true == ((JsonElement)data["done"]).GetBoolean())
187 {
188 return true;
189 }
190 Thread.Sleep(1000);
191 elapsed += 1000;
192 }
193 GPAL.PublishSimpleEvent(GPALEventType.ERROR, $"[{serviceId}] enable timeout.", null, GPALObjectType.None);
194 return false;
195 }
196 return false;
197 }
198
199 public bool EnableApi(string googleProjectId, GoogleApi api)
200 {
201 string serviceId = api switch
202 {
203 GoogleApi.ServiceUsage => "serviceusage.googleapis.com",
204 GoogleApi.ApiKeys => "apikeys.googleapis.com",
205 GoogleApi.Sheets => "sheets.googleapis.com",
206 GoogleApi.Drive => "drive.googleapis.com",
207 GoogleApi.Scripts => "script.googleapis.com",
208 _ => throw new ArgumentException("Invalid Google API type.")
209 };
210 return EnableApi(googleProjectId, serviceId);
211 }
212
213 public string GenerateApiKey(string googleProjectId)
214 {
215 if (true == string.IsNullOrEmpty(googleProjectId))
216 {
217 GPAL.PublishSimpleEvent(GPALEventType.ERROR, "Project ID cannot be empty for API key generation.", null, GPALObjectType.None);
218 return "";
219 }
220
221 var apiKeyPayload = JsonSerializer.Serialize(new { displayName = $"gpal-api-key-{Guid.NewGuid().ToString().Substring(0, 8)}" });
222 var apiKeyClient = GPAL.RESTClient
223 .WithAPIBase("https://apikeys.googleapis.com")
224 .WithEndpoint($"/v2/projects/{googleProjectId}/locations/global/keys")
225 .WithParameters(apiKeyPayload)
226 .WithHeader("Authorization", $"Bearer {((Credentials)_credentials).AccessToken}")
227 .Execute();
228
229 var apiKeyOpData = JsonSerializer.Deserialize<Dictionary<string, object>>(apiKeyClient);
230 if (true == apiKeyOpData.ContainsKey("keyString"))
231 {
232 return apiKeyOpData["keyString"].ToString();
233 }
234 else if (true == apiKeyOpData.ContainsKey("name"))
235 {
236 var operationName = apiKeyOpData["name"].ToString();
237 int elapsed = 0;
238 while (30000 > elapsed)
239 {
240 var opClient = GPAL.RESTClient
241 .WithAPIBase("https://apikeys.googleapis.com")
242 .WithEndpoint($"/v2/{operationName}")
243 .WithHeader("Authorization", $"Bearer {((Credentials)_credentials).AccessToken}")
244 .Execute();
245 var opData = JsonSerializer.Deserialize<Dictionary<string, object>>(opClient);
246 if (true == opData.ContainsKey("error"))
247 {
248 GPAL.PublishSimpleEvent(GPALEventType.ERROR, $"API key creation failed: [{opData["error"]}]", null, GPALObjectType.None);
249 return "";
250 }
251 if (true == opData.ContainsKey("done") && true == ((JsonElement)opData["done"]).GetBoolean())
252 {
253 var responseJson = opData["response"].ToString();
254 var response = JsonSerializer.Deserialize<Dictionary<string, object>>(responseJson);
255 return response["keyString"].ToString();
256 }
257 Thread.Sleep(1000);
258 elapsed += 1000;
259 }
260 GPAL.PublishSimpleEvent(GPALEventType.ERROR, "API key creation timeout or failed.", null, GPALObjectType.None);
261 return "";
262 }
263 return "";
264 }
265 }
266}
Everything starts here, all the GPAL controls and global settings using fluent syntax are here....
Definition GPAL.cs:49
static IAllowRESTEndpoint RESTClient
Instantiate a new fluent RESTClient.
Definition GPAL.cs:914
static void PublishSimpleEvent(GPALEventType gPALEventType, string msg, dynamic gPALObject=null, Enums.GPALObjectType gPALObjectType=GPALObjectType.None, Exception ex=null)
Publish a message to either the information channel or exception channel (if exception passed in) Pub...
Definition GPAL.cs:2406