Skip to content

Commit 495faf4

Browse files
Added support for viewing DynamoDB items
1 parent f28c686 commit 495faf4

File tree

7 files changed

+409
-18
lines changed

7 files changed

+409
-18
lines changed

GuiStack/Extensions/DynamoDBExtensions.cs

Lines changed: 144 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using System;
1111
using System.Collections;
1212
using System.Collections.Generic;
13+
using System.Globalization;
1314
using System.IO;
1415
using System.Linq;
1516
using System.Net;
@@ -57,16 +58,15 @@ public static class DynamoDBExtensions
5758
private static readonly Dictionary<string, DynamoDBFieldType> DynamoDBFieldTypeMap =
5859
FieldTypeDynamoDBMap.ToDictionary(kvp => kvp.Value, kvp => kvp.Key);
5960

60-
private static AttributeValue GetDynamoDBAttributeValue(KeyValuePair<string, DynamoDBFieldModel> field)
61+
private static AttributeValue GetDynamoDBAttributeValue(string name, DynamoDBFieldModel field)
6162
{
62-
if(field.Value?.Value == null || field.Value.Type == DynamoDBFieldType.Null)
63+
if(field.Value == null || field.Type == DynamoDBFieldType.Null)
6364
return new AttributeValue() { NULL = true };
6465

65-
var name = field.Key;
66-
var value = field.Value.Value;
66+
var value = field.Value;
6767
var attributeValue = new AttributeValue();
6868

69-
switch(field.Value.Type)
69+
switch(field.Type)
7070
{
7171
case DynamoDBFieldType.String:
7272
if(value is not string str)
@@ -157,15 +157,131 @@ private static AttributeValue GetDynamoDBAttributeValue(KeyValuePair<string, Dyn
157157
attributeValue.BOOL = boolVal;
158158
break;
159159

160-
case DynamoDBFieldType.List: // TODO: Add support in the future?
161-
case DynamoDBFieldType.Map: // TODO: Add support in the future?
160+
case DynamoDBFieldType.List:
161+
if(value is not IEnumerable<DynamoDBFieldModel> list)
162+
throw new AmazonDynamoDBException($"Field '{name}' was expected to be a list of values", ErrorType.Sender, "GuiStack_InvalidField", null, HttpStatusCode.BadRequest);
163+
164+
attributeValue.L = new List<AttributeValue>();
165+
166+
int listIndex = -1;
167+
foreach(var item in list)
168+
{
169+
listIndex++;
170+
171+
if(item == null)
172+
continue;
173+
174+
var attributeItem = GetDynamoDBAttributeValue($"{name}[{listIndex}]", item);
175+
attributeValue.L.Add(attributeItem);
176+
}
177+
break;
178+
179+
case DynamoDBFieldType.Map:
180+
if(value is not IDictionary<string, DynamoDBFieldModel> map)
181+
throw new AmazonDynamoDBException($"Field '{name}' was expected to be a map of values", ErrorType.Sender, "GuiStack_InvalidField", null, HttpStatusCode.BadRequest);
182+
183+
attributeValue.M = new Dictionary<string, AttributeValue>();
184+
185+
foreach(var kvp in map)
186+
{
187+
if(string.IsNullOrWhiteSpace(kvp.Key) || kvp.Value == null)
188+
continue;
189+
190+
var attributeItem = GetDynamoDBAttributeValue($"{name}[{kvp.Key}]", kvp.Value);
191+
attributeValue.M.Add(kvp.Key, attributeItem);
192+
}
193+
break;
194+
162195
default:
163-
throw new AmazonDynamoDBException($"Unexpected field type '{field.Value.Type}'", ErrorType.Sender, "GuiStack_InvalidField", null, HttpStatusCode.BadRequest);
196+
throw new AmazonDynamoDBException($"Unknown type '{field.Type}' of field '{name}'", ErrorType.Sender, "GuiStack_InvalidField", null, HttpStatusCode.BadRequest);
164197
}
165198

166199
return attributeValue;
167200
}
168201

202+
private static DynamoDBFieldModel GetDynamoDBFieldModel(string name, AttributeValue attribute)
203+
{
204+
if(attribute == null || attribute.NULL)
205+
return new DynamoDBFieldModel() { Type = DynamoDBFieldType.Null };
206+
207+
switch(GetDynamoDBFieldType(attribute))
208+
{
209+
case DynamoDBFieldType.Null:
210+
return DynamoDBFieldModel.Null();
211+
212+
case DynamoDBFieldType.Bool:
213+
return DynamoDBFieldModel.Bool(attribute.BOOL);
214+
215+
case DynamoDBFieldType.Binary:
216+
return DynamoDBFieldModel.Binary(attribute.B.ToArray());
217+
218+
case DynamoDBFieldType.BinarySet:
219+
return DynamoDBFieldModel.BinarySet(
220+
attribute.BS
221+
.Where(stream => stream != null)
222+
.Select(stream => stream.ToArray())
223+
);
224+
225+
case DynamoDBFieldType.List:
226+
return DynamoDBFieldModel.List(
227+
attribute.L
228+
.Where(item => item != null)
229+
.Select((item, index) => GetDynamoDBFieldModel($"{name}[{index}]", item))
230+
);
231+
232+
case DynamoDBFieldType.Map:
233+
return DynamoDBFieldModel.Map(
234+
attribute.M
235+
.Where(kvp => !string.IsNullOrWhiteSpace(kvp.Key) && kvp.Value != null)
236+
.ToDictionary(kvp => kvp.Key, kvp => GetDynamoDBFieldModel($"{name}[{kvp.Key}]", kvp.Value))
237+
);
238+
239+
case DynamoDBFieldType.Number:
240+
return DynamoDBFieldModel.Number(decimal.Parse(attribute.N, CultureInfo.InvariantCulture));
241+
242+
case DynamoDBFieldType.NumberSet:
243+
return DynamoDBFieldModel.NumberSet(
244+
attribute.NS
245+
.Where(s => !string.IsNullOrWhiteSpace(s))
246+
.Select(s => decimal.Parse(s, CultureInfo.InvariantCulture))
247+
);
248+
249+
case DynamoDBFieldType.String:
250+
return DynamoDBFieldModel.String(attribute.S);
251+
252+
case DynamoDBFieldType.StringSet:
253+
return DynamoDBFieldModel.StringSet(attribute.SS);
254+
}
255+
256+
throw new AmazonDynamoDBException($"Unable to determine type of DynamoDB attribute '{name}'", ErrorType.Sender, "GuiStack_InvalidField", null, HttpStatusCode.BadRequest);
257+
}
258+
259+
private static DynamoDBFieldType GetDynamoDBFieldType(AttributeValue attribute)
260+
{
261+
if(attribute.NULL)
262+
return DynamoDBFieldType.Null;
263+
if(attribute.IsBOOLSet)
264+
return DynamoDBFieldType.Bool;
265+
if(attribute.IsLSet)
266+
return DynamoDBFieldType.List;
267+
if(attribute.IsMSet)
268+
return DynamoDBFieldType.Map;
269+
if(attribute.B != null)
270+
return DynamoDBFieldType.Binary;
271+
if(attribute.BS != null && attribute.BS.Count > 0)
272+
return DynamoDBFieldType.BinarySet;
273+
if(!string.IsNullOrWhiteSpace(attribute.N))
274+
return DynamoDBFieldType.Number;
275+
if(attribute.NS != null && attribute.NS.Count > 0)
276+
return DynamoDBFieldType.NumberSet;
277+
if(attribute.S != null)
278+
return DynamoDBFieldType.String;
279+
if(attribute.SS != null && attribute.SS.Count > 0)
280+
return DynamoDBFieldType.StringSet;
281+
282+
return DynamoDBFieldType.Unknown;
283+
}
284+
169285
/// <summary>
170286
/// Converts the <see cref="BillingMode"/> into a human-readable string.
171287
/// </summary>
@@ -240,12 +356,31 @@ public static DynamoDBItem ToDynamoDBItem(this IDictionary<string, DynamoDBField
240356
throw new AmazonDynamoDBException($"DynamoDB field name cannot be empty", ErrorType.Sender, "GuiStack_InvalidField", null, HttpStatusCode.BadRequest);
241357

242358
var name = field.Key;
243-
var attributeValue = GetDynamoDBAttributeValue(field);
359+
var attributeValue = GetDynamoDBAttributeValue(field.Key, field.Value);
244360

245361
result.Attributes.Add(name, attributeValue);
246362
}
247363

248364
return result;
249365
}
366+
367+
/// <summary>
368+
/// Converts the item data into a DynamoDB item model.
369+
/// </summary>
370+
/// <param name="itemData">The item data to convert.</param>
371+
public static DynamoDBItemModel ToDynamoDBItemModel(this IDictionary<string, AttributeValue> itemData)
372+
{
373+
var result = new DynamoDBItemModel();
374+
375+
foreach(var field in itemData)
376+
{
377+
if(string.IsNullOrWhiteSpace(field.Key) || field.Value == null)
378+
continue;
379+
380+
result.Add(field.Key, GetDynamoDBFieldModel(field.Key, field.Value));
381+
}
382+
383+
return result;
384+
}
250385
}
251386
}

GuiStack/Models/DynamoDBFieldModel.cs

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,141 @@
88
*/
99

1010
using System;
11+
using System.Collections.Generic;
12+
using System.Linq;
1113

1214
namespace GuiStack.Models
1315
{
1416
public class DynamoDBFieldModel
1517
{
1618
public DynamoDBFieldType Type { get; set; }
1719
public object Value { get; set; }
20+
21+
private static DynamoDBFieldModel NumberInternal<T>(T number)
22+
where T : struct
23+
{
24+
return new DynamoDBFieldModel() {
25+
Type = DynamoDBFieldType.Number,
26+
Value = number
27+
};
28+
}
29+
30+
private static DynamoDBFieldModel NumberSetInternal<T>(IEnumerable<T> numbers)
31+
where T : struct
32+
{
33+
if(numbers == null)
34+
throw new ArgumentNullException(nameof(numbers));
35+
36+
return new DynamoDBFieldModel() {
37+
Type = DynamoDBFieldType.NumberSet,
38+
Value = numbers.ToArray()
39+
};
40+
}
41+
42+
public static DynamoDBFieldModel Binary(byte[] data)
43+
{
44+
if(data == null)
45+
throw new ArgumentNullException(nameof(data));
46+
47+
return new DynamoDBFieldModel() {
48+
Type = DynamoDBFieldType.Binary,
49+
Value = Convert.ToBase64String(data)
50+
};
51+
}
52+
53+
public static DynamoDBFieldModel BinarySet(IEnumerable<byte[]> dataSets)
54+
{
55+
if(dataSets == null)
56+
throw new ArgumentNullException(nameof(dataSets));
57+
58+
return new DynamoDBFieldModel() {
59+
Type = DynamoDBFieldType.BinarySet,
60+
Value = dataSets.Select(data => Convert.ToBase64String(data)).ToArray()
61+
};
62+
}
63+
64+
public static DynamoDBFieldModel Bool(bool value)
65+
{
66+
return new DynamoDBFieldModel() {
67+
Type = DynamoDBFieldType.Bool,
68+
Value = value
69+
};
70+
}
71+
72+
public static DynamoDBFieldModel List(IEnumerable<DynamoDBFieldModel> list)
73+
{
74+
if(list == null)
75+
throw new ArgumentNullException(nameof(list));
76+
77+
return new DynamoDBFieldModel() {
78+
Type = DynamoDBFieldType.List,
79+
Value = list.ToArray()
80+
};
81+
}
82+
83+
public static DynamoDBFieldModel Map(IDictionary<string, DynamoDBFieldModel> map)
84+
{
85+
if(map == null)
86+
throw new ArgumentNullException(nameof(map));
87+
88+
return new DynamoDBFieldModel() {
89+
Type = DynamoDBFieldType.Map,
90+
Value = map.ToDictionary(kvp => kvp.Key, kvp => kvp.Value)
91+
};
92+
}
93+
94+
public static DynamoDBFieldModel Null()
95+
{
96+
return new DynamoDBFieldModel() {
97+
Type = DynamoDBFieldType.Null
98+
};
99+
}
100+
101+
public static DynamoDBFieldModel String(string str)
102+
{
103+
if(str == null)
104+
throw new ArgumentNullException(nameof(str));
105+
106+
return new DynamoDBFieldModel() {
107+
Type = DynamoDBFieldType.String,
108+
Value = str
109+
};
110+
}
111+
112+
public static DynamoDBFieldModel StringSet(IEnumerable<string> strings)
113+
{
114+
if(strings == null)
115+
throw new ArgumentNullException(nameof(strings));
116+
117+
return new DynamoDBFieldModel() {
118+
Type = DynamoDBFieldType.StringSet,
119+
Value = strings.ToArray()
120+
};
121+
}
122+
123+
public static DynamoDBFieldModel Number(sbyte number) => NumberInternal(number);
124+
public static DynamoDBFieldModel Number(byte number) => NumberInternal(number);
125+
public static DynamoDBFieldModel Number(short number) => NumberInternal(number);
126+
public static DynamoDBFieldModel Number(ushort number) => NumberInternal(number);
127+
public static DynamoDBFieldModel Number(int number) => NumberInternal(number);
128+
public static DynamoDBFieldModel Number(uint number) => NumberInternal(number);
129+
public static DynamoDBFieldModel Number(long number) => NumberInternal(number);
130+
public static DynamoDBFieldModel Number(ulong number) => NumberInternal(number);
131+
public static DynamoDBFieldModel Number(float number) => NumberInternal(number);
132+
public static DynamoDBFieldModel Number(double number) => NumberInternal(number);
133+
public static DynamoDBFieldModel Number(decimal number) => NumberInternal(number);
134+
135+
public static DynamoDBFieldModel NumberSet(IEnumerable<sbyte> numbers) => NumberSetInternal(numbers);
136+
public static DynamoDBFieldModel NumberSet(IEnumerable<byte> numbers) => NumberSetInternal(numbers);
137+
public static DynamoDBFieldModel NumberSet(IEnumerable<short> numbers) => NumberSetInternal(numbers);
138+
public static DynamoDBFieldModel NumberSet(IEnumerable<ushort> numbers) => NumberSetInternal(numbers);
139+
public static DynamoDBFieldModel NumberSet(IEnumerable<int> numbers) => NumberSetInternal(numbers);
140+
public static DynamoDBFieldModel NumberSet(IEnumerable<uint> numbers) => NumberSetInternal(numbers);
141+
public static DynamoDBFieldModel NumberSet(IEnumerable<long> numbers) => NumberSetInternal(numbers);
142+
public static DynamoDBFieldModel NumberSet(IEnumerable<ulong> numbers) => NumberSetInternal(numbers);
143+
public static DynamoDBFieldModel NumberSet(IEnumerable<float> numbers) => NumberSetInternal(numbers);
144+
public static DynamoDBFieldModel NumberSet(IEnumerable<double> numbers) => NumberSetInternal(numbers);
145+
public static DynamoDBFieldModel NumberSet(IEnumerable<decimal> numbers) => NumberSetInternal(numbers);
18146
}
19147

20148
public enum DynamoDBFieldType

GuiStack/Pages/DynamoDB/Index.cshtml.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
*/
99

1010
using System;
11+
using System.Threading.Tasks;
1112
using GuiStack.Repositories;
1213
using Microsoft.AspNetCore.Mvc;
1314
using Microsoft.AspNetCore.Mvc.RazorPages;
@@ -29,5 +30,11 @@ public IndexModel(IDynamoDBRepository dynamodbRepository)
2930
public void OnGet()
3031
{
3132
}
33+
34+
public async Task<IActionResult> OnGetRenderTableContentsPartial([FromQuery] int limit = 50)
35+
{
36+
var items = await DynamoDBRepository.ScanAsync(Table, limit);
37+
return Partial("~/Pages/DynamoDB/_TableContentsPartial.cshtml", items);
38+
}
3239
}
3340
}

0 commit comments

Comments
 (0)