From 61379a286255ac0cc7ce8193681c825a2521106b Mon Sep 17 00:00:00 2001 From: SR Murthy Date: Tue, 10 Dec 2024 11:45:54 +0000 Subject: [PATCH] Release `v0.4.0` (#10) * chore: bump version refs to `0.4.0` * feat: add client method for regulated markets search + update tests and docs * chore: tweak project TOML * refactor: API base URL to include current API version + related updates --- CONTRIBUTING.md | 2 +- README.md | 2 +- docs/sources/contributing.rst | 2 +- docs/sources/fs-register-api.rst | 28 ++++++----- docs/sources/usage.rst | 44 ++++++++++++++++- pyproject.toml | 1 + src/fsrapiclient/__version__.py | 2 +- src/fsrapiclient/api.py | 84 ++++++++++++++++++++++++++++++-- src/fsrapiclient/constants.py | 6 +-- tests/units/test_api.py | 13 +++++ tests/units/test_constants.py | 2 +- 11 files changed, 162 insertions(+), 24 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f344e62..f207675 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -130,7 +130,7 @@ and the [CodeQL Analysis YML](https://github.com/sr-murthy/fsrapiclient/blob/mai ## Versioning and Releases -The [PyPI package](https://pypi.org/project/fsrapiclient/) is currently at version `0.3.0`. +The [PyPI package](https://pypi.org/project/fsrapiclient/) is currently at version `0.4.0`. There is currently no dedicated pipeline for releases - both [GitHub releases](https://github.com/sr-murthy/fsrapiclient/releases) and [PyPI packages](https://pypi.org/project/fsrapiclient) are published manually, but both have the same version tag. diff --git a/README.md b/README.md index a887b57..ad1d2c2 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ A lightweight Python client library for the UK [Financial Services Register](https://register.fca.org.uk/s/) [RESTful API](https://register.fca.org.uk/Developer/s/). -The [PyPI package](https://pypi.org/project/fsrapiclient) is currently at version `0.3.0`. +The [PyPI package](https://pypi.org/project/fsrapiclient) is currently at version `0.4.0`. The Financial Services Register, or FS Register, is a **public** database of all firms, individuals, funds, and other entities, that are either currently, or have been previously, authorised and/or regulated by the UK [Financial Conduct Authority (FCA)](https://www.fca.org.uk) and/or the [Prudential Regulation Authority (PRA)](http://bankofengland.co.uk/pra). diff --git a/docs/sources/contributing.rst b/docs/sources/contributing.rst index 8bf77d8..049573b 100644 --- a/docs/sources/contributing.rst +++ b/docs/sources/contributing.rst @@ -158,7 +158,7 @@ and the `CodeQL Analysis YML `_ is currently at version ``0.3.0``. +The `PyPI package `_ is currently at version ``0.4.0``. There is currently no dedicated pipeline for releases - both `GitHub releases `_ and `PyPI packages `_ are published manually, but both have the same version tag. diff --git a/docs/sources/fs-register-api.rst b/docs/sources/fs-register-api.rst index 9cefee6..65781ff 100644 --- a/docs/sources/fs-register-api.rst +++ b/docs/sources/fs-register-api.rst @@ -25,7 +25,7 @@ There are three main categories of resource about which information can be reque - **individuals** - individuals associated with the type of firms described above, either current or past. Individuals in the register have unique **individual reference numbers (IRN)** and their endpoints usually take these as one of the parameters. They are described in more detail :ref:`here `. - **funds** - investment funds or collective investment schemes (CIS),including subfunds of funds. Funds in the register have unique **product reference numbers (PRN)** and their endpoints usually take these as one of the parameters. They are described in more detail :ref:`here `. -There is also a **common search** API endpoint that allows a search for any of these resources by a name substring and a corresponding type specification (firm, individual, or fund). This is described in more detail :ref:`here `. +There are also search endpoints that allow a search for *(a)* any of these resources by a name substring and a corresponding type specification (firm, individual, or fund), or *(b)* `regulated markets `_. These are described in more detail :ref:`here `. .. _fs-register-api.request-headers: @@ -189,7 +189,7 @@ The common search API endpoint is a parameterised search endpoint which is summa * - API Endpoint - Request Method - Search Parameters - * - ``/V0.1/CommonSearch`` + * - ``/V0.1/Search`` - GET - ``q`` (resource name), ``type`` (resource type - ``'firm'``, ``'individual'``, or ``'fund'``) @@ -197,7 +197,7 @@ Requests should be of the form: .. code:: http - GET https://register.fca.org.uk/services/V0.1/CommonSearch?q=resource_name&type=resource_type HTTP/1.1 + GET https://register.fca.org.uk/services/V0.1/Search?q=resource_name&type=resource_type HTTP/1.1 For example, here are a few valid common search requests. @@ -205,42 +205,48 @@ For example, here are a few valid common search requests. .. code:: http - GET https://register.fca.org.uk/services/V0.1/CommonSearch?q=Barclays+Bank+plc&type=firm HTTP/1.1 + GET https://register.fca.org.uk/services/V0.1/Search?q=Barclays+Bank+plc&type=firm HTTP/1.1 * Common search for Hastings Insurance Services Limited (FRN #311492) .. code:: http - GET https://register.fca.org.uk/services/V0.1/CommonSearch?q=hastings+insurance+services&type=firm HTTP/1.1 + GET https://register.fca.org.uk/services/V0.1/Search?q=hastings+insurance+services&type=firm HTTP/1.1 * Common search for all Natwest-related firms: .. code:: http - GET https://register.fca.org.uk/services/V0.1/CommonSearch?q=Natwest&type=firm HTTP/1.1 + GET https://register.fca.org.uk/services/V0.1/Search?q=Natwest&type=firm HTTP/1.1 * Common search for a specific individual, Mark Carney (IRN #MXC29012): .. code:: http - GET https://register.fca.org.uk/services/V0.1/CommonSearch?q=mark+carney&type=individual HTTP/1.1 + GET https://register.fca.org.uk/services/V0.1/Search?q=mark+carney&type=individual HTTP/1.1 * Common search for a generic individual name "John Smith", with multiple results: .. code:: http - GET https://register.fca.org.uk/services/V0.1/CommonSearch?q=John+Smith&type=individual HTTP/1.1 + GET https://register.fca.org.uk/services/V0.1/Search?q=John+Smith&type=individual HTTP/1.1 * Common search for a specific fund, Jupiter Asia Pacific Income (PRN #635641): .. code:: http - GET https://register.fca.org.uk/services/V0.1/CommonSearch?q=jupiter+asia+pacific+income&type=fund HTTP/1.1 + GET https://register.fca.org.uk/services/V0.1/Search?q=jupiter+asia+pacific+income&type=fund HTTP/1.1 * Common search for a specific fund, abrdn Multi-Asset Fund (PRN #637980): .. code:: http - GET https://register.fca.org.uk/services/V0.1/CommonSearch?q=abrdn+multi-asset+fund&type=fund HTTP/1.1 + GET https://register.fca.org.uk/services/V0.1/Search?q=abrdn+multi-asset+fund&type=fund HTTP/1.1 -For details and examples on calling this endpoint via this library see :ref:`this `. +One particular type of common search-based endpoint that the API provides separately is for `regulated markets `_. These are special markets which are regulated by UK and/or EU/EEA financial legislation. API requests for regulated markets should look as below: + +.. code:: http + + GET https://register.fca.org.uk/services/V0.1/CommonSearch?q=RM HTTP/1.1 + +For details and examples on calling these endpoint via this library see :ref:`this `. diff --git a/docs/sources/usage.rst b/docs/sources/usage.rst index 910fc1e..1331066 100644 --- a/docs/sources/usage.rst +++ b/docs/sources/usage.rst @@ -63,7 +63,7 @@ The common search endpoint can be used via the :py:meth:`~fsrapiclient.api.FsrAp .. code:: bash - q=&type= + q=resource_name&type=resource_type where ``q`` is a parameter whose value should be the name (or name substring) of a resource (firm, individual, or fund), and ``type`` is a parameter whose value should be one of ``'firm'``, ``'individual'``, ``'fund'``. @@ -127,6 +127,48 @@ The response data as stored in the :py:attr:`~fsrapiclient.api.FsrApiResponse.fs >>> client.common_search(urlencode({'q': 'natwest', 'type': 'individual'})).fsr_data # Null +.. _usage.regulated-markets: + +Regulated Markets +----------------- + +The client implements a `regulated markets `_ search endpoint via the :py:meth:`~fsrapiclient.api.FsrApiClient.get_regulated_markets` method: + +.. code:: python + + >>> c.get_regulated_markets().fsr_data + + [{'Name': 'The London Metal Exchange', + 'TradingName': '', + 'Type of business or Individual': 'Exchange - RM', + 'Reference Number': '', + 'Status': '', + 'FirmURL': 'https://register.fca.org.uk/services/V0.1/Firm/'}, + {'Name': 'ICE Futures Europe', + 'TradingName': '', + 'Type of business or Individual': 'Exchange - RM', + 'Reference Number': '', + 'Status': '', + 'FirmURL': 'https://register.fca.org.uk/services/V0.1/Firm/'}, + {'Name': 'London Stock Exchange', + 'TradingName': '', + 'Type of business or Individual': 'Exchange - RM', + 'Reference Number': '', + 'Status': '', + 'FirmURL': 'https://register.fca.org.uk/services/V0.1/Firm/'}, + {'Name': 'Aquis Stock Exchange Limited', + 'TradingName': 'ICAP Securities & Derivatives Exchange Limited', + 'Type of business or Individual': 'Exchange - RM', + 'Reference Number': '', + 'Status': '', + 'FirmURL': 'https://register.fca.org.uk/services/V0.1/Firm/'}, + {'Name': 'Cboe Europe Equities Regulated Market', + 'TradingName': '', + 'Type of business or Individual': 'Exchange - RM', + 'Reference Number': '', + 'Status': '', + 'FirmURL': 'https://register.fca.org.uk/services/V0.1/Firm/'}] + .. _usage.searching-ref-numbers: Searching for FRNs, IRNs and PRNs diff --git a/pyproject.toml b/pyproject.toml index f63d18d..1c154ea 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,6 +38,7 @@ keywords = [ "financial services", "financial services register", "prudential regulation authority", + "regulated markets", "restful api", "uk", ] diff --git a/src/fsrapiclient/__version__.py b/src/fsrapiclient/__version__.py index ecad360..7e443c5 100644 --- a/src/fsrapiclient/__version__.py +++ b/src/fsrapiclient/__version__.py @@ -1,2 +1,2 @@ -__version__ = "0.3.0" +__version__ = "0.4.0" diff --git a/src/fsrapiclient/api.py b/src/fsrapiclient/api.py index c84a6da..216c2a4 100644 --- a/src/fsrapiclient/api.py +++ b/src/fsrapiclient/api.py @@ -331,7 +331,7 @@ def common_search(self, search_str: str) -> FsrApiResponse: >>> client.common_search(urlencode({'q': 'Hastings Direct', 'type': 'firm'})) """ - url = f'{FSR_API_CONSTANTS.BASEURL.value}/{self.api_version}/Search?{search_str}' + url = f'{FSR_API_CONSTANTS.BASEURL.value}/Search?{search_str}' try: return FsrApiResponse(self.api_session.get(url)) @@ -352,7 +352,7 @@ def _search_ref_number(self, resource_name: str, resource_type: str) -> str: /V0.1/Search?q=resource_name&type=resource_type to perform a case-insensitive search for resources of type - ``resource_type``in the FS Register on the given resource name + ``resource_type`` in the FS Register on the given resource name substring. Returns a non-null string of the resource ref. number if there is @@ -564,8 +564,6 @@ def _get_resource_info(self, resource_ref_number: str, resource_type: str, modif url = ( f'{FSR_API_CONSTANTS.BASEURL.value}' '/' - f'{self.api_version}' - '/' f'{resource_endpoint_base}' '/' f'{resource_ref_number}' @@ -1518,6 +1516,84 @@ def get_fund_subfunds(self, prn: str) -> FsrApiResponse: modifiers=('Subfund',) ) + def get_regulated_markets(self) -> FsrApiResponse: + """:py:class:`~fsrapiclient.api.FsrApiResponse` : Returns a response containing details of all current regulated markets, as defined in UK and EU / EEA financial services legislation. + + For further information consult the API documentation: + + https://register.fca.org.uk/Developer/s/ + + or the FCA glossary: + + https://www.handbook.fca.org.uk/handbook/glossary/G978.html?date=2007-01-20 + + Returns + ------- + FsrApiResponse + Wrapper of the API response object - there may be no data in + the response if the common search query produces no results. + + Examples + -------- + >>> import json, os + >>> client = FsrApiClient(os.environ['API_USERNAME'], os.environ['API_KEY']) + >>> res = client.get_regulated_markets() + >>> print(json.dumps(res.fsr_data, indent=True)) + [ + { + "Name": "The London Metal Exchange", + "TradingName": "", + "Type of business or Individual": "Exchange - RM", + "Reference Number": "", + "Status": "", + "FirmURL": "https://register.fca.org.uk/services/V0.1/Firm/" + }, + { + "Name": "ICE Futures Europe", + "TradingName": "", + "Type of business or Individual": "Exchange - RM", + "Reference Number": "", + "Status": "", + "FirmURL": "https://register.fca.org.uk/services/V0.1/Firm/" + }, + { + "Name": "London Stock Exchange", + "TradingName": "", + "Type of business or Individual": "Exchange - RM", + "Reference Number": "", + "Status": "", + "FirmURL": "https://register.fca.org.uk/services/V0.1/Firm/" + }, + { + "Name": "Aquis Stock Exchange Limited", + "TradingName": "ICAP Securities & Derivatives Exchange Limited", + "Type of business or Individual": "Exchange - RM", + "Reference Number": "", + "Status": "", + "FirmURL": "https://register.fca.org.uk/services/V0.1/Firm/" + }, + { + "Name": "Cboe Europe Equities Regulated Market", + "TradingName": "", + "Type of business or Individual": "Exchange - RM", + "Reference Number": "", + "Status": "", + "FirmURL": "https://register.fca.org.uk/services/V0.1/Firm/" + } + ] + """ + url = ( + f'{FSR_API_CONSTANTS.BASEURL.value}' + '/' + 'CommonSearch' + '?' + f'{urlencode({"q": "RM"})}' + ) + + return FsrApiResponse( + self.api_session.get(url) + ) + if __name__ == "__main__": # pragma: no cover # Doctest the module from the project root using # diff --git a/src/fsrapiclient/constants.py b/src/fsrapiclient/constants.py index d5ca84f..8760e7f 100644 --- a/src/fsrapiclient/constants.py +++ b/src/fsrapiclient/constants.py @@ -16,16 +16,16 @@ class FSR_API_CONSTANTS(Enum): Examples -------- - >>> FSR_API_CONSTANTS.BASEURL.value - 'https://register.fca.org.uk/services' >>> FSR_API_CONSTANTS.API_VERSION.value 'V0.1' + >>> FSR_API_CONSTANTS.BASEURL.value + 'https://register.fca.org.uk/services/V0.1' >>> FSR_API_CONSTANTS.RESOURCE_TYPES.value {'firm': {'type_name': 'firm', 'endpoint_base': 'Firm'}, 'fund': {'type_name': 'fund', 'endpoint_base': 'CIS'}, 'individual': {'type_name': 'individual', 'endpoint_base': 'Individuals'}} """ - BASEURL = 'https://register.fca.org.uk/services' API_VERSION = 'V0.1' + BASEURL = f'https://register.fca.org.uk/services/{API_VERSION}' RESOURCE_TYPES = { 'firm': {'type_name': 'firm', 'endpoint_base': 'Firm'}, 'fund': {'type_name': 'fund', 'endpoint_base': 'CIS'}, diff --git a/tests/units/test_api.py b/tests/units/test_api.py index 223bdd3..8b13eb5 100644 --- a/tests/units/test_api.py +++ b/tests/units/test_api.py @@ -917,3 +917,16 @@ def test_fsr_api_client__get_fund_subfunds(self): recv_response = test_client.get_fund_subfunds('1234567890') assert recv_response.ok assert not recv_response.fsr_data + + def test_fsr_api_client__get_regulated_markets(self): + test_client = FsrApiClient(self._api_username, self._api_key) + + # Covers the regulated markets API endpoint, which ATM returns a + # list of markets regulated by UK and/or EU/EEA financial legislation. + # + # We don't bother to check the exact list of markets returned by the + # endpoint, as this may change - it is enough to check that the endpoint + # should provide some data. + recv_response = test_client.get_regulated_markets() + assert recv_response.ok + assert recv_response.fsr_data diff --git a/tests/units/test_constants.py b/tests/units/test_constants.py index 728e12c..15f3b2f 100644 --- a/tests/units/test_constants.py +++ b/tests/units/test_constants.py @@ -11,8 +11,8 @@ class TestFsrApiConstants: def test_fsr_api_constants(self): - assert FSR_API_CONSTANTS.BASEURL.value == 'https://register.fca.org.uk/services' assert FSR_API_CONSTANTS.API_VERSION.value == 'V0.1' + assert FSR_API_CONSTANTS.BASEURL.value == 'https://register.fca.org.uk/services/V0.1' assert FSR_API_CONSTANTS.RESOURCE_TYPES.value == { 'firm': {'type_name': 'firm', 'endpoint_base': 'Firm'}, 'fund': {'type_name': 'fund', 'endpoint_base': 'CIS'},