diff --git a/synapse/app/homeserver.py b/synapse/app/homeserver.py
index 500cae05fb..afb46d2e23 100755
--- a/synapse/app/homeserver.py
+++ b/synapse/app/homeserver.py
@@ -32,15 +32,13 @@ from twisted.web.resource import Resource
from twisted.web.static import File
from twisted.web.server import Site
from synapse.http.server import JsonResource, RootRedirect
-from synapse.rest.appservice.v1 import AppServiceRestResource
from synapse.rest.media.v0.content_repository import ContentRepoResource
from synapse.rest.media.v1.media_repository import MediaRepositoryResource
from synapse.http.server_key_resource import LocalKey
from synapse.http.matrixfederationclient import MatrixFederationHttpClient
from synapse.api.urls import (
CLIENT_PREFIX, FEDERATION_PREFIX, WEB_CLIENT_PREFIX, CONTENT_REPO_PREFIX,
- SERVER_KEY_PREFIX, MEDIA_PREFIX, CLIENT_V2_ALPHA_PREFIX, APP_SERVICE_PREFIX,
- STATIC_PREFIX
+ SERVER_KEY_PREFIX, MEDIA_PREFIX, CLIENT_V2_ALPHA_PREFIX, STATIC_PREFIX
)
from synapse.config.homeserver import HomeServerConfig
from synapse.crypto import context_factory
@@ -78,9 +76,6 @@ class SynapseHomeServer(HomeServer):
def build_resource_for_federation(self):
return JsonResource(self)
- def build_resource_for_app_services(self):
- return AppServiceRestResource(self)
-
def build_resource_for_web_client(self):
import syweb
syweb_path = os.path.dirname(syweb.__file__)
@@ -141,7 +136,6 @@ class SynapseHomeServer(HomeServer):
(CONTENT_REPO_PREFIX, self.get_resource_for_content_repo()),
(SERVER_KEY_PREFIX, self.get_resource_for_server_key()),
(MEDIA_PREFIX, self.get_resource_for_media_repository()),
- (APP_SERVICE_PREFIX, self.get_resource_for_app_services()),
(STATIC_PREFIX, self.get_resource_for_static_content()),
]
diff --git a/synapse/appservice/__init__.py b/synapse/appservice/__init__.py
index 4a6cdbc2be..ab0a6955f0 100644
--- a/synapse/appservice/__init__.py
+++ b/synapse/appservice/__init__.py
@@ -95,7 +95,7 @@ class ApplicationService(object):
# rooms: [ {regex: "[A-z]+.*", exclusive: true}, ...],
# }
if not namespaces:
- return None
+ namespaces = {}
for ns in ApplicationService.NS_LIST:
if ns not in namespaces:
diff --git a/synapse/rest/appservice/v1/__init__.py b/synapse/config/appservice.py
index a7877609ad..399a716d80 100644
--- a/synapse/rest/appservice/v1/__init__.py
+++ b/synapse/config/appservice.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
# Copyright 2015 OpenMarket Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -12,18 +11,21 @@
# 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 . import register
-from synapse.http.server import JsonResource
+from ._base import Config
-class AppServiceRestResource(JsonResource):
- """A resource for version 1 of the matrix application service API."""
+class AppServiceConfig(Config):
- def __init__(self, hs):
- JsonResource.__init__(self, hs)
- self.register_servlets(self, hs)
+ def __init__(self, args):
+ super(AppServiceConfig, self).__init__(args)
+ self.app_service_config_files = args.app_service_config_files
- @staticmethod
- def register_servlets(appservice_resource, hs):
- register.register_servlets(hs, appservice_resource)
+ @classmethod
+ def add_arguments(cls, parser):
+ super(AppServiceConfig, cls).add_arguments(parser)
+ group = parser.add_argument_group("appservice")
+ group.add_argument(
+ "--app-service-config-files", type=str, nargs='+',
+ help="A list of application service config files to use."
+ )
diff --git a/synapse/config/homeserver.py b/synapse/config/homeserver.py
index 241afdf872..3edfadb98b 100644
--- a/synapse/config/homeserver.py
+++ b/synapse/config/homeserver.py
@@ -24,12 +24,13 @@ from .email import EmailConfig
from .voip import VoipConfig
from .registration import RegistrationConfig
from .metrics import MetricsConfig
+from .appservice import AppServiceConfig
class HomeServerConfig(TlsConfig, ServerConfig, DatabaseConfig, LoggingConfig,
RatelimitConfig, ContentRepositoryConfig, CaptchaConfig,
EmailConfig, VoipConfig, RegistrationConfig,
- MetricsConfig,):
+ MetricsConfig, AppServiceConfig,):
pass
diff --git a/synapse/handlers/appservice.py b/synapse/handlers/appservice.py
index 58b5b60bb7..59cf15b037 100644
--- a/synapse/handlers/appservice.py
+++ b/synapse/handlers/appservice.py
@@ -16,10 +16,8 @@
from twisted.internet import defer
from synapse.api.constants import EventTypes, Membership
-from synapse.api.errors import Codes, StoreError, SynapseError
from synapse.appservice import ApplicationService
from synapse.types import UserID
-import synapse.util.stringutils as stringutils
import logging
@@ -50,38 +48,6 @@ class ApplicationServicesHandler(object):
self.started_scheduler = False
@defer.inlineCallbacks
- def register(self, app_service):
- logger.info("Register -> %s", app_service)
- # check the token is recognised
- try:
- stored_service = yield self.store.get_app_service_by_token(
- app_service.token
- )
- if not stored_service:
- raise StoreError(404, "Application service not found")
- app_service.id = stored_service.id
- except StoreError:
- raise SynapseError(
- 403, "Unrecognised application services token. "
- "Consult the home server admin.",
- errcode=Codes.FORBIDDEN
- )
- app_service.hs_token = self._generate_hs_token()
-
- # create a sender for this application service which is used when
- # creating rooms, etc..
- account = yield self.hs.get_handlers().registration_handler.register()
- app_service.sender = account[0]
-
- yield self.store.update_app_service(app_service)
- defer.returnValue(app_service)
-
- @defer.inlineCallbacks
- def unregister(self, token):
- logger.info("Unregister as_token=%s", token)
- yield self.store.unregister_app_service(token)
-
- @defer.inlineCallbacks
def notify_interested_services(self, event):
"""Notifies (pushes) all application services interested in this event.
@@ -223,6 +189,3 @@ class ApplicationServicesHandler(object):
exists = yield self.query_user_exists(user_id)
defer.returnValue(exists)
defer.returnValue(True)
-
- def _generate_hs_token(self):
- return stringutils.random_string(24)
diff --git a/synapse/rest/appservice/__init__.py b/synapse/rest/appservice/__init__.py
deleted file mode 100644
index 1a84d94cd9..0000000000
--- a/synapse/rest/appservice/__init__.py
+++ /dev/null
@@ -1,14 +0,0 @@
-# -*- coding: utf-8 -*-
-# 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.
diff --git a/synapse/rest/appservice/v1/base.py b/synapse/rest/appservice/v1/base.py
deleted file mode 100644
index 65d5bcf9be..0000000000
--- a/synapse/rest/appservice/v1/base.py
+++ /dev/null
@@ -1,48 +0,0 @@
-# -*- coding: utf-8 -*-
-# 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.
-
-"""This module contains base REST classes for constructing client v1 servlets.
-"""
-
-from synapse.http.servlet import RestServlet
-from synapse.api.urls import APP_SERVICE_PREFIX
-import re
-
-import logging
-
-
-logger = logging.getLogger(__name__)
-
-
-def as_path_pattern(path_regex):
- """Creates a regex compiled appservice path with the correct path
- prefix.
-
- Args:
- path_regex (str): The regex string to match. This should NOT have a ^
- as this will be prefixed.
- Returns:
- SRE_Pattern
- """
- return re.compile("^" + APP_SERVICE_PREFIX + path_regex)
-
-
-class AppServiceRestServlet(RestServlet):
- """A base Synapse REST Servlet for the application services version 1 API.
- """
-
- def __init__(self, hs):
- self.hs = hs
- self.handler = hs.get_handlers().appservice_handler
diff --git a/synapse/rest/appservice/v1/register.py b/synapse/rest/appservice/v1/register.py
deleted file mode 100644
index ea24d88f79..0000000000
--- a/synapse/rest/appservice/v1/register.py
+++ /dev/null
@@ -1,99 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright 2015 OpenMarket Ltd
-#
-# Licensensed 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.
-
-"""This module contains REST servlets to do with registration: /register"""
-from twisted.internet import defer
-
-from base import AppServiceRestServlet, as_path_pattern
-from synapse.api.errors import CodeMessageException, SynapseError
-from synapse.storage.appservice import ApplicationService
-
-import json
-import logging
-
-logger = logging.getLogger(__name__)
-
-
-class RegisterRestServlet(AppServiceRestServlet):
- """Handles AS registration with the home server.
- """
-
- PATTERN = as_path_pattern("/register$")
-
- @defer.inlineCallbacks
- def on_POST(self, request):
- params = _parse_json(request)
-
- # sanity check required params
- try:
- as_token = params["as_token"]
- as_url = params["url"]
- if (not isinstance(as_token, basestring) or
- not isinstance(as_url, basestring)):
- raise ValueError
- except (KeyError, ValueError):
- raise SynapseError(
- 400, "Missed required keys: as_token(str) / url(str)."
- )
-
- try:
- app_service = ApplicationService(
- as_token, as_url, params["namespaces"]
- )
- except ValueError as e:
- raise SynapseError(400, e.message)
-
- app_service = yield self.handler.register(app_service)
- hs_token = app_service.hs_token
-
- defer.returnValue((200, {
- "hs_token": hs_token
- }))
-
-
-class UnregisterRestServlet(AppServiceRestServlet):
- """Handles AS registration with the home server.
- """
-
- PATTERN = as_path_pattern("/unregister$")
-
- def on_POST(self, request):
- params = _parse_json(request)
- try:
- as_token = params["as_token"]
- if not isinstance(as_token, basestring):
- raise ValueError
- except (KeyError, ValueError):
- raise SynapseError(400, "Missing required key: as_token(str)")
-
- yield self.handler.unregister(as_token)
-
- raise CodeMessageException(500, "Not implemented")
-
-
-def _parse_json(request):
- try:
- content = json.loads(request.content.read())
- if type(content) != dict:
- raise SynapseError(400, "Content must be a JSON object.")
- return content
- except ValueError as e:
- logger.warn(e)
- raise SynapseError(400, "Content not JSON.")
-
-
-def register_servlets(hs, http_server):
- RegisterRestServlet(hs).register(http_server)
- UnregisterRestServlet(hs).register(http_server)
diff --git a/synapse/server.py b/synapse/server.py
index c7772244ba..0bd87bdd77 100644
--- a/synapse/server.py
+++ b/synapse/server.py
@@ -79,7 +79,6 @@ class BaseHomeServer(object):
'resource_for_content_repo',
'resource_for_server_key',
'resource_for_media_repository',
- 'resource_for_app_services',
'resource_for_metrics',
'event_sources',
'ratelimiter',
diff --git a/synapse/storage/appservice.py b/synapse/storage/appservice.py
index 93304a745f..f8cbb3f323 100644
--- a/synapse/storage/appservice.py
+++ b/synapse/storage/appservice.py
@@ -13,155 +13,35 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import logging
+import urllib
+import yaml
from simplejson import JSONDecodeError
import simplejson as json
from twisted.internet import defer
from synapse.api.constants import Membership
-from synapse.api.errors import StoreError
from synapse.appservice import ApplicationService, AppServiceTransaction
from synapse.storage.roommember import RoomsForUser
+from synapse.types import UserID
from ._base import SQLBaseStore
logger = logging.getLogger(__name__)
-def log_failure(failure):
- logger.error("Failed to detect application services: %s", failure.value)
- logger.error(failure.getTraceback())
-
-
class ApplicationServiceStore(SQLBaseStore):
def __init__(self, hs):
super(ApplicationServiceStore, self).__init__(hs)
+ self.hostname = hs.hostname
self.services_cache = []
- self.cache_defer = self._populate_appservice_cache()
- self.cache_defer.addErrback(log_failure)
-
- @defer.inlineCallbacks
- def unregister_app_service(self, token):
- """Unregisters this service.
-
- This removes all AS specific regex and the base URL. The token is the
- only thing preserved for future registration attempts.
- """
- yield self.cache_defer # make sure the cache is ready
- yield self.runInteraction(
- "unregister_app_service",
- self._unregister_app_service_txn,
- token,
- )
- # update cache TODO: Should this be in the txn?
- for service in self.services_cache:
- if service.token == token:
- service.url = None
- service.namespaces = None
- service.hs_token = None
-
- def _unregister_app_service_txn(self, txn, token):
- # kill the url to prevent pushes
- txn.execute(
- "UPDATE application_services SET url=NULL WHERE token=?",
- (token,)
+ self._populate_appservice_cache(
+ hs.config.app_service_config_files
)
- # cleanup regex
- as_id = self._get_as_id_txn(txn, token)
- if not as_id:
- logger.warning(
- "unregister_app_service_txn: Failed to find as_id for token=",
- token
- )
- return False
-
- txn.execute(
- "DELETE FROM application_services_regex WHERE as_id=?",
- (as_id,)
- )
- return True
-
- @defer.inlineCallbacks
- def update_app_service(self, service):
- """Update an application service, clobbering what was previously there.
-
- Args:
- service(ApplicationService): The updated service.
- """
- yield self.cache_defer # make sure the cache is ready
-
- # NB: There is no "insert" since we provide no public-facing API to
- # allocate new ASes. It relies on the server admin inserting the AS
- # token into the database manually.
-
- if not service.token or not service.url:
- raise StoreError(400, "Token and url must be specified.")
-
- if not service.hs_token:
- raise StoreError(500, "No HS token")
-
- as_id = yield self.runInteraction(
- "update_app_service",
- self._update_app_service_txn,
- service
- )
- service.id = as_id
-
- # update cache TODO: Should this be in the txn?
- for (index, cache_service) in enumerate(self.services_cache):
- if service.token == cache_service.token:
- self.services_cache[index] = service
- logger.info("Updated: %s", service)
- return
- # new entry
- self.services_cache.append(service)
- logger.info("Updated(new): %s", service)
-
- def _update_app_service_txn(self, txn, service):
- as_id = self._get_as_id_txn(txn, service.token)
- if not as_id:
- logger.warning(
- "update_app_service_txn: Failed to find as_id for token=",
- service.token
- )
- return
-
- txn.execute(
- "UPDATE application_services SET url=?, hs_token=?, sender=? "
- "WHERE id=?",
- (service.url, service.hs_token, service.sender, as_id,)
- )
- # cleanup regex
- txn.execute(
- "DELETE FROM application_services_regex WHERE as_id=?",
- (as_id,)
- )
- for (ns_int, ns_str) in enumerate(ApplicationService.NS_LIST):
- if ns_str in service.namespaces:
- for regex_obj in service.namespaces[ns_str]:
- txn.execute(
- "INSERT INTO application_services_regex("
- "as_id, namespace, regex) values(?,?,?)",
- (as_id, ns_int, json.dumps(regex_obj))
- )
- return as_id
-
- def _get_as_id_txn(self, txn, token):
- cursor = txn.execute(
- "SELECT id FROM application_services WHERE token=?",
- (token,)
- )
- res = cursor.fetchone()
- if res:
- return res[0]
-
- @defer.inlineCallbacks
def get_app_services(self):
- yield self.cache_defer # make sure the cache is ready
- defer.returnValue(self.services_cache)
+ return defer.succeed(self.services_cache)
- @defer.inlineCallbacks
def get_app_service_by_user_id(self, user_id):
"""Retrieve an application service from their user ID.
@@ -175,37 +55,23 @@ class ApplicationServiceStore(SQLBaseStore):
Returns:
synapse.appservice.ApplicationService or None.
"""
-
- yield self.cache_defer # make sure the cache is ready
-
for service in self.services_cache:
if service.sender == user_id:
- defer.returnValue(service)
- return
- defer.returnValue(None)
+ return defer.succeed(service)
+ return defer.succeed(None)
- @defer.inlineCallbacks
- def get_app_service_by_token(self, token, from_cache=True):
+ def get_app_service_by_token(self, token):
"""Get the application service with the given appservice token.
Args:
token (str): The application service token.
- from_cache (bool): True to get this service from the cache, False to
- check the database.
- Raises:
- StoreError if there was a problem retrieving this service.
+ Returns:
+ synapse.appservice.ApplicationService or None.
"""
- yield self.cache_defer # make sure the cache is ready
-
- if from_cache:
- for service in self.services_cache:
- if service.token == token:
- defer.returnValue(service)
- return
- defer.returnValue(None)
-
- # TODO: The from_cache=False impl
- # TODO: This should be JOINed with the application_services_regex table.
+ for service in self.services_cache:
+ if service.token == token:
+ return defer.succeed(service)
+ return defer.succeed(None)
def get_app_service_rooms(self, service):
"""Get a list of RoomsForUser for this application service.
@@ -336,18 +202,69 @@ class ApplicationServiceStore(SQLBaseStore):
))
return service_list
- @defer.inlineCallbacks
- def _populate_appservice_cache(self):
- """Populates the ApplicationServiceCache from the database."""
- sql = ("SELECT r.*, a.* FROM application_services AS a LEFT JOIN "
- "application_services_regex AS r ON a.id = r.as_id")
+ def _load_appservice(self, as_info):
+ required_string_fields = [
+ "url", "as_token", "hs_token", "sender_localpart"
+ ]
+ for field in required_string_fields:
+ if not isinstance(as_info.get(field), basestring):
+ raise KeyError("Required string field: '%s'", field)
+
+ localpart = as_info["sender_localpart"]
+ if urllib.quote(localpart) != localpart:
+ raise ValueError(
+ "sender_localpart needs characters which are not URL encoded."
+ )
+ user = UserID(localpart, self.hostname)
+ user_id = user.to_string()
+
+ # namespace checks
+ if not isinstance(as_info.get("namespaces"), dict):
+ raise KeyError("Requires 'namespaces' object.")
+ for ns in ApplicationService.NS_LIST:
+ # specific namespaces are optional
+ if ns in as_info["namespaces"]:
+ # expect a list of dicts with exclusive and regex keys
+ for regex_obj in as_info["namespaces"][ns]:
+ if not isinstance(regex_obj, dict):
+ raise ValueError(
+ "Expected namespace entry in %s to be an object,"
+ " but got %s", ns, regex_obj
+ )
+ if not isinstance(regex_obj.get("regex"), basestring):
+ raise ValueError(
+ "Missing/bad type 'regex' key in %s", regex_obj
+ )
+ if not isinstance(regex_obj.get("exclusive"), bool):
+ raise ValueError(
+ "Missing/bad type 'exclusive' key in %s", regex_obj
+ )
+ return ApplicationService(
+ token=as_info["as_token"],
+ url=as_info["url"],
+ namespaces=as_info["namespaces"],
+ hs_token=as_info["hs_token"],
+ sender=user_id,
+ id=as_info["as_token"] # the token is the only unique thing here
+ )
- results = yield self._execute_and_decode("appservice_cache", sql)
- services = self._parse_services_dict(results)
+ def _populate_appservice_cache(self, config_files):
+ """Populates a cache of Application Services from the config files."""
+ if not isinstance(config_files, list):
+ logger.warning(
+ "Expected %s to be a list of AS config files.", config_files
+ )
+ return
- for service in services:
- logger.info("Found application service: %s", service)
- self.services_cache.append(service)
+ for config_file in config_files:
+ try:
+ with open(config_file, 'r') as f:
+ appservice = self._load_appservice(yaml.load(f))
+ logger.info("Loaded application service: %s", appservice)
+ self.services_cache.append(appservice)
+ except Exception as e:
+ logger.error("Failed to load appservice from '%s'", config_file)
+ logger.exception(e)
class ApplicationServiceTransactionStore(SQLBaseStore):
@@ -365,16 +282,20 @@ class ApplicationServiceTransactionStore(SQLBaseStore):
A Deferred which resolves to a list of ApplicationServices, which
may be empty.
"""
- sql = (
- "SELECT r.*, a.* FROM application_services_state AS s LEFT JOIN"
- " application_services AS a ON a.id=s.as_id LEFT JOIN"
- " application_services_regex AS r ON r.as_id=a.id WHERE state = ?"
- )
- results = yield self._execute_and_decode(
- "get_appservices_by_state", sql, state
+ results = yield self._simple_select_list(
+ "application_services_state",
+ dict(state=state),
+ ["as_id"]
)
# NB: This assumes this class is linked with ApplicationServiceStore
- defer.returnValue(self._parse_services_dict(results))
+ as_list = yield self.get_app_services()
+ services = []
+
+ for res in results:
+ for service in as_list:
+ if service.id == res["as_id"]:
+ services.append(service)
+ defer.returnValue(services)
@defer.inlineCallbacks
def get_appservice_state(self, service):
diff --git a/synapse/storage/schema/delta/15/appservice_txns.sql b/synapse/storage/schema/delta/15/appservice_txns.sql
index 13bbb2de2e..2b27e2a429 100644
--- a/synapse/storage/schema/delta/15/appservice_txns.sql
+++ b/synapse/storage/schema/delta/15/appservice_txns.sql
@@ -14,14 +14,13 @@
*/
CREATE TABLE IF NOT EXISTS application_services_state(
- as_id INTEGER PRIMARY KEY,
+ as_id TEXT PRIMARY KEY,
state TEXT,
- last_txn TEXT,
- FOREIGN KEY(as_id) REFERENCES application_services(id)
+ last_txn TEXT
);
CREATE TABLE IF NOT EXISTS application_services_txns(
- as_id INTEGER NOT NULL,
+ as_id TEXT NOT NULL,
txn_id INTEGER NOT NULL,
event_ids TEXT NOT NULL,
UNIQUE(as_id, txn_id) ON CONFLICT ROLLBACK
diff --git a/tests/storage/test_appservice.py b/tests/storage/test_appservice.py
index e79599f7fb..675959c56c 100644
--- a/tests/storage/test_appservice.py
+++ b/tests/storage/test_appservice.py
@@ -22,6 +22,8 @@ from synapse.storage.appservice import (
)
import json
+import os
+import yaml
from mock import Mock
from tests.utils import SQLiteMemoryDbPool, MockClock
@@ -30,63 +32,39 @@ class ApplicationServiceStoreTestCase(unittest.TestCase):
@defer.inlineCallbacks
def setUp(self):
+ self.as_yaml_files = []
db_pool = SQLiteMemoryDbPool()
yield db_pool.prepare()
hs = HomeServer(
- "test", db_pool=db_pool, clock=MockClock(), config=Mock()
+ "test", db_pool=db_pool, clock=MockClock(),
+ config=Mock(
+ app_service_config_files=self.as_yaml_files
+ )
)
+
self.as_token = "token1"
- db_pool.runQuery(
- "INSERT INTO application_services(token) VALUES(?)",
- (self.as_token,)
- )
- db_pool.runQuery(
- "INSERT INTO application_services(token) VALUES(?)", ("token2",)
- )
- db_pool.runQuery(
- "INSERT INTO application_services(token) VALUES(?)", ("token3",)
- )
+ self.as_url = "some_url"
+ self._add_appservice(self.as_token, self.as_url, "some_hs_token", "bob")
+ self._add_appservice("token2", "some_url", "some_hs_token", "bob")
+ self._add_appservice("token3", "some_url", "some_hs_token", "bob")
# must be done after inserts
self.store = ApplicationServiceStore(hs)
- @defer.inlineCallbacks
- def test_update_and_retrieval_of_service(self):
- url = "https://matrix.org/appservices/foobar"
- hs_token = "hstok"
- user_regex = [
- {"regex": "@foobar_.*:matrix.org", "exclusive": True}
- ]
- alias_regex = [
- {"regex": "#foobar_.*:matrix.org", "exclusive": False}
- ]
- room_regex = [
-
- ]
- service = ApplicationService(
- url=url, hs_token=hs_token, token=self.as_token, namespaces={
- ApplicationService.NS_USERS: user_regex,
- ApplicationService.NS_ALIASES: alias_regex,
- ApplicationService.NS_ROOMS: room_regex
- })
- yield self.store.update_app_service(service)
-
- stored_service = yield self.store.get_app_service_by_token(
- self.as_token
- )
- self.assertEquals(stored_service.token, self.as_token)
- self.assertEquals(stored_service.url, url)
- self.assertEquals(
- stored_service.namespaces[ApplicationService.NS_ALIASES],
- alias_regex
- )
- self.assertEquals(
- stored_service.namespaces[ApplicationService.NS_ROOMS],
- room_regex
- )
- self.assertEquals(
- stored_service.namespaces[ApplicationService.NS_USERS],
- user_regex
- )
+ def tearDown(self):
+ # TODO: suboptimal that we need to create files for tests!
+ for f in self.as_yaml_files:
+ try:
+ os.remove(f)
+ except:
+ pass
+
+ def _add_appservice(self, as_token, url, hs_token, sender):
+ as_yaml = dict(url=url, as_token=as_token, hs_token=hs_token,
+ sender_localpart=sender, namespaces={})
+ # use the token as the filename
+ with open(as_token, 'w') as outfile:
+ outfile.write(yaml.dump(as_yaml))
+ self.as_yaml_files.append(as_token)
@defer.inlineCallbacks
def test_retrieve_unknown_service_token(self):
@@ -99,7 +77,7 @@ class ApplicationServiceStoreTestCase(unittest.TestCase):
self.as_token
)
self.assertEquals(stored_service.token, self.as_token)
- self.assertEquals(stored_service.url, None)
+ self.assertEquals(stored_service.url, self.as_url)
self.assertEquals(
stored_service.namespaces[ApplicationService.NS_ALIASES],
[]
@@ -123,42 +101,48 @@ class ApplicationServiceTransactionStoreTestCase(unittest.TestCase):
@defer.inlineCallbacks
def setUp(self):
+ self.as_yaml_files = []
self.db_pool = SQLiteMemoryDbPool()
yield self.db_pool.prepare()
- hs = HomeServer(
- "test", db_pool=self.db_pool, clock=MockClock(), config=Mock()
- )
self.as_list = [
{
"token": "token1",
"url": "https://matrix-as.org",
- "id": 3
+ "id": "token1"
},
{
"token": "alpha_tok",
"url": "https://alpha.com",
- "id": 5
+ "id": "alpha_tok"
},
{
"token": "beta_tok",
"url": "https://beta.com",
- "id": 6
+ "id": "beta_tok"
},
{
"token": "delta_tok",
"url": "https://delta.com",
- "id": 7
+ "id": "delta_tok"
},
]
for s in self.as_list:
- yield self._add_service(s["id"], s["url"], s["token"])
- self.store = TestTransactionStore(hs)
+ yield self._add_service(s["url"], s["token"])
- def _add_service(self, as_id, url, token):
- return self.db_pool.runQuery(
- "INSERT INTO application_services(id, url, token) VALUES(?,?,?)",
- (as_id, url, token)
+ hs = HomeServer(
+ "test", db_pool=self.db_pool, clock=MockClock(), config=Mock(
+ app_service_config_files=self.as_yaml_files
+ )
)
+ self.store = TestTransactionStore(hs)
+
+ def _add_service(self, url, as_token):
+ as_yaml = dict(url=url, as_token=as_token, hs_token="something",
+ sender_localpart="a_sender", namespaces={})
+ # use the token as the filename
+ with open(as_token, 'w') as outfile:
+ outfile.write(yaml.dump(as_yaml))
+ self.as_yaml_files.append(as_token)
def _set_state(self, id, state, txn=None):
return self.db_pool.runQuery(
@@ -410,8 +394,10 @@ class ApplicationServiceTransactionStoreTestCase(unittest.TestCase):
ApplicationServiceState.DOWN
)
self.assertEquals(2, len(services))
- self.assertEquals(self.as_list[2]["id"], services[0].id)
- self.assertEquals(self.as_list[0]["id"], services[1].id)
+ self.assertEquals(
+ set([self.as_list[2]["id"], self.as_list[0]["id"]]),
+ set([services[0].id, services[1].id])
+ )
# required for ApplicationServiceTransactionStoreTestCase tests
|