- Add Dependency type to internal/kc and extract silo/dependencies.json
from .kc files on commit
- Create ItemDependencyRepository with ReplaceForRevision, ListByItem,
and Resolve (LEFT JOIN against items table)
- Add GET /{partNumber}/dependencies and
GET /{partNumber}/dependencies/resolve endpoints
- Index dependencies in extractKCMetadata with SSE broadcast
- Pack real dependency data into .kc files on checkout
- Update PackInput.Dependencies from []any to []Dependency
Closes #143
126 lines
3.7 KiB
Go
126 lines
3.7 KiB
Go
package api
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
"github.com/kindredsystems/silo/internal/storage"
|
|
)
|
|
|
|
// DependencyResponse is the JSON representation for GET /dependencies.
|
|
type DependencyResponse struct {
|
|
UUID string `json:"uuid"`
|
|
PartNumber *string `json:"part_number"`
|
|
Revision *int `json:"revision"`
|
|
Quantity *float64 `json:"quantity"`
|
|
Label *string `json:"label"`
|
|
Relationship string `json:"relationship"`
|
|
}
|
|
|
|
// ResolvedDependencyResponse is the JSON representation for GET /dependencies/resolve.
|
|
type ResolvedDependencyResponse struct {
|
|
UUID string `json:"uuid"`
|
|
PartNumber *string `json:"part_number"`
|
|
Label *string `json:"label"`
|
|
Revision *int `json:"revision"`
|
|
Quantity *float64 `json:"quantity"`
|
|
Resolved bool `json:"resolved"`
|
|
FileAvailable bool `json:"file_available"`
|
|
}
|
|
|
|
// HandleGetDependencies returns the raw dependency list for an item.
|
|
// GET /api/items/{partNumber}/dependencies
|
|
func (s *Server) HandleGetDependencies(w http.ResponseWriter, r *http.Request) {
|
|
ctx := r.Context()
|
|
partNumber := chi.URLParam(r, "partNumber")
|
|
|
|
item, err := s.items.GetByPartNumber(ctx, partNumber)
|
|
if err != nil {
|
|
s.logger.Error().Err(err).Msg("failed to get item")
|
|
writeError(w, http.StatusInternalServerError, "internal_error", "Failed to get item")
|
|
return
|
|
}
|
|
if item == nil {
|
|
writeError(w, http.StatusNotFound, "not_found", "Item not found")
|
|
return
|
|
}
|
|
|
|
deps, err := s.deps.ListByItem(ctx, item.ID)
|
|
if err != nil {
|
|
s.logger.Error().Err(err).Msg("failed to list dependencies")
|
|
writeError(w, http.StatusInternalServerError, "internal_error", "Failed to list dependencies")
|
|
return
|
|
}
|
|
|
|
resp := make([]DependencyResponse, len(deps))
|
|
for i, d := range deps {
|
|
resp[i] = DependencyResponse{
|
|
UUID: d.ChildUUID,
|
|
PartNumber: d.ChildPartNumber,
|
|
Revision: d.ChildRevision,
|
|
Quantity: d.Quantity,
|
|
Label: d.Label,
|
|
Relationship: d.Relationship,
|
|
}
|
|
}
|
|
|
|
writeJSON(w, http.StatusOK, resp)
|
|
}
|
|
|
|
// HandleResolveDependencies returns dependencies with UUIDs resolved to part numbers
|
|
// and file availability status.
|
|
// GET /api/items/{partNumber}/dependencies/resolve
|
|
func (s *Server) HandleResolveDependencies(w http.ResponseWriter, r *http.Request) {
|
|
ctx := r.Context()
|
|
partNumber := chi.URLParam(r, "partNumber")
|
|
|
|
item, err := s.items.GetByPartNumber(ctx, partNumber)
|
|
if err != nil {
|
|
s.logger.Error().Err(err).Msg("failed to get item")
|
|
writeError(w, http.StatusInternalServerError, "internal_error", "Failed to get item")
|
|
return
|
|
}
|
|
if item == nil {
|
|
writeError(w, http.StatusNotFound, "not_found", "Item not found")
|
|
return
|
|
}
|
|
|
|
deps, err := s.deps.Resolve(ctx, item.ID)
|
|
if err != nil {
|
|
s.logger.Error().Err(err).Msg("failed to resolve dependencies")
|
|
writeError(w, http.StatusInternalServerError, "internal_error", "Failed to resolve dependencies")
|
|
return
|
|
}
|
|
|
|
resp := make([]ResolvedDependencyResponse, len(deps))
|
|
for i, d := range deps {
|
|
// Use resolved part number if available, fall back to .kc-provided value.
|
|
pn := d.ChildPartNumber
|
|
rev := d.ChildRevision
|
|
if d.Resolved {
|
|
pn = d.ResolvedPartNumber
|
|
rev = d.ResolvedRevision
|
|
}
|
|
|
|
fileAvailable := false
|
|
if d.Resolved && pn != nil && rev != nil && s.storage != nil {
|
|
key := storage.FileKey(*pn, *rev)
|
|
if exists, err := s.storage.Exists(ctx, key); err == nil {
|
|
fileAvailable = exists
|
|
}
|
|
}
|
|
|
|
resp[i] = ResolvedDependencyResponse{
|
|
UUID: d.ChildUUID,
|
|
PartNumber: pn,
|
|
Label: d.Label,
|
|
Revision: rev,
|
|
Quantity: d.Quantity,
|
|
Resolved: d.Resolved,
|
|
FileAvailable: fileAvailable,
|
|
}
|
|
}
|
|
|
|
writeJSON(w, http.StatusOK, resp)
|
|
}
|