From d46665edc9fe4b0c1949d2ad73b4c91b8d787e46 Mon Sep 17 00:00:00 2001 From: Dmitry Date: Tue, 18 Jun 2019 12:09:10 +0300 Subject: [PATCH] Add simple API to send and read messages Prepare docker image for tests Use make build in dockerfile Add mailservers to transport object Add readme with example how to use API Forgot to add a stream for contact Rewrite correct variable Apply suggestions --- .dockerignore | 2 ++ API.md | 53 +++++++++++++++++++++++++++++++ Dockerfile | 23 ++++++++++++++ Makefile | 16 ++++++++-- _assets/api.conf | 6 ++++ protocol/client/messenger.go | 3 +- protocol/gethservice/api.go | 61 ++++++++++++++++++++++++++++++++++-- 7 files changed, 158 insertions(+), 6 deletions(-) create mode 100644 .dockerignore create mode 100644 API.md create mode 100644 Dockerfile create mode 100644 _assets/api.conf diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..83cb547c --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +.git +bin/ diff --git a/API.md b/API.md new file mode 100644 index 00000000..64c9aac8 --- /dev/null +++ b/API.md @@ -0,0 +1,53 @@ +RPC API +====== +Start service with configuration file that enables API: + +``` +./bin/status-term-client -node-config ./_assets/api.conf -no-ui --keyhex=0x123 +``` + +Configuration example: + +```json +{ + "HTTPHost": "localhost", + "HTTPPort": 8777, + "HTTPEnabled": true, + "APIModules": "ssm" +} +``` + +Add contact +=========== + +``` +curl -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"ssm_addContact","params":[{"name": "222"}],"id":1}' http://localhost:8777 +``` + + +Returns error if failed otherwise null. + +Send to contact +=============== + +``` +curl -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"ssm_sendToContact","params":[{"name": "222"}, "plain text"],"id":1}' http://localhost:8777 +``` + +Returns error if failed otherwise null. + +Read all (including ours) from contact +====================================== + + +To read all messages use 0 as offset: + +``` +curl -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"ssm_readContactMessages","params":[{"name": "222"}, 0],"id":1}' http://localhost:8777 +``` + +To read new messages use last known offset: + +``` +curl -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"ssm_readContactMessages","params":[{"name": "222"}, 2],"id":1}' http://localhost:8777 +``` diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..897d13f4 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,23 @@ +# Build status-go in a Go builder container +FROM golang:1.12.5-alpine as builder + +RUN apk add --no-cache make gcc musl-dev linux-headers + +RUN mkdir -p /go/src/github.com/status-im/status-console-client +ADD . /go/src/github.com/status-im/status-console-client +WORKDIR /go/src/github.com/status-im/status-console-client +RUN make build + +# Copy the binary to the second image +FROM alpine:latest + +LABEL maintainer="support@status.im" +LABEL source="https://github.com/status-im/status-console-client" + +COPY --from=builder /go/src/github.com/status-im/status-console-client/bin/status-term-client /usr/local/bin/ + +# 30304 is used for Discovery v5 +EXPOSE 8080 8545 30303 30303/udp 30304/udp + +ENTRYPOINT ["/usr/local/bin/status-term-client", "-no-ui"] +CMD ["--help"] \ No newline at end of file diff --git a/Makefile b/Makefile index 4e24f321..c66f2399 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,16 @@ -SHELL := /bin/bash - GO111MODULE = on +ENABLE_METRICS ?= true +BUILD_FLAGS ?= $(shell echo "-ldflags '\ + -X github.com/status-im/status-console-client/vendor/github.com/ethereum/go-ethereum/metrics.EnabledStr=$(ENABLE_METRICS)'") + + +DOCKER_IMAGE_NAME ?= statusteam/status-client +DOCKER_CUSTOM_TAG ?= $(shell git rev-parse --short HEAD) + build: GOFLAGS ?= "-mod=vendor" build: - GOFLAGS=$(GOFLAGS) go build -o ./bin/status-term-client . + GOFLAGS=$(GOFLAGS) go build $(BUILD_FLAGS) -o ./bin/status-term-client . .PHONY: build run: ARGS ?= @@ -58,3 +64,7 @@ mock: gen-migrations: pushd protocol/client/migrations/ && rm -f bindata.go && go-bindata -pkg migrations ./ && popd .PHONY: gen-migrations + +image: + docker build . -t $(DOCKER_IMAGE_NAME):latest -t $(DOCKER_IMAGE_NAME):$(DOCKER_CUSTOM_TAG) +.PHONY: image diff --git a/_assets/api.conf b/_assets/api.conf new file mode 100644 index 00000000..b2868175 --- /dev/null +++ b/_assets/api.conf @@ -0,0 +1,6 @@ +{ + "HTTPHost": "localhost", + "HTTPPort": 8777, + "HTTPEnabled": true, + "APIModules": "ssm" +} \ No newline at end of file diff --git a/protocol/client/messenger.go b/protocol/client/messenger.go index ed8e1c0d..61bb396b 100644 --- a/protocol/client/messenger.go +++ b/protocol/client/messenger.go @@ -141,7 +141,8 @@ func (m *Messenger) Join(ctx context.Context, c Contact) error { } opts := protocol.DefaultRequestOptions() - if err := m.Request(ctx, c, opts); err != nil { + // NOTE(dshulyak) join ctx shouldn't have an impact on history timeout. + if err := m.Request(context.Background(), c, opts); err != nil { return err } return m.db.UpdateHistories([]History{{Contact: c, Synced: opts.To}}) diff --git a/protocol/gethservice/api.go b/protocol/gethservice/api.go index c88568e4..59ab0ffe 100644 --- a/protocol/gethservice/api.go +++ b/protocol/gethservice/api.go @@ -47,8 +47,8 @@ type RequestParams struct { // Contact type Contact struct { - Name string `json:"name"` - Type string `json:"type"` + Name string `json:"name"` + PublicKey hexutil.Bytes `json:"key"` } // PublicAPI provides an JSON-RPC API to interact with @@ -244,6 +244,63 @@ func (api *PublicAPI) RequestAll(ctx context.Context, newest bool) error { return api.service.messenger.RequestAll(ctx, newest) } +// AddContact will ensure that contact is added to messenger database and new stream spawned for a contact if needed. +func (api *PublicAPI) AddContact(ctx context.Context, contact Contact) (err error) { + if api.service.messenger == nil { + return ErrMessengerNotSet + } + var c client.Contact + if len(contact.PublicKey) != 0 { + c, err = client.CreateContactPrivate(contact.Name, contact.PublicKey.String(), client.ContactAdded) + if err != nil { + return err + } + } else { + c = client.CreateContactPublicRoom(contact.Name, client.ContactAdded) + } + err = api.service.messenger.AddContact(c) + if err != nil { + return err + } + return api.service.messenger.Join(ctx, c) +} + +// SendToContact send payload to specified contact. Contact should be added before sending message, otherwise error will +// be received. +func (api *PublicAPI) SendToContact(ctx context.Context, contact Contact, payload string) (err error) { + if api.service.messenger == nil { + return ErrMessengerNotSet + } + var c client.Contact + if len(contact.PublicKey) != 0 { + c, err = client.CreateContactPrivate(contact.Name, contact.PublicKey.String(), client.ContactAdded) + if err != nil { + return err + } + } else { + c = client.CreateContactPublicRoom(contact.Name, client.ContactAdded) + } + return api.service.messenger.Send(c, []byte(payload)) +} + +// ReadContactMessages read contact messages starting from offset. +// To read all offset should be zero. To read only new set offset to total number of previously read messages. +func (api *PublicAPI) ReadContactMessages(ctx context.Context, contact Contact, offset int64) (rst []*protocol.Message, err error) { + if api.service.messenger == nil { + return nil, ErrMessengerNotSet + } + var c client.Contact + if len(contact.PublicKey) != 0 { + c, err = client.CreateContactPrivate(contact.Name, contact.PublicKey.String(), client.ContactAdded) + if err != nil { + return nil, err + } + } else { + c = client.CreateContactPublicRoom(contact.Name, client.ContactAdded) + } + return api.service.messenger.Messages(c, offset) +} + func unmarshalPubKey(b hexutil.Bytes) (*ecdsa.PublicKey, error) { if len(b) == 0 { return nil, nil