feat(t230): use OCR for ERP session login
This commit is contained in:
@@ -30,6 +30,10 @@ type FreightService struct {
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
type FreightSourcePreflight interface {
|
||||
EnsureAuthenticated(context.Context) error
|
||||
}
|
||||
|
||||
type CreateFreightSyncCommand struct {
|
||||
CreatorSubject string
|
||||
ActorUserID string
|
||||
@@ -101,6 +105,9 @@ func (service *FreightService) CreateOrderSync(
|
||||
fields,
|
||||
)
|
||||
}
|
||||
if err := service.ensureSource(ctx); err != nil {
|
||||
return CreateFreightSyncResult{}, err
|
||||
}
|
||||
runID, err := service.ids.NewID()
|
||||
if err != nil {
|
||||
return CreateFreightSyncResult{}, wrapRepositoryError(err)
|
||||
@@ -166,6 +173,9 @@ func (service *FreightService) CreateDateSync(
|
||||
fields,
|
||||
)
|
||||
}
|
||||
if err := service.ensureSource(ctx); err != nil {
|
||||
return CreateFreightSyncResult{}, err
|
||||
}
|
||||
|
||||
now := service.clock.Now().UTC()
|
||||
location, err := time.LoadLocation("Asia/Shanghai")
|
||||
@@ -278,6 +288,24 @@ func (service *FreightService) CreateDateSync(
|
||||
return CreateFreightSyncResult{Run: run, Replayed: !created}, nil
|
||||
}
|
||||
|
||||
func (service *FreightService) ensureSource(ctx context.Context) error {
|
||||
preflight, ok := service.source.(FreightSourcePreflight)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
if err := preflight.EnsureAuthenticated(ctx); err != nil {
|
||||
result := newError(
|
||||
ErrorKindUnavailable,
|
||||
freightSourceErrorCode(err),
|
||||
"freight source session is unavailable",
|
||||
err,
|
||||
)
|
||||
result.Retryable = true
|
||||
return result
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (service *FreightService) execute(run domain.FreightSyncRun) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), service.timeout)
|
||||
defer cancel()
|
||||
@@ -679,6 +707,8 @@ func freightSourceErrorCode(err error) string {
|
||||
return "ERP_FREIGHT_NOT_FOUND"
|
||||
case errors.Is(err, domain.ErrFreightSourceProtocol):
|
||||
return "ERP_RESPONSE_INVALID"
|
||||
case errors.Is(err, domain.ErrFreightSourceOCRInvalid):
|
||||
return "OCR_SERVICE_INVALID"
|
||||
default:
|
||||
return "ERP_UNAVAILABLE"
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@ func TestFreightSourceErrorCodesAreSourceNeutral(t *testing.T) {
|
||||
{domain.ErrFreightSourceSessionNeeded, "ERP_SESSION_REQUIRED"},
|
||||
{domain.ErrFreightSourceNotFound, "ERP_FREIGHT_NOT_FOUND"},
|
||||
{domain.ErrFreightSourceProtocol, "ERP_RESPONSE_INVALID"},
|
||||
{domain.ErrFreightSourceOCRInvalid, "OCR_SERVICE_INVALID"},
|
||||
{errors.New("temporary source failure"), "ERP_UNAVAILABLE"},
|
||||
}
|
||||
for _, testCase := range cases {
|
||||
@@ -73,6 +74,29 @@ func TestFreightSourceErrorCodesAreSourceNeutral(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateFreightOrderSyncStopsBeforePersistingWhenOCRIsInvalid(t *testing.T) {
|
||||
repository := &dateCaptureRepository{}
|
||||
service, err := NewFreightService(
|
||||
repository,
|
||||
&recordingDateSource{ensureErr: domain.ErrFreightSourceOCRInvalid},
|
||||
fakeClock{},
|
||||
&sequenceIDs{},
|
||||
time.Minute,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("NewFreightService() error = %v", err)
|
||||
}
|
||||
_, err = service.CreateOrderSync(context.Background(), CreateFreightSyncCommand{
|
||||
CreatorSubject: "local-admin",
|
||||
ActorUserID: "00000000-0000-4000-8000-000000000099",
|
||||
IdempotencyKey: "ocr-invalid",
|
||||
OrderNumber: "ORDER-123",
|
||||
})
|
||||
if !errors.Is(err, domain.ErrFreightSourceOCRInvalid) || repository.createCalls != 0 {
|
||||
t.Fatalf("CreateOrderSync() error/calls = %v / %d", err, repository.createCalls)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFreightDateQuerySplitsIntoSevenDayWindows(t *testing.T) {
|
||||
source := &recordingDateSource{}
|
||||
service := &FreightService{source: source}
|
||||
@@ -230,8 +254,15 @@ func validFreightSource() domain.FreightSourceBatch {
|
||||
var errDateSourceFailure = errors.New("date source failed")
|
||||
|
||||
type recordingDateSource struct {
|
||||
calls [][2]string
|
||||
failOnCall int
|
||||
calls [][2]string
|
||||
failOnCall int
|
||||
ensureCalls int
|
||||
ensureErr error
|
||||
}
|
||||
|
||||
func (source *recordingDateSource) EnsureAuthenticated(context.Context) error {
|
||||
source.ensureCalls++
|
||||
return source.ensureErr
|
||||
}
|
||||
|
||||
func (source *recordingDateSource) QueryOrder(
|
||||
@@ -261,7 +292,8 @@ func (source *recordingDateSource) QueryCreatedRange(
|
||||
}
|
||||
|
||||
type dateCaptureRepository struct {
|
||||
watermark *domain.FreightSyncWatermark
|
||||
watermark *domain.FreightSyncWatermark
|
||||
createCalls int
|
||||
}
|
||||
|
||||
func (repository *dateCaptureRepository) CreateFreightSync(
|
||||
@@ -269,6 +301,7 @@ func (repository *dateCaptureRepository) CreateFreightSync(
|
||||
run domain.FreightSyncRun,
|
||||
_, _ string,
|
||||
) (domain.FreightSyncRun, bool, error) {
|
||||
repository.createCalls++
|
||||
return run, false, nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user