Skip to content

Commit 280321f

Browse files
authored
2.22.0 release dev -> main (#3706)
2 parents 89f2188 + 2f93b47 commit 280321f

35 files changed

+882
-237
lines changed

.circleci/scripts/config-template.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ jobs: # Each project will have individual jobs for each specific task it has to
240240
- run:
241241
name: Sync Certs
242242
command: |
243-
& $env:SSM\smksp_cert_sync.exe
243+
& $env:SSM\smctl.exe windows certsync
244244
- run:
245245
name: Build Installer Signed
246246
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%

All.sln

+6
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,9 @@ EndProject
427427
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ConnectorCore", "ConnectorCore", "{DA9DFC36-C53F-4B19-8911-BF7605230BA7}"
428428
EndProject
429429
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConnectorRhino8", "ConnectorRhino\ConnectorRhino8\ConnectorRhino8.csproj", "{D22A887D-976C-4DBF-AE5B-9039F169E61C}"
430+
ProjectSection(ProjectDependencies) = postProject
431+
{89996067-3233-410A-A6A1-39E2F11F0626} = {89996067-3233-410A-A6A1-39E2F11F0626}
432+
EndProjectSection
430433
EndProject
431434
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConverterRhino8", "Objects\Converters\ConverterRhinoGh\ConverterRhino8\ConverterRhino8.csproj", "{89996067-3233-410A-A6A1-39E2F11F0626}"
432435
EndProject
@@ -458,6 +461,9 @@ EndProject
458461
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RevitSharedResources2025", "ConnectorRevit\RevitSharedResources2025\RevitSharedResources2025.csproj", "{7B02BACC-D9B6-4FFE-A450-7ECB5F71F209}"
459462
EndProject
460463
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConnectorRevit2025", "ConnectorRevit\ConnectorRevit2025\ConnectorRevit2025.csproj", "{D607BD0A-9F7F-4C3A-9B9C-FEAD6BA49C7C}"
464+
ProjectSection(ProjectDependencies) = postProject
465+
{C0295BF9-9A40-4FCD-BE39-E943985CA3F8} = {C0295BF9-9A40-4FCD-BE39-E943985CA3F8}
466+
EndProjectSection
461467
EndProject
462468
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConverterRevit2025", "Objects\Converters\ConverterRevit\ConverterRevit2025\ConverterRevit2025.csproj", "{C0295BF9-9A40-4FCD-BE39-E943985CA3F8}"
463469
EndProject

ConnectorRevit/RevitSharedResources/Extensions/SpeckleExtensions/IRevitDocumentAggregateCacheExtensions.cs

+13-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,19 @@ public static void CacheInitializer(IRevitObjectCache<Category> cache, Document
4646
{
4747
var _categories = new Dictionary<string, Category>();
4848

49-
foreach (Category category in doc.Settings.Categories)
49+
// Document.Settings.Categories only returns the parent categories.
50+
// To avoid iterating over all subcategories of each parent category, we add some extra categories that are not returned by Document.Settings.Categories #3615
51+
var extraCategories = new Category[]
52+
{
53+
Category.GetCategory(doc, BuiltInCategory.OST_Gutter),
54+
Category.GetCategory(doc, BuiltInCategory.OST_Fascia),
55+
Category.GetCategory(doc, BuiltInCategory.OST_RoofSoffit),
56+
Category.GetCategory(doc, BuiltInCategory.OST_EdgeSlab), // Slab Edges
57+
Category.GetCategory(doc, BuiltInCategory.OST_Cornices), // Wall Sweeps
58+
};
59+
var allCategories = doc.Settings.Categories.Cast<Category>().Concat(extraCategories);
60+
61+
foreach (Category category in allCategories)
5062
{
5163
if (!Helpers.Extensions.Extensions.IsCategorySupported(category))
5264
{

Core/Core/Api/GraphQL/Client.cs

+3
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ public sealed partial class Client : ISpeckleGraphQLClient, IDisposable
3535
public ActiveUserResource ActiveUser { get; }
3636
public OtherUserResource OtherUser { get; }
3737
public ProjectInviteResource ProjectInvite { get; }
38+
public WorkspaceResource Workspace { get; }
3839
public CommentResource Comment { get; }
3940
public SubscriptionResource Subscription { get; }
4041

@@ -65,6 +66,7 @@ public Client(Account account)
6566
ProjectInvite = new(this);
6667
Comment = new(this);
6768
Subscription = new(this);
69+
Workspace = new(this);
6870

6971
HttpClient = CreateHttpClient(account);
7072

@@ -371,6 +373,7 @@ private static HttpClient CreateHttpClient(Account account)
371373
return httpClient;
372374
}
373375

376+
[Obsolete("You can now simply use " + nameof(ProjectResource) + nameof(ProjectResource.Get))]
374377
public async Task<string?> GetWorkspaceId(string projectId, CancellationToken cancellationToken = default)
375378
{
376379
Version serverVersion = await GQLClient.GetServerVersion(cancellationToken).ConfigureAwait(false);
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1-
namespace Speckle.Core.Api.GraphQL.Enums;
1+
using System;
2+
3+
namespace Speckle.Core.Api.GraphQL.Enums;
24

35
public enum ProjectVisibility
46
{
57
Private,
8+
9+
[Obsolete("Use Unlisted instead")]
610
Public,
711
Unlisted
812
}

Core/Core/Api/GraphQL/Inputs/ProjectInputs.cs

+8-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ public sealed record ProjectCommentsFilter(bool? includeArchived, bool? loadedVe
77

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

10+
public sealed record WorkspaceProjectCreateInput(
11+
string? name,
12+
string? description,
13+
ProjectVisibility? visibility,
14+
string workspaceId
15+
);
16+
1017
public sealed record ProjectInviteCreateInput(string? email, string? role, string? serverRole, string? userId);
1118

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

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

39-
public sealed record UserProjectsFilter(string search, IReadOnlyList<string>? onlyWithRoles = null);
46+
public sealed record UserProjectsFilter(string? search, IReadOnlyList<string>? onlyWithRoles = null);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
namespace Speckle.Core.Api.GraphQL.Inputs;
2+
3+
public sealed record UserWorkspacesFilter(string? search);

Core/Core/Api/GraphQL/Legacy/Client.GraphqlCleintOperations/Client.StreamOperations.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ public async Task<List<Stream>> StreamSearch(
251251
Variables = new { query, limit }
252252
};
253253

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

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

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System;
2+
3+
namespace Speckle.Core.Api.GraphQL.Models;
4+
5+
public static class ModelExtensions
6+
{
7+
/// <inheritdoc cref="CanReceive(Project)"/>
8+
[Obsolete(DeprecationMessages.FE1_DEPRECATION_MESSAGE)]
9+
public static bool CanReceive(this Stream project) =>
10+
project.role is StreamRoles.STREAM_OWNER or StreamRoles.STREAM_CONTRIBUTOR;
11+
12+
/// <param name="project"></param>
13+
/// <returns><see langword="True"/> if the <see cref="Stream.role"/> allows for receive</returns>
14+
public static bool CanReceive(this Project project) =>
15+
project.role is StreamRoles.STREAM_OWNER or StreamRoles.STREAM_CONTRIBUTOR;
16+
}
+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
using Speckle.Core.Logging;
2+
3+
namespace Speckle.Core.Api.GraphQL.Models;
4+
5+
public sealed class Workspace
6+
{
7+
public string id { get; init; }
8+
public string name { get; init; }
9+
public string role { get; init; }
10+
public string slug { get; init; }
11+
public string? description { get; init; }
12+
public WorkspacePermissionChecks permissions { get; init; }
13+
}
14+
15+
public sealed class WorkspacePermissionChecks
16+
{
17+
public PermissionCheckResult canCreateProject { get; init; }
18+
}
19+
20+
public sealed class PermissionCheckResult
21+
{
22+
public bool authorized { get; init; }
23+
public string code { get; init; }
24+
public string message { get; init; }
25+
26+
/// <exception cref="SpeckleException">Throws when <see cref="PermissionCheckResult.authorized"/> is <see langword="false"/></exception>
27+
public void EnsureAuthorised()
28+
{
29+
if (!authorized)
30+
{
31+
throw new SpeckleException(message);
32+
}
33+
}
34+
}

Core/Core/Api/GraphQL/Resources/ActiveUserResource.cs

+102-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ internal ActiveUserResource(ISpeckleGraphQLClient client)
2121
/// Gets the currently active user profile.
2222
/// </summary>
2323
/// <param name="cancellationToken"></param>
24-
/// <returns></returns>
2524
/// <returns>the requested user, or null if the user does not exist (i.e. <see cref="Client"/> was initialised with an unauthenticated account)</returns>
2625
/// <inheritdoc cref="ISpeckleGraphQLClient.ExecuteGraphQLRequest{T}"/>
2726
public async Task<User?> Get(CancellationToken cancellationToken = default)
@@ -51,6 +50,108 @@ query User {
5150
return response.activeUser;
5251
}
5352

53+
/// <summary>
54+
///
55+
/// </summary>
56+
/// <remarks>Only supported on server versions >=2.23.17</remarks>
57+
/// <param name="cancellationToken"></param>
58+
/// <returns></returns>
59+
public async Task<PermissionCheckResult> CanCreatePersonalProjects(CancellationToken cancellationToken = default)
60+
{
61+
//language=graphql
62+
const string QUERY = """
63+
query CanCreatePersonalProject {
64+
data:activeUser {
65+
data:permissions {
66+
data:canCreatePersonalProject {
67+
authorized
68+
code
69+
message
70+
}
71+
}
72+
}
73+
}
74+
""";
75+
var request = new GraphQLRequest { Query = QUERY, };
76+
77+
var response = await _client
78+
.ExecuteGraphQLRequest<OptionalResponse<RequiredResponse<RequiredResponse<PermissionCheckResult>>>>(
79+
request,
80+
cancellationToken
81+
)
82+
.ConfigureAwait(false);
83+
84+
if (response.data is null)
85+
{
86+
throw new SpeckleGraphQLException("GraphQL response indicated that the ActiveUser could not be found");
87+
}
88+
89+
return response.data.data.data;
90+
}
91+
92+
/// <summary>Ret</summary>
93+
/// <remarks>This feature is only available on Workspace enabled servers (server versions >=2.23.17) e.g. app.speckle.systems</remarks>
94+
/// <param name="cancellationToken"></param>
95+
/// <returns></returns>
96+
/// <inheritdoc cref="ISpeckleGraphQLClient.ExecuteGraphQLRequest{T}"/>
97+
public async Task<ResourceCollection<Workspace>> GetWorkspaces(
98+
int limit = 25,
99+
string? cursor = null,
100+
UserWorkspacesFilter? filter = null,
101+
CancellationToken cancellationToken = default
102+
)
103+
{
104+
//language=graphql
105+
const string QUERY = """
106+
query ActiveUser($limit: Int!, $cursor: String, $filter: UserWorkspacesFilter) {
107+
data:activeUser {
108+
data:workspaces(limit: $limit, cursor: $cursor, filter: $filter) {
109+
cursor
110+
totalCount
111+
items {
112+
id
113+
name
114+
role
115+
slug
116+
description
117+
permissions {
118+
canCreateProject {
119+
authorized
120+
code
121+
message
122+
}
123+
}
124+
}
125+
}
126+
}
127+
}
128+
""";
129+
var request = new GraphQLRequest
130+
{
131+
Query = QUERY,
132+
Variables = new
133+
{
134+
limit,
135+
cursor,
136+
filter
137+
}
138+
};
139+
140+
var response = await _client
141+
.ExecuteGraphQLRequest<OptionalResponse<RequiredResponse<ResourceCollection<Workspace>>>>(
142+
request,
143+
cancellationToken
144+
)
145+
.ConfigureAwait(false);
146+
147+
if (response.data is null)
148+
{
149+
throw new SpeckleGraphQLException("GraphQL response indicated that the ActiveUser could not be found");
150+
}
151+
152+
return response.data.data;
153+
}
154+
54155
/// <param name="limit">Max number of projects to fetch</param>
55156
/// <param name="cursor">Optional cursor for pagination</param>
56157
/// <param name="filter">Optional filter</param>

0 commit comments

Comments
 (0)