Skip to content
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

Sync with develop #1

Merged
merged 55 commits into from
Mar 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
384014f
create job action
sokolenkoolha Mar 11, 2024
c3aaa51
added retry logic
sokolenkoolha Mar 11, 2024
2556db7
Added job actions
vitalii-bezuhlyi Mar 13, 2024
c35195c
Put action in job
vitalii-bezuhlyi Mar 13, 2024
233a1d7
Updated job outputs
vitalii-bezuhlyi Mar 14, 2024
aae3269
Added requests actions
vitalii-bezuhlyi Mar 14, 2024
4f7bd5a
Updates into request actions
vitalii-bezuhlyi Mar 14, 2024
ee7ad87
Updates into request actions
vitalii-bezuhlyi Mar 14, 2024
5119d40
Divided Create request action into two
vitalii-bezuhlyi Mar 14, 2024
509fbca
Updated Update request content
vitalii-bezuhlyi Mar 14, 2024
ca64c51
Updated Get requests action
vitalii-bezuhlyi Mar 14, 2024
ae3e8c9
Changes in delete request
vitalii-bezuhlyi Mar 14, 2024
bc5809d
Updated in Get requests
vitalii-bezuhlyi Mar 14, 2024
8abfd1a
Improved Create request action
vitalii-bezuhlyi Mar 14, 2024
30ab7cf
Fixed deserialization bug
vitalii-bezuhlyi Mar 14, 2024
6d95ea5
Fixed 'Create source content request' action
vitalii-bezuhlyi Mar 14, 2024
5c6ff1b
Added file action and fixed update request action
vitalii-bezuhlyi Mar 15, 2024
f194d78
Updated an action
vitalii-bezuhlyi Mar 15, 2024
5d18889
Added retrieve file action
vitalii-bezuhlyi Mar 15, 2024
1d557b7
Improved update job action
vitalii-bezuhlyi Mar 15, 2024
1c11862
Added support assets actions
vitaliy-bezugly Mar 18, 2024
c347745
Rename jobId property
vitaliy-bezugly Mar 18, 2024
d9bfe88
Rename jobId property
vitaliy-bezugly Mar 18, 2024
5d3e515
Updated collection type
vitaliy-bezugly Mar 18, 2024
c1101bc
Translation memory actions
vitaliy-bezugly Mar 18, 2024
a67a12e
Improved file inputs
vitaliy-bezugly Mar 18, 2024
b42b230
small fixes
vitaliy-bezugly Mar 18, 2024
efea186
Added TranslationContentActions
vitaliy-bezugly Mar 18, 2024
8de6a46
Added first test event
vitaliy-bezugly Mar 18, 2024
2ec33e3
Added on request status updated event
vitaliy-bezugly Mar 18, 2024
8cabb5e
Added optional inputs instead of required
vitaliy-bezugly Mar 18, 2024
ec09052
Fixed request webhook
vitaliy-bezugly Mar 18, 2024
58fa01c
Added bridge support
vitaliy-bezugly Mar 19, 2024
4708100
Added error handling to webhooks
vitaliy-bezugly Mar 19, 2024
d7f5d1a
Added error handling to webhooks
vitaliy-bezugly Mar 19, 2024
ed6485d
updates bridge service
vitaliy-bezugly Mar 19, 2024
daa9fb9
Updated bridge service
vitaliy-bezugly Mar 19, 2024
c249863
updated dynamic inputs
vitaliy-bezugly Mar 19, 2024
2532c0f
Submit job in sync way
vitaliy-bezugly Mar 19, 2024
8d8f1e5
Provided fixes
vitaliy-bezugly Mar 20, 2024
926642e
Improved inputs
vitaliy-bezugly Mar 20, 2024
8ec1070
Fixed optional inputs
vitaliy-bezugly Mar 20, 2024
75c0bba
Better error handling
vitaliy-bezugly Mar 20, 2024
0527d61
Updated in bridge service
vitaliy-bezugly Mar 20, 2024
b72a8bd
test commit
vitalii-bezuhlyi Mar 20, 2024
2d6160a
Updated webhooks
vitalii-bezuhlyi Mar 20, 2024
5878aa1
Fixed webhooks
vitalii-bezuhlyi Mar 20, 2024
6f812ee
Updated bridge url
vitalii-bezuhlyi Mar 20, 2024
f5df082
Changes into bridge registration
vitalii-bezuhlyi Mar 20, 2024
1a0ce58
Update check
vitalii-bezuhlyi Mar 20, 2024
c025d48
Improved webhooks
vitalii-bezuhlyi Mar 20, 2024
4746dab
Improved optional inputs
vitalii-bezuhlyi Mar 20, 2024
a31468f
Changes after review
vitalii-bezuhlyi Mar 21, 2024
5ad8bc6
Added docs and improved action descriptions
vitalii-bezuhlyi Mar 21, 2024
96a25ed
Updated readme
vitalii-bezuhlyi Mar 21, 2024
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
168 changes: 168 additions & 0 deletions Apps.Lionbridge/Actions/JobActions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
using Apps.Lionbridge.Api;
using Apps.Lionbridge.Constants;
using Apps.Lionbridge.Extensions;
using Apps.Lionbridge.Models.Dtos;
using Apps.Lionbridge.Models.Requests.Job;
using Apps.Lionbridge.Models.Requests.Provider;
using Blackbird.Applications.Sdk.Common;
using Blackbird.Applications.Sdk.Common.Actions;
using Blackbird.Applications.Sdk.Common.Invocation;
using Blackbird.Applications.Sdk.Utils.Extensions.Http;
using RestSharp;

namespace Apps.Lionbridge.Actions;

[ActionList]
public class JobActions(InvocationContext invocationContext) : LionbridgeInvocable(invocationContext)
{
[Action("Create job", Description = "Create a new job")]
public async Task<JobDto> CreateJob([ActionParameter] CreateJobRequest input)
{
var request = new LionbridgeRequest("/jobs", Method.Post)
.WithJsonBody(new
{
jobName = input.JobName,
description = input.Description,
providerId = input.ProviderId,
extendedMetadata = EnumerableExtensions.ToDictionary(input.MetadataKeys, input.MetadataValues),
labels = EnumerableExtensions.ToDictionary(input.LabelKeys, input.LabelValues)
});

return await Client.ExecuteWithErrorHandling<JobDto>(request);
}

[Action("Delete job", Description = "Delete a job")]
public async Task DeleteJob([ActionParameter] GetJobRequest request)
{
var apiRequest = new LionbridgeRequest($"{ApiEndpoints.Jobs}/{request.JobId}", Method.Delete);
await Client.ExecuteWithErrorHandling(apiRequest);
}

[Action("Get job", Description = "Get a job")]
public async Task<JobDto> GetJob([ActionParameter] GetJobRequest request)
{
var apiRequest = new LionbridgeRequest($"{ApiEndpoints.Jobs}/{request.JobId}");
return await Client.ExecuteWithErrorHandling<JobDto>(apiRequest);
}

[Action("Update job", Description = "Update a job, update only the fields that are specified. To complete a job, set the Job status to 'Completed'. To set a job to 'IN_TRANSLATION', set the Job status to 'In translation'")]
public async Task<JobDto> UpdateJob([ActionParameter] GetJobRequest jobRequest, [ActionParameter] UpdateJobRequest request)
{
var apiUpdateRequest = new UpdateJobApiRequest();

if(request.JobName != null)
{
apiUpdateRequest.JobName = request.JobName;
}

if(request.Description != null)
{
apiUpdateRequest.Description = request.Description;
}

if(request.ProviderId != null)
{
apiUpdateRequest.ProviderId = request.ProviderId;
}

if(request.MetadataKeys != null && request.MetadataValues != null)
{
apiUpdateRequest.ExtendedMetadata = EnumerableExtensions.ToDictionary(request.MetadataKeys, request.MetadataValues);
}

if(request.LabelKeys != null && request.LabelValues != null)
{
apiUpdateRequest.Labels = EnumerableExtensions.ToDictionary(request.LabelKeys, request.LabelValues);
}

if(request.DueDate != null)
{
apiUpdateRequest.DueDate = request.DueDate.Value.ToString("yyyy-MM-ddTHH:mm:ssZ");
}

if(request.ShouldQuote != null)
{
apiUpdateRequest.ShouldQuote = request.ShouldQuote;
}

if(request.ConnectorName != null)
{
apiUpdateRequest.ConnectorName = request.ConnectorName;
}

if(request.ConnectorVersion != null)
{
apiUpdateRequest.ConnectorVersion = request.ConnectorVersion;
}

if(request.ServiceType != null)
{
apiUpdateRequest.ServiceType = request.ServiceType;
}

var apiRequest = new LionbridgeRequest($"{ApiEndpoints.Jobs}/{jobRequest.JobId}", Method.Patch)
.WithJsonBody(new
{
jobName = apiUpdateRequest.JobName,
description = apiUpdateRequest.Description,
providerId = apiUpdateRequest.ProviderId,
extendedMetadata = apiUpdateRequest.ExtendedMetadata,
labels = apiUpdateRequest.Labels,
dueDate = apiUpdateRequest.DueDate,
shouldQuote = apiUpdateRequest.ShouldQuote,
connectorName = apiUpdateRequest.ConnectorName,
connectorVersion = apiUpdateRequest.ConnectorVersion,
serviceType = apiUpdateRequest.ServiceType
});

if(request.JobCompletionStatus != null)
{
if(request.JobCompletionStatus == "COMPLETED")
{
return await CompleteJob(jobRequest.JobId);
}

if(request.JobCompletionStatus == "IN_TRANSLATION")
{
return await IntranslateJob(jobRequest.JobId);
}
}

return await Client.ExecuteWithErrorHandling<JobDto>(apiRequest);
}

[Action("Submit job", Description = "Send a job for translation with a selected provider")]
public async Task<JobDto> SubmitJob([ActionParameter] GetJobRequest request, [ActionParameter] GetProviderRequest providerRequest)
{
var apiRequest = new LionbridgeRequest($"{ApiEndpoints.Jobs}/{request.JobId}/submit", Method.Put)
.WithJsonBody(new { providerId = providerRequest.ProviderId });

return await Client.ExecuteWithErrorHandling<JobDto>(apiRequest);
}

[Action("Archive job", Description = "Move a job to storage for safekeeping")]
public async Task<JobDto> ArchiveJob([ActionParameter] GetJobRequest request)
{
var apiRequest = new LionbridgeRequest($"{ApiEndpoints.Jobs}/{request.JobId}/archive", Method.Put);
return await Client.ExecuteWithErrorHandling<JobDto>(apiRequest);
}

[Action("Unarchive job", Description = "Retrieve a job from storage back into active status")]
public async Task<JobDto> UnarchiveJob([ActionParameter] GetJobRequest request)
{
var apiRequest = new LionbridgeRequest($"{ApiEndpoints.Jobs}/{request.JobId}/unarchive", Method.Put);
return await Client.ExecuteWithErrorHandling<JobDto>(apiRequest);
}

protected async Task<JobDto> CompleteJob(string jobId)
{
var apiRequest = new LionbridgeRequest($"{ApiEndpoints.Jobs}/{jobId}/complete", Method.Put);
return await Client.ExecuteWithErrorHandling<JobDto>(apiRequest);
}

protected async Task<JobDto> IntranslateJob(string jobId)
{
var apiRequest = new LionbridgeRequest($"{ApiEndpoints.Jobs}/{jobId}/intranslation", Method.Put);
return await Client.ExecuteWithErrorHandling<JobDto>(apiRequest);
}
}
165 changes: 165 additions & 0 deletions Apps.Lionbridge/Actions/RequestActions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
using Apps.Lionbridge.Api;
using Apps.Lionbridge.Constants;
using Apps.Lionbridge.Extensions;
using Apps.Lionbridge.Models.Dtos;
using Apps.Lionbridge.Models.Requests;
using Apps.Lionbridge.Models.Requests.File;
using Apps.Lionbridge.Models.Requests.Job;
using Apps.Lionbridge.Models.Requests.Request;
using Apps.Lionbridge.Models.Responses.Request;
using Apps.Lionbridge.Models.Responses.TranslationContent;
using Blackbird.Applications.Sdk.Common;
using Blackbird.Applications.Sdk.Common.Actions;
using Blackbird.Applications.Sdk.Common.Invocation;
using Blackbird.Applications.SDK.Extensions.FileManagement.Interfaces;
using Blackbird.Applications.Sdk.Utils.Extensions.Http;
using RestSharp;

namespace Apps.Lionbridge.Actions;

[ActionList]
public class RequestActions(InvocationContext invocationContext, IFileManagementClient fileManagementClient)
: LionbridgeInvocable(invocationContext)
{
[Action("Get requests", Description = "View a list of your translation requests")]
public async Task<GetRequestsResponse> GetRequests([ActionParameter] GetRequestsAsOptional jobRequest)
{
return await GetRequests(jobRequest.JobId, jobRequest.RequestIds);
}

// [Action("Create source content request", Description = "Create a new translation request.")]
public async Task<RequestDto> CreateSingleRequest([ActionParameter] AddSourceRequestModel request,
[ActionParameter] GetJobRequest jobRequest)
{
string sourceContentId =
await CreateTranslationContent(jobRequest.JobId, request.FieldsKeys, request.FieldsValues);

var metadata = EnumerableExtensions.ToDictionary(request.MetadataKeys, request.MetadataValues);
var apiRequest =
new LionbridgeRequest($"{ApiEndpoints.Jobs}/{jobRequest.JobId}" + ApiEndpoints.Requests + ApiEndpoints.Add,
Method.Post)
.WithJsonBody(new
{
requestName = request.RequestName ?? Guid.NewGuid().ToString(),
sourceNativeId = request.SourceNativeId ?? Guid.NewGuid().ToString(),
sourceNativeLanguageCode = request.SourceNativeLanguageCode,
targetNativeIds = request.TargetNativeIds,
targetNativeLanguageCodes = new List<string> { request.TargetNativeLanguage },
wordCount = request.WordCount,
extendedMetadata = metadata,
sourcecontentId = sourceContentId
});

var response = await Client.ExecuteWithErrorHandling<RequestsResponse>(apiRequest);
return response.Embedded.Requests.First();
}

[Action("Create file request", Description = "Start a new request to translate a document")]
public async Task<RequestDto> CreateFileRequest([ActionParameter] GetJobRequest jobRequest,
[ActionParameter] AddSourceFileRequest sourceFileRequest)
{
var uploadResponse = await UploadFmsFile(jobRequest.JobId, new AddFileRequest(sourceFileRequest), fileManagementClient);

var metadata =
EnumerableExtensions.ToDictionary(sourceFileRequest.MetadataKeys, sourceFileRequest.MetadataValues);
var apiRequest =
new LionbridgeRequest($"{ApiEndpoints.Jobs}/{jobRequest.JobId}" + ApiEndpoints.Requests + ApiEndpoints.Add,
Method.Post)
.WithJsonBody(new
{
requestName = sourceFileRequest.RequestName ?? Guid.NewGuid().ToString(),
sourceNativeId = sourceFileRequest.SourceNativeId ?? Guid.NewGuid().ToString(),
sourceNativeLanguageCode = sourceFileRequest.SourceNativeLanguageCode,
targetNativeIds = sourceFileRequest.TargetNativeIds,
targetNativeLanguageCodes = new List<string> { sourceFileRequest.TargetNativeLanguage },
wordCount = sourceFileRequest.WordCount,
extendedMetadata = metadata,
fmsFileId = uploadResponse.FmsFileId
});

var response = await Client.ExecuteWithErrorHandling<RequestsResponse>(apiRequest);
return response.Embedded.Requests.First();
}

[Action("Get request", Description = "View details of a specific translation request")]
public async Task<RequestDto> GetRequest([ActionParameter] GetRequest request)
{
return await GetRequest(request.JobId, request.RequestId);
}

[Action("Delete request", Description = "Remove a translation request no longer needed")]
public async Task<RequestDto> DeleteRequest([ActionParameter] GetRequest request)
{
var apiRequest =
new LionbridgeRequest(
$"{ApiEndpoints.Jobs}/{request.JobId}" + $"{ApiEndpoints.Requests}/{request.RequestId}", Method.Delete);

return await Client.ExecuteWithErrorHandling<RequestDto>(apiRequest);
}

[Action("Approve request", Description = "Approve a request to send it to the provider")]
public async Task ApproveRequest([ActionParameter] GetRequests request)
{
var apiRequest =
new LionbridgeRequest($"{ApiEndpoints.Jobs}/{request.JobId}" + ApiEndpoints.Requests + ApiEndpoints.Approve,
Method.Put)
.WithJsonBody(new
{
requestIds = request.RequestIds
});

await Client.ExecuteWithErrorHandling(apiRequest);
}

[Action("Reject request", Description = "Reject a translation request")]
public async Task RejectRequest([ActionParameter] GetRequests request)
{
var apiRequest =
new LionbridgeRequest($"{ApiEndpoints.Jobs}/{request.JobId}" + ApiEndpoints.Requests + ApiEndpoints.Reject,
Method.Put)
.WithJsonBody(new
{
requestIds = request.RequestIds
});

await Client.ExecuteWithErrorHandling(apiRequest);
}

[Action("Update request content", Description = "Make changes to the details of an existing translation request.")]
public async Task<RequestDto> UpdateRequestContent([ActionParameter] GetRequest request,
[ActionParameter] UpdateRequestModel updateRequestContentModel)
{
string endpoint = $"{ApiEndpoints.Jobs}/{request.JobId}{ApiEndpoints.Requests}/{request}";
var apiRequest =
new LionbridgeRequest(endpoint, Method.Patch)
.WithJsonBody(new
{
requestName = updateRequestContentModel.RequestName,
sourceNativeId = updateRequestContentModel.SourceNativeId,
sourceNativeLanguageCode = updateRequestContentModel.SourceNativeLanguageCode,
targetNativeId = updateRequestContentModel.TargetNativeId,
targetNativeLanguageCode = updateRequestContentModel.TargetNativeLanguageCode,
extendedMetadata = EnumerableExtensions.ToDictionary(updateRequestContentModel.MetadataKeys,
updateRequestContentModel.MetadataValues),
fileId = updateRequestContentModel.FileId,
sourceContentId = updateRequestContentModel.SourceContentId
});

var response = await Client.ExecuteWithErrorHandling<RequestDto>(apiRequest);
return response;
}

private async Task<string> CreateTranslationContent(string jobId, IEnumerable<string>? keys,
IEnumerable<string>? values)
{
var listOfKeyValuePairs = CreateListOfKeyValuePairs(keys, values);
var apiRequest = new LionbridgeRequest($"{ApiEndpoints.Jobs}/{jobId}{ApiEndpoints.TranslationContent}", Method.Post)
.WithJsonBody(new
{
fields = listOfKeyValuePairs
});

var response = await Client.ExecuteWithErrorHandling<TranslationContentDtoResponse>(apiRequest);
return response.SourceContentId;
}
}
39 changes: 39 additions & 0 deletions Apps.Lionbridge/Actions/SourceFileActions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using Apps.Lionbridge.Api;
using Apps.Lionbridge.Constants;
using Apps.Lionbridge.Models.Requests.Request;
using Apps.Lionbridge.Models.Responses.SourceFile;
using Blackbird.Applications.Sdk.Common;
using Blackbird.Applications.Sdk.Common.Actions;
using Blackbird.Applications.Sdk.Common.Invocation;
using Blackbird.Applications.SDK.Extensions.FileManagement.Interfaces;

namespace Apps.Lionbridge.Actions;

[ActionList]
public class SourceFileActions(InvocationContext invocationContext, IFileManagementClient fileManagementClient)
: LionbridgeInvocable(invocationContext)
{
[Action("Retrieve file", Description = "Download a document from a specific request")]
public async Task<RetrieveFileResponse> RetrieveFile([ActionParameter] GetRequest request)
{
string endpoint = $"{ApiEndpoints.Jobs}/{request.JobId}{ApiEndpoints.Requests}/{request.RequestId}{ApiEndpoints.RetrieveFile}";
var apiRequest = new LionbridgeRequest(endpoint);

var response = await Client.ExecuteWithErrorHandling(apiRequest);
var bytes = response.RawBytes;

if(bytes == null || bytes.Length == 0)
{
throw new Exception("Uploaded file is empty");
}

var requestModel = await GetRequest(request.JobId, request.RequestId);

var memoryStream = new MemoryStream(bytes);
string fileName = requestModel.FileName ?? requestModel.RequestName + ".xml"; // if there is no file name it means that request was created from source content
string contentType = response.ContentType ?? MimeTypes.GetMimeType(fileName);
var fileReference = await fileManagementClient.UploadAsync(memoryStream, contentType, fileName);

return new RetrieveFileResponse { File = fileReference };
}
}
Loading