Skip to content

Commit 805473e

Browse files
committed
Various unit text fixes and coverage expansion.
1 parent a976a38 commit 805473e

16 files changed

Lines changed: 411 additions & 171 deletions

Figment.Common/Data/IThingStorageProvider.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public interface IThingStorageProvider
3232
/// <param name="schema">The <see cref="Schema"/> to which the <see cref="Thing"/> specified by <paramref name="thingGuid"/> shall be associated.</param>
3333
/// <param name="cancellationToken">The cancellation token.</param>
3434
/// <returns>A task returning a <see cref="bool"/> indicating whether the operation was successful and an updated <see cref="Thing"/> loaded from the data store after the modification was made, if successful.</returns>
35-
public Task<(bool, Thing?)> AssociateWithSchemaAsync(string thingGuid, Schema schema, CancellationToken cancellationToken);
35+
public Task<(bool success, Thing? thing)> AssociateWithSchemaAsync(string thingGuid, Schema schema, CancellationToken cancellationToken);
3636

3737
/// <summary>
3838
/// Creates a new <see cref="Thing"/> in its underlying data store.

Figment.Common/Data/ThingStorageProviderBase.cs

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ You should have received a copy of the GNU Affero General Public License
1818

1919
using System.Collections;
2020
using System.Runtime.CompilerServices;
21+
using Figment.Common.Errors;
2122

2223
namespace Figment.Common.Data;
2324

@@ -27,7 +28,7 @@ namespace Figment.Common.Data;
2728
public abstract class ThingStorageProviderBase : IThingStorageProvider
2829
{
2930
/// <inheritdoc/>
30-
public abstract Task<(bool, Thing?)> AssociateWithSchemaAsync(string thingGuid, Schema schema, CancellationToken cancellationToken);
31+
public abstract Task<(bool success, Thing? thing)> AssociateWithSchemaAsync(string thingGuid, Schema schema, CancellationToken cancellationToken);
3132

3233
/// <inheritdoc/>
3334
public abstract Task<CreateThingResult> CreateAsync(Schema? schema, string thingName, Dictionary<string, object?> properties, CancellationToken cancellationToken);
@@ -225,4 +226,54 @@ async Task<bool> ThingMatches(Thing thing)
225226

226227
/// <inheritdoc/>
227228
public abstract Task<bool> RenumberIncrementField(string schemaGuid, CancellationToken cancellationToken);
229+
230+
/// <summary>
231+
/// If this schema has an increment field, set its value.
232+
/// </summary>
233+
/// <param name="schema">The schema used to create the thing.</param>
234+
/// <param name="thing">The thing.</param>
235+
/// <param name="cancellationToken">The cancellation token.</param>
236+
/// <returns>Results of the attempt.</returns>
237+
protected async Task<(bool success, string? message)> CreateAsyncIncrementFieldInternal(Schema? schema, Thing thing, CancellationToken cancellationToken)
238+
{
239+
// If this schema has an increment field, set its value.
240+
if (schema != null)
241+
{
242+
var increment = schema.GetIncrementField();
243+
if (increment != null)
244+
{
245+
var next = increment.NextValue;
246+
var tsrIncrement = await thing.Set(increment.Name, next, cancellationToken);
247+
if (!tsrIncrement.Success)
248+
{
249+
AmbientErrorContext.Provider.LogWarning($"Unable to update increment field {increment.Name}: {((tsrIncrement.Messages == null || tsrIncrement.Messages.Length == 0) ? "No error message provided." : string.Join("; ", tsrIncrement.Messages))}");
250+
return (false, $"Unable to update increment field {increment.Name}: {((tsrIncrement.Messages == null || tsrIncrement.Messages.Length == 0) ? "No error message provided." : string.Join("; ", tsrIncrement.Messages))}");
251+
}
252+
253+
var (saveSuccess, saveMessage) = await thing.SaveAsync(cancellationToken);
254+
if (!saveSuccess)
255+
{
256+
AmbientErrorContext.Provider.LogWarning($"Unable to save increment field {increment.Name} update: {saveMessage}");
257+
return (false, $"Unable to save increment field {increment.Name} update: {saveMessage}");
258+
}
259+
260+
// Update schema so next = next + 1.
261+
var ssp = AmbientStorageContext.StorageProvider?.GetSchemaStorageProvider();
262+
if (ssp == null)
263+
{
264+
AmbientErrorContext.Provider.LogError(AmbientStorageContext.RESOURCE_ERR_UNABLE_TO_LOAD_SCHEMA_STORAGE_PROVIDER);
265+
return (false, AmbientStorageContext.RESOURCE_ERR_UNABLE_TO_LOAD_SCHEMA_STORAGE_PROVIDER);
266+
}
267+
268+
increment.NextValue += 1;
269+
var (schemaSavedSuccess, schemaSavedMessage) = await ssp.SaveAsync(schema, cancellationToken);
270+
if (!schemaSavedSuccess)
271+
{
272+
AmbientErrorContext.Provider.LogError($"Unable to save schema '{schema.Name}' ({schema.Guid}): {schemaSavedMessage}");
273+
}
274+
}
275+
}
276+
277+
return (true, null);
278+
}
228279
}

Figment.Common/FileUtility.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ You should have received a copy of the GNU Affero General Public License
1616
along with this program. If not, see <http://www.gnu.org/licenses/>.
1717
*/
1818

19-
using Figment.Common.Calculations.Functions;
20-
2119
namespace Figment.Common;
2220

2321
/// <summary>

Figment.Common/Schema.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,28 @@ public SchemaDateField AddDateField(string name)
228228
return sdf;
229229
}
230230

231+
/// <summary>
232+
/// Creates a new <see cref="SchemaIncrementField"/> and adds it as a field of this schema.
233+
/// </summary>
234+
/// <param name="name">The name of the new increment field.</param>
235+
/// <returns>The new increment field.</returns>
236+
/// <exception cref="ArgumentException">Thrown if the <paramref name="name"/> is null or if a field of this name already exists on the schema.</exception>
237+
public SchemaIncrementField AddIncrementField(string name)
238+
{
239+
MarkAccessed();
240+
241+
ArgumentException.ThrowIfNullOrWhiteSpace(name);
242+
243+
if (Properties.ContainsKey(name))
244+
{
245+
throw new ArgumentException($"A field named '{name}' already exists on this schema", nameof(name));
246+
}
247+
248+
var sdf = new SchemaIncrementField(name);
249+
Properties.Add(name, sdf);
250+
return sdf;
251+
}
252+
231253
/// <summary>
232254
/// Creates a new <see cref="SchemaMonthDayField"/> and adds it as a field of this schema.
233255
/// </summary>

Figment.Common/Thing.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ public string Name
111111
/// <summary>
112112
/// Gets a value indicating whether this object has been changed since it was loaded.
113113
/// </summary>
114+
[JsonIgnore]
114115
public bool IsDirty { get; private set; }
115116

116117
/// <summary>

Figment.Data.Local/LocalDirectoryStorageProvider.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,12 +89,11 @@ public Task<bool> InitializeAsync(IDictionary<string, string> settings, Cancella
8989
{
9090
ArgumentNullException.ThrowIfNull(settings);
9191

92-
if (!settings.ContainsKey(SETTINGS_KEY_DB_PATH))
92+
if (!settings.TryGetValue(SETTINGS_KEY_DB_PATH, out string? tentative))
9393
{
9494
throw new ArgumentException($"Settings do not contain required key {SETTINGS_KEY_DB_PATH}", nameof(settings));
9595
}
9696

97-
var tentative = settings[SETTINGS_KEY_DB_PATH];
9897
if (string.IsNullOrWhiteSpace(tentative))
9998
{
10099
throw new ArgumentException($"Setting {SETTINGS_KEY_DB_PATH} must be specified.", nameof(settings));

Figment.Data.Local/LocalDirectoryThingStorageProvider.cs

Lines changed: 3 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -552,41 +552,10 @@ public override async Task<CreateThingResult> CreateAsync(Schema? schema, string
552552
}
553553

554554
// If this schema has an increment field, set its value.
555-
if (schema != null)
555+
var caifi = await CreateAsyncIncrementFieldInternal(schema, thing, cancellationToken);
556+
if (!caifi.success)
556557
{
557-
var increment = schema.GetIncrementField();
558-
if (increment != null)
559-
{
560-
var next = increment.NextValue;
561-
var tsrIncrement = await thing.Set(increment.Name, next, cancellationToken);
562-
if (!tsrIncrement.Success)
563-
{
564-
AmbientErrorContext.Provider.LogWarning($"Unable to update increment field {increment.Name}: {((tsrIncrement.Messages == null || tsrIncrement.Messages.Length == 0) ? "No error message provided." : string.Join("; ", tsrIncrement.Messages))}");
565-
return new CreateThingResult { Success = false, Message = $"Unable to update increment field {increment.Name}: {((tsrIncrement.Messages == null || tsrIncrement.Messages.Length == 0) ? "No error message provided." : string.Join("; ", tsrIncrement.Messages))}" };
566-
}
567-
568-
var (saveSuccess, saveMessage) = await thing.SaveAsync(cancellationToken);
569-
if (!saveSuccess)
570-
{
571-
AmbientErrorContext.Provider.LogWarning($"Unable to save increment field {increment.Name} update: {saveMessage}");
572-
return new CreateThingResult { Success = false, Message = $"Unable to save increment field {increment.Name} update: {saveMessage}" };
573-
}
574-
575-
// Update schema so next = next + 1.
576-
var ssp = AmbientStorageContext.StorageProvider?.GetSchemaStorageProvider();
577-
if (ssp == null)
578-
{
579-
AmbientErrorContext.Provider.LogError(AmbientStorageContext.RESOURCE_ERR_UNABLE_TO_LOAD_SCHEMA_STORAGE_PROVIDER);
580-
return new CreateThingResult { Success = false, Message = AmbientStorageContext.RESOURCE_ERR_UNABLE_TO_LOAD_SCHEMA_STORAGE_PROVIDER };
581-
}
582-
583-
increment.NextValue += 1;
584-
var (schemaSavedSuccess, schemaSavedMessage) = await ssp.SaveAsync(schema, cancellationToken);
585-
if (!schemaSavedSuccess)
586-
{
587-
AmbientErrorContext.Provider.LogError($"Unable to save schema '{schema.Name}' ({schema.Guid}): {schemaSavedMessage}");
588-
}
589-
}
558+
return new CreateThingResult { Success = caifi.success, Message = caifi.message };
590559
}
591560

592561
var tsr = await thing.Set(properties, cancellationToken);

Figment.Data.Local/LocalThing.cs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,11 @@ namespace Figment.Data.Local;
2323
/// <summary>
2424
/// A version of a <see cref="Thing"/> that was loaded from <see cref="LocalDirectoryThingStorageProvider"/>
2525
/// </summary>
26-
public class LocalThing : Thing
26+
/// <inheritdoc/>
27+
public class LocalThing(string guid, string newName, string filePath) : Thing(guid, newName)
2728
{
2829
/// <summary>
2930
/// The file from which this thing was loaded.
3031
/// </summary>
31-
public string FilePath { get; init; }
32-
33-
/// <inheritdoc/>
34-
public LocalThing(string guid, string newName, string filePath) : base(guid, newName)
35-
{
36-
FilePath = filePath;
37-
}
32+
public string FilePath { get; init; } = filePath;
3833
}

Figment.Data.Memory/MemoryThingStorageProvider.cs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,9 @@ public override Task<bool> GuidExists(string thingGuid, CancellationToken _)
162162
/// <inheritdoc/>
163163
public override async Task<CreateThingResult> CreateAsync(Schema? schema, string thingName, Dictionary<string, object?> properties, CancellationToken cancellationToken)
164164
{
165+
ArgumentException.ThrowIfNullOrWhiteSpace(thingName);
166+
ArgumentNullException.ThrowIfNull(properties);
167+
165168
var thingGuid = Guid.NewGuid().ToString();
166169
var thing = new Thing(thingGuid, thingName)
167170
{
@@ -175,6 +178,13 @@ public override async Task<CreateThingResult> CreateAsync(Schema? schema, string
175178
if (schema != null)
176179
await AssociateWithSchemaInternal(thing, schema, cancellationToken);
177180

181+
// If this schema has an increment field, set its value.
182+
var caifi = await CreateAsyncIncrementFieldInternal(schema, thing, cancellationToken);
183+
if (!caifi.success)
184+
{
185+
return new CreateThingResult { Success = caifi.success, Message = caifi.message };
186+
}
187+
178188
var tsr = await thing.Set(properties, cancellationToken);
179189
if (!tsr.Success)
180190
{
@@ -194,16 +204,19 @@ public override async Task<CreateThingResult> CreateAsync(Schema? schema, string
194204
}
195205

196206
/// <inheritdoc/>
197-
public override async Task<(bool, Thing?)> AssociateWithSchemaAsync(string thingGuid, Schema schema, CancellationToken cancellationToken)
207+
public override async Task<(bool success, Thing? thing)> AssociateWithSchemaAsync(string thingGuid, Schema schema, CancellationToken cancellationToken)
198208
{
209+
ArgumentException.ThrowIfNullOrWhiteSpace(thingGuid);
210+
ArgumentNullException.ThrowIfNull(schema);
211+
199212
var thing = await LoadAsync(thingGuid, cancellationToken);
200213
if (thing == null)
201214
return (false, null);
202215

203216
return await AssociateWithSchemaInternal(thing, schema, cancellationToken);
204217
}
205218

206-
private static async Task<(bool, Thing?)> AssociateWithSchemaInternal(Thing thing, Schema schema, CancellationToken cancellationToken)
219+
private static async Task<(bool success, Thing? thing)> AssociateWithSchemaInternal(Thing thing, Schema schema, CancellationToken cancellationToken)
207220
{
208221
ArgumentNullException.ThrowIfNull(thing);
209222
ArgumentNullException.ThrowIfNull(schema);
@@ -222,6 +235,9 @@ public override async Task<CreateThingResult> CreateAsync(Schema? schema, string
222235
/// <inheritdoc/>
223236
public override async Task<(bool, Thing?)> DissociateFromSchemaAsync(string thingGuid, string schemaGuid, CancellationToken cancellationToken)
224237
{
238+
ArgumentException.ThrowIfNullOrWhiteSpace(thingGuid);
239+
ArgumentException.ThrowIfNullOrWhiteSpace(schemaGuid);
240+
225241
var thing = await LoadAsync(thingGuid, cancellationToken);
226242
if (thing == null)
227243
return (false, null);
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using Figment.Common.Data;
2+
using Figment.Data.Local;
3+
4+
namespace Figment.Test.Data.Local;
5+
6+
[TestClass]
7+
public static class LocalSetup
8+
{
9+
internal static string? TemporaryDirectory;
10+
11+
[AssemblyInitialize]
12+
public static void AssemblyInit(TestContext context)
13+
{
14+
var dir = Directory.CreateTempSubdirectory();
15+
TemporaryDirectory = dir.FullName;
16+
17+
AmbientStorageContext.StorageProvider = new LocalDirectoryStorageProvider();
18+
_ = AmbientStorageContext.StorageProvider.InitializeAsync(
19+
new Dictionary<string, string>() { { LocalDirectoryStorageProvider.SETTINGS_KEY_DB_PATH, TemporaryDirectory } },
20+
CancellationToken.None).Result;
21+
}
22+
23+
[AssemblyCleanup]
24+
public static void AssemblyCleanup()
25+
{
26+
if (!string.IsNullOrWhiteSpace(TemporaryDirectory) && Directory.Exists(TemporaryDirectory))
27+
{
28+
Directory.Delete(TemporaryDirectory, true);
29+
}
30+
}
31+
}

0 commit comments

Comments
 (0)