[FEAT]: Custom Properties (#2933)

* add custom properties model and clients

* observable

* observable tests

* add search

* error CS8370: 'target-typed object creation'

* Error CS8370: 'target-typed object creation'

* add patch with body that return status code

* fixes for failed ConventionTests

* working UnitTests

* (de)serialization and model tests

* Update Repository.cs
This commit is contained in:
Colby Williams
2024-06-17 17:01:20 -05:00
committed by GitHub
parent 7d54cb0d85
commit 9a3177e385
53 changed files with 3121 additions and 1 deletions
@@ -0,0 +1,42 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using Octokit.Internal;
namespace Octokit
{
/// <summary>
/// Custom property name and associated value
/// </summary>
[DebuggerDisplay("{DebuggerDisplay,nq}")]
public class CustomPropertyValueUpdate
{
public CustomPropertyValueUpdate() { }
public CustomPropertyValueUpdate(string propertyName, string value)
{
PropertyName = propertyName;
Value = value;
}
public CustomPropertyValueUpdate(string propertyName, IReadOnlyList<string> value)
{
PropertyName = propertyName;
Value = value;
}
/// <summary>
/// The name of the property
/// </summary>
public string PropertyName { get; set; }
/// <summary>
/// The value assigned to the property
/// </summary>
[SerializeNull]
[Parameter(Key = "value")]
public object Value { get; private set; }
internal string DebuggerDisplay => string.Format(CultureInfo.InvariantCulture, "PropertyName: {0}", PropertyName);
}
}
@@ -0,0 +1,66 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
namespace Octokit
{
[DebuggerDisplay("{DebuggerDisplay,nq}")]
public class OrganizationCustomPropertyUpdate
{
public OrganizationCustomPropertyUpdate() { }
public OrganizationCustomPropertyUpdate(string propertyName, CustomPropertyValueType valueType, string defaultValue)
{
PropertyName = propertyName;
ValueType = valueType;
Required = true;
DefaultValue = defaultValue;
}
public OrganizationCustomPropertyUpdate(string propertyName, CustomPropertyValueType valueType, IReadOnlyList<string> defaultValue)
{
PropertyName = propertyName;
ValueType = valueType;
Required = true;
DefaultValue = defaultValue;
}
/// <summary>
/// The name of the property
/// </summary>
public string PropertyName { get; set; }
/// <summary>
/// The type of the value for the property
/// </summary>
public StringEnum<CustomPropertyValueType> ValueType { get; set; }
/// <summary>
/// Whether the property is required
/// </summary>
public bool Required { get; set; }
/// <summary>
/// Default value of the property
/// </summary>
public object DefaultValue { get; private set; }
/// <summary>
/// Short description of the property
/// </summary>
public string Description { get; set; }
/// <summary>
/// An ordered list of the allowed values of the property.
/// The property can have up to 200 allowed values.
/// </summary>
public IEnumerable<string> AllowedValues { get; set; }
/// <summary>
/// Who can edit the values of the property
/// </summary>
public StringEnum<CustomPropertyValuesEditableBy>? ValuesEditableBy { get; set; }
internal string DebuggerDisplay => string.Format(CultureInfo.InvariantCulture, "PropertyName: {0}", PropertyName);
}
}
@@ -0,0 +1,51 @@
using System.Collections.Generic;
using System.Diagnostics;
namespace Octokit
{
[DebuggerDisplay("{DebuggerDisplay,nq}")]
public class OrganizationCustomPropertyValuesRequest : SearchRepositoriesRequest
{
/// <summary>
/// Initializes a new instance of the <see cref="OrganizationCustomPropertyValuesRequest"/> class.
/// </summary>
public OrganizationCustomPropertyValuesRequest() : base()
{ }
/// <summary>
/// Initializes a new instance of the <see cref="OrganizationCustomPropertyValuesRequest"/> class.
/// </summary>
public OrganizationCustomPropertyValuesRequest(string term)
: base(term)
{ }
public override string Sort => null;
/// <summary>
/// Get the query parameters that will be appending onto the search
/// </summary>
public new IDictionary<string, string> Parameters
{
get
{
var parameters = base.Parameters;
// Remove the default sort and order parameters as they are not supported by the API
parameters.Remove("order");
parameters.Remove("sort");
// Replace the default query parameter "q" with the custom query parameter
var query = parameters["q"];
if (!string.IsNullOrWhiteSpace(query))
{
parameters.Add("repository_query", query);
}
parameters.Remove("q");
return parameters;
}
}
}
}
@@ -0,0 +1,38 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using Octokit.Internal;
namespace Octokit
{
/// <summary>
/// Used to create or update custom property values for a repository
/// </summary>
[DebuggerDisplay("{DebuggerDisplay,nq}")]
public class UpsertOrganizationCustomProperties
{
public UpsertOrganizationCustomProperties() { }
public UpsertOrganizationCustomProperties(List<OrganizationCustomPropertyUpdate> properties)
{
Properties = properties;
}
/// <summary>
/// List of organization custom properties
/// </summary>
/// <remarks>
/// See the <a href="https://docs.github.com/rest/orgs/custom-properties#create-or-update-custom-properties-for-an-organization">API documentation</a> for more information.
/// </remarks>
[Parameter(Value = "properties")]
public List<OrganizationCustomPropertyUpdate> Properties { get; set; }
internal string DebuggerDisplay
{
get
{
return string.Format(CultureInfo.InvariantCulture, "Count: {0}", Properties?.Count);
}
}
}
}
@@ -0,0 +1,67 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
namespace Octokit
{
/// <summary>
/// Used to create or update a custom property value for a repository
/// </summary>
[DebuggerDisplay("{DebuggerDisplay,nq}")]
public class UpsertOrganizationCustomProperty
{
public UpsertOrganizationCustomProperty() { }
public UpsertOrganizationCustomProperty(CustomPropertyValueType valueType)
{
ValueType = valueType;
}
public UpsertOrganizationCustomProperty(CustomPropertyValueType valueType, string defaultValue)
{
ValueType = valueType;
Required = true;
DefaultValue = defaultValue;
}
public UpsertOrganizationCustomProperty(CustomPropertyValueType valueType, IReadOnlyList<string> defaultValue)
{
ValueType = valueType;
Required = true;
DefaultValue = defaultValue;
}
/// <summary>
/// The type of the value for the property
/// </summary>
public StringEnum<CustomPropertyValueType> ValueType { get; set; }
/// <summary>
/// Whether the property is required
/// </summary>
public bool Required { get; set; }
/// <summary>
/// Default value of the property
/// </summary>
public object DefaultValue { get; private set; }
/// <summary>
/// Short description of the property
/// </summary>
public string Description { get; set; }
/// <summary>
/// An ordered list of the allowed values of the property.
/// The property can have up to 200 allowed values.
/// </summary>
public IEnumerable<string> AllowedValues { get; set; }
/// <summary>
/// Who can edit the values of the property
/// </summary>
public StringEnum<CustomPropertyValuesEditableBy>? ValuesEditableBy { get; set; }
internal string DebuggerDisplay => string.Format(CultureInfo.InvariantCulture, "ValueType: {0}", ValueType.DebuggerDisplay);
}
}
@@ -0,0 +1,48 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using Octokit.Internal;
namespace Octokit
{
/// <summary>
/// Used to create or update custom property values for organization repositories
/// </summary>
[DebuggerDisplay("{DebuggerDisplay,nq}")]
public class UpsertOrganizationCustomPropertyValues
{
public UpsertOrganizationCustomPropertyValues() { }
public UpsertOrganizationCustomPropertyValues(List<string> repositoryNames, List<CustomPropertyValueUpdate> properties)
{
RepositoryNames = repositoryNames;
Properties = properties;
}
/// <summary>
/// List of repository names that should create or update custom property values
/// </summary>
/// <remarks>
/// See the <a href="https://docs.github.com/rest/orgs/custom-properties#create-or-update-custom-property-values-for-organization-repositories">API documentation</a> for more information.
/// </remarks>
[Parameter(Value = "repository_names")]
public List<string> RepositoryNames { get; set; }
/// <summary>
/// List of organization custom properties
/// </summary>
/// <remarks>
/// See the <a href="https://docs.github.com/rest/orgs/custom-properties#create-or-update-custom-properties-for-an-organization">API documentation</a> for more information.
/// </remarks>
[Parameter(Value = "properties")]
public List<CustomPropertyValueUpdate> Properties { get; set; }
internal string DebuggerDisplay
{
get
{
return string.Format(CultureInfo.InvariantCulture, "Count: {0}", Properties?.Count);
}
}
}
}
@@ -0,0 +1,31 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using Octokit.Internal;
namespace Octokit
{
/// <summary>
/// Used to create or update custom property values for a repository
/// </summary>
[DebuggerDisplay("{DebuggerDisplay,nq}")]
public class UpsertRepositoryCustomPropertyValues
{
/// <summary>
/// List of custom property names and associated values
/// </summary>
/// <remarks>
/// See the <a href="https://docs.github.com/rest/repos/custom-properties#create-or-update-custom-property-values-for-a-repository">API documentation</a> for more information.
/// </remarks>
[Parameter(Value = "properties")]
public List<CustomPropertyValueUpdate> Properties { get; set; }
internal string DebuggerDisplay
{
get
{
return string.Format(CultureInfo.InvariantCulture, "Count: {0}", Properties?.Count);
}
}
}
}
@@ -0,0 +1,79 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using Octokit.Internal;
namespace Octokit
{
/// <summary>
/// Custom property name and associated value
/// </summary>
[DebuggerDisplay("{DebuggerDisplay,nq}")]
[ExcludeFromCtorWithAllPropertiesConventionTest(nameof(Values))]
public class CustomPropertyValue
{
public CustomPropertyValue() { }
public CustomPropertyValue(string propertyName, object value)
{
PropertyName = propertyName;
this.value = value;
}
/// <summary>
/// The name of the property
/// </summary>
[Parameter(Key = "property_name")]
public string PropertyName { get; private set; }
[Parameter(Key = "value")]
public object value { get; private set; }
/// <summary>
/// The value assigned to the property
/// </summary>
public string Value {
get {
if (value is null)
{
return null;
}
if (value is string stringValue)
{
return stringValue;
}
else if (value is JsonArray stringValues)
{
return "[" + string.Join(",", stringValues.ConvertAll(x => x.ToString())) + "]";
}
return null;
}
}
/// <summary>
/// The array of values assigned to the property
/// </summary>
public IReadOnlyList<string> Values {
get {
if (value is null)
{
return null;
}
else if (value is string stringValue)
{
return new List<string> { stringValue };
}
else if (value is JsonArray stringValues)
{
return stringValues.ConvertAll(x => x.ToString());
}
return null;
}
}
internal string DebuggerDisplay => string.Format(CultureInfo.InvariantCulture, "PropertyName: {0}", PropertyName);
}
}
@@ -0,0 +1,16 @@
using Octokit.Internal;
namespace Octokit
{
public enum CustomPropertyValueType
{
[Parameter(Value = "string")]
String,
[Parameter(Value = "single_select")]
SingleSelect,
[Parameter(Value = "multi_select")]
MultiSelect,
[Parameter(Value = "true_false")]
TrueFalse,
}
}
@@ -0,0 +1,12 @@
using Octokit.Internal;
namespace Octokit
{
public enum CustomPropertyValuesEditableBy
{
[Parameter(Value = "org_actors")]
OrgActors,
[Parameter(Value = "org_and_repo_actors")]
OrgAndRepoActors,
}
}
@@ -0,0 +1,105 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using Octokit.Internal;
namespace Octokit
{
[DebuggerDisplay("{DebuggerDisplay,nq}")]
[ExcludeFromCtorWithAllPropertiesConventionTest(nameof(DefaultValues))]
public class OrganizationCustomProperty
{
public OrganizationCustomProperty() { }
public OrganizationCustomProperty(string propertyName, CustomPropertyValueType valueType, bool required, object defaultValue, string description, IReadOnlyList<string> allowedValues, CustomPropertyValuesEditableBy? valuesEditableBy)
{
PropertyName = propertyName;
ValueType = valueType;
Required = required;
this.defaultValue = defaultValue;
Description = description;
AllowedValues = allowedValues;
ValuesEditableBy = valuesEditableBy;
}
[Parameter(Key = "default_value")]
public object defaultValue { get; private set; }
/// <summary>
/// The name of the property
/// </summary>
public string PropertyName { get; private set; }
/// <summary>
/// The type of the value for the property
/// </summary>
public StringEnum<CustomPropertyValueType>? ValueType { get; private set; }
/// <summary>
/// Whether the property is required
/// </summary>
public bool Required { get; private set; }
/// <summary>
/// Default value of the property
/// </summary>
public string DefaultValue {
get {
if (defaultValue is null)
{
return null;
}
if (defaultValue is string stringValue)
{
return stringValue;
}
else if (defaultValue is JsonArray stringValues)
{
return "[" + string.Join(",", stringValues.ConvertAll(x => x.ToString())) + "]";
}
return null;
}
}
/// <summary>
/// Default values of the property
/// </summary>
public IReadOnlyList<string> DefaultValues {
get {
if (defaultValue is null)
{
return null;
}
else if (defaultValue is string stringValue)
{
return new List<string> { stringValue };
}
else if (defaultValue is JsonArray stringValues)
{
return stringValues.ConvertAll(x => x.ToString());
}
return null;
}
}
/// <summary>
/// Short description of the property
/// </summary>
public string Description { get; private set; }
/// <summary>
/// An ordered list of the allowed values of the property.
/// The property can have up to 200 allowed values.
/// </summary>
public IReadOnlyList<string> AllowedValues { get; private set; }
/// <summary>
/// Who can edit the values of the property
/// </summary>
public StringEnum<CustomPropertyValuesEditableBy>? ValuesEditableBy { get; private set; }
internal string DebuggerDisplay => string.Format(CultureInfo.InvariantCulture, "PropertyName: {0}", PropertyName);
}
}
@@ -0,0 +1,45 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
namespace Octokit
{
/// <summary>
/// List of custom property values for a repository
/// </summary>
[DebuggerDisplay("{DebuggerDisplay,nq}")]
public class OrganizationCustomPropertyValues
{
public OrganizationCustomPropertyValues() { }
public OrganizationCustomPropertyValues(long repositoryId, string repositoryName, string repositoryFullName, IReadOnlyList<CustomPropertyValue> properties)
{
RepositoryId = repositoryId;
RepositoryName = repositoryName;
RepositoryFullName = repositoryFullName;
Properties = properties;
}
/// <summary>
/// The repository Id
/// </summary>
public long RepositoryId { get; private set; }
/// <summary>
/// The name of the repository
/// </summary>
public string RepositoryName { get; private set; }
/// <summary>
/// The full name of the repository (owner/repo)
/// </summary>
public string RepositoryFullName { get; private set; }
/// <summary>
/// List of custom property names and associated values
/// </summary>
public IReadOnlyList<CustomPropertyValue> Properties { get; private set; }
internal string DebuggerDisplay => string.Format(CultureInfo.InvariantCulture, "RepositoryFullName: {0}", RepositoryFullName);
}
}
+4 -1
View File
@@ -17,7 +17,7 @@ namespace Octokit
Id = id;
}
public Repository(string url, string htmlUrl, string cloneUrl, string gitUrl, string sshUrl, string svnUrl, string mirrorUrl, string archiveUrl, long id, string nodeId, User owner, string name, string fullName, bool isTemplate, string description, string homepage, string language, bool @private, bool fork, int forksCount, int stargazersCount, string defaultBranch, int openIssuesCount, DateTimeOffset? pushedAt, DateTimeOffset createdAt, DateTimeOffset updatedAt, RepositoryPermissions permissions, Repository parent, Repository source, LicenseMetadata license, bool hasDiscussions, bool hasIssues, bool hasWiki, bool hasDownloads, bool hasPages, int subscribersCount, long size, bool? allowRebaseMerge, bool? allowSquashMerge, bool? allowMergeCommit, bool archived, int watchersCount, bool? deleteBranchOnMerge, RepositoryVisibility visibility, IEnumerable<string> topics, bool? allowAutoMerge, bool? allowUpdateBranch, bool? webCommitSignoffRequired)
public Repository(string url, string htmlUrl, string cloneUrl, string gitUrl, string sshUrl, string svnUrl, string mirrorUrl, string archiveUrl, long id, string nodeId, User owner, string name, string fullName, bool isTemplate, string description, string homepage, string language, bool @private, bool fork, int forksCount, int stargazersCount, string defaultBranch, int openIssuesCount, DateTimeOffset? pushedAt, DateTimeOffset createdAt, DateTimeOffset updatedAt, RepositoryPermissions permissions, Repository parent, Repository source, LicenseMetadata license, bool hasDiscussions, bool hasIssues, bool hasWiki, bool hasDownloads, bool hasPages, int subscribersCount, long size, bool? allowRebaseMerge, bool? allowSquashMerge, bool? allowMergeCommit, bool archived, int watchersCount, bool? deleteBranchOnMerge, RepositoryVisibility visibility, IEnumerable<string> topics, bool? allowAutoMerge, bool? allowUpdateBranch, bool? webCommitSignoffRequired, IReadOnlyDictionary<string, object> customProperties)
{
Url = url;
HtmlUrl = htmlUrl;
@@ -69,6 +69,7 @@ namespace Octokit
AllowAutoMerge = allowAutoMerge;
AllowUpdateBranch = allowUpdateBranch;
WebCommitSignoffRequired = webCommitSignoffRequired;
CustomProperties = customProperties;
}
public string Url { get; private set; }
@@ -170,6 +171,8 @@ namespace Octokit
public bool? WebCommitSignoffRequired { get; private set; }
public IReadOnlyDictionary<string, object> CustomProperties { get; private set; }
internal string DebuggerDisplay
{
get