query.go 8.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// Copyright 2017 Vector Creations Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

15 16 17 18 19 20 21
package api

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
22 23

	"github.com/matrix-org/gomatrixserverlib"
24 25 26 27
)

// QueryLatestEventsAndStateRequest is a request to QueryLatestEventsAndState
type QueryLatestEventsAndStateRequest struct {
28
	// The room ID to query the latest events for.
29
	RoomID string `json:"room_id"`
30 31
	// The state key tuples to fetch from the room current state.
	// If this list is empty or nil then no state events are returned.
32
	StateToFetch []gomatrixserverlib.StateKeyTuple `json:"state_to_fetch"`
33 34 35
}

// QueryLatestEventsAndStateResponse is a response to QueryLatestEventsAndState
36 37
// This is used when sending events to set the prev_events, auth_events and depth.
// It is also used to tell whether the event is allowed by the event auth rules.
38 39 40 41 42
type QueryLatestEventsAndStateResponse struct {
	// Copy of the request for debugging.
	QueryLatestEventsAndStateRequest
	// Does the room exist?
	// If the room doesn't exist this will be false and LatestEvents will be empty.
43
	RoomExists bool `json:"room_exists"`
44
	// The latest events in the room.
45
	// These are used to set the prev_events when sending an event.
46
	LatestEvents []gomatrixserverlib.EventReference `json:"latest_events"`
47
	// The state events requested.
48
	// This list will be in an arbitrary order.
49 50
	// These are used to set the auth_events when sending an event.
	// These are used to check whether the event is allowed.
51
	StateEvents []gomatrixserverlib.Event `json:"state_events"`
52 53 54
	// The depth of the latest events.
	// This is one greater than the maximum depth of the latest events.
	// This is used to set the depth when sending an event.
55
	Depth int64 `json:"depth"`
56 57
}

58 59 60
// QueryStateAfterEventsRequest is a request to QueryStateAfterEvents
type QueryStateAfterEventsRequest struct {
	// The room ID to query the state in.
61
	RoomID string `json:"room_id"`
62
	// The list of previous events to return the events after.
63
	PrevEventIDs []string `json:"prev_event_ids"`
64
	// The state key tuples to fetch from the state
65
	StateToFetch []gomatrixserverlib.StateKeyTuple `json:"state_to_fetch"`
66 67 68 69 70 71 72 73
}

// QueryStateAfterEventsResponse is a response to QueryStateAfterEvents
type QueryStateAfterEventsResponse struct {
	// Copy of the request for debugging.
	QueryStateAfterEventsRequest
	// Does the room exist on this roomserver?
	// If the room doesn't exist this will be false and StateEvents will be empty.
74
	RoomExists bool `json:"room_exists"`
75 76
	// Do all the previous events exist on this roomserver?
	// If some of previous events do not exist this will be false and StateEvents will be empty.
77
	PrevEventsExist bool `json:"prev_events_exist"`
78
	// The state events requested.
79
	// This list will be in an arbitrary order.
80
	StateEvents []gomatrixserverlib.Event `json:"state_events"`
81 82
}

83 84 85
// QueryEventsByIDRequest is a request to QueryEventsByID
type QueryEventsByIDRequest struct {
	// The event IDs to look up.
86
	EventIDs []string `json:"event_ids"`
87 88 89 90 91 92 93 94 95 96 97 98 99
}

// QueryEventsByIDResponse is a response to QueryEventsByID
type QueryEventsByIDResponse struct {
	// Copy of the request for debugging.
	QueryEventsByIDRequest
	// A list of events with the requested IDs.
	// If the roomserver does not have a copy of a requested event
	// then it will omit that event from the list.
	// If the roomserver thinks it has a copy of the event, but
	// fails to read it from the database then it will fail
	// the entire request.
	// This list will be in an arbitrary order.
100
	Events []gomatrixserverlib.Event `json:"events"`
101 102
}

103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
// QueryMembershipsForRoomRequest is a request to QueryMembershipsForRoom
type QueryMembershipsForRoomRequest struct {
	// ID of the room to fetch memberships from
	RoomID string `json:"room_id"`
	// ID of the user sending the request
	Sender string `json:"sender"`
}

// QueryMembershipsForRoomResponse is a response to QueryMembershipsForRoom
type QueryMembershipsForRoomResponse struct {
	// The "m.room.member" events (of "join" membership) in the client format
	JoinEvents []gomatrixserverlib.ClientEvent `json:"join_events"`
	// True if the user has been in room before and has either stayed in it or
	// left it.
	HasBeenInRoom bool `json:"has_been_in_room"`
}

120 121 122 123 124 125 126
// RoomserverQueryAPI is used to query information from the room server.
type RoomserverQueryAPI interface {
	// Query the latest events and state for a room from the room server.
	QueryLatestEventsAndState(
		request *QueryLatestEventsAndStateRequest,
		response *QueryLatestEventsAndStateResponse,
	) error
127 128 129 130 131 132

	// Query the state after a list of events in a room from the room server.
	QueryStateAfterEvents(
		request *QueryStateAfterEventsRequest,
		response *QueryStateAfterEventsResponse,
	) error
133 134 135 136 137 138

	// Query a list of events by event ID.
	QueryEventsByID(
		request *QueryEventsByIDRequest,
		response *QueryEventsByIDResponse,
	) error
139 140 141 142 143 144

	// Query a list of membership events for a room
	QueryMembershipsForRoom(
		request *QueryMembershipsForRoomRequest,
		response *QueryMembershipsForRoomResponse,
	) error
145 146 147
}

// RoomserverQueryLatestEventsAndStatePath is the HTTP path for the QueryLatestEventsAndState API.
148
const RoomserverQueryLatestEventsAndStatePath = "/api/roomserver/queryLatestEventsAndState"
149

150
// RoomserverQueryStateAfterEventsPath is the HTTP path for the QueryStateAfterEvents API.
151
const RoomserverQueryStateAfterEventsPath = "/api/roomserver/queryStateAfterEvents"
152

153
// RoomserverQueryEventsByIDPath is the HTTP path for the QueryEventsByID API.
154
const RoomserverQueryEventsByIDPath = "/api/roomserver/queryEventsByID"
155

156 157 158
// RoomserverQueryMembershipsForRoomPath is the HTTP path for the QueryMembershipsForRoom API
const RoomserverQueryMembershipsForRoomPath = "/api/roomserver/queryMembershipsForRoom"

159 160 161 162 163 164
// NewRoomserverQueryAPIHTTP creates a RoomserverQueryAPI implemented by talking to a HTTP POST API.
// If httpClient is nil then it uses the http.DefaultClient
func NewRoomserverQueryAPIHTTP(roomserverURL string, httpClient *http.Client) RoomserverQueryAPI {
	if httpClient == nil {
		httpClient = http.DefaultClient
	}
165
	return &httpRoomserverQueryAPI{roomserverURL, httpClient}
166 167 168 169
}

type httpRoomserverQueryAPI struct {
	roomserverURL string
170
	httpClient    *http.Client
171 172 173 174 175 176 177 178 179 180 181
}

// QueryLatestEventsAndState implements RoomserverQueryAPI
func (h *httpRoomserverQueryAPI) QueryLatestEventsAndState(
	request *QueryLatestEventsAndStateRequest,
	response *QueryLatestEventsAndStateResponse,
) error {
	apiURL := h.roomserverURL + RoomserverQueryLatestEventsAndStatePath
	return postJSON(h.httpClient, apiURL, request, response)
}

182 183 184 185 186 187 188 189 190
// QueryStateAfterEvents implements RoomserverQueryAPI
func (h *httpRoomserverQueryAPI) QueryStateAfterEvents(
	request *QueryStateAfterEventsRequest,
	response *QueryStateAfterEventsResponse,
) error {
	apiURL := h.roomserverURL + RoomserverQueryStateAfterEventsPath
	return postJSON(h.httpClient, apiURL, request, response)
}

191 192 193 194 195 196 197 198 199
// QueryEventsByID implements RoomserverQueryAPI
func (h *httpRoomserverQueryAPI) QueryEventsByID(
	request *QueryEventsByIDRequest,
	response *QueryEventsByIDResponse,
) error {
	apiURL := h.roomserverURL + RoomserverQueryEventsByIDPath
	return postJSON(h.httpClient, apiURL, request, response)
}

200 201 202 203 204 205 206 207 208
// QueryMembershipsForRoom implements RoomserverQueryAPI
func (h *httpRoomserverQueryAPI) QueryMembershipsForRoom(
	request *QueryMembershipsForRoomRequest,
	response *QueryMembershipsForRoomResponse,
) error {
	apiURL := h.roomserverURL + RoomserverQueryMembershipsForRoomPath
	return postJSON(h.httpClient, apiURL, request, response)
}

209
func postJSON(httpClient *http.Client, apiURL string, request, response interface{}) error {
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
	jsonBytes, err := json.Marshal(request)
	if err != nil {
		return err
	}
	res, err := httpClient.Post(apiURL, "application/json", bytes.NewReader(jsonBytes))
	if res != nil {
		defer res.Body.Close()
	}
	if err != nil {
		return err
	}
	if res.StatusCode != 200 {
		var errorBody struct {
			Message string `json:"message"`
		}
		if err = json.NewDecoder(res.Body).Decode(&errorBody); err != nil {
			return err
		}
		return fmt.Errorf("api: %d: %s", res.StatusCode, errorBody.Message)
	}
	return json.NewDecoder(res.Body).Decode(response)
}