Skip to content

Commit f891800

Browse files
Detect DPMA error XML
1 parent ba42b03 commit f891800

File tree

2 files changed

+282
-0
lines changed

2 files changed

+282
-0
lines changed

xml.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,27 @@ func (e *XMLParseError) Unwrap() error {
1919
return e.Err
2020
}
2121

22+
// detectErrorXML checks if XML data contains a DPMA error response
23+
// (Transaction with TransactionErrorDetails) rather than expected data.
24+
// Returns a typed error if error XML is detected, nil otherwise.
25+
func detectErrorXML(data []byte) error {
26+
var errResp ErrorResponse
27+
if err := xml.Unmarshal(data, &errResp); err != nil {
28+
return nil
29+
}
30+
code, text := errResp.errorCodeAndText()
31+
if code == "" && text == "" {
32+
return nil
33+
}
34+
if (code == "E001" || code == "Error") && text == "Data not available" {
35+
return &DataNotAvailableError{}
36+
}
37+
return &APIError{
38+
Code: code,
39+
Message: text,
40+
}
41+
}
42+
2243
// --- Public types ---
2344

2445
// Party represents a person or organization (applicant, inventor, etc.)
@@ -521,6 +542,9 @@ func extractDesignApplicants(raw []xmlDesignApplicant) []Party {
521542

522543
// ParsePatentSearch parses a patent search XML response.
523544
func ParsePatentSearch(data []byte) (*PatentSearchResult, error) {
545+
if err := detectErrorXML(data); err != nil {
546+
return nil, err
547+
}
524548
var raw xmlPatentHitList
525549
if err := xml.Unmarshal(data, &raw); err != nil {
526550
return nil, &XMLParseError{Operation: "ParsePatentSearch", Err: err}
@@ -563,6 +587,9 @@ func ParsePatentSearch(data []byte) (*PatentSearchResult, error) {
563587

564588
// ParsePatentInfo parses a patent info XML response (ST36 format).
565589
func ParsePatentInfo(data []byte) (*PatentInfo, error) {
590+
if err := detectErrorXML(data); err != nil {
591+
return nil, err
592+
}
566593
var raw xmlDPMAPatentDocument
567594
if err := xml.Unmarshal(data, &raw); err != nil {
568595
return nil, &XMLParseError{Operation: "ParsePatentInfo", Err: err}
@@ -625,6 +652,9 @@ func ParsePatentInfo(data []byte) (*PatentInfo, error) {
625652

626653
// ParseTrademarkSearch parses a trademark search XML response.
627654
func ParseTrademarkSearch(data []byte) (*TrademarkSearchResult, error) {
655+
if err := detectErrorXML(data); err != nil {
656+
return nil, err
657+
}
628658
var raw xmlTrademarkHitList
629659
if err := xml.Unmarshal(data, &raw); err != nil {
630660
return nil, &XMLParseError{Operation: "ParseTrademarkSearch", Err: err}
@@ -655,6 +685,9 @@ func ParseTrademarkSearch(data []byte) (*TrademarkSearchResult, error) {
655685

656686
// ParseTrademarkInfo parses a trademark info XML response (ST66 format).
657687
func ParseTrademarkInfo(data []byte) (*TrademarkInfo, error) {
688+
if err := detectErrorXML(data); err != nil {
689+
return nil, err
690+
}
658691
var raw xmlTrademarkTransaction
659692
if err := xml.Unmarshal(data, &raw); err != nil {
660693
return nil, &XMLParseError{Operation: "ParseTrademarkInfo", Err: err}
@@ -700,6 +733,9 @@ func ParseTrademarkInfo(data []byte) (*TrademarkInfo, error) {
700733

701734
// ParseDesignSearch parses a design search XML response.
702735
func ParseDesignSearch(data []byte) (*DesignSearchResult, error) {
736+
if err := detectErrorXML(data); err != nil {
737+
return nil, err
738+
}
703739
var raw xmlDesignHitList
704740
if err := xml.Unmarshal(data, &raw); err != nil {
705741
return nil, &XMLParseError{Operation: "ParseDesignSearch", Err: err}
@@ -720,6 +756,9 @@ func ParseDesignSearch(data []byte) (*DesignSearchResult, error) {
720756

721757
// ParseDesignInfo parses a design info XML response (ST86 format).
722758
func ParseDesignInfo(data []byte) (*DesignInfo, error) {
759+
if err := detectErrorXML(data); err != nil {
760+
return nil, err
761+
}
723762
var raw xmlDesignTransaction
724763
if err := xml.Unmarshal(data, &raw); err != nil {
725764
return nil, &XMLParseError{Operation: "ParseDesignInfo", Err: err}

xml_test.go

Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,249 @@ func TestParsePatentSearch_BOMPrefixed(t *testing.T) {
464464
}
465465
}
466466

467+
func TestDetectErrorXML(t *testing.T) {
468+
tests := []struct {
469+
name string
470+
data []byte
471+
wantErr bool
472+
errType string
473+
}{
474+
{
475+
name: "valid patent search XML",
476+
data: []byte(`<?xml version="1.0" encoding="UTF-8"?><PatentHitList HitCount="0"/>`),
477+
wantErr: false,
478+
},
479+
{
480+
name: "non-XML data",
481+
data: []byte("not xml at all"),
482+
wantErr: false,
483+
},
484+
{
485+
name: "patent error - data not available",
486+
data: []byte(`<?xml version="1.0" encoding="UTF-8"?>
487+
<Transaction>
488+
<PatentTransactionBody>
489+
<TransactionErrorDetails>
490+
<TransactionError>
491+
<TransactionErrorCode>E001</TransactionErrorCode>
492+
<TransactionErrorText>Data not available</TransactionErrorText>
493+
</TransactionError>
494+
</TransactionErrorDetails>
495+
</PatentTransactionBody>
496+
</Transaction>`),
497+
wantErr: true,
498+
errType: "DataNotAvailableError",
499+
},
500+
{
501+
name: "trademark error - permission denied",
502+
data: []byte(`<?xml version="1.0" encoding="UTF-8"?>
503+
<Transaction>
504+
<TradeMarkTransactionBody>
505+
<TransactionErrorDetails>
506+
<TransactionError>
507+
<TransactionErrorCode>E002</TransactionErrorCode>
508+
<TransactionErrorText>Permission denied</TransactionErrorText>
509+
</TransactionError>
510+
</TransactionErrorDetails>
511+
</TradeMarkTransactionBody>
512+
</Transaction>`),
513+
wantErr: true,
514+
errType: "APIError",
515+
},
516+
{
517+
name: "design error",
518+
data: []byte(`<?xml version="1.0" encoding="UTF-8"?>
519+
<Transaction>
520+
<DesignTransactionBody>
521+
<TransactionErrorDetails>
522+
<TransactionError>
523+
<TransactionErrorCode>E003</TransactionErrorCode>
524+
<TransactionErrorText>Resource not found</TransactionErrorText>
525+
</TransactionError>
526+
</TransactionErrorDetails>
527+
</DesignTransactionBody>
528+
</Transaction>`),
529+
wantErr: true,
530+
errType: "APIError",
531+
},
532+
{
533+
name: "transaction without error details",
534+
data: []byte(`<?xml version="1.0" encoding="UTF-8"?>
535+
<Transaction>
536+
<TradeMarkTransactionBody>
537+
</TradeMarkTransactionBody>
538+
</Transaction>`),
539+
wantErr: false,
540+
},
541+
}
542+
543+
for _, tt := range tests {
544+
t.Run(tt.name, func(t *testing.T) {
545+
err := detectErrorXML(tt.data)
546+
if (err != nil) != tt.wantErr {
547+
t.Errorf("detectErrorXML() error = %v, wantErr %v", err, tt.wantErr)
548+
return
549+
}
550+
if !tt.wantErr {
551+
return
552+
}
553+
switch tt.errType {
554+
case "DataNotAvailableError":
555+
var dna *DataNotAvailableError
556+
if !errors.As(err, &dna) {
557+
t.Errorf("expected *DataNotAvailableError, got %T: %v", err, err)
558+
}
559+
case "APIError":
560+
var apiErr *APIError
561+
if !errors.As(err, &apiErr) {
562+
t.Errorf("expected *APIError, got %T: %v", err, err)
563+
}
564+
}
565+
})
566+
}
567+
}
568+
569+
func TestParsePatentSearch_ErrorXML(t *testing.T) {
570+
errorXML := []byte(`<?xml version="1.0" encoding="UTF-8"?>
571+
<Transaction>
572+
<PatentTransactionBody>
573+
<TransactionErrorDetails>
574+
<TransactionError>
575+
<TransactionErrorCode>E001</TransactionErrorCode>
576+
<TransactionErrorText>Data not available</TransactionErrorText>
577+
</TransactionError>
578+
</TransactionErrorDetails>
579+
</PatentTransactionBody>
580+
</Transaction>`)
581+
582+
_, err := ParsePatentSearch(errorXML)
583+
if err == nil {
584+
t.Fatal("expected error for error XML")
585+
}
586+
var dna *DataNotAvailableError
587+
if !errors.As(err, &dna) {
588+
t.Errorf("expected *DataNotAvailableError, got %T: %v", err, err)
589+
}
590+
}
591+
592+
func TestParsePatentInfo_ErrorXML(t *testing.T) {
593+
errorXML := []byte(`<?xml version="1.0" encoding="UTF-8"?>
594+
<Transaction>
595+
<PatentTransactionBody>
596+
<TransactionErrorDetails>
597+
<TransactionError>
598+
<TransactionErrorCode>E002</TransactionErrorCode>
599+
<TransactionErrorText>Permission denied</TransactionErrorText>
600+
</TransactionError>
601+
</TransactionErrorDetails>
602+
</PatentTransactionBody>
603+
</Transaction>`)
604+
605+
_, err := ParsePatentInfo(errorXML)
606+
if err == nil {
607+
t.Fatal("expected error for error XML")
608+
}
609+
var apiErr *APIError
610+
if !errors.As(err, &apiErr) {
611+
t.Errorf("expected *APIError, got %T: %v", err, err)
612+
}
613+
if apiErr.Code != "E002" {
614+
t.Errorf("error code = %q, want %q", apiErr.Code, "E002")
615+
}
616+
}
617+
618+
func TestParseTrademarkInfo_ErrorXML(t *testing.T) {
619+
errorXML := []byte(`<?xml version="1.0" encoding="UTF-8"?>
620+
<Transaction>
621+
<TradeMarkTransactionBody>
622+
<TransactionErrorDetails>
623+
<TransactionError>
624+
<TransactionErrorCode>E001</TransactionErrorCode>
625+
<TransactionErrorText>Data not available</TransactionErrorText>
626+
</TransactionError>
627+
</TransactionErrorDetails>
628+
</TradeMarkTransactionBody>
629+
</Transaction>`)
630+
631+
_, err := ParseTrademarkInfo(errorXML)
632+
if err == nil {
633+
t.Fatal("expected error for error XML")
634+
}
635+
var dna *DataNotAvailableError
636+
if !errors.As(err, &dna) {
637+
t.Errorf("expected *DataNotAvailableError, got %T: %v", err, err)
638+
}
639+
}
640+
641+
func TestParseTrademarkSearch_ErrorXML(t *testing.T) {
642+
errorXML := []byte(`<?xml version="1.0" encoding="UTF-8"?>
643+
<Transaction>
644+
<TradeMarkTransactionBody>
645+
<TransactionErrorDetails>
646+
<TransactionError>
647+
<TransactionErrorCode>E001</TransactionErrorCode>
648+
<TransactionErrorText>Data not available</TransactionErrorText>
649+
</TransactionError>
650+
</TransactionErrorDetails>
651+
</TradeMarkTransactionBody>
652+
</Transaction>`)
653+
654+
_, err := ParseTrademarkSearch(errorXML)
655+
if err == nil {
656+
t.Fatal("expected error for error XML")
657+
}
658+
var dna *DataNotAvailableError
659+
if !errors.As(err, &dna) {
660+
t.Errorf("expected *DataNotAvailableError, got %T: %v", err, err)
661+
}
662+
}
663+
664+
func TestParseDesignSearch_ErrorXML(t *testing.T) {
665+
errorXML := []byte(`<?xml version="1.0" encoding="UTF-8"?>
666+
<Transaction>
667+
<DesignTransactionBody>
668+
<TransactionErrorDetails>
669+
<TransactionError>
670+
<TransactionErrorCode>E003</TransactionErrorCode>
671+
<TransactionErrorText>Resource not found</TransactionErrorText>
672+
</TransactionError>
673+
</TransactionErrorDetails>
674+
</DesignTransactionBody>
675+
</Transaction>`)
676+
677+
_, err := ParseDesignSearch(errorXML)
678+
if err == nil {
679+
t.Fatal("expected error for error XML")
680+
}
681+
var apiErr *APIError
682+
if !errors.As(err, &apiErr) {
683+
t.Errorf("expected *APIError, got %T: %v", err, err)
684+
}
685+
}
686+
687+
func TestParseDesignInfo_ErrorXML(t *testing.T) {
688+
errorXML := []byte(`<?xml version="1.0" encoding="UTF-8"?>
689+
<Transaction>
690+
<DesignTransactionBody>
691+
<TransactionErrorDetails>
692+
<TransactionError>
693+
<TransactionErrorCode>E003</TransactionErrorCode>
694+
<TransactionErrorText>Resource not found</TransactionErrorText>
695+
</TransactionError>
696+
</TransactionErrorDetails>
697+
</DesignTransactionBody>
698+
</Transaction>`)
699+
700+
_, err := ParseDesignInfo(errorXML)
701+
if err == nil {
702+
t.Fatal("expected error for error XML")
703+
}
704+
var apiErr *APIError
705+
if !errors.As(err, &apiErr) {
706+
t.Errorf("expected *APIError, got %T: %v", err, err)
707+
}
708+
}
709+
467710
func TestXMLParseError_Unwrap(t *testing.T) {
468711
inner := errors.New("underlying error")
469712
xmlErr := &XMLParseError{Operation: "TestOp", Err: inner}

0 commit comments

Comments
 (0)