Is it possible to post comments with attached files with the api to speckle.xyz?
It is!
The broad steps are to upload a file to the blob storage API, and then when creating a comment and adding it to a stream or commit, you include any fileId
s to that payload.
This is a two-stepper using our REST API with GraphQL or can be achieved with SDK helper functions.
What environment would you like to do this? Python, Javascript, C# or something else?
Okey great, is there any more information on the REST and GraphQL apis than here REST API | Speckle Docs ?
We’re using javascript or C#.
There isn’t, that’s on us. (
I will write examples here, and we’ll work on it!
Oki, do you have an example on how to post a comment to at commit?
I’m working something up for you right now.
In the meantime, I have an example to post thousands of comments over here: Gotta catch-em-all
That example is using python but should be reasonably easy to parse.
Hey @Sandra, this took a while longer than I had hoped. Nevertheless, this is an example of attaching files to new comments.
I include below C# code to achieve this. I could post javascript afterwards though it has a few gotchas to work around that I mention later.
The first step is to set the server (host_url
) and an personal access token (token
), if we are not relying on a local installation using Speckle Manager to handle accounts.
string host_url = "HOST_SERVER";
string token = "ACCESS_TOKEN";
With those in place, we can create a SpeckleApiClient
object, which will be used to interact with the server.
using Speckle.Core.Api;
using Speckle.Core.Credentials;
Account account = new Account();
account.serverInfo = new ServerInfo(){ url=host_url };
account.token = token;
var stream_id = "bb8470f5b6";
var commit_id = "34247c2a44";
var client = new Client(account);
In this example I will attach a file I have local to my script and will read it into a Stream
and from there into a Blob object.
using System.IO;
var filePath = "./assets/blue.png";
byte[] blob;
using (var stream = new FileStream(filePath, FileMode.Open))
{
using (var memoryStream = new MemoryStream())
{
stream.CopyTo(memoryStream);
blob = memoryStream.ToArray();
}
}
With that in place and with an authenticated client, we can add that File Blob to the Stream with an effective POST
request.
I’m posting this as C# code as it isn’t encombered by web sandboxing. If you are using javascript to do this to the speckle.xyz server, or any server on a different host to your javascript app, you will have to use a CORS proxy.
A POST request with mode no-cors
will add the file to the server, but will not allow you to receive the return object.
Also fine would be to use Postman or similar to upload the file.
The server endpoint is in the form of host_url/api/streams/{stream_id}/blob
and the request body is a FormData
object with a file
key and the File Blob as the value.
using System.Net.Http;
using System.Net.Http.Headers;
var fileContent = new ByteArrayContent(File.ReadAllBytes(filePath));
fileContent.Headers.ContentType =
MediaTypeHeaderValue.Parse("application/octet-stream");
var formData = new MultipartFormDataContent();
formData.Add(fileContent, "file", Path.GetFileName(filePath));
System.Net.Http.HttpResponseMessage response;
using (var http_client = new HttpClient())
{
http_client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", token);
response = await http_client.PostAsync(
$"{host_url}/api/stream/{stream_id}/blob", formData);
}
The server will return a JSON object with the file resource information, which you can then use to add the file to a comment.
The response object is in the form:
{
"uploadResults": [
{
"blobId": "9ec.....",
"fileName": "blue.png",
"uploadStatus": 1,
"fileSize": 4760,
"formKey": "file"
}
]
}
Speckle includes a namespaced version of Newtonsoft.Json, so you can use that to parse the response, but it isn’t critical.
using Speckle.Newtonsoft.Json;
string blobId = null;
if (response.IsSuccessStatusCode)
{
var responseContent = await response.Content.ReadAsStringAsync();
dynamic fileUploadResponse = JsonConvert.DeserializeObject(responseContent);
blobId = fileUploadResponse.uploadResults[0].blobId;
}
With that key blobId, you can add the file to a comment.
We have a few other posts here on the forum that discuss the GraphQL for comments, but the minimal viable query that is effective is:
using GraphQL;
using GraphQL.Client.Http;
var query = @"mutation CommentCreateInput(
$streamId: String!,
$blobIds: [String!]!,
$commitId: String!
) {
commentCreate(
input: {
streamId: $streamId,
resources: [
{
resourceType: commit
resourceId: $commitId,
}
],
blobIds: $blobIds
screenshot: null
text: {
type: ""doc"",
content: [{type: ""paragraph"", content: [
{type: ""text"", text: ""My Comment""}]}]}
},
data: { location: { x: 10, y: 7.775978194702619, z: 6.737419726085228 } }
}
)
}";
var variables = new
{
streamId = stream_id,
commitId = commit_id,
blobIds = new[] { blobId }
};
var request = new GraphQLRequest { Query = query, Variables = variables };
With the query and variable substitutions in place, we can use the Speckle GQLClient
to send the query.
using System.Threading;
using System.Threading.Tasks;
await client.GQLClient
.SendMutationAsync<LimitedUserData>(request, CancellationToken.None)
.ConfigureAwait(false);
And that’s it!!
We are missing a few functions with the commenting API that would make replicating the functions available in the Speckle Frontend at speckle.xyz, but this is the gist of it.
Currently missing features:
- The ability to add a screenshot of the comment-defined viewport
- The ability to add a selection of objects to the comment
I think the answer is yes; you can add files to comments. Some parts of this workflow have been in flux for a while, but we hear the need to do better with the documentation!!!
I have wrapped the above up into a Polyglot Jupyter Notebook
speckle-comment-attachments.ipynb (12.8 KB)
The only additional code is to get the Environment Variables where I store the Access Token.
I will add that comment text is internally stored in a ProseMirror document structure, so if anyone’s wondering about how to properly format text
for the commentCreate
mutation you can find more information here:
ProseMirror Guide
ProseMirror Reference manual
Or it might be easier to just see the TypeScript type for this structure: tiptap/types.ts at acca921184e13d7d1ac0eb63fc3dc3b5bcc3f58d · ueberdosis/tiptap · GitHub
Jonathon’s example shows an example of this structure being sent to the mutation - the structure represents a paragraph with a basic text node without any formatting.