diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index bb3f2ce..116c59f 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 `1.0.0`.
+The [PyPI package](https://pypi.org/project/fsrapiclient/) is currently at version `1.1.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 6889379..393efd2 100644
--- a/README.md
+++ b/README.md
@@ -8,11 +8,8 @@
[](https://opensource.org/licenses/MPL-2.0)
[](https://financial-services-register-api.readthedocs.io/en/latest/?badge=latest)
-
-
-
[](https://pypi.org/project/financial-services-register-api)
-
+
@@ -20,7 +17,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/financial-services-register-api) is currently at version `1.0.0`.
+The [PyPI package](https://pypi.org/project/financial-services-register-api) is currently at version `1.1.0`.
> [!NOTE]
> The new package `financial-services-register-api` supersedes the older package `fsrapiclient`, which will no longer be published. Existing versions of the older package may be retracted in the future. Please use the new package.
diff --git a/docs/conf.py b/docs/conf.py
index 894c255..77d6ae3 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -76,9 +76,8 @@
# Publish author(s)
show_authors = True
-# Add any Sphinx extension module names here, as strings. They can be
-# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
-# ones.
+# Sphinx extensions: not all of these are used or required, but they are still
+# listed here if requirements change.
extensions = ['jupyter_sphinx',
'matplotlib.sphinxext.plot_directive',
'myst_parser',
@@ -106,10 +105,10 @@
# For more on all available autodoc defaults see
# https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html#confval-autodoc_default_options
autodoc_default_options = {
- 'exclude-members': '__call__,__weakref__,__slots__,__match_args__',
+ 'exclude-members': '',
'member-order': 'bysource',
- 'private-members': True,
- 'special-members': '__eq__, __init__, __new__'
+ 'private-members': False,
+ 'special-members': '__init__,__new__'
}
# Sphinx autodoc autosummary settings
@@ -122,24 +121,10 @@
numpydoc_attributes_as_param_list = False
numpydoc_xref_param_type = False
-# Intersphinx mappings to reference external documentation domains
-intersphinx_mapping = {
- 'coverage': ('https://coverage.readthedocs.io/en/7.3.1/', None),
- 'matplotlib': ('https://matplotlib.org/stable/', None),
- 'networkx': ('https://networkx.org/documentation/stable/', None),
- 'numpy': ('https://numpy.org/doc/stable/', None),
- 'pandas': ('https://pandas.pydata.org/pandas-docs/stable/', None),
- 'pdm': ('https://pdm-project.org/latest/', None),
- 'pygraphviz': ('https://pygraphviz.github.io/documentation/stable/', None),
- 'pytest': ('https://docs.pytest.org/en/7.4.x/', None),
- 'python': ('https://docs.python.org/3', None),
- 'requests': ('https://requests.readthedocs.io/en/latest/', None),
- 'scipy': ('https://docs.scipy.org/doc/scipy/', None),
- 'sympy': ('https://docs.sympy.org/latest/', None),
- 'sphinx': ('https://www.sphinx-doc.org/en/master/', None),
-}
+# Intersphinx mappings to reference external documentation domains - none required.
+intersphinx_mapping = {}
-# Add any paths that contain templates here, relative to this directory.
+# Static template paths
templates_path = ['_templates']
# The suffix of source filenames.
diff --git a/docs/index.rst b/docs/index.rst
index 0f217fe..c14c796 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -8,13 +8,13 @@ financial-services-register-api
A Python client library for the UK `Financial Services Register `_ `RESTful API `_.
-The `PyPI package `_ is currently at version `1.0.0`.
+The `PyPI package `_ is currently at version `1.1.0`.
.. note::
The new package `financial-services-register-api` supersedes the older package `fsrapiclient`, which will no longer be published. Existing versions of the older package may be retracted in the future. Please use the new package.
-The Financial Services Register (alternatively, 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) `_ and/or the `Prudential Regulation Authority (PRA) `_.
+The Financial Services Register (or simply, the 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) `_ and/or the `Prudential Regulation Authority (PRA) `_.
.. note::
diff --git a/docs/sources/contributing.rst b/docs/sources/contributing.rst
index 8d771a2..ca6e788 100644
--- a/docs/sources/contributing.rst
+++ b/docs/sources/contributing.rst
@@ -158,7 +158,7 @@ and the `CodeQL Analysis YML `_ is currently at version ``1.0.0``.
+The `PyPI package `_ is currently at version ``1.1.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/financial-services-register-api.rst b/docs/sources/financial-services-register-api.rst
index 460f994..7710c0d 100644
--- a/docs/sources/financial-services-register-api.rst
+++ b/docs/sources/financial-services-register-api.rst
@@ -56,7 +56,7 @@ According to the `API documentation `_
Firm Requests
=============
-Firms in the FS Register are identified by unique firm reference numbers (FRN). The following table summarises firm-specific API endpoints. For further details consult the `API documentation `_.
+Firms in the Register are identified by unique firm reference numbers (FRN). The following table summarises firm-specific API endpoints. For further details consult the `API documentation `_.
.. list-table::
:align: left
@@ -121,7 +121,7 @@ For details and examples on calling these endpoints via this library see :ref:`t
Individual Requests
===================
-Individuals associated with firms in the FS Register are identified by unique individual reference numbers (IRN). The following table summarises individual-specific API endpoints.
+Individuals associated with firms in the Register are identified by unique individual reference numbers (IRN). The following table summarises individual-specific API endpoints.
.. list-table::
:align: left
@@ -152,7 +152,7 @@ For how to call these endpoints see :ref:`this `.
Fund Requests
=============
-Funds, also referred to as collective investment schemes (CIS) in the FS Register, are identified by unique product reference numbers (PRN). The following table summarises fund-specific API endpoints.
+Funds, also referred to as collective investment schemes (CIS) in the Register, are identified by unique product reference numbers (PRN). The following table summarises fund-specific API endpoints.
.. list-table::
:align: left
diff --git a/docs/sources/financial_services_register_api/api.rst b/docs/sources/financial_services_register_api/api.rst
index 1bc1935..d54c754 100644
--- a/docs/sources/financial_services_register_api/api.rst
+++ b/docs/sources/financial_services_register_api/api.rst
@@ -8,4 +8,4 @@
.. automodule:: financial_services_register_api.api
:members:
- :private-members:
+ :special-members:
diff --git a/docs/sources/usage.rst b/docs/sources/usage.rst
index 13cdc50..f9148e3 100644
--- a/docs/sources/usage.rst
+++ b/docs/sources/usage.rst
@@ -33,11 +33,11 @@ storing the API username (signup email) and API key. These, and also the API ver
>>> client.api_version
'V0.1'
-Almost all public client methods return :py:class:`~financial_services_register_api.api.FinancialServicesRegisterApiResponse` objects, which have four properties specific to the FS Register API:
+Almost all public client methods return :py:class:`~financial_services_register_api.api.FinancialServicesRegisterApiResponse` objects, which have four properties specific to the API:
-- :py:attr:`~financial_services_register_api.api.FinancialServicesRegisterApiResponse.status` - an FS Register-specific status indicator for the
+- :py:attr:`~financial_services_register_api.api.FinancialServicesRegisterApiResponse.status` - an API-specific status indicator for the
request
-- :py:attr:`~financial_services_register_api.api.FinancialServicesRegisterApiResponse.message` - an FS Register-specific status message for the
+- :py:attr:`~financial_services_register_api.api.FinancialServicesRegisterApiResponse.message` - an API-specific status message for the
request
- :py:attr:`~financial_services_register_api.api.FinancialServicesRegisterApiResponse.data` - the response data
- :py:attr:`~financial_services_register_api.api.FinancialServicesRegisterApiResponse.resultinfo` - pagination information for the response data
@@ -172,48 +172,41 @@ The client implements a `regulated markets >> client.search_frn('hiscox insurance company limited')
'113849'
-Imprecise names in the search can produce multiple records, and will trigger an :py:class:`~financial_services_register_api.exceptions.FinancialServicesRegisterApiResponseException` indicating the problem, e.g.:
+Imprecise or inadequality specified names in the search can produce non-unique matches, in which all matching records are returned in a JSON array, for example:
.. code:: python
>>> client.search_frn('hiscox')
- Traceback (most recent call last):
- ...
- financial_services_register_api.api.FinancialServicesRegisterApiResponseException: Multiple firms returned. Firm name needs to be more precise. If you are unsure of the results please use the common search endpoint
-
-In this case the exception was generated because a common search for ``'hiscox'`` shows that there are multiple firms entries containing this name fragment:
-
-.. code:: python
-
- >>> client.common_search(urlencode({'q': 'hiscox', 'type': 'firm'})).data
[{'URL': 'https://register.fca.org.uk/services/V0.1/Firm/812274',
'Status': 'No longer authorised',
'Reference Number': '812274',
'Type of business or Individual': 'Firm',
'Name': 'HISCOX ASSURE'},
- ...
+ ...
+ ...
{'URL': 'https://register.fca.org.uk/services/V0.1/Firm/732312',
'Status': 'Authorised',
'Reference Number': '732312',
'Type of business or Individual': 'Firm',
- 'Name': 'Hiscox MGA Ltd (Postcode: EC2N 4BQ)'}]
+ 'Name': 'Hiscox MGA Ltd (Postcode: EC2N 4BQ)'}
+ ]
-Searches for non-existent firms will trigger an :py:class:`~financial_services_register_api.exceptions.FinancialServicesRegisterApiResponseException` indicating that no data found in the FS Register for the given name:
+Searches for non-existent firms will trigger an :py:class:`~financial_services_register_api.exceptions.FinancialServicesRegisterApiRequestException` indicating that no data found in the Register for the given name:
.. code:: python
@@ -230,9 +223,18 @@ A few examples are given below of IRN searches.
'MXC29012'
#
>>> client.search_irn('mark c')
- Traceback (most recent call last):
- ...
- financial_services_register_api.api.FinancialServicesRegisterApiResponseException: Multiple individuals returned. The individual name needs to be more precise. If you are unsure of the results please use the common search endpoint
+ [{'URL': 'https://register.fca.org.uk/services/V0.1/Individuals/MWC01033',
+ 'Status': 'Active',
+ 'Reference Number': 'MWC01033',
+ 'Type of business or Individual': 'Individual',
+ 'Name': 'Mark William Cowell'},
+ ...
+ ...
+ {'URL': 'https://register.fca.org.uk/services/V0.1/Individuals/RMG01106',
+ 'Status': 'Active',
+ 'Reference Number': 'RMG01106',
+ 'Type of business or Individual': 'Individual',
+ 'Name': 'Richard Mark Greenfield'}]
#
>>> client.search_irn('a nonexistent individual')
Traceback (most recent call last):
@@ -247,9 +249,18 @@ A few examples are given below of PRN searches.
'635641'
#
>>> client.search_prn('jupiter asia')
- Traceback (most recent call last):
- ...
- financial_services_register_api.api.FinancialServicesRegisterApiResponseException: Multiple funds returned. The fund name needs to be more precise. If you are unsure of the results please use the common search endpoint
+ [{'URL': 'https://register.fca.org.uk/services/V0.1/CIS/718428',
+ 'Status': 'Authorised',
+ 'Reference Number': '718428',
+ 'Type of business or Individual': 'Collective investment scheme',
+ 'Name': 'Jupiter Asian Income Fund'},
+ ...
+ ...
+ {'URL': 'https://register.fca.org.uk/services/V0.1/CIS/140620',
+ 'Status': 'Terminated',
+ 'Reference Number': '140620',
+ 'Type of business or Individual': 'Collective investment scheme',
+ 'Name': 'JUPITER ASIAN FUND'}]
#
>>> client.search_prn('a nonexistent fund')
Traceback (most recent call last):
diff --git a/src/financial_services_register_api/__version__.py b/src/financial_services_register_api/__version__.py
index 5becc17..6849410 100644
--- a/src/financial_services_register_api/__version__.py
+++ b/src/financial_services_register_api/__version__.py
@@ -1 +1 @@
-__version__ = "1.0.0"
+__version__ = "1.1.0"
diff --git a/src/financial_services_register_api/api.py b/src/financial_services_register_api/api.py
index 02e466c..3aa2e3d 100644
--- a/src/financial_services_register_api/api.py
+++ b/src/financial_services_register_api/api.py
@@ -201,10 +201,9 @@ class FinancialServicesRegisterApiClient:
>>> assert res.resultinfo
>>> client.search_frn("Hastings Insurance Services Limited")
'311492'
- >>> client.search_frn('direct line')
- Traceback (most recent call last):
- ...
- financial_services_register_api.exceptions.FinancialServicesRegisterApiResponseException: Multiple firms returned. The firm name needs to be more precise. If you are unsure of the results please use the common search endpoint.
+ >>> res = client.search_frn('direct line')
+ >>> assert isinstance(res, list)
+ >>> assert (isinstance(rec, dict) for rec in res)
>>> client.search_frn('direct line insurance plc')
'202684'
>>> assert client.get_firm('122702').data
@@ -334,8 +333,8 @@ def common_search(self, resource_name: str, resource_type: Literal['firm', 'indi
except requests.RequestException as e:
raise FinancialServicesRegisterApiRequestException(e)
- def _search_ref_number(self, resource_name: str, resource_type: str) -> str:
- """:py:class:`str`: A private base handler for public methods for searching for unique firm, individual and product reference numbers.
+ def _search_ref_number(self, resource_name: str, resource_type: str, /) -> str | list[dict[str, str]]:
+ """:py:class:`str` or :py:class:`list`: A private base handler for public search methods for unique firm, individual and product reference numbers.
.. note::
@@ -348,11 +347,14 @@ 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
- substring.
+ ``resource_type`` in the Financial Services Register on the given
+ resource name substring.
Returns a non-null string of the resource ref. number if there is
- a unique associated resource.
+ a unique associated resource. Otherwise returns :py:class.
+
+ If there are multiple resources matching the given resource name
+ substring then a JSON array of the matching records is returned.
Parameters
@@ -362,21 +364,26 @@ def _search_ref_number(self, resource_name: str, resource_type: str) -> str:
The name needs to be precise enough to guarantee a unique return
value, otherwise multiple records exist and an exception is raised.
+ resource_type : str
+ The resource type, which should be one of ``'firm'``,
+ ``'individual'``, or ``'fund'``.
+
+ Returns
+ -------
+ str, list
+ The unique resource reference number, if found. Otherwise
+ a JSON array of matching records.
+
Raises
------
ValueError
If the resource type is not of ``'firm'``, ``'individual'``, or
``'fund'``.
FinancialServicesRegisterApiRequestException
- If there was a request exception from calling the common search
- handler.
- FinancialServicesRegisterApiException
- If there was an error in the API response or in processing the response.
-
- Returns
- -------
- str
- The unique resource reference number, if found.
+ If there was an API request exception.
+ FinancialServicesRegisterApiResponseException
+ If the API response does not conform to the expected structure, or
+ no data was found for the given resource type and name.
"""
if resource_type not in API_CONSTANTS.RESOURCE_TYPES.value:
raise ValueError(
@@ -390,54 +397,51 @@ def _search_ref_number(self, resource_name: str, resource_type: str) -> str:
raise
if res.ok and res.data:
+ if len(res.data) == 1:
+ try:
+ return res.data[0]['Reference Number']
+ except KeyError:
+ raise FinancialServicesRegisterApiResponseException(
+ 'Unexpected response data structure from the API for '
+ f'{resource_type} search by name "{resource_name}"! '
+ 'Please check the API developer documentation at '
+ f'{API_CONSTANTS.DEVELOPER_PORTAL.value}.'
+ )
if len(res.data) > 1:
- raise FinancialServicesRegisterApiResponseException(
- f'Multiple {resource_type}s returned. The {resource_type} '
- 'name needs to be more precise. If you are unsure of the '
- 'results please use the common search endpoint.'
- )
-
- try:
- return res.data[0]['Reference Number']
- except (KeyError, IndexError):
- raise FinancialServicesRegisterApiResponseException(
- 'Unexpected response data structure from the FS Register '
- f'API for general {resource_type} search by name! Please '
- 'check the FS Register API developer documentation at '
- 'https://register.fca.org.uk/Developer/s/.'
- )
+ return res.data
+ elif not res.ok:
+ raise FinancialServicesRegisterApiRequestException(
+ f'API search request failed for an unknown reason: '
+ f'{res.reason}. Please check the search parameters and try again.'
+ )
elif not res.data:
- raise FinancialServicesRegisterApiResponseException(
- 'No data found in FS Register API response. Please check the search '
+ raise FinancialServicesRegisterApiRequestException(
+ 'No data found in the API response. Please check the search '
'parameters and try again.'
)
- else:
- raise FinancialServicesRegisterApiResponseException(
- f'FS Register API search request failed for some other reason: '
- f'{res.reason}.'
- )
- def search_frn(self, firm_name: str) -> str:
- """:py:class:`str`: Returns the unique firm reference number (FRN) of a given firm, if found.
+ def search_frn(self, firm_name: str) -> str | list[dict[str, str]]:
+ """:py:class:`str` or :py:class:`list`: Returns the unique firm reference number (FRN) of a given firm, if found, or else a JSON array of matching records.
Calls the private method
:py:meth:`~financial_services_register_api.FinancialServicesRegisterApiClient._search_ref_number` to do the
search.
Returns a non-null string of the FRN if there is a unique associated
- firm.
+ firm. Otherwise, a JSON array of all matching records is returned.
Parameters
----------
firm_name : str
- The firm name - need not be in any particular case. The name
- needs to be precise enough to guarantee a unique return value,
- otherwise multiple records exist and an exception is raised.
+ The firm name (case insensitive). The name needs to be precise
+ enough to guarantee a unique return value, otherwise a JSON array
+ of all matching records are returned.
Returns
-------
str
- A string version of the firm reference number (FRN), if found.
+ A string version of the firm reference number (FRN), if found, or
+ a JSON array of all matching records.
Examples
--------
@@ -447,22 +451,15 @@ def search_frn(self, firm_name: str) -> str:
'311492'
>>> client.search_frn('hiscox insurance company limited')
'113849'
- >>> client.search_frn('direct line')
- Traceback (most recent call last):
- ...
- financial_services_register_api.exceptions.FinancialServicesRegisterApiResponseException: Multiple firms returned. The firm name needs to be more precise. If you are unsure of the results please use the common search endpoint.
- >>> client.search_frn('direct line insurance')
- Traceback (most recent call last):
- ...
- financial_services_register_api.exceptions.FinancialServicesRegisterApiResponseException: Multiple firms returned. The firm name needs to be more precise. If you are unsure of the results please use the common search endpoint.
- >>> client.search_frn('direct line insurance plc')
- '202684'
- >>> client.search_frn('Hiscxo Insurance Company')
- Traceback (most recent call last):
- ...
- financial_services_register_api.exceptions.FinancialServicesRegisterApiResponseException: No data found in FS Register API response. Please check the search parameters and try again.
+ >>> res = client.search_frn('direct line')
+ >>> assert isinstance(res, list)
+ >>> assert all(isinstance(rec, dict) for rec in res)
>>> client.search_frn('hiscox insurance company')
'113849'
+ >>> client.search_frn('nonexistent company')
+ Traceback (most recent call last):
+ ...
+ financial_services_register_api.exceptions.FinancialServicesRegisterApiRequestException: No data found in the API response. Please check the search parameters and try again.
"""
return self._search_ref_number(
firm_name,
@@ -1160,41 +1157,29 @@ def get_firm_appointed_representatives(self, frn: str) -> FinancialServicesRegis
modifiers=('AR',)
)
- def search_irn(self, individual_name: str) -> str:
- """:py:class:`str`: Returns the unique individual reference number (IRN) of a given individual, if found.
-
- Uses the API common search endpoint:
- ::
-
- /V0.1/Search?q=&type=individual
+ def search_irn(self, individual_name: str) -> str | list[dict[str, str]]:
+ """:py:class:`str` or :py:class:`list`: Returns the unique individual reference number (IRN) of a given individual, if found, or else a JSON array of matching records.
- to perform a case-insensitive individual-type search in the FS Register on the
- given name.
+ Calls the private method
+ :py:meth:`~financial_services_register_api.FinancialServicesRegisterApiClient._search_ref_number`
+ to do the search.
Returns a non-null string of the IRN if there is a unique associated
- individual.
+ individual. Otherwise, a JSON array of all matching records is
+ returned.
Parameters
----------
- individual_name : str
- The individual name - need not be in any particular case. The name
- needs to be precise enough to guarantee a unique return value,
- otherwise multiple records exist and an exception is raised.
-
- Raises
- ------
- FinancialServicesRegisterApiRequestException
- If there was a request exception from calling the common search
- handler.
-
- FinancialServicesRegisterApiException
- If there was an error in the API response or in processing the response.
+ firm_name : str
+ The individual name (case insensitive). The name needs to be precise
+ enough to guarantee a unique return value, otherwise a JSON array
+ of all matching records are returned.
Returns
-------
str
- A string version of the individual reference number (IRN), if
- found.
+ A string version of the individual reference number (IRN), if found, or
+ a JSON array of all matching records.
Examples
--------
@@ -1204,14 +1189,13 @@ def search_irn(self, individual_name: str) -> str:
'MXC29012'
>>> client.search_irn('mark Carney')
'MXC29012'
- >>> client.search_irn('Mark C')
+ >>> res = client.search_irn('Mark C')
+ >>> assert isinstance(res, list)
+ >>> assert all(isinstance(rec, dict) for rec in res)
+ >>> client.search_irn('nonexistent individual')
Traceback (most recent call last):
...
- financial_services_register_api.exceptions.FinancialServicesRegisterApiResponseException: Multiple individuals returned. The individual name needs to be more precise. If you are unsure of the results please use the common search endpoint.
- >>> client.search_irn('A Nonexistent Person')
- Traceback (most recent call last):
- ...
- financial_services_register_api.exceptions.FinancialServicesRegisterApiResponseException: No data found in FS Register API response. Please check the search parameters and try again.
+ financial_services_register_api.exceptions.FinancialServicesRegisterApiRequestException: No data found in the API response. Please check the search parameters and try again.
"""
return self._search_ref_number(
individual_name,
@@ -1336,60 +1320,42 @@ def get_individual_disciplinary_history(self, irn: str) -> FinancialServicesRegi
modifiers=('DisciplinaryHistory',)
)
- def search_prn(self, fund_name: str) -> str:
- """:py:class:`str` : Returns the unique product reference number (PRN) of a given fund or collective investment scheme (CIS), including subfunds, if it exists.
-
- Uses the API common search endpoint:
- ::
-
- /V0.1/Search?q=&type=fund
+ def search_prn(self, fund_name: str) -> str | list[dict[str, str]]:
+ """:py:class:`str` or :py:class:`list`: Returns the unique product reference number (PRN) of a given fund, if found, or else a JSON array of matching records.
- to perform a case-insensitive fund-type search in the FS Register on
- the given name.
+ Calls the private method
+ :py:meth:`~financial_services_register_api.FinancialServicesRegisterApiClient._search_ref_number`
+ to do the search.
Returns a non-null string of the PRN if there is a unique associated
- fund.
+ fund. Otherwise, a JSON array of all matching records is returned.
Parameters
----------
- fund_name : str
- The fund name - need not be in any particular case. The name needs
- to be precise enough to guarantee a unique return value, otherwise
- multiple records exist and an exception is raised.
-
- Raises
- ------
- FinancialServicesRegisterApiRequestException
- If there was a request exception from calling the common search
- handler.
-
- FinancialServicesRegisterApiResponseException
- If there was an error in the API response or in processing the
- response.
+ firm_name : str
+ The fund name (case insensitive). The name needs to be precise
+ enough to guarantee a unique return value, otherwise a JSON array
+ of all matching records are returned.
Returns
-------
str
- A string version of the product reference number (PRN), if found.
+ A string version of the product reference number (PRN), if found, or
+ a JSON array of all matching records.
Examples
--------
>>> import os
>>> client = FinancialServicesRegisterApiClient(os.environ['API_USERNAME'], os.environ['API_KEY'])
- >>> client.search_prn('Northern Trust')
- Traceback (most recent call last):
- ...
- financial_services_register_api.exceptions.FinancialServicesRegisterApiResponseException: Multiple funds returned. The fund name needs to be more precise. If you are unsure of the results please use the common search endpoint.
- >>> client.search_prn('Northern Trust High Dividend ESG World Equity')
- Traceback (most recent call last):
- ...
- financial_services_register_api.exceptions.FinancialServicesRegisterApiResponseException: Multiple funds returned. The fund name needs to be more precise. If you are unsure of the results please use the common search endpoint.
>>> client.search_prn('Northern Trust High Dividend ESG World Equity Feeder Fund')
'913937'
- >>> client.search_prn('A nonexistent fund')
+ >>> res = client.search_prn('Northern Trust')
+ >>> assert isinstance(res, list)
+ >>> assert all(isinstance(rec, dict) for rec in res)
+ >>> client.search_prn('nonexistent fund')
Traceback (most recent call last):
...
- financial_services_register_api.exceptions.FinancialServicesRegisterApiResponseException: No data found in FS Register API response. Please check the search parameters and try again.
+ financial_services_register_api.exceptions.FinancialServicesRegisterApiRequestException: No data found in the API response. Please check the search parameters and try again.
"""
return self._search_ref_number(
fund_name,
@@ -1593,7 +1559,7 @@ def get_regulated_markets(self) -> FinancialServicesRegisterApiResponse:
if __name__ == "__main__": # pragma: no cover
# Doctest the module from the project root using
#
- # export API_USERNAME= && export API_KEY= && python -m doctest -v src/financial_services_register_api/api.py && unset API_USERNAME && unset API_KEY
+ # export API_USERNAME= && export API_KEY= && PYTHONPATH=src python -m doctest -v src/financial_services_register_api/api.py && unset API_USERNAME && unset API_KEY
#
import doctest
doctest.testmod()
diff --git a/src/financial_services_register_api/constants.py b/src/financial_services_register_api/constants.py
index cb9f64e..ea25b20 100644
--- a/src/financial_services_register_api/constants.py
+++ b/src/financial_services_register_api/constants.py
@@ -26,6 +26,7 @@ class FINANCIAL_SERVICES_REGISTER_API_CONSTANTS(Enum):
API_VERSION = 'V0.1'
BASEURL = f'https://register.fca.org.uk/services/{API_VERSION}'
+ DEVELOPER_PORTAL = 'https://register.fca.org.uk/Developer/s/'
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 36530fa..5f01669 100644
--- a/tests/units/test_api.py
+++ b/tests/units/test_api.py
@@ -126,16 +126,16 @@ def test_financial_services_register_api_client___search_ref_number__exceptional
test_client._search_ref_number('exceptional search', 'individual')
test_client._search_ref_number('exceptional search', 'fund')
- def test_financial_services_register_api_client___search_ref_number__response_not_ok__api_response_exception_raised(self):
+ def test_financial_services_register_api_client___search_ref_number__response_not_ok__api_request_exception_raised(self):
test_client = FinancialServicesRegisterApiClient(self._api_username, self._api_key)
with mock.patch('financial_services_register_api.api.FinancialServicesRegisterApiClient.common_search', return_value=mock.MagicMock(ok=False)):
- with pytest.raises(FinancialServicesRegisterApiResponseException):
+ with pytest.raises(FinancialServicesRegisterApiRequestException):
test_client._search_ref_number('exceptional search', 'firm')
test_client._search_ref_number('exceptional search', 'individual')
test_client._search_ref_number('exceptional search', 'fund')
- def test_financial_services_register_api_client___search_ref_number__no_fs_register_data_in_response__api_response_exception_raised(self):
+ def test_financial_services_register_api_client___search_ref_number__no_fs_register_data_in_response__api_request_exception_raised(self):
test_client = FinancialServicesRegisterApiClient(self._api_username, self._api_key)
with mock.patch('financial_services_register_api.api.FinancialServicesRegisterApiSession.get') as mock_api_session_get:
@@ -143,25 +143,12 @@ def test_financial_services_register_api_client___search_ref_number__no_fs_regis
mock_response.json = mock.MagicMock(name='json', return_value=dict())
mock_api_session_get.return_value = mock_response
- with pytest.raises(FinancialServicesRegisterApiResponseException):
- test_client._search_ref_number('exceptional search', 'firm')
- test_client._search_ref_number('exceptional search', 'individual')
- test_client._search_ref_number('exceptional search', 'fund')
-
- def test_financial_services_register_api_client___search_ref_number__fs_register_data_with_index_error__api_response_exception_raised(self):
- test_client = FinancialServicesRegisterApiClient(self._api_username, self._api_key)
-
- with mock.patch('financial_services_register_api.api.FinancialServicesRegisterApiSession.get') as mock_api_session_get:
- mock_response = mock.create_autospec(requests.Response)
- mock_response.json = mock.MagicMock(name='json', return_value={'Data': []})
- mock_api_session_get.return_value = mock_response
-
- with pytest.raises(FinancialServicesRegisterApiResponseException):
- test_client._search_ref_number('exceptional search', 'firm')
- test_client._search_ref_number('exceptional search', 'individual')
- test_client._search_ref_number('exceptional search', 'fund')
+ with pytest.raises(FinancialServicesRegisterApiRequestException):
+ test_client._search_ref_number('bad search', 'firm')
+ test_client._search_ref_number('bad search', 'individual')
+ test_client._search_ref_number('bad search', 'fund')
- def test_financial_services_register_api_client___search_ref_number__fs_register_data_with_key_error__api_response_exception_raised(self):
+ def test_financial_services_register_api_client___search_ref_number__fs_register_data_with_key_error__api_request_exception_raised(self):
test_client = FinancialServicesRegisterApiClient(self._api_username, self._api_key)
with mock.patch('financial_services_register_api.api.FinancialServicesRegisterApiSession.get') as mock_api_session_get:
@@ -170,23 +157,23 @@ def test_financial_services_register_api_client___search_ref_number__fs_register
mock_api_session_get.return_value = mock_response
with pytest.raises(FinancialServicesRegisterApiResponseException):
- test_client._search_ref_number('exceptional search', 'firm')
- test_client._search_ref_number('exceptional search', 'individual')
- test_client._search_ref_number('exceptional search', 'fund')
+ test_client._search_ref_number('bad response', 'firm')
+ test_client._search_ref_number('bad response', 'individual')
+ test_client._search_ref_number('bad response', 'fund')
def test_financial_services_register_api_client___search_ref_number__incorrectly_specified_resource__no_fs_register_data__api_response_exception_raised(self):
test_client = FinancialServicesRegisterApiClient(self._api_username, self._api_key)
# Covers the case of a failed FRN search for an incorrectly specified firm
- with pytest.raises(FinancialServicesRegisterApiResponseException):
+ with pytest.raises(FinancialServicesRegisterApiRequestException):
test_client._search_ref_number('nonexistent123 insurance company', 'firm')
# Covers the case of a failed IRN search for an incorrectly specified individual
- with pytest.raises(FinancialServicesRegisterApiResponseException):
+ with pytest.raises(FinancialServicesRegisterApiRequestException):
test_client._search_ref_number('a nonexistent individual', 'individual')
# Covers the case of a failed PRN search for an incorrectly specified firm
- with pytest.raises(FinancialServicesRegisterApiResponseException):
+ with pytest.raises(FinancialServicesRegisterApiRequestException):
test_client._search_ref_number('a nonexistent fund', 'fund')
def test_financial_services_register_api_client___search_ref_number__inadequately_specified_resource__nonunique_fs_register_data__api_response_exception_raised(self):
@@ -194,18 +181,21 @@ def test_financial_services_register_api_client___search_ref_number__inadequatel
# Covers the case of an FRN search based on an inadequately specified firm
# that produces multiple results
- with pytest.raises(FinancialServicesRegisterApiResponseException):
- test_client._search_ref_number('direct line', 'firm')
+ recv_recs = test_client._search_ref_number('direct line', 'firm')
+ assert isinstance(recv_recs, list)
+ assert all(isinstance(rec, dict) for rec in recv_recs)
# Covers the case of an IRN search based on an inadequately specified individual
# that produces multiple results
- with pytest.raises(FinancialServicesRegisterApiResponseException):
- test_client._search_ref_number('john smith', 'individual')
+ recv_recs = test_client._search_ref_number('john smith', 'individual')
+ assert isinstance(recv_recs, list)
+ assert all(isinstance(rec, dict) for rec in recv_recs)
# Covers the case of an PRN search based on an inadequately specified firm
# that produces multiple results
- with pytest.raises(FinancialServicesRegisterApiResponseException):
- test_client._search_ref_number('jupiter', 'fund')
+ recv_recs = test_client._search_ref_number('jupiter', 'fund')
+ assert isinstance(recv_recs, list)
+ assert all(isinstance(rec, dict) for rec in recv_recs)
def test_financial_services_register_api_client___search_ref_number__correctly_and_adequately_specced_resource__unique_fs_register_data__response_returned_ok(self):
test_client = FinancialServicesRegisterApiClient(self._api_username, self._api_key)
@@ -241,6 +231,18 @@ def test_financial_services_register_api_client___search_frn__correctly_and_adeq
assert isinstance(recv_frn, str)
assert recv_frn
+ def test_financial_services_register_api_client___search_frn__inadequately_specced_firm__json_array_of_matching_records__response_returned_ok(self):
+ test_client = FinancialServicesRegisterApiClient(self._api_username, self._api_key)
+
+ # Covers the case of a successful FRN search for existing, unique firms
+ recv_recs = test_client._search_ref_number('hsbc', 'firm')
+ assert isinstance(recv_recs, list)
+ assert all(isinstance(rec, dict) for rec in recv_recs)
+
+ recv_recs = test_client._search_ref_number('northern', 'firm')
+ assert isinstance(recv_recs, list)
+ assert all(isinstance(rec, dict) for rec in recv_recs)
+
def test_financial_services_register_api_client___get_resource_info__invalid_resource_type__no_modifiers__value_error_raised(self):
test_client = FinancialServicesRegisterApiClient(self._api_username, self._api_key)
@@ -837,6 +839,14 @@ def test_financial_services_register_api_client___search_irn__correctly_and_adeq
assert isinstance(recv_irn, str)
assert recv_irn
+ def test_financial_services_register_api_client___search_irn__inadequately_specced_individual__json_array_of_matching_records__response_returned_ok(self):
+ test_client = FinancialServicesRegisterApiClient(self._api_username, self._api_key)
+
+ # Covers the case of a successful IRN search for existing, unique individuals
+ recv_recs = test_client.search_irn('john smith')
+ assert isinstance(recv_recs, list)
+ assert (isinstance(rec, dict) for rec in recv_recs)
+
def test_financial_services_register_api_client__get_individual(self):
test_client = FinancialServicesRegisterApiClient(self._api_username, self._api_key)
@@ -898,6 +908,14 @@ def test_financial_services_register_api_client___search_prn__correctly_and_adeq
assert isinstance(recv_prn, str)
assert recv_prn
+ def test_financial_services_register_api_client___search_prn__inadequately_specced_fund__json_array_of_matching_records__response_returned_ok(self):
+ test_client = FinancialServicesRegisterApiClient(self._api_username, self._api_key)
+
+ # Covers the case of a successful PRN search for existing, unique funds
+ recv_recs = test_client.search_prn('jupiter')
+ assert isinstance(recv_recs, list)
+ assert all(isinstance(rec, dict) for rec in recv_recs)
+
def test_financial_services_register_api_client__get_fund(self):
test_client = FinancialServicesRegisterApiClient(self._api_username, self._api_key)