feat: add FabWorks API client for ExportDXF

Add IFabWorksApiClient interface, FabWorksApiClient implementation,
and DTO classes for communicating with the FabWorks API from the
SolidWorks add-in.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-18 20:36:06 -05:00
parent 2273a83e42
commit f75b83d483
3 changed files with 264 additions and 0 deletions

View File

@@ -0,0 +1,157 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Json;
using System.Threading.Tasks;
namespace ExportDXF.ApiClient
{
public class FabWorksApiClient : IFabWorksApiClient
{
private readonly HttpClient _http;
public FabWorksApiClient(HttpClient httpClient)
{
_http = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
}
public async Task<ApiExportDetail> CreateExportAsync(string drawingNumber, string equipmentNo, string drawingNo, string sourceFilePath, string outputFolder, string title = null)
{
var request = new ApiCreateExportRequest
{
DrawingNumber = drawingNumber,
Title = title,
EquipmentNo = equipmentNo,
DrawingNo = drawingNo,
SourceFilePath = sourceFilePath,
OutputFolder = outputFolder
};
var response = await _http.PostAsJsonAsync("api/exports", request);
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<ApiExportDetail>();
}
public async Task<ApiExportDetail> GetExportBySourceFileAsync(string filePath)
{
var response = await _http.GetAsync($"api/exports/by-source?path={Uri.EscapeDataString(filePath)}");
if (response.StatusCode == HttpStatusCode.NotFound)
return null;
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<ApiExportDetail>();
}
public async Task<List<string>> GetDrawingNumbersAsync()
{
var response = await _http.GetAsync("api/exports/drawing-numbers");
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<List<string>>();
}
public async Task<List<string>> GetEquipmentNumbersAsync()
{
var response = await _http.GetAsync("api/exports/equipment-numbers");
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<List<string>>();
}
public async Task<List<string>> GetDrawingNumbersByEquipmentAsync(string equipmentNo = null)
{
var url = "api/exports/drawing-numbers-by-equipment";
if (!string.IsNullOrEmpty(equipmentNo))
url += $"?equipmentNo={Uri.EscapeDataString(equipmentNo)}";
var response = await _http.GetAsync(url);
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<List<string>>();
}
public async Task<string> GetNextItemNumberAsync(string drawingNumber)
{
var response = await _http.GetAsync($"api/exports/next-item-number?drawingNumber={Uri.EscapeDataString(drawingNumber)}");
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
public async Task UpdatePdfHashAsync(int exportId, string pdfContentHash)
{
var request = new ApiUpdatePdfHashRequest { PdfContentHash = pdfContentHash };
var response = await _http.PatchAsJsonAsync($"api/exports/{exportId}/pdf-hash", request);
response.EnsureSuccessStatusCode();
}
public async Task<string> GetPreviousPdfHashAsync(string drawingNumber, int? excludeId = null)
{
var url = $"api/exports/previous-pdf-hash?drawingNumber={Uri.EscapeDataString(drawingNumber)}";
if (excludeId.HasValue)
url += $"&excludeId={excludeId.Value}";
var response = await _http.GetAsync(url);
if (response.StatusCode == HttpStatusCode.NotFound)
return null;
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
public async Task<ApiBomItem> FindExistingBomItemAsync(int exportId, string partName, string configurationName)
{
var url = $"api/exports/{exportId}/bom-items/find?partName={Uri.EscapeDataString(partName ?? "")}&configurationName={Uri.EscapeDataString(configurationName ?? "")}";
var response = await _http.GetAsync(url);
if (response.StatusCode == HttpStatusCode.NotFound)
return null;
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<ApiBomItem>();
}
public async Task<ApiBomItem> CreateBomItemAsync(int exportId, ApiBomItem bomItem)
{
var response = await _http.PostAsJsonAsync($"api/exports/{exportId}/bom-items", bomItem);
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<ApiBomItem>();
}
public async Task<ApiCutTemplate> GetPreviousCutTemplateAsync(string drawingNumber, string itemNo)
{
var response = await _http.GetAsync($"api/exports/previous-cut-template?drawingNumber={Uri.EscapeDataString(drawingNumber)}&itemNo={Uri.EscapeDataString(itemNo)}");
if (response.StatusCode == HttpStatusCode.NotFound)
return null;
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<ApiCutTemplate>();
}
public async Task<ApiFileUploadResponse> UploadDxfAsync(string localFilePath, string equipment, string drawingNo, string itemNo, string contentHash)
{
using var content = new MultipartFormDataContent();
using var fileStream = new FileStream(localFilePath, FileMode.Open, FileAccess.Read);
var fileContent = new StreamContent(fileStream);
content.Add(fileContent, "file", Path.GetFileName(localFilePath));
content.Add(new StringContent(equipment ?? ""), "equipment");
content.Add(new StringContent(drawingNo ?? ""), "drawingNo");
content.Add(new StringContent(itemNo ?? ""), "itemNo");
content.Add(new StringContent(contentHash ?? ""), "contentHash");
var response = await _http.PostAsync("api/files/dxf", content);
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<ApiFileUploadResponse>();
}
public async Task<ApiFileUploadResponse> UploadPdfAsync(string localFilePath, string equipment, string drawingNo, string contentHash, int? exportRecordId = null)
{
using var content = new MultipartFormDataContent();
using var fileStream = new FileStream(localFilePath, FileMode.Open, FileAccess.Read);
var fileContent = new StreamContent(fileStream);
content.Add(fileContent, "file", Path.GetFileName(localFilePath));
content.Add(new StringContent(equipment ?? ""), "equipment");
content.Add(new StringContent(drawingNo ?? ""), "drawingNo");
content.Add(new StringContent(contentHash ?? ""), "contentHash");
if (exportRecordId.HasValue)
content.Add(new StringContent(exportRecordId.Value.ToString()), "exportRecordId");
var response = await _http.PostAsync("api/files/pdf", content);
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<ApiFileUploadResponse>();
}
}
}

View File

@@ -0,0 +1,85 @@
using System;
using System.Collections.Generic;
namespace ExportDXF.ApiClient
{
public class ApiExportDetail
{
public int Id { get; set; }
public string DrawingNumber { get; set; }
public string Title { get; set; }
public string EquipmentNo { get; set; }
public string DrawingNo { get; set; }
public string SourceFilePath { get; set; }
public string OutputFolder { get; set; }
public DateTime ExportedAt { get; set; }
public string ExportedBy { get; set; }
public string PdfContentHash { get; set; }
public List<ApiBomItem> BomItems { get; set; } = new();
}
public class ApiBomItem
{
public int ID { get; set; }
public string ItemNo { get; set; }
public string PartNo { get; set; }
public int SortOrder { get; set; }
public int? Qty { get; set; }
public int? TotalQty { get; set; }
public string Description { get; set; }
public string PartName { get; set; }
public string ConfigurationName { get; set; }
public string Material { get; set; }
public ApiCutTemplate CutTemplate { get; set; }
public ApiFormProgram FormProgram { get; set; }
}
public class ApiCutTemplate
{
public int Id { get; set; }
public string DxfFilePath { get; set; }
public string ContentHash { get; set; }
public double? Thickness { get; set; }
public double? KFactor { get; set; }
public double? DefaultBendRadius { get; set; }
}
public class ApiFormProgram
{
public int Id { get; set; }
public string ProgramFilePath { get; set; }
public string ContentHash { get; set; }
public string ProgramName { get; set; }
public double? Thickness { get; set; }
public string MaterialType { get; set; }
public double? KFactor { get; set; }
public int BendCount { get; set; }
public string UpperToolNames { get; set; }
public string LowerToolNames { get; set; }
public string SetupNotes { get; set; }
}
public class ApiCreateExportRequest
{
public string DrawingNumber { get; set; }
public string Title { get; set; }
public string EquipmentNo { get; set; }
public string DrawingNo { get; set; }
public string SourceFilePath { get; set; }
public string OutputFolder { get; set; }
}
public class ApiUpdatePdfHashRequest
{
public string PdfContentHash { get; set; }
}
public class ApiFileUploadResponse
{
public string StoredFilePath { get; set; }
public string ContentHash { get; set; }
public string FileName { get; set; }
public bool WasUnchanged { get; set; }
public bool IsNewFile { get; set; }
}
}

View File

@@ -0,0 +1,22 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace ExportDXF.ApiClient
{
public interface IFabWorksApiClient
{
Task<ApiExportDetail> CreateExportAsync(string drawingNumber, string equipmentNo, string drawingNo, string sourceFilePath, string outputFolder, string title = null);
Task<ApiExportDetail> GetExportBySourceFileAsync(string filePath);
Task<List<string>> GetDrawingNumbersAsync();
Task<List<string>> GetEquipmentNumbersAsync();
Task<List<string>> GetDrawingNumbersByEquipmentAsync(string equipmentNo = null);
Task<string> GetNextItemNumberAsync(string drawingNumber);
Task UpdatePdfHashAsync(int exportId, string pdfContentHash);
Task<string> GetPreviousPdfHashAsync(string drawingNumber, int? excludeId = null);
Task<ApiBomItem> FindExistingBomItemAsync(int exportId, string partName, string configurationName);
Task<ApiBomItem> CreateBomItemAsync(int exportId, ApiBomItem bomItem);
Task<ApiCutTemplate> GetPreviousCutTemplateAsync(string drawingNumber, string itemNo);
Task<ApiFileUploadResponse> UploadDxfAsync(string localFilePath, string equipment, string drawingNo, string itemNo, string contentHash);
Task<ApiFileUploadResponse> UploadPdfAsync(string localFilePath, string equipment, string drawingNo, string contentHash, int? exportRecordId = null);
}
}