Skip to content

Commit

Permalink
Merge pull request #6 from Victorvhn/feature/key-case-style
Browse files Browse the repository at this point in the history
Support for multiple naming conventions
  • Loading branch information
Victor Hugo do Nascimento committed Apr 16, 2023
2 parents 7886c7a + ab48d70 commit 564714d
Show file tree
Hide file tree
Showing 14 changed files with 226 additions and 23 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,15 @@ jobs:

steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v3

- name: Initialize CodeQL
uses: github/codeql-action/init@v1
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}

- name: Autobuild
uses: github/codeql-action/autobuild@v1
uses: github/codeql-action/autobuild@v2

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
uses: github/codeql-action/analyze@v2
6 changes: 3 additions & 3 deletions .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
name: Build and Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3

- name: Setup .NET
uses: actions/setup-dotnet@v1
Expand Down Expand Up @@ -42,15 +42,15 @@ jobs:
needs: build_and_test
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3

- name: Setup .NET
uses: actions/setup-dotnet@v1
with:
dotnet-version: ${{ env.DOTNET_VERSION }}

- name: Publish to nuget on version change
uses: alirezanet/publish-nuget@v3.0.1
uses: alirezanet/publish-nuget@v3.0.4
with:
PROJECT_FILE_PATH: Uri.Query.String.Composer/UriQueryStringComposer.csproj
PACKAGE_NAME: Uri.QueryString.Composer
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -348,3 +348,5 @@ MigrationBackup/

# Ionide (cross platform F# VS Code tools) working folder
.ionide/

.idea
43 changes: 43 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,28 @@ The implementation is available through a static class `QueryStringComposer`.

_The two available overloads perform the same conversion._

### Global configuration
You can use the method `Configure` provided by `QueryStringComposerConfiguration` in your service configuration to change the key case style globally.

``` csharp
QueryStringComposerConfiguration.Configure(options =>
{
options.KeyNameCaseStyle = StringCaseStyle.TrainCase;
});
```

There are some supported style cases, they are:
```csharp
public enum StringCaseStyle
{
CamelCase = 1,
PascalCase = 2,
SnakeCase = 3,
KebabCase = 4,
TrainCase = 5
}
```

### String overload

Code result: `http://localhost?SomeName=Victor&SomeAge=20`
Expand Down Expand Up @@ -98,6 +120,27 @@ class YourClass
}
```

### QueryStringKeyCaseStyleAttribute
In case you need to change the key case style of a single property and don't want to change it globally. You can use the `QueryStringKeyCaseStyleAttribute` attribute for this.

Code result: `http://localhost?Key-In-Train-Case=value`
``` csharp
const string baseUrl = "http://localhost";

var queryObject = new YourClass
{
KeyInTrainCase = "value",
};

var result = QueryStringComposer.Compose(uri, queryObject);

class YourClass
{
[QueryStringKeyCaseStyleAttribute(StringCaseStyle.TrainCase)]
public string KeyInTrainCase { get; set; }
}
```

## Attention when using

Some types are not supported for conversion:
Expand Down
54 changes: 51 additions & 3 deletions Uri.Query.String.Composer.Tests/QueryStringComposerTests.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
using FluentAssertions;
using FluentAssertions;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using UriQueryStringComposer.Attributes;
using UriQueryStringComposer.Enums;
using Xunit;

namespace UriQueryStringComposer.Tests;
Expand Down Expand Up @@ -119,7 +120,7 @@ public void Should_compose_when_a_dateTime_is_provided()
result
.Query
.Should()
.Be("?DateTimeObj=2021-12-20T11:10:25");
.Be("?dateTimeObj=2021-12-20T11:10:25");
}

[Fact]
Expand All @@ -132,7 +133,7 @@ public void Should_compose_when_a_unknown_object_is_provided()
result
.Query
.Should()
.Be("?Obj1=9173212&Obj2=123.43");
.Be("?obj1=9173212&obj2=123.43");
}

[Fact]
Expand Down Expand Up @@ -202,6 +203,49 @@ public void Should_create_the_query_if_it_not_exists()
.Be("?aTest=testValue");
}

[Fact]
public void Should_use_custom_key_case_style_if_it_is_provided_by_attribute()
{
const string baseUrl = "http://localhost";

var queryObject = new TestClass
{
List = new List<string>(),
KebabCaseStyle = 1
};

var result = QueryStringComposer.Compose(baseUrl, queryObject);

result
.Query
.Should()
.Be("?kebab-case-style=1");
}

[Fact]
public void Should_use_global_custom_key_case_style_if_it_is_provided()
{
const string baseUrl = "http://localhost";

var queryObject = new TestClass
{
List = new List<string>(),
GlobalCustomStyle = 4
};

QueryStringComposerConfiguration.Configure(options =>
{
options.KeyNameCaseStyle = StringCaseStyle.TrainCase;
});

var result = QueryStringComposer.Compose(baseUrl, queryObject);

result
.Query
.Should()
.Be("?Global-Custom-Style=4");
}

[ExcludeFromCodeCoverage]
private class TestClass
{
Expand All @@ -219,5 +263,9 @@ public TestClass()

[QueryStringKeyName("custom")]
public int? Int { get; set; }

[QueryStringKeyCaseStyle(StringCaseStyle.KebabCase)]
public int? KebabCaseStyle { get; set; }
public int? GlobalCustomStyle { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System;
using UriQueryStringComposer.Enums;

namespace UriQueryStringComposer.Attributes
{
[AttributeUsage(AttributeTargets.Property)]
public class QueryStringKeyCaseStyleAttribute : Attribute
{
public QueryStringKeyCaseStyleAttribute(StringCaseStyle caseStyle)
{
CaseStyle = caseStyle;
}

public StringCaseStyle CaseStyle { get; }
}
}
13 changes: 13 additions & 0 deletions Uri.Query.String.Composer/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Change Log

## [2.0.0] - 2023-04-17

Support for multiple naming conventions and a new default key case style.

### Added
- [PR](https://github.com/Victorvhn/uri-query-string-composer/pull/6)
New Attribute to change key case style and a way to change it globally.

### Changed
- [PR](https://github.com/Victorvhn/uri-query-string-composer/pull/6)
Default key case style changed to CamelCase.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using UriQueryStringComposer.Enums;

namespace UriQueryStringComposer.Configurations
{
public class QueryStringComposerOptions
{
public StringCaseStyle KeyNameCaseStyle { get; set; } = StringCaseStyle.CamelCase;
}
}
2 changes: 1 addition & 1 deletion Uri.Query.String.Composer/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ internal static class Constants
{
public const string DateTimeFormat = "s";
public const string Comma = ",";
public const char Interrogation = '?';
public const char QuestionMark = '?';
public const char Ampersand = '&';
}
}
11 changes: 11 additions & 0 deletions Uri.Query.String.Composer/Enums/StringCaseStyle.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace UriQueryStringComposer.Enums
{
public enum StringCaseStyle
{
CamelCase = 1,
PascalCase = 2,
SnakeCase = 3,
KebabCase = 4,
TrainCase = 5
}
}
53 changes: 53 additions & 0 deletions Uri.Query.String.Composer/QueryStringComposer.GetKeyName.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using CaseExtensions;
using UriQueryStringComposer.Attributes;
using UriQueryStringComposer.Enums;

namespace UriQueryStringComposer
{
public static partial class QueryStringComposer
{
private static string GetPropertyKey(MemberInfo property)
{
var customNameAttribute = property.GetCustomAttribute(typeof(QueryStringKeyNameAttribute));

if (customNameAttribute != null)
{
return ((QueryStringKeyNameAttribute)customNameAttribute).Name;
}

var stringCaseStyle = GetStringStyle(property);

return GetStyledKey(property.Name, stringCaseStyle);
}

private static StringCaseStyle GetStringStyle(MemberInfo property)
{
var customStringCaseStyleAttribute = property.GetCustomAttribute(typeof(QueryStringKeyCaseStyleAttribute));

if (customStringCaseStyleAttribute != null)
{
var attribute = (QueryStringKeyCaseStyleAttribute) customStringCaseStyleAttribute;

return attribute.CaseStyle;
}

return QueryStringComposerConfiguration.Options.KeyNameCaseStyle;
}

[ExcludeFromCodeCoverage]
private static string GetStyledKey(string key, StringCaseStyle style)
{
return style switch
{
StringCaseStyle.CamelCase => key.ToCamelCase(),
StringCaseStyle.PascalCase => key.ToPascalCase(),
StringCaseStyle.SnakeCase => key.ToSnakeCase(),
StringCaseStyle.KebabCase => key.ToKebabCase(),
StringCaseStyle.TrainCase => key.ToTrainCase(),
_ => key
};
}
}
}
13 changes: 2 additions & 11 deletions Uri.Query.String.Composer/QueryStringComposer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

namespace UriQueryStringComposer
{
public static class QueryStringComposer
public static partial class QueryStringComposer
{
public static Uri Compose(string baseUrl, object? queryStringObject = null)
{
Expand Down Expand Up @@ -68,7 +68,7 @@ private static Uri MergeParametersInUri(Uri uri, IDictionary<string, string> par
var uriBuilder = new UriBuilder(uri);

if (!uriBuilder.Query.Any())
stringBuilder[0] = Constants.Interrogation;
stringBuilder[0] = Constants.QuestionMark;

var query = stringBuilder.ToString();

Expand All @@ -80,15 +80,6 @@ private static Uri MergeParametersInUri(Uri uri, IDictionary<string, string> par
private static bool GetPropertiesWithoutIgnoreAttribute(PropertyInfo propertyInfo) =>
propertyInfo.GetCustomAttribute(typeof(QueryStringIgnoreAttribute)) == null;

private static string GetPropertyKey(MemberInfo property)
{
var customNameAttribute = property.GetCustomAttribute(typeof(QueryStringKeyNameAttribute));

return customNameAttribute != null
? ((QueryStringKeyNameAttribute)customNameAttribute).Name
: property.Name;
}

private static string GetPropertyValue(object @object, PropertyInfo property)
{
string value;
Expand Down
13 changes: 13 additions & 0 deletions Uri.Query.String.Composer/QueryStringComposerConfiguration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;
using UriQueryStringComposer.Configurations;

namespace UriQueryStringComposer
{
public static class QueryStringComposerConfiguration
{
public static QueryStringComposerOptions Options { get; } = new QueryStringComposerOptions();

public static void Configure(Action<QueryStringComposerOptions> setupAction) =>
setupAction(Options);
}
}
6 changes: 5 additions & 1 deletion Uri.Query.String.Composer/UriQueryStringComposer.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<PackageProjectUrl>https://github.com/Victorvhn/uri-query-string-composer</PackageProjectUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageId>Uri.QueryString.Composer</PackageId>
<PackageVersion>1.0.6-alpha</PackageVersion>
<PackageVersion>2.0.0</PackageVersion>
<Product>Uri.QueryString.Composer</Product>
<PackageTags>http-client;open-source;querystring</PackageTags>
<PackageReleaseNotes>It's a simple library that allows you to turn a class into a query string for https calls.</PackageReleaseNotes>
Expand All @@ -25,4 +25,8 @@
</None>
</ItemGroup>

<ItemGroup>
<PackageReference Include="CaseExtensions" Version="1.1.0" />
</ItemGroup>

</Project>

0 comments on commit 564714d

Please sign in to comment.