Skip to content

Commit 3c0854b

Browse files
committed
Updates to wrapper and specification.
1 parent 55fe249 commit 3c0854b

File tree

2 files changed

+89
-11
lines changed

2 files changed

+89
-11
lines changed

dotnetv4/Redshift/Actions/RedshiftWrapper.cs

Lines changed: 60 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,14 @@ public async Task<Cluster> CreateClusterAsync(string clusterIdentifier, string d
6161
Console.WriteLine($"Created cluster {clusterIdentifier}");
6262
return response.Cluster;
6363
}
64+
catch (ClusterAlreadyExistsException ex)
65+
{
66+
Console.WriteLine($"Cluster already exists: {ex.Message}");
67+
throw;
68+
}
6469
catch (Exception ex)
6570
{
66-
Console.WriteLine($"Error creating cluster: {ex.Message}");
71+
Console.WriteLine($"Couldn't create cluster. Here's why: {ex.Message}");
6772
throw;
6873
}
6974
}
@@ -88,9 +93,14 @@ public async Task<List<Cluster>> DescribeClustersAsync(string? clusterIdentifier
8893
var response = await _redshiftClient.DescribeClustersAsync(request);
8994
return response.Clusters;
9095
}
96+
catch (ClusterNotFoundException ex)
97+
{
98+
Console.WriteLine($"Cluster not found: {ex.Message}");
99+
throw;
100+
}
91101
catch (Exception ex)
92102
{
93-
Console.WriteLine($"Error describing clusters: {ex.Message}");
103+
Console.WriteLine($"Couldn't describe clusters. Here's why: {ex.Message}");
94104
throw;
95105
}
96106
}
@@ -117,9 +127,14 @@ public async Task<Cluster> ModifyClusterAsync(string clusterIdentifier, string p
117127
Console.WriteLine($"The modified cluster was successfully modified and has {preferredMaintenanceWindow} as the maintenance window");
118128
return response.Cluster;
119129
}
130+
catch (ClusterNotFoundException ex)
131+
{
132+
Console.WriteLine($"Cluster not found: {ex.Message}");
133+
throw;
134+
}
120135
catch (Exception ex)
121136
{
122-
Console.WriteLine($"Error modifying cluster: {ex.Message}");
137+
Console.WriteLine($"Couldn't modify cluster. Here's why: {ex.Message}");
123138
throw;
124139
}
125140
}
@@ -145,9 +160,14 @@ public async Task<Cluster> DeleteClusterAsync(string clusterIdentifier)
145160
Console.WriteLine($"The {clusterIdentifier} was deleted");
146161
return response.Cluster;
147162
}
163+
catch (ClusterNotFoundException ex)
164+
{
165+
Console.WriteLine($"Cluster not found: {ex.Message}");
166+
throw;
167+
}
148168
catch (Exception ex)
149169
{
150-
Console.WriteLine($"Error deleting cluster: {ex.Message}");
170+
Console.WriteLine($"Couldn't delete cluster. Here's why: {ex.Message}");
151171
throw;
152172
}
153173
}
@@ -183,9 +203,14 @@ public async Task<List<string>> ListDatabasesAsync(string clusterIdentifier, str
183203

184204
return databases;
185205
}
206+
catch (Amazon.RedshiftDataAPIService.Model.ValidationException ex)
207+
{
208+
Console.WriteLine($"Validation error: {ex.Message}");
209+
throw;
210+
}
186211
catch (Exception ex)
187212
{
188-
Console.WriteLine($"Error listing databases: {ex.Message}");
213+
Console.WriteLine($"Couldn't list databases. Here's why: {ex.Message}");
189214
throw;
190215
}
191216
}
@@ -223,9 +248,14 @@ year INTEGER NOT NULL
223248
Console.WriteLine("Table created: Movies");
224249
return response.Id;
225250
}
251+
catch (Amazon.RedshiftDataAPIService.Model.ValidationException ex)
252+
{
253+
Console.WriteLine($"Validation error: {ex.Message}");
254+
throw;
255+
}
226256
catch (Exception ex)
227257
{
228-
Console.WriteLine($"Error creating table: {ex.Message}");
258+
Console.WriteLine($"Couldn't create table. Here's why: {ex.Message}");
229259
throw;
230260
}
231261
}
@@ -268,9 +298,14 @@ public async Task<string> InsertMovieAsync(string clusterIdentifier, string data
268298
Console.WriteLine($"Inserted: {title} ({year})");
269299
return response.Id;
270300
}
301+
catch (Amazon.RedshiftDataAPIService.Model.ValidationException ex)
302+
{
303+
Console.WriteLine($"Validation error: {ex.Message}");
304+
throw;
305+
}
271306
catch (Exception ex)
272307
{
273-
Console.WriteLine($"Error inserting movie: {ex.Message}");
308+
Console.WriteLine($"Couldn't insert movie. Here's why: {ex.Message}");
274309
throw;
275310
}
276311
}
@@ -324,9 +359,14 @@ public async Task<List<string>> QueryMoviesByYearAsync(string clusterIdentifier,
324359

325360
return movieTitles;
326361
}
362+
catch (Amazon.RedshiftDataAPIService.Model.ValidationException ex)
363+
{
364+
Console.WriteLine($"Validation error: {ex.Message}");
365+
throw;
366+
}
327367
catch (Exception ex)
328368
{
329-
Console.WriteLine($"Error querying movies: {ex.Message}");
369+
Console.WriteLine($"Couldn't query movies. Here's why: {ex.Message}");
330370
throw;
331371
}
332372
}
@@ -350,9 +390,14 @@ public async Task<DescribeStatementResponse> DescribeStatementAsync(string state
350390
var response = await _redshiftDataClient.DescribeStatementAsync(request);
351391
return response;
352392
}
393+
catch (Amazon.RedshiftDataAPIService.Model.ResourceNotFoundException ex)
394+
{
395+
Console.WriteLine($"Statement not found: {ex.Message}");
396+
throw;
397+
}
353398
catch (Exception ex)
354399
{
355-
Console.WriteLine($"Error describing statement: {ex.Message}");
400+
Console.WriteLine($"Couldn't describe statement. Here's why: {ex.Message}");
356401
throw;
357402
}
358403
}
@@ -376,9 +421,14 @@ public async Task<List<List<Field>>> GetStatementResultAsync(string statementId)
376421
var response = await _redshiftDataClient.GetStatementResultAsync(request);
377422
return response.Records;
378423
}
424+
catch (Amazon.RedshiftDataAPIService.Model.ResourceNotFoundException ex)
425+
{
426+
Console.WriteLine($"Statement not found: {ex.Message}");
427+
throw;
428+
}
379429
catch (Exception ex)
380430
{
381-
Console.WriteLine($"Error getting statement result: {ex.Message}");
431+
Console.WriteLine($"Couldn't get statement result. Here's why: {ex.Message}");
382432
throw;
383433
}
384434
}
@@ -411,7 +461,6 @@ private async Task WaitForStatementToCompleteAsync(string statementId)
411461
var errorMessage = response?.Error ?? "Unknown error";
412462
Console.WriteLine($"The statement failed with status: {status}");
413463
Console.WriteLine($"Error message: {errorMessage}");
414-
throw new Exception($"Statement execution failed with status: {status}. Error: {errorMessage}");
415464
}
416465
}
417466

scenarios/basics/redshift/SPECIFICATION.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,20 @@ The following user input is required for this SDK getting started scenario:
1313
- The year to use to query records from the database.
1414
- Whether or not to delete the Amazon Redshift cluster.
1515

16+
## SQL Statement Requirements
17+
18+
All SQL statements that include user input or variable data MUST use parameterized queries to prevent SQL injection vulnerabilities. This applies to:
19+
20+
- INSERT statements when adding movie records (use parameters for id, title, and year values)
21+
- SELECT statements when querying by year (use parameters for the year value)
22+
- Any other SQL operations that incorporate dynamic values
23+
24+
Example of parameterized query usage:
25+
- Instead of: `SELECT * FROM Movies WHERE year = 2013`
26+
- Use: `SELECT * FROM Movies WHERE year = :year` with a parameter binding for `:year`
27+
28+
This security best practice ensures that user input is properly escaped and prevents malicious SQL code injection.
29+
1630
## Hello Redshift
1731
This program is intended for users not familiar with the Redshift SDK to easily get up an running. The logic is to show use of `redshiftClient.describeClustersPaginator()`.
1832

@@ -148,6 +162,21 @@ This concludes the Amazon Redshift SDK Getting Started scenario.
148162
149163
```
150164
165+
## Exception Handling
166+
167+
The following table lists the exceptions that should be caught and handled for each action in the scenario:
168+
169+
| Action | Exception | Handling |
170+
|---------------------------|--------------------------------|---------------------------------------------------------------------------------------------|
171+
| `createCluster` | ClusterAlreadyExistsFault | Notify the user that a cluster with this identifier already exists and exit. |
172+
| `describeClusters` | ClusterNotFoundFault | Notify the user that the specified cluster was not found. |
173+
| `listDatabases` | ValidationException | Notify the user that the cluster is not available or parameters are invalid. |
174+
| `executeStatement` | ValidationException | Notify the user of SQL syntax errors or invalid query parameters. |
175+
| `describeStatement` | ResourceNotFoundException | Notify the user that the statement ID was not found. |
176+
| `getStatementResult` | ResourceNotFoundException | Notify the user that results are not available for the statement ID. |
177+
| `modifyCluster` | ClusterNotFoundFault | Notify the user that the cluster to modify was not found. |
178+
| `deleteCluster` | ClusterNotFoundFault | Notify the user that the cluster to delete was not found. |
179+
151180
## SOS Tags
152181
153182
The following table describes the metadata used in this SDK Getting Started Scenario.

0 commit comments

Comments
 (0)