Skip to content

Commit 5a4a68b

Browse files
authored
Merge pull request #65 from serverlessworkflow/feat-catalog-component
Added a new `CatalogDefinition` model
2 parents 01af5d0 + 873c61b commit 5a4a68b

11 files changed

+229
-10
lines changed

src/ServerlessWorkflow.Sdk.Builders/ServerlessWorkflow.Sdk.Builders.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<ImplicitUsings>enable</ImplicitUsings>
66
<Nullable>enable</Nullable>
77
<VersionPrefix>1.0.0</VersionPrefix>
8-
<VersionSuffix>alpha4.1</VersionSuffix>
8+
<VersionSuffix>alpha5</VersionSuffix>
99
<AssemblyVersion>$(VersionPrefix)</AssemblyVersion>
1010
<FileVersion>$(VersionPrefix)</FileVersion>
1111
<NeutralLanguage>en</NeutralLanguage>

src/ServerlessWorkflow.Sdk.IO/ServerlessWorkflow.Sdk.IO.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<ImplicitUsings>enable</ImplicitUsings>
66
<Nullable>enable</Nullable>
77
<VersionPrefix>1.0.0</VersionPrefix>
8-
<VersionSuffix>alpha4.1</VersionSuffix>
8+
<VersionSuffix>alpha5</VersionSuffix>
99
<AssemblyVersion>$(VersionPrefix)</AssemblyVersion>
1010
<FileVersion>$(VersionPrefix)</FileVersion>
1111
<NeutralLanguage>en</NeutralLanguage>
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright © 2024-Present The Serverless Workflow Specification Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License"),
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
namespace ServerlessWorkflow.Sdk.Models;
15+
16+
/// <summary>
17+
/// Represents the definition of a workflow component catalog
18+
/// </summary>
19+
[DataContract]
20+
public record CatalogDefinition
21+
{
22+
23+
/// <summary>
24+
/// Gets the name of the default catalog
25+
/// </summary>
26+
public const string DefaultCatalogName = "default";
27+
28+
/// <summary>
29+
/// Gets/sets the endpoint that defines the root URL at which the catalog is located
30+
/// </summary>
31+
[IgnoreDataMember, JsonIgnore, YamlIgnore]
32+
public virtual EndpointDefinition Endpoint
33+
{
34+
get => this.EndpointValue.T1Value ?? new() { Uri = this.EndpointUri };
35+
set => this.EndpointValue = value;
36+
}
37+
38+
/// <summary>
39+
/// Gets/sets the endpoint that defines the root URL at which the catalog is located
40+
/// </summary>
41+
[IgnoreDataMember, JsonIgnore, YamlIgnore]
42+
public virtual Uri EndpointUri
43+
{
44+
get => this.EndpointValue.T1Value?.Uri ?? this.EndpointValue.T2Value!;
45+
set => this.EndpointValue = value;
46+
}
47+
48+
/// <summary>
49+
/// Gets/sets the endpoint that defines the root URL at which the catalog is located
50+
/// </summary>
51+
[Required]
52+
[DataMember(Name = "endpoint", Order = 1), JsonInclude, JsonPropertyName("endpoint"), JsonPropertyOrder(1), YamlMember(Alias = "endpoint", Order = 1)]
53+
protected virtual OneOf<EndpointDefinition, Uri> EndpointValue { get; set; } = null!;
54+
55+
}

src/ServerlessWorkflow.Sdk/Models/ComponentDefinitionCollection.cs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,40 +26,46 @@ public record ComponentDefinitionCollection
2626
[DataMember(Name = "authentications", Order = 1), JsonPropertyName("authentications"), JsonPropertyOrder(1), YamlMember(Alias = "authentications", Order = 1)]
2727
public virtual EquatableDictionary<string, AuthenticationPolicyDefinition>? Authentications { get; set; }
2828

29+
/// <summary>
30+
/// Gets/sets a name/value mapping of the catalogs, if any, from which to import reusable components used within the workflow
31+
/// </summary>
32+
[DataMember(Name = "catalogs", Order = 2), JsonPropertyName("catalogs"), JsonPropertyOrder(2), YamlMember(Alias = "catalogs", Order = 2)]
33+
public virtual EquatableDictionary<string, CatalogDefinition>? Catalogs { get; set; }
34+
2935
/// <summary>
3036
/// Gets/sets a name/value mapping of the workflow's errors, if any
3137
/// </summary>
32-
[DataMember(Name = "errors", Order = 2), JsonPropertyName("errors"), JsonPropertyOrder(2), YamlMember(Alias = "errors", Order = 2)]
38+
[DataMember(Name = "errors", Order = 3), JsonPropertyName("errors"), JsonPropertyOrder(3), YamlMember(Alias = "errors", Order = 3)]
3339
public virtual EquatableDictionary<string, ErrorDefinition>? Errors { get; set; }
3440

3541
/// <summary>
3642
/// Gets/sets a name/value mapping of the workflow's extensions, if any
3743
/// </summary>
38-
[DataMember(Name = "extensions", Order = 3), JsonPropertyName("extensions"), JsonPropertyOrder(3), YamlMember(Alias = "extensions", Order = 3)]
44+
[DataMember(Name = "extensions", Order = 4), JsonPropertyName("extensions"), JsonPropertyOrder(4), YamlMember(Alias = "extensions", Order = 4)]
3945
public virtual EquatableDictionary<string, ExtensionDefinition>? Extensions { get; set; }
4046

4147
/// <summary>
4248
/// Gets/sets a name/value mapping of the workflow's reusable functions
4349
/// </summary>
44-
[DataMember(Name = "functions", Order = 4), JsonPropertyName("functions"), JsonPropertyOrder(4), YamlMember(Alias = "functions", Order = 4)]
50+
[DataMember(Name = "functions", Order = 5), JsonPropertyName("functions"), JsonPropertyOrder(5), YamlMember(Alias = "functions", Order = 5)]
4551
public virtual EquatableDictionary<string, TaskDefinition>? Functions { get; set; }
4652

4753
/// <summary>
4854
/// Gets/sets a name/value mapping of the workflow's reusable retry policies
4955
/// </summary>
50-
[DataMember(Name = "retries", Order = 5), JsonPropertyName("retries"), JsonPropertyOrder(5), YamlMember(Alias = "retries", Order = 5)]
56+
[DataMember(Name = "retries", Order = 6), JsonPropertyName("retries"), JsonPropertyOrder(6), YamlMember(Alias = "retries", Order = 6)]
5157
public virtual EquatableDictionary<string, RetryPolicyDefinition>? Retries { get; set; }
5258

5359
/// <summary>
5460
/// Gets/sets a list containing the workflow's secrets
5561
/// </summary>
56-
[DataMember(Name = "secrets", Order = 6), JsonPropertyName("secrets"), JsonPropertyOrder(6), YamlMember(Alias = "secrets", Order = 6)]
62+
[DataMember(Name = "secrets", Order = 7), JsonPropertyName("secrets"), JsonPropertyOrder(7), YamlMember(Alias = "secrets", Order = 7)]
5763
public virtual EquatableList<string>? Secrets { get; set; }
5864

5965
/// <summary>
6066
/// Gets/sets a name/value mapping of the workflow's reusable timeouts
6167
/// </summary>
62-
[DataMember(Name = "timeouts", Order = 7), JsonPropertyName("timeouts"), JsonPropertyOrder(7), YamlMember(Alias = "timeouts", Order = 7)]
68+
[DataMember(Name = "timeouts", Order = 7), JsonPropertyName("timeouts"), JsonPropertyOrder(8), YamlMember(Alias = "timeouts", Order = 8)]
6369
public virtual EquatableDictionary<string, TimeoutDefinition>? Timeouts { get; set; }
6470

6571
}

src/ServerlessWorkflow.Sdk/Properties/ValidationErrors.Designer.cs

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/ServerlessWorkflow.Sdk/Properties/ValidationErrors.resx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,4 +132,10 @@
132132
<data name="UndefinedTimeout" xml:space="preserve">
133133
<value>Undefined timeout</value>
134134
</data>
135+
<data name="UndefinedCatalog" xml:space="preserve">
136+
<value>Undefined catalog</value>
137+
</data>
138+
<data name="InvalidCatalogedFunctionCallFormat" xml:space="preserve">
139+
<value>Invalid cataloged function call format. Expected format '{functionName}:{functionSemanticVersion}@{catalogName}'</value>
140+
</data>
135141
</root>

src/ServerlessWorkflow.Sdk/ServerlessWorkflow.Sdk.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<ImplicitUsings>enable</ImplicitUsings>
66
<Nullable>enable</Nullable>
77
<VersionPrefix>1.0.0</VersionPrefix>
8-
<VersionSuffix>alpha4.1</VersionSuffix>
8+
<VersionSuffix>alpha5</VersionSuffix>
99
<AssemblyVersion>$(VersionPrefix)</AssemblyVersion>
1010
<FileVersion>$(VersionPrefix)</FileVersion>
1111
<NeutralLanguage>en</NeutralLanguage>

src/ServerlessWorkflow.Sdk/Validation/CallTaskDefinitionValidator.cs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
using FluentValidation;
1515
using Microsoft.Extensions.DependencyInjection;
1616
using Neuroglia.Serialization;
17+
using Semver;
1718
using ServerlessWorkflow.Sdk.Models;
1819
using ServerlessWorkflow.Sdk.Models.Calls;
1920
using ServerlessWorkflow.Sdk.Models.Tasks;
@@ -37,6 +38,14 @@ public CallTaskDefinitionValidator(IServiceProvider serviceProvider, ComponentDe
3738
.Must(ReferenceAnExistingFunction)
3839
.When(c => !Uri.TryCreate(c.Call, UriKind.Absolute, out _) && !c.Call.Contains('@'))
3940
.WithMessage(ValidationErrors.UndefinedFunction);
41+
this.RuleFor(c => c.Call)
42+
.Must(BeWellFormedCatalogedFunctionCall)
43+
.When(c => c.Call.Contains('@') && (!Uri.TryCreate(c.Call, UriKind.Absolute, out var uri) || string.IsNullOrWhiteSpace(uri.Host)))
44+
.WithMessage(ValidationErrors.InvalidCatalogedFunctionCallFormat);
45+
this.RuleFor(c => c.Call)
46+
.Must(ReferenceAnExistingCatalog)
47+
.When(c => c.Call.Contains('@') && (!Uri.TryCreate(c.Call, UriKind.Absolute, out var uri) || string.IsNullOrWhiteSpace(uri.Host)))
48+
.WithMessage(ValidationErrors.UndefinedCatalog);
4049
this.When(c => c.Call == Function.AsyncApi, () =>
4150
{
4251
this.RuleFor(c => (AsyncApiCallDefinition)this.JsonSerializer.Convert(c.With, typeof(AsyncApiCallDefinition))!)
@@ -81,9 +90,43 @@ public CallTaskDefinitionValidator(IServiceProvider serviceProvider, ComponentDe
8190
/// <returns>A boolean indicating whether or not the specified function exists</returns>
8291
protected virtual bool ReferenceAnExistingFunction(string name)
8392
{
93+
if (string.IsNullOrWhiteSpace(name)) return false;
8494
if (Function.AsEnumerable().Contains(name)) return true;
8595
else if (this.Components?.Functions?.ContainsKey(name) == true) return true;
8696
else return false;
8797
}
8898

99+
/// <summary>
100+
/// Determines whether or not the format of the call is a valid cataloged function call
101+
/// </summary>
102+
/// <param name="name">The name of the function to check</param>
103+
/// <returns>A boolean indicatingwhether or not the format of the call is a valid cataloged function call</returns>
104+
protected virtual bool BeWellFormedCatalogedFunctionCall(string name)
105+
{
106+
if (string.IsNullOrWhiteSpace(name)) return false;
107+
var components = name.Split('@', StringSplitOptions.RemoveEmptyEntries);
108+
if (components.Length != 2) return false;
109+
var qualifiedName = components[0];
110+
components = qualifiedName.Split(':');
111+
if (components.Length != 2) return false;
112+
var version = components[1];
113+
if (!SemVersion.TryParse(version, SemVersionStyles.Strict, out var semver)) return false;
114+
return true;
115+
}
116+
117+
/// <summary>
118+
/// Determines whether or not the catalog from which the specified function is imported exists
119+
/// </summary>
120+
/// <param name="name">The name of the function to check</param>
121+
/// <returns>A boolean indicating whether or not the catalog from which the specified function is imported exists</returns>
122+
protected virtual bool ReferenceAnExistingCatalog(string name)
123+
{
124+
if (string.IsNullOrWhiteSpace(name)) return false;
125+
var components = name.Split('@', StringSplitOptions.RemoveEmptyEntries);
126+
var catalogName = components[1];
127+
if (catalogName == CatalogDefinition.DefaultCatalogName) return true;
128+
else if(this.Components?.Catalogs?.ContainsKey(catalogName) == true) return true;
129+
else return false;
130+
}
131+
89132
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright © 2024-Present The Serverless Workflow Specification Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License"),
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
using FluentValidation;
15+
using ServerlessWorkflow.Sdk.Models;
16+
17+
namespace ServerlessWorkflow.Sdk.Validation;
18+
19+
/// <summary>
20+
/// Represents the <see cref="IValidator"/> used to validate <see cref="CatalogDefinition"/>s
21+
/// </summary>
22+
public class CatalogDefinitionValidator
23+
: AbstractValidator<CatalogDefinition>
24+
{
25+
26+
/// <inheritdoc/>
27+
public CatalogDefinitionValidator(IServiceProvider serviceProvider)
28+
{
29+
this.ServiceProvider = serviceProvider;
30+
this.RuleFor(c => c.Endpoint)
31+
.NotNull()
32+
.When(c => c.EndpointUri == null);
33+
this.RuleFor(c => c.EndpointUri)
34+
.NotNull()
35+
.When(c => c.Endpoint == null);
36+
}
37+
38+
/// <summary>
39+
/// Gets the current <see cref="IServiceProvider"/>
40+
/// </summary>
41+
protected IServiceProvider ServiceProvider { get; }
42+
43+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Copyright © 2024-Present The Serverless Workflow Specification Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License"),
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
using FluentValidation;
15+
using ServerlessWorkflow.Sdk.Models;
16+
17+
namespace ServerlessWorkflow.Sdk.Validation;
18+
19+
/// <summary>
20+
/// Represents the <see cref="IValidator"/> used to validate <see cref="CatalogDefinition"/> key/value pairs
21+
/// </summary>
22+
public class CatalogKeyValuePairValidator
23+
: AbstractValidator<KeyValuePair<string, CatalogDefinition>>
24+
{
25+
26+
/// <inheritdoc/>
27+
public CatalogKeyValuePairValidator(IServiceProvider serviceProvider)
28+
{
29+
this.ServiceProvider = serviceProvider;
30+
this.RuleFor(t => t.Value)
31+
.Custom((value, context) =>
32+
{
33+
var key = context.InstanceToValidate.Key;
34+
var validator = new CatalogDefinitionValidator(serviceProvider);
35+
var validationResult = validator.Validate(value);
36+
foreach (var error in validationResult.Errors) context.AddFailure($"{key}.{error.PropertyName}", error.ErrorMessage);
37+
});
38+
}
39+
40+
/// <summary>
41+
/// Gets the current <see cref="IServiceProvider"/>
42+
/// </summary>
43+
protected IServiceProvider ServiceProvider { get; }
44+
45+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy