Browse Source

Initial commit

Alexey Kim 9 months ago
commit
710a6b0ea1
10 changed files with 470 additions and 0 deletions
  1. 1 0
      .gitignore
  2. 28 0
      Makefile
  3. 9 0
      action.go
  4. 155 0
      client.go
  5. 77 0
      client_test.go
  6. 13 0
      go.mod
  7. 8 0
      go.sum
  8. 9 0
      iclient.go
  9. 12 0
      result.go
  10. 158 0
      result_easyjson.go

+ 1 - 0
.gitignore

@@ -0,0 +1 @@
+.idea

+ 28 - 0
Makefile

@@ -0,0 +1,28 @@
+.DEFAULT_GOAL := all
+.DELETE_ON_ERROR:
+.SUFFIXES:
+
+.PHONY: all
+all: deps fmt gen-json test
+
+.PHONY: deps
+deps:
+	-@echo "-> $@"
+	go mod tidy
+
+.PHONY: fmt
+fmt:
+	-@echo "-> $@"
+	-go fmt $(shell go list ./...)
+
+.PHONY: gen-json
+gen-json:
+	-@echo "-> $@"
+	-@echo "=== Generating easyJson bindings"
+	easyjson result.go
+
+.PHONY: test
+test:
+	-@echo "-> $@"
+	go test $(shell go list ./...)
+	-go vet $(shell go list ./...)

+ 9 - 0
action.go

@@ -0,0 +1,9 @@
+package webmail
+
+type Action string
+
+const (
+	CreateAlias Action = "add"
+	RemoveAlias Action = "delete"
+	ListAliases Action = "select"
+)

+ 155 - 0
client.go

@@ -0,0 +1,155 @@
+package webmail
+
+import (
+	"compress/gzip"
+	"compress/zlib"
+	"fmt"
+	"github.com/mailru/easyjson"
+	"golang.org/x/net/html/charset"
+	"io"
+	"net/http"
+	"net/url"
+	"strings"
+	"time"
+)
+
+type client struct {
+	http     *http.Client
+	endpoint string
+}
+
+func NewClient(endpoint string, timeout time.Duration) Client {
+	t := http.DefaultTransport.(*http.Transport)
+	t.Proxy = http.ProxyFromEnvironment
+
+	cli := &client{
+		endpoint: endpoint,
+		http: &http.Client{
+			Transport: t,
+			Timeout:   timeout,
+		},
+	}
+	return cli
+}
+
+func (cli *client) Close() error {
+	return nil
+}
+
+func (cli *client) CreateAlias(alias string) error {
+	return cli.action(CreateAlias, alias)
+}
+
+func (cli *client) RemoveAlias(alias string) error {
+	return cli.action(RemoveAlias, alias)
+}
+
+func (cli *client) action(e Action, alias string) error {
+	alias = strings.TrimSpace(alias)
+	if alias == "" {
+		return fmt.Errorf("illegal arguments: alias must not be an empty string")
+	}
+
+	var (
+		d      []byte
+		result = new(Result)
+		err    error
+	)
+
+	if d, err = cli.do(e, alias); err != nil {
+		return err
+	}
+
+	if err = easyjson.Unmarshal(d, result); err != nil {
+		return err
+	}
+
+	var errNo int64
+	if errNo, err = result.Error.Int64(); err != nil {
+		return err
+	}
+
+	if result.Ok != "ok" || errNo > 0 {
+		return fmt.Errorf("got unexpected result: `%s`", string(d))
+	}
+
+	return nil
+}
+
+func (cli *client) ListAliases() ([]string, error) {
+	var (
+		d      []byte
+		result = ResultList{}
+		err    error
+	)
+
+	if d, err = cli.do(ListAliases, ""); err != nil {
+		return nil, err
+	}
+
+	if err = easyjson.Unmarshal(d, &result); err != nil {
+		return nil, err
+	}
+
+	return result, nil
+}
+
+func (cli *client) do(action Action, alias string) ([]byte, error) {
+	var (
+		request  *http.Request
+		response *http.Response
+		err      error
+	)
+
+	values := url.Values{}
+	values.Set("act", string(action))
+	if ListAliases != action {
+		values.Set("alias", alias)
+	}
+
+	if request, err = http.NewRequest(http.MethodPost, cli.endpoint, strings.NewReader(values.Encode())); err != nil {
+		return nil, err
+	}
+
+	request.Header.Add("Content-Type", "application/x-www-form-urlencoded")
+	if response, err = cli.http.Do(request); err != nil {
+		return nil, err
+	}
+
+	if response.StatusCode < 200 || response.StatusCode > 299 {
+		return nil, fmt.Errorf("got unexpected http status code [%d] %s",
+			response.StatusCode, response.Status)
+	}
+
+	if response.ContentLength != -1 && response.ContentLength < 1 {
+		return nil, nil
+	}
+
+	var (
+		contentEncoding           = response.Header.Get("Content-Encoding")
+		contentType               = response.Header.Get("Content-Type")
+		contentReader   io.Reader = response.Body
+	)
+
+	if "gzip" == contentEncoding {
+		if r, err := gzip.NewReader(response.Body); err == nil {
+			defer r.Close()
+			contentReader = r
+		}
+	} else if "deflate" == contentEncoding {
+		if r, err := zlib.NewReader(response.Body); err == nil {
+			defer r.Close()
+			contentReader = r
+		}
+	}
+
+	if "application/octet-stream" != contentType {
+		if r, err := charset.NewReader(contentReader, contentType); err == nil {
+			contentReader = r
+		} else {
+			return nil, err
+		}
+	}
+
+	return io.ReadAll(contentReader)
+}

+ 77 - 0
client_test.go

@@ -0,0 +1,77 @@
+package webmail
+
+import (
+	"reflect"
+	"testing"
+	"time"
+)
+
+var (
+	cli = NewClient("http://api.izene.org/winmail/mailalias.php", 60*time.Second)
+)
+
+func Test_client_CreateAlias(t *testing.T) {
+	tests := []struct {
+		alias   string
+		wantErr bool
+	}{
+		{
+			alias:   "gl0001",
+			wantErr: false,
+		},
+	}
+	for _, tt := range tests {
+		t.Run("CreateAlias", func(t *testing.T) {
+			if err := cli.CreateAlias(tt.alias); (err != nil) != tt.wantErr {
+				t.Errorf("CreateAlias() error = %v, wantErr %v", err, tt.wantErr)
+			}
+		})
+	}
+}
+
+func Test_client_ListAliases(t *testing.T) {
+	tests := []struct {
+		want    []string
+		wantErr bool
+	}{
+		{
+			want: []string{
+				"bf111", "bf123", "bfbfbf", "support002", "support003", "support004", "test", "test1",
+			},
+			wantErr: false,
+		},
+	}
+	for _, tt := range tests {
+		t.Run("ListAliases", func(t *testing.T) {
+			got, err := cli.ListAliases()
+
+			if (err != nil) != tt.wantErr {
+				t.Errorf("ListAliases() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+
+			if !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("ListAliases() got = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func Test_client_RemoveAlias(t *testing.T) {
+	tests := []struct {
+		alias   string
+		wantErr bool
+	}{
+		{
+			alias:   "gl0001",
+			wantErr: false,
+		},
+	}
+	for _, tt := range tests {
+		t.Run("RemoveAlias", func(t *testing.T) {
+			if err := cli.RemoveAlias(tt.alias); (err != nil) != tt.wantErr {
+				t.Errorf("RemoveAlias() error = %v, wantErr %v", err, tt.wantErr)
+			}
+		})
+	}
+}

+ 13 - 0
go.mod

@@ -0,0 +1,13 @@
+module gitlab.izene.com/gonglao/webmail
+
+go 1.20
+
+require (
+	github.com/mailru/easyjson v0.7.7
+	golang.org/x/net v0.14.0
+)
+
+require (
+	github.com/josharian/intern v1.0.0 // indirect
+	golang.org/x/text v0.12.0 // indirect
+)

+ 8 - 0
go.sum

@@ -0,0 +1,8 @@
+github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
+github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
+github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
+github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
+golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
+golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
+golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
+golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=

+ 9 - 0
iclient.go

@@ -0,0 +1,9 @@
+package webmail
+
+type Client interface {
+	Close() error
+
+	CreateAlias(alias string) error
+	RemoveAlias(alias string) error
+	ListAliases() ([]string, error)
+}

+ 12 - 0
result.go

@@ -0,0 +1,12 @@
+package webmail
+
+import "encoding/json"
+
+//easyjson:json
+type Result struct {
+	Ok    string      `json:"result" yaml:"result"`
+	Error json.Number `json:"errno" yaml:"errno"`
+}
+
+//easyjson:json
+type ResultList []string

+ 158 - 0
result_easyjson.go

@@ -0,0 +1,158 @@
+// Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT.
+
+package webmail
+
+import (
+	json "encoding/json"
+	easyjson "github.com/mailru/easyjson"
+	jlexer "github.com/mailru/easyjson/jlexer"
+	jwriter "github.com/mailru/easyjson/jwriter"
+)
+
+// suppress unused package warning
+var (
+	_ *json.RawMessage
+	_ *jlexer.Lexer
+	_ *jwriter.Writer
+	_ easyjson.Marshaler
+)
+
+func easyjsonD3b49167DecodeGitlabIzeneComGonglaoWebmail(in *jlexer.Lexer, out *ResultList) {
+	isTopLevel := in.IsStart()
+	if in.IsNull() {
+		in.Skip()
+		*out = nil
+	} else {
+		in.Delim('[')
+		if *out == nil {
+			if !in.IsDelim(']') {
+				*out = make(ResultList, 0, 4)
+			} else {
+				*out = ResultList{}
+			}
+		} else {
+			*out = (*out)[:0]
+		}
+		for !in.IsDelim(']') {
+			var v1 string
+			v1 = string(in.String())
+			*out = append(*out, v1)
+			in.WantComma()
+		}
+		in.Delim(']')
+	}
+	if isTopLevel {
+		in.Consumed()
+	}
+}
+func easyjsonD3b49167EncodeGitlabIzeneComGonglaoWebmail(out *jwriter.Writer, in ResultList) {
+	if in == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 {
+		out.RawString("null")
+	} else {
+		out.RawByte('[')
+		for v2, v3 := range in {
+			if v2 > 0 {
+				out.RawByte(',')
+			}
+			out.String(string(v3))
+		}
+		out.RawByte(']')
+	}
+}
+
+// MarshalJSON supports json.Marshaler interface
+func (v ResultList) MarshalJSON() ([]byte, error) {
+	w := jwriter.Writer{}
+	easyjsonD3b49167EncodeGitlabIzeneComGonglaoWebmail(&w, v)
+	return w.Buffer.BuildBytes(), w.Error
+}
+
+// MarshalEasyJSON supports easyjson.Marshaler interface
+func (v ResultList) MarshalEasyJSON(w *jwriter.Writer) {
+	easyjsonD3b49167EncodeGitlabIzeneComGonglaoWebmail(w, v)
+}
+
+// UnmarshalJSON supports json.Unmarshaler interface
+func (v *ResultList) UnmarshalJSON(data []byte) error {
+	r := jlexer.Lexer{Data: data}
+	easyjsonD3b49167DecodeGitlabIzeneComGonglaoWebmail(&r, v)
+	return r.Error()
+}
+
+// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
+func (v *ResultList) UnmarshalEasyJSON(l *jlexer.Lexer) {
+	easyjsonD3b49167DecodeGitlabIzeneComGonglaoWebmail(l, v)
+}
+func easyjsonD3b49167DecodeGitlabIzeneComGonglaoWebmail1(in *jlexer.Lexer, out *Result) {
+	isTopLevel := in.IsStart()
+	if in.IsNull() {
+		if isTopLevel {
+			in.Consumed()
+		}
+		in.Skip()
+		return
+	}
+	in.Delim('{')
+	for !in.IsDelim('}') {
+		key := in.UnsafeFieldName(false)
+		in.WantColon()
+		if in.IsNull() {
+			in.Skip()
+			in.WantComma()
+			continue
+		}
+		switch key {
+		case "result":
+			out.Ok = string(in.String())
+		case "errno":
+			out.Error = in.JsonNumber()
+		default:
+			in.SkipRecursive()
+		}
+		in.WantComma()
+	}
+	in.Delim('}')
+	if isTopLevel {
+		in.Consumed()
+	}
+}
+func easyjsonD3b49167EncodeGitlabIzeneComGonglaoWebmail1(out *jwriter.Writer, in Result) {
+	out.RawByte('{')
+	first := true
+	_ = first
+	{
+		const prefix string = ",\"result\":"
+		out.RawString(prefix[1:])
+		out.String(string(in.Ok))
+	}
+	{
+		const prefix string = ",\"errno\":"
+		out.RawString(prefix)
+		out.String(string(in.Error))
+	}
+	out.RawByte('}')
+}
+
+// MarshalJSON supports json.Marshaler interface
+func (v Result) MarshalJSON() ([]byte, error) {
+	w := jwriter.Writer{}
+	easyjsonD3b49167EncodeGitlabIzeneComGonglaoWebmail1(&w, v)
+	return w.Buffer.BuildBytes(), w.Error
+}
+
+// MarshalEasyJSON supports easyjson.Marshaler interface
+func (v Result) MarshalEasyJSON(w *jwriter.Writer) {
+	easyjsonD3b49167EncodeGitlabIzeneComGonglaoWebmail1(w, v)
+}
+
+// UnmarshalJSON supports json.Unmarshaler interface
+func (v *Result) UnmarshalJSON(data []byte) error {
+	r := jlexer.Lexer{Data: data}
+	easyjsonD3b49167DecodeGitlabIzeneComGonglaoWebmail1(&r, v)
+	return r.Error()
+}
+
+// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
+func (v *Result) UnmarshalEasyJSON(l *jlexer.Lexer) {
+	easyjsonD3b49167DecodeGitlabIzeneComGonglaoWebmail1(l, v)
+}