-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(Sdk): Added a new IOneOf abstraction and implementation
feat(IO): Added JSON and YAML serializers for the new OneOf type fix(Sdk): Transformed endpoints into OneOf Signed-off-by: Charles d'Avernas <charles.davernas@neuroglia.io>
- Loading branch information
Showing
17 changed files
with
463 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
// Copyright © 2024-Present The Serverless Workflow Specification Authors | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"), | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
namespace ServerlessWorkflow.Sdk; | ||
|
||
/// <summary> | ||
/// Defines the fundamentals of a service that wraps around multiple alternative value types | ||
/// </summary> | ||
public interface IOneOf | ||
{ | ||
|
||
/// <summary> | ||
/// Gets the object's current value | ||
/// </summary> | ||
/// <returns>The object's current value</returns> | ||
object? GetValue(); | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
// Copyright © 2024-Present The Serverless Workflow Specification Authors | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"), | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
using ServerlessWorkflow.Sdk.Serialization.Json; | ||
|
||
namespace ServerlessWorkflow.Sdk.Models; | ||
|
||
/// <summary> | ||
/// Gets an object that is one of the specified types | ||
/// </summary> | ||
/// <typeparam name="T1">A first type alternative</typeparam> | ||
/// <typeparam name="T2">A second type alternative</typeparam> | ||
[DataContract, JsonConverter(typeof(OneOfConverter))] | ||
public class OneOf<T1, T2> | ||
: IOneOf | ||
{ | ||
|
||
/// <summary> | ||
/// Initializes a new <see cref="OneOf{T1, T2}"/> | ||
/// </summary> | ||
/// <param name="value">The value of the <see cref="OneOf{T1, T2}"/></param> | ||
public OneOf(T1 value) | ||
{ | ||
this.TypeIndex = 1; | ||
this.T1Value = value!; | ||
} | ||
|
||
/// <summary> | ||
/// Initializes a new <see cref="OneOf{T1, T2}"/> | ||
/// </summary> | ||
/// <param name="value">The value of the <see cref="OneOf{T1, T2}"/></param> | ||
public OneOf(T2 value) | ||
{ | ||
this.TypeIndex = 2; | ||
this.T2Value = value!; | ||
} | ||
|
||
/// <summary> | ||
/// Gets the index of the discriminated type | ||
/// </summary> | ||
public int TypeIndex { get; } | ||
|
||
/// <summary> | ||
/// Gets the first possible value | ||
/// </summary> | ||
[DataMember(Order = 1), JsonIgnore, YamlIgnore] | ||
public T1? T1Value { get; } | ||
|
||
/// <summary> | ||
/// Gets the second possible value | ||
/// </summary> | ||
[DataMember(Order = 2), JsonIgnore, YamlIgnore] | ||
public T2? T2Value { get; } | ||
|
||
object? IOneOf.GetValue() => this.TypeIndex switch | ||
{ | ||
1 => this.T1Value, | ||
2 => this.T2Value, | ||
_ => null | ||
}; | ||
|
||
/// <summary> | ||
/// Implicitly convert the specified value into a new <see cref="OneOf{T1, T2}"/> | ||
/// </summary> | ||
/// <param name="value">The value to convert</param> | ||
public static implicit operator OneOf<T1, T2>(T1 value) => new(value); | ||
|
||
/// <summary> | ||
/// Implicitly convert the specified value into a new <see cref="OneOf{T1, T2}"/> | ||
/// </summary> | ||
/// <param name="value">The value to convert</param> | ||
public static implicit operator OneOf<T1, T2>(T2 value) => new(value); | ||
|
||
/// <summary> | ||
/// Implicitly convert the specified <see cref="OneOf{T1, T2}"/> into a new value | ||
/// </summary> | ||
/// <param name="value">The <see cref="OneOf{T1, T2}"/> to convert</param> | ||
public static implicit operator T1?(OneOf<T1, T2> value) => value.T1Value; | ||
|
||
/// <summary> | ||
/// Implicitly convert the specified <see cref="OneOf{T1, T2}"/> into a new value | ||
/// </summary> | ||
/// <param name="value">The <see cref="OneOf{T1, T2}"/> to convert</param> | ||
public static implicit operator T2?(OneOf<T1, T2> value) => value.T2Value; | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
95 changes: 95 additions & 0 deletions
95
src/ServerlessWorkflow.Sdk/Serialization/Json/OneOfConverter.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
// Copyright © 2024-Present The Serverless Workflow Specification Authors | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"), | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
using ServerlessWorkflow.Sdk.Models; | ||
using System.Collections.Concurrent; | ||
using System.Text.Json; | ||
|
||
namespace ServerlessWorkflow.Sdk.Serialization.Json; | ||
|
||
/// <summary> | ||
/// Represents the <see cref="JsonConverterFactory"/> used to serialize/deserialize to/from <see cref="IOneOf"/> instances | ||
/// </summary> | ||
public class OneOfConverter | ||
: JsonConverterFactory | ||
{ | ||
|
||
static readonly ConcurrentDictionary<Type, JsonConverter> ConverterCache = new(); | ||
|
||
/// <inheritdoc/> | ||
public override bool CanConvert(Type typeToConvert) | ||
{ | ||
if (!typeToConvert.IsGenericType) return false; | ||
var genericType = typeToConvert.GetGenericTypeDefinition(); | ||
return genericType == typeof(OneOf<,>); | ||
} | ||
|
||
/// <inheritdoc/> | ||
public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options) | ||
{ | ||
return ConverterCache.GetOrAdd(typeToConvert, (type) => | ||
{ | ||
var typeArgs = type.GetGenericArguments(); | ||
var converterType = typeof(OneOfConverterInner<,>).MakeGenericType(typeArgs); | ||
return (JsonConverter?)Activator.CreateInstance(converterType)!; | ||
}); | ||
} | ||
|
||
class OneOfConverterInner<T1, T2> : JsonConverter<OneOf<T1, T2>> | ||
{ | ||
|
||
/// <inheritdoc/> | ||
public override OneOf<T1, T2>? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) | ||
{ | ||
if (reader.TokenType == JsonTokenType.Null) return null; | ||
var document = JsonDocument.ParseValue(ref reader); | ||
var rootElement = document.RootElement; | ||
try | ||
{ | ||
var value1 = JsonSerializer.Deserialize<T1>(rootElement.GetRawText(), options); | ||
if (value1 != null) return new OneOf<T1, T2>(value1); | ||
} | ||
catch (JsonException) { } | ||
try | ||
{ | ||
var value2 = JsonSerializer.Deserialize<T2>(rootElement.GetRawText(), options); | ||
if (value2 != null) return new OneOf<T1, T2>(value2); | ||
} | ||
catch (JsonException) { throw new JsonException($"Cannot deserialize {rootElement.GetRawText()} as either {typeof(T1).Name} or {typeof(T2).Name}"); } | ||
throw new JsonException("Unexpected error during deserialization."); | ||
} | ||
|
||
public override void Write(Utf8JsonWriter writer, OneOf<T1, T2> value, JsonSerializerOptions options) | ||
{ | ||
if (value is null) | ||
{ | ||
writer.WriteNullValue(); | ||
return; | ||
} | ||
switch (value.TypeIndex) | ||
{ | ||
case 1: | ||
JsonSerializer.Serialize(writer, value.T1Value, options); | ||
break; | ||
case 2: | ||
JsonSerializer.Serialize(writer, value.T2Value, options); | ||
break; | ||
default: | ||
throw new JsonException("Invalid index value."); | ||
} | ||
} | ||
|
||
} | ||
|
||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
53 changes: 53 additions & 0 deletions
53
src/ServerlessWorkflow.Sdk/Serialization/Yaml/OneOfConverter.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
// Copyright © 2024-Present The Serverless Workflow Specification Authors | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"), | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
using Neuroglia.Serialization.Json; | ||
using Neuroglia.Serialization.Yaml; | ||
using ServerlessWorkflow.Sdk.Models; | ||
using YamlDotNet.Core; | ||
using YamlDotNet.Core.Events; | ||
|
||
namespace ServerlessWorkflow.Sdk.Serialization.Yaml; | ||
|
||
/// <summary> | ||
/// Represents the <see cref="IYamlTypeConverter"/> used to serialize and deserialize <see cref="OneOf{T1, T2}"/> instances | ||
/// </summary> | ||
public class OneOfConverter | ||
: IYamlTypeConverter | ||
{ | ||
|
||
/// <inheritdoc/> | ||
public virtual bool Accepts(Type type) => type.GetGenericType(typeof(OneOf<,>)) != null; | ||
|
||
/// <inheritdoc/> | ||
public virtual object? ReadYaml(IParser parser, Type type) => throw new NotImplementedException(); | ||
|
||
/// <inheritdoc/> | ||
public virtual void WriteYaml(IEmitter emitter, object? value, Type type) | ||
{ | ||
if (value == null || value is not IOneOf oneOf) | ||
{ | ||
emitter.Emit(new Scalar(null, null, string.Empty)); | ||
return; | ||
} | ||
var toSerialize = oneOf.GetValue(); | ||
if (toSerialize == null) | ||
{ | ||
emitter.Emit(new Scalar(null, null, string.Empty)); | ||
return; | ||
} | ||
var jsonNode = JsonSerializer.Default.SerializeToNode(toSerialize); | ||
new JsonNodeTypeConverter().WriteYaml(emitter, jsonNode, toSerialize.GetType()); | ||
} | ||
|
||
} |
42 changes: 42 additions & 0 deletions
42
src/ServerlessWorkflow.Sdk/Serialization/Yaml/OneOfNodeDeserializer.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
// Copyright © 2024-Present The Serverless Workflow Specification Authors | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"), | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
using ServerlessWorkflow.Sdk.Models; | ||
using Neuroglia.Serialization.Json; | ||
using YamlDotNet.Core; | ||
|
||
namespace ServerlessWorkflow.Sdk.Serialization.Yaml; | ||
|
||
/// <summary> | ||
/// Represents the service used to deserialize <see cref="OneOf{T1, T2}"/>s from YAML | ||
/// </summary> | ||
/// <param name="inner">The inner <see cref="INodeDeserializer"/></param> | ||
public class OneOfNodeDeserializer(INodeDeserializer inner) | ||
: INodeDeserializer | ||
{ | ||
|
||
/// <summary> | ||
/// Gets the inner <see cref="INodeDeserializer"/> | ||
/// </summary> | ||
protected INodeDeserializer Inner { get; } = inner; | ||
|
||
/// <inheritdoc/> | ||
public virtual bool Deserialize(IParser reader, Type expectedType, Func<IParser, Type, object?> nestedObjectDeserializer, out object? value) | ||
{ | ||
if (!typeof(IOneOf).IsAssignableFrom(expectedType)) return this.Inner.Deserialize(reader, expectedType, nestedObjectDeserializer, out value); | ||
if (!this.Inner.Deserialize(reader, typeof(Dictionary<object, object>), nestedObjectDeserializer, out value)) return false; | ||
value = JsonSerializer.Default.Deserialize(JsonSerializer.Default.SerializeToText(value!), expectedType); | ||
return true; | ||
} | ||
|
||
} |
Oops, something went wrong.