Skip to content

2.22.0 release dev -> main #3706

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Apr 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .circleci/scripts/config-template.yml
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ jobs: # Each project will have individual jobs for each specific task it has to
- run:
name: Sync Certs
command: |
& $env:SSM\smksp_cert_sync.exe
& $env:SSM\smctl.exe windows certsync
- run:
name: Build Installer Signed
command: speckle-sharp-ci-tools\InnoSetup\ISCC.exe speckle-sharp-ci-tools\%SLUG%.iss /Sbyparam=$p /DSIGN_INSTALLER /DCODE_SIGNING_CERT_FINGERPRINT=%SM_CODE_SIGNING_CERT_SHA1_HASH%
Expand Down
6 changes: 6 additions & 0 deletions All.sln
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,9 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ConnectorCore", "ConnectorCore", "{DA9DFC36-C53F-4B19-8911-BF7605230BA7}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConnectorRhino8", "ConnectorRhino\ConnectorRhino8\ConnectorRhino8.csproj", "{D22A887D-976C-4DBF-AE5B-9039F169E61C}"
ProjectSection(ProjectDependencies) = postProject
{89996067-3233-410A-A6A1-39E2F11F0626} = {89996067-3233-410A-A6A1-39E2F11F0626}
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConverterRhino8", "Objects\Converters\ConverterRhinoGh\ConverterRhino8\ConverterRhino8.csproj", "{89996067-3233-410A-A6A1-39E2F11F0626}"
EndProject
Expand Down Expand Up @@ -458,6 +461,9 @@ EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RevitSharedResources2025", "ConnectorRevit\RevitSharedResources2025\RevitSharedResources2025.csproj", "{7B02BACC-D9B6-4FFE-A450-7ECB5F71F209}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConnectorRevit2025", "ConnectorRevit\ConnectorRevit2025\ConnectorRevit2025.csproj", "{D607BD0A-9F7F-4C3A-9B9C-FEAD6BA49C7C}"
ProjectSection(ProjectDependencies) = postProject
{C0295BF9-9A40-4FCD-BE39-E943985CA3F8} = {C0295BF9-9A40-4FCD-BE39-E943985CA3F8}
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConverterRevit2025", "Objects\Converters\ConverterRevit\ConverterRevit2025\ConverterRevit2025.csproj", "{C0295BF9-9A40-4FCD-BE39-E943985CA3F8}"
EndProject
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,19 @@ public static void CacheInitializer(IRevitObjectCache<Category> cache, Document
{
var _categories = new Dictionary<string, Category>();

foreach (Category category in doc.Settings.Categories)
// Document.Settings.Categories only returns the parent categories.
// To avoid iterating over all subcategories of each parent category, we add some extra categories that are not returned by Document.Settings.Categories #3615
var extraCategories = new Category[]
{
Category.GetCategory(doc, BuiltInCategory.OST_Gutter),
Category.GetCategory(doc, BuiltInCategory.OST_Fascia),
Category.GetCategory(doc, BuiltInCategory.OST_RoofSoffit),
Category.GetCategory(doc, BuiltInCategory.OST_EdgeSlab), // Slab Edges
Category.GetCategory(doc, BuiltInCategory.OST_Cornices), // Wall Sweeps
};
var allCategories = doc.Settings.Categories.Cast<Category>().Concat(extraCategories);

foreach (Category category in allCategories)
{
if (!Helpers.Extensions.Extensions.IsCategorySupported(category))
{
Expand Down
3 changes: 3 additions & 0 deletions Core/Core/Api/GraphQL/Client.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public sealed partial class Client : ISpeckleGraphQLClient, IDisposable
public ActiveUserResource ActiveUser { get; }
public OtherUserResource OtherUser { get; }
public ProjectInviteResource ProjectInvite { get; }
public WorkspaceResource Workspace { get; }
public CommentResource Comment { get; }
public SubscriptionResource Subscription { get; }

Expand Down Expand Up @@ -65,6 +66,7 @@ public Client(Account account)
ProjectInvite = new(this);
Comment = new(this);
Subscription = new(this);
Workspace = new(this);

HttpClient = CreateHttpClient(account);

Expand Down Expand Up @@ -371,6 +373,7 @@ private static HttpClient CreateHttpClient(Account account)
return httpClient;
}

[Obsolete("You can now simply use " + nameof(ProjectResource) + nameof(ProjectResource.Get))]
public async Task<string?> GetWorkspaceId(string projectId, CancellationToken cancellationToken = default)
{
Version serverVersion = await GQLClient.GetServerVersion(cancellationToken).ConfigureAwait(false);
Expand Down
6 changes: 5 additions & 1 deletion Core/Core/Api/GraphQL/Enums/ProjectVisibility.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
namespace Speckle.Core.Api.GraphQL.Enums;
using System;

namespace Speckle.Core.Api.GraphQL.Enums;

public enum ProjectVisibility
{
Private,

[Obsolete("Use Unlisted instead")]
Public,
Unlisted
}
9 changes: 8 additions & 1 deletion Core/Core/Api/GraphQL/Inputs/ProjectInputs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ public sealed record ProjectCommentsFilter(bool? includeArchived, bool? loadedVe

public sealed record ProjectCreateInput(string? name, string? description, ProjectVisibility? visibility);

public sealed record WorkspaceProjectCreateInput(
string? name,
string? description,
ProjectVisibility? visibility,
string workspaceId
);

public sealed record ProjectInviteCreateInput(string? email, string? role, string? serverRole, string? userId);

public sealed record ProjectInviteUseInput(bool accept, string projectId, string token);
Expand Down Expand Up @@ -36,4 +43,4 @@ public sealed record ProjectUpdateInput(

public sealed record ProjectUpdateRoleInput(string userId, string projectId, string? role);

public sealed record UserProjectsFilter(string search, IReadOnlyList<string>? onlyWithRoles = null);
public sealed record UserProjectsFilter(string? search, IReadOnlyList<string>? onlyWithRoles = null);
3 changes: 3 additions & 0 deletions Core/Core/Api/GraphQL/Inputs/UserInputs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
namespace Speckle.Core.Api.GraphQL.Inputs;

public sealed record UserWorkspacesFilter(string? search);
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ public async Task<List<Stream>> StreamSearch(
Variables = new { query, limit }
};

var res = await GQLClient.SendMutationAsync<StreamsData>(request, cancellationToken).ConfigureAwait(false); //WARN: Why do we do this?
// var res = await GQLClient.SendMutationAsync<StreamsData>(request, cancellationToken).ConfigureAwait(false); //WARN: Why do we do this?
return (await ExecuteGraphQLRequest<StreamsData>(request, cancellationToken).ConfigureAwait(false)).streams.items;
}

Expand Down Expand Up @@ -400,7 +400,7 @@ public async Task<Stream> StreamGetPendingCollaborators(
}",
Variables = new { id = streamId }
};
var res = await GQLClient.SendMutationAsync<StreamData>(request, cancellationToken).ConfigureAwait(false); //WARN: Why do we do this?
// var res = await GQLClient.SendMutationAsync<StreamData>(request, cancellationToken).ConfigureAwait(false); //WARN: Why do we do this?
return (await ExecuteGraphQLRequest<StreamData>(request, cancellationToken).ConfigureAwait(false)).stream;
}

Expand Down
16 changes: 16 additions & 0 deletions Core/Core/Api/GraphQL/Models/ModelExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System;

namespace Speckle.Core.Api.GraphQL.Models;

public static class ModelExtensions
{
/// <inheritdoc cref="CanReceive(Project)"/>
[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)]
public static bool CanReceive(this Stream project) =>
project.role is StreamRoles.STREAM_OWNER or StreamRoles.STREAM_CONTRIBUTOR;

/// <param name="project"></param>
/// <returns><see langword="True"/> if the <see cref="Stream.role"/> allows for receive</returns>
public static bool CanReceive(this Project project) =>
project.role is StreamRoles.STREAM_OWNER or StreamRoles.STREAM_CONTRIBUTOR;
}
34 changes: 34 additions & 0 deletions Core/Core/Api/GraphQL/Models/Workspace.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using Speckle.Core.Logging;

namespace Speckle.Core.Api.GraphQL.Models;

public sealed class Workspace
{
public string id { get; init; }
public string name { get; init; }
public string role { get; init; }
public string slug { get; init; }
public string? description { get; init; }
public WorkspacePermissionChecks permissions { get; init; }
}

public sealed class WorkspacePermissionChecks
{
public PermissionCheckResult canCreateProject { get; init; }
}

public sealed class PermissionCheckResult
{
public bool authorized { get; init; }
public string code { get; init; }
public string message { get; init; }

/// <exception cref="SpeckleException">Throws when <see cref="PermissionCheckResult.authorized"/> is <see langword="false"/></exception>
public void EnsureAuthorised()
{
if (!authorized)
{
throw new SpeckleException(message);
}
}
}
103 changes: 102 additions & 1 deletion Core/Core/Api/GraphQL/Resources/ActiveUserResource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ internal ActiveUserResource(ISpeckleGraphQLClient client)
/// Gets the currently active user profile.
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
/// <returns>the requested user, or null if the user does not exist (i.e. <see cref="Client"/> was initialised with an unauthenticated account)</returns>
/// <inheritdoc cref="ISpeckleGraphQLClient.ExecuteGraphQLRequest{T}"/>
public async Task<User?> Get(CancellationToken cancellationToken = default)
Expand Down Expand Up @@ -51,6 +50,108 @@ query User {
return response.activeUser;
}

/// <summary>
///
/// </summary>
/// <remarks>Only supported on server versions >=2.23.17</remarks>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task<PermissionCheckResult> CanCreatePersonalProjects(CancellationToken cancellationToken = default)
{
//language=graphql
const string QUERY = """
query CanCreatePersonalProject {
data:activeUser {
data:permissions {
data:canCreatePersonalProject {
authorized
code
message
}
}
}
}
""";
var request = new GraphQLRequest { Query = QUERY, };

var response = await _client
.ExecuteGraphQLRequest<OptionalResponse<RequiredResponse<RequiredResponse<PermissionCheckResult>>>>(
request,
cancellationToken
)
.ConfigureAwait(false);

if (response.data is null)
{
throw new SpeckleGraphQLException("GraphQL response indicated that the ActiveUser could not be found");
}

return response.data.data.data;
}

/// <summary>Ret</summary>
/// <remarks>This feature is only available on Workspace enabled servers (server versions >=2.23.17) e.g. app.speckle.systems</remarks>
/// <param name="cancellationToken"></param>
/// <returns></returns>
/// <inheritdoc cref="ISpeckleGraphQLClient.ExecuteGraphQLRequest{T}"/>
public async Task<ResourceCollection<Workspace>> GetWorkspaces(
int limit = 25,
string? cursor = null,
UserWorkspacesFilter? filter = null,
CancellationToken cancellationToken = default
)
{
//language=graphql
const string QUERY = """
query ActiveUser($limit: Int!, $cursor: String, $filter: UserWorkspacesFilter) {
data:activeUser {
data:workspaces(limit: $limit, cursor: $cursor, filter: $filter) {
cursor
totalCount
items {
id
name
role
slug
description
permissions {
canCreateProject {
authorized
code
message
}
}
}
}
}
}
""";
var request = new GraphQLRequest
{
Query = QUERY,
Variables = new
{
limit,
cursor,
filter
}
};

var response = await _client
.ExecuteGraphQLRequest<OptionalResponse<RequiredResponse<ResourceCollection<Workspace>>>>(
request,
cancellationToken
)
.ConfigureAwait(false);

if (response.data is null)
{
throw new SpeckleGraphQLException("GraphQL response indicated that the ActiveUser could not be found");
}

return response.data.data;
}

/// <param name="limit">Max number of projects to fetch</param>
/// <param name="cursor">Optional cursor for pagination</param>
/// <param name="filter">Optional filter</param>
Expand Down
Loading