diff --git a/synapse/handlers/appservice.py b/synapse/handlers/appservice.py
index c9f56c41eb..8bd475cbfd 100644
--- a/synapse/handlers/appservice.py
+++ b/synapse/handlers/appservice.py
@@ -16,7 +16,7 @@
from twisted.internet import defer
from ._base import BaseHandler
-from synapse.api.errors import StoreError, SynapseError
+from synapse.api.errors import Codes, StoreError, SynapseError
import logging
@@ -36,11 +36,12 @@ class ApplicationServicesHandler(BaseHandler):
try:
stored_service = yield self.store.get_app_service(app_service.token)
if not stored_service:
- raise StoreError(404, "Not found")
+ raise StoreError(404, "Application Service Not found")
except StoreError:
raise SynapseError(
403, "Unrecognised application services token. "
- "Consult the home server admin."
+ "Consult the home server admin.",
+ errcode=Codes.FORBIDDEN
)
# TODO store this AS
diff --git a/synapse/rest/appservice/v1/register.py b/synapse/rest/appservice/v1/register.py
index 5786cf873e..e374d538e7 100644
--- a/synapse/rest/appservice/v1/register.py
+++ b/synapse/rest/appservice/v1/register.py
@@ -64,9 +64,9 @@ class RegisterRestServlet(AppServiceRestServlet):
yield self.handler.register(app_service)
hs_token = "_not_implemented_yet" # TODO: Pull this from self.hs?
- defer.returnValue({
+ defer.returnValue((200, {
"hs_token": hs_token
- })
+ }))
def _parse_namespace(self, target_ns, origin_ns, ns):
if ns not in target_ns or ns not in origin_ns:
diff --git a/synapse/storage/__init__.py b/synapse/storage/__init__.py
index 9431c1a32d..e86b981b47 100644
--- a/synapse/storage/__init__.py
+++ b/synapse/storage/__init__.py
@@ -62,6 +62,7 @@ SCHEMAS = [
"event_edges",
"event_signatures",
"media_repository",
+ "application_services"
]
diff --git a/synapse/storage/appservice.py b/synapse/storage/appservice.py
index 533fac4972..5a0e47e0d4 100644
--- a/synapse/storage/appservice.py
+++ b/synapse/storage/appservice.py
@@ -12,12 +12,15 @@
# 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.
-
-from synapse.api.errors import StoreError
+import logging
+from twisted.internet import defer
from ._base import SQLBaseStore
+logger = logging.getLogger(__name__)
+
+
# XXX: This feels like it should belong in a "models" module, not storage.
class ApplicationService(object):
"""Defines an application service.
@@ -30,7 +33,22 @@ class ApplicationService(object):
if url:
self.url = url
if namespaces:
- self.namespaces = namespaces
+ self._set_namespaces(namespaces)
+
+ def _set_namespaces(self, namespaces):
+ # Sanity check that it is of the form:
+ # {
+ # users: ["regex",...],
+ # aliases: ["regex",...],
+ # rooms: ["regex",...],
+ # }
+ for ns in ["users", "rooms", "aliases"]:
+ if type(namespaces[ns]) != list:
+ raise ValueError("Bad namespace value for '%s'", ns)
+ for regex in namespaces[ns]:
+ if not isinstance(regex, basestring):
+ raise ValueError("Expected string regex for ns '%s'", ns)
+ self.namespaces = namespaces
def is_interested(self, event):
"""Check if this service is interested in this event.
@@ -133,15 +151,64 @@ class ApplicationServiceStore(SQLBaseStore):
return service
return None
+ # TODO: The from_cache=False impl
# TODO: This should be JOINed with the application_services_regex table.
- row = self._simple_select_one(
- "application_services", {"token": token},
- ["url", "token"]
- )
- if not row:
- raise StoreError(400, "Bad application services token supplied.")
- return row
+
+ @defer.inlineCallbacks
def _populate_cache(self):
"""Populates the ApplicationServiceCache from the database."""
- pass
+ sql = ("SELECT * FROM application_services LEFT JOIN "
+ "application_services_regex ON application_services.id = "
+ "application_services_regex.as_id")
+
+ namespace_enum = [
+ "users", # 0
+ "aliases", # 1
+ "rooms" # 2
+ ]
+ # SQL results in the form:
+ # [
+ # {
+ # 'regex': "something",
+ # 'url': "something",
+ # 'namespace': enum,
+ # 'as_id': 0,
+ # 'token': "something",
+ # 'id': 0
+ # }
+ # ]
+ services = {}
+ results = yield self._execute_and_decode(sql)
+ for res in results:
+ as_token = res["token"]
+ if as_token not in services:
+ # add the service
+ services[as_token] = {
+ "url": res["url"],
+ "token": as_token,
+ "namespaces": {
+ "users": [],
+ "aliases": [],
+ "rooms": []
+ }
+ }
+ # add the namespace regex if one exists
+ ns_int = res["namespace"]
+ if ns_int is None:
+ continue
+ try:
+ services[as_token]["namespaces"][namespace_enum[ns_int]].append(
+ res["regex"]
+ )
+ except IndexError:
+ logger.error("Bad namespace enum '%s'. %s", ns_int, res)
+
+ for service in services.values():
+ logger.info("Found application service: %s", service)
+ self.cache.services.append(ApplicationService(
+ service["token"],
+ service["url"],
+ service["namespaces"]
+ ))
+
diff --git a/synapse/storage/schema/application_services.sql b/synapse/storage/schema/application_services.sql
new file mode 100644
index 0000000000..6d245fc807
--- /dev/null
+++ b/synapse/storage/schema/application_services.sql
@@ -0,0 +1,32 @@
+/* Copyright 2015 OpenMarket 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.
+ */
+
+CREATE TABLE IF NOT EXISTS application_services(
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ url TEXT,
+ token TEXT,
+ UNIQUE(token) ON CONFLICT ROLLBACK
+);
+
+CREATE TABLE IF NOT EXISTS application_services_regex(
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ as_id INTEGER NOT NULL,
+ namespace INTEGER, /* enum[room_id|room_alias|user_id] */
+ regex TEXT,
+ FOREIGN KEY(as_id) REFERENCES application_services(id)
+);
+
+
+
|