changeset 5607:f2b51ac3d5cf erdms2

Modified SOAP client to fit new needs.
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Mon, 31 Oct 2022 17:02:55 +0100
parents 5049f7ad9faa
children df40d55f7c9a
files pkg/soap/soap.go pkg/soap/xsdDateTime.go
diffstat 2 files changed, 361 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/pkg/soap/soap.go	Wed Aug 10 12:01:22 2022 +0200
+++ b/pkg/soap/soap.go	Mon Oct 31 17:02:55 2022 +0100
@@ -224,6 +224,10 @@
 	s.headers = append(s.headers, header)
 }
 
+func (s *SOAPClient) CallContext(ctx context.Context, soapAction string, request, response interface{}) error {
+	return s.Call(soapAction, request, response)
+}
+
 func (s *SOAPClient) Call(soapAction string, request, response interface{}) error {
 	envelope := SOAPEnvelope{}
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pkg/soap/xsdDateTime.go	Mon Oct 31 17:02:55 2022 +0100
@@ -0,0 +1,357 @@
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package soap
+
+import (
+	"encoding/xml"
+	"strings"
+	"time"
+)
+
+const (
+	dateLayout = "2006-01-02Z07:00"
+	timeLayout = "15:04:05.999999999Z07:00"
+)
+
+//
+// DateTime struct
+//
+
+// XSDDateTime is a type for representing xsd:datetime in Golang
+type XSDDateTime struct {
+	innerTime time.Time
+	hasTz     bool
+}
+
+// StripTz removes TZ information from the datetime
+func (xdt *XSDDateTime) StripTz() {
+	xdt.hasTz = false
+}
+
+// ToGoTime converts the time to time.Time by checking if a TZ is specified.
+// If there is a TZ, that TZ is used, otherwise local TZ is used
+func (xdt *XSDDateTime) ToGoTime() time.Time {
+	if xdt.hasTz {
+		return xdt.innerTime
+	}
+	return time.Date(xdt.innerTime.Year(), xdt.innerTime.Month(), xdt.innerTime.Day(),
+		xdt.innerTime.Hour(), xdt.innerTime.Minute(), xdt.innerTime.Second(),
+		xdt.innerTime.Nanosecond(), time.Local)
+}
+
+// MarshalXML implements xml.MarshalerAttr on XSDDateTime
+func (xdt XSDDateTime) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+	xdtString := xdt.string()
+	if xdtString != "" {
+		return e.EncodeElement(xdtString, start)
+	}
+	return nil
+}
+
+// MarshalXMLAttr implements xml.MarshalerAttr on XSDDateTime
+func (xdt XSDDateTime) MarshalXMLAttr(name xml.Name) (xml.Attr, error) {
+	xdtString := xdt.string()
+	attr := xml.Attr{}
+	if xdtString != "" {
+		attr.Name = name
+		attr.Value = xdtString
+	}
+	return attr, nil
+}
+
+// returns string representation and skips "zero" time values. It also checks if nanoseconds and TZ exist.
+func (xdt XSDDateTime) string() string {
+	if !xdt.innerTime.IsZero() {
+		dateTimeLayout := time.RFC3339Nano
+		if xdt.innerTime.Nanosecond() == 0 {
+			dateTimeLayout = time.RFC3339
+		}
+		dtString := xdt.innerTime.Format(dateTimeLayout)
+		if !xdt.hasTz {
+			// split off time portion
+			dateAndTime := strings.SplitN(dtString, "T", 2)
+			toks := strings.SplitN(dateAndTime[1], "Z", 2)
+			toks = strings.SplitN(toks[0], "+", 2)
+			toks = strings.SplitN(toks[0], "-", 2)
+			dtString = dateAndTime[0] + "T" + toks[0]
+		}
+		return dtString
+	}
+	return ""
+}
+
+// UnmarshalXML implements xml.Unmarshaler on XSDDateTime to use time.RFC3339Nano
+func (xdt *XSDDateTime) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
+	var content string
+	err := d.DecodeElement(&content, &start)
+	if err != nil {
+		return err
+	}
+	xdt.innerTime, xdt.hasTz, err = fromString(content, time.RFC3339Nano)
+	return err
+}
+
+// UnmarshalXMLAttr implements xml.UnmarshalerAttr on XSDDateTime to use time.RFC3339Nano
+func (xdt *XSDDateTime) UnmarshalXMLAttr(attr xml.Attr) error {
+	var err error
+	xdt.innerTime, xdt.hasTz, err = fromString(attr.Value, time.RFC3339Nano)
+	return err
+}
+
+func fromString(content string, format string) (time.Time, bool, error) {
+	var t time.Time
+	if content == "" {
+		return t, true, nil
+	}
+	hasTz := false
+	if strings.Contains(content, "T") { // check if we have a time portion
+		// split into date and time portion
+		dateAndTime := strings.SplitN(content, "T", 2)
+		if len(dateAndTime) > 1 {
+			if strings.Contains(dateAndTime[1], "Z") ||
+				strings.Contains(dateAndTime[1], "+") ||
+				strings.Contains(dateAndTime[1], "-") {
+				hasTz = true
+			}
+		}
+		if !hasTz {
+			content += "Z"
+		}
+		if content == "0001-01-01T00:00:00Z" {
+			return t, true, nil
+		}
+	} else {
+		// we don't see to have a time portion, check timezone
+		if strings.Contains(content, "Z") ||
+			strings.Contains(content, ":") {
+			hasTz = true
+		}
+		if !hasTz {
+			content += "Z"
+		}
+	}
+	t, err := time.Parse(format, content)
+	return t, hasTz, err
+}
+
+// CreateXsdDateTime creates an object represent xsd:datetime object in Golang
+func CreateXsdDateTime(dt time.Time, hasTz bool) XSDDateTime {
+	return XSDDateTime{
+		innerTime: dt,
+		hasTz:     hasTz,
+	}
+}
+
+// XSDDate is a type for representing xsd:date in Golang
+type XSDDate struct {
+	innerDate time.Time
+	hasTz     bool
+}
+
+// StripTz removes the TZ information from the date
+func (xd *XSDDate) StripTz() {
+	xd.hasTz = false
+}
+
+// ToGoTime converts the date to Golang time.Time by checking if a TZ is specified.
+// If there is a TZ, that TZ is used, otherwise local TZ is used
+func (xd *XSDDate) ToGoTime() time.Time {
+	if xd.hasTz {
+		return xd.innerDate
+	}
+	return time.Date(xd.innerDate.Year(), xd.innerDate.Month(), xd.innerDate.Day(),
+		0, 0, 0, 0, time.Local)
+}
+
+// MarshalXML implementation on XSDDate
+func (xd XSDDate) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+	xdtString := xd.string()
+	if xdtString != "" {
+		return e.EncodeElement(xdtString, start)
+	}
+	return nil
+}
+
+// MarshalXMLAttr implementation on XSDDate
+func (xd XSDDate) MarshalXMLAttr(name xml.Name) (xml.Attr, error) {
+	xdString := xd.string()
+	attr := xml.Attr{}
+	if xdString != "" {
+		attr.Name = name
+		attr.Value = xdString
+	}
+	return attr, nil
+}
+
+// returns string representation and skips "zero" time values
+func (xd XSDDate) string() string {
+	if !xd.innerDate.IsZero() {
+		dateString := xd.innerDate.Format(dateLayout) // serialize with TZ
+		if !xd.hasTz {
+			if strings.Contains(dateString, "Z") {
+				// UTC Tz
+				toks := strings.SplitN(dateString, "Z", 2)
+				dateString = toks[0]
+			} else {
+				// [+-]00:00 Tz, remove last 6 chars
+				if len(dateString) > 5 { // this should always be true
+					start := len(dateString) - 6 // locate at "-"
+					dateString = dateString[0:start]
+				}
+			}
+		}
+		return dateString
+	}
+	return ""
+}
+
+// UnmarshalXML implements xml.Unmarshaler on XSDDate to use dateLayout
+func (xd *XSDDate) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
+	var content string
+	err := d.DecodeElement(&content, &start)
+	if err != nil {
+		return err
+	}
+	xd.innerDate, xd.hasTz, err = fromString(content, dateLayout)
+	return err
+}
+
+// UnmarshalXMLAttr implements xml.UnmarshalerAttr on XSDDate to use dateLayout
+func (xd *XSDDate) UnmarshalXMLAttr(attr xml.Attr) error {
+	var err error
+	xd.innerDate, xd.hasTz, err = fromString(attr.Value, dateLayout)
+	return err
+}
+
+// CreateXsdDate creates an object represent xsd:datetime object in Golang
+func CreateXsdDate(date time.Time, hasTz bool) XSDDate {
+	return XSDDate{
+		innerDate: date,
+		hasTz:     hasTz,
+	}
+}
+
+// XSDTime is a type for representing xsd:time
+type XSDTime struct {
+	innerTime time.Time
+	hasTz     bool
+}
+
+// MarshalXML implements xml.Marshaler on XSDTime
+func (xt XSDTime) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+	xdtString := xt.string()
+	if xdtString != "" {
+		return e.EncodeElement(xdtString, start)
+	}
+	return nil
+}
+
+// MarshalXMLAttr implements xml.MarshalerAttr on XSDTime
+func (xt XSDTime) MarshalXMLAttr(name xml.Name) (xml.Attr, error) {
+	xdString := xt.string()
+	attr := xml.Attr{}
+	if xdString != "" {
+		attr.Name = name
+		attr.Value = xdString
+	}
+	return attr, nil
+}
+
+// returns string representation and skips "zero" time values
+func (xt XSDTime) string() string {
+	if !xt.innerTime.IsZero() {
+		dateTimeLayout := time.RFC3339Nano
+		if xt.innerTime.Nanosecond() == 0 {
+			dateTimeLayout = time.RFC3339
+		}
+		// split off date portion
+		dateAndTime := strings.SplitN(xt.innerTime.Format(dateTimeLayout), "T", 2)
+		timeString := dateAndTime[1]
+		if !xt.hasTz {
+			toks := strings.SplitN(timeString, "Z", 2)
+			toks = strings.SplitN(toks[0], "+", 2)
+			toks = strings.SplitN(toks[0], "-", 2)
+			timeString = toks[0]
+		}
+		return timeString
+	}
+	return ""
+}
+
+// UnmarshalXML implements xml.Unmarshaler on XSDTime to use dateTimeLayout
+func (xt *XSDTime) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
+	var err error
+	var content string
+	err = d.DecodeElement(&content, &start)
+	if err != nil {
+		return err
+	}
+	return xt.fromString(content)
+}
+
+// UnmarshalXMLAttr implements xml.UnmarshalerAttr on XSDTime to use dateTimeLayout
+func (xt *XSDTime) UnmarshalXMLAttr(attr xml.Attr) error {
+	return xt.fromString(attr.Value)
+}
+
+func (xt *XSDTime) fromString(content string) error {
+	var t time.Time
+	var err error
+	if content == "" {
+		xt.innerTime = t
+		return nil
+	}
+	xt.hasTz = false
+	if strings.Contains(content, "Z") ||
+		strings.Contains(content, "+") ||
+		strings.Contains(content, "-") {
+		xt.hasTz = true
+	}
+	if !xt.hasTz {
+		content += "Z"
+	}
+	xt.innerTime, err = time.Parse(timeLayout, content)
+	return err
+}
+
+// Hour returns hour of the xsd:time
+func (xt XSDTime) Hour() int {
+	return xt.innerTime.Hour()
+}
+
+// Minute returns minutes of the xsd:time
+func (xt XSDTime) Minute() int {
+	return xt.innerTime.Minute()
+}
+
+// Second returns seconds of the xsd:time
+func (xt XSDTime) Second() int {
+	return xt.innerTime.Second()
+}
+
+// Nanosecond returns nanosecond of the xsd:time
+func (xt XSDTime) Nanosecond() int {
+	return xt.innerTime.Nanosecond()
+}
+
+// Location returns the TZ information of the xsd:time
+func (xt XSDTime) Location() *time.Location {
+	if xt.hasTz {
+		return xt.innerTime.Location()
+	}
+	return nil
+}
+
+// CreateXsdTime creates an object representing xsd:time in Golang
+func CreateXsdTime(hour int, min int, sec int, nsec int, loc *time.Location) XSDTime {
+	realLoc := loc
+	if loc == nil {
+		realLoc = time.Local
+	}
+	return XSDTime{
+		innerTime: time.Date(1951, 10, 22, hour, min, sec, nsec, realLoc),
+		hasTz:     loc != nil,
+	}
+}