From bd039a3d0da6caf16022496e58d75d7e461aef9b Mon Sep 17 00:00:00 2001 From: Anish Bhusal Date: Thu, 12 Oct 2023 11:53:31 -0500 Subject: [PATCH] Add classmethod for initializing Sinequa class (#18) * added classmethod for initializing Sinequa class * updated docs --- README.md | 49 ++++++++++++++++++++++---------------- pynequa/api/api.py | 20 +++++++++------- pynequa/core.py | 46 +++++++++++++++++++++++++++++------ tests/test_api.py | 4 ++-- tests/test_search_query.py | 39 ++++++++++++++++++++++++++++-- 5 files changed, 118 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index b523aaa..6f95cf9 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ # Pynequa -A python library to handle communication with Sinequa REST API. +A python library to handle communication with Sinequa REST API. [![Documentation Status](https://readthedocs.org/projects/pynequa/badge/?version=latest)](https://pynequa.readthedocs.io/en/latest/?badge=latest) ![Build](https://github.com/NASA-IMPACT/pynequa/actions/workflows/pkg_build_check.yml/badge.svg) > Sinequa is an enterprise search tool. It provides a cognitive search and analytics platform that helps organizations gain insights from their structured and unstructured data spread across various sources, including databases, documents, emails, websites, and more. -## Installation +## Installation ``` $ pip install pynequa @@ -14,37 +14,46 @@ A python library to handle communication with Sinequa REST API. ## Example Usage ``` -import pynequa +import pynequa from pynequa.models import QueryParams -# provide following config parameters +# provide following config parameters config = { - "base_url": "", - "app_name": "", + "base_url": "", + "app_name": "", "access_token":"", "query_name": "" } -#initialize a Sinequa connector instance -sinequa=pynequa.Sinequa(config=config) +# initialize a Sinequa connector instance +sinequa=pynequa.Sinequa.from_config(config) + +# OR +# you can directly instantiate Sinequa using +sinequa = pynequa.Sinequa( + access_token: config["access_token"], + base_url: config["base_url"], + app_name: config["app_name"], + query_name: config["query_name"], +) params = QueryParams() params.search_text = "" -..... #other params +# other params -#perform a search query operation +# perform a search query operation results=sinequa.search_query(params) ``` -## Feature Roadmap -Implement following REST endpoints to manage requests with Sinequa API. +## Feature Roadmap +Implement following REST endpoints to manage requests with Sinequa API. **Search Endpoints:** -- [x] search.app +- [x] search.app - [x] search.dataset - [x] search.query - [x] queryintent @@ -64,12 +73,12 @@ Implement following REST endpoints to manage requests with Sinequa API. - [ ] search.suggest - [ ] search.custom - [ ] suggestField -- [ ] engine.sql +- [ ] engine.sql **Indexing Endpoints** - [ ] indexing.collection - [ ] indexing.customCollection -- [ ] indexing.partition +- [ ] indexing.partition **Operating Task Endpoints** - [ ] operation.actionStatus @@ -83,15 +92,15 @@ Implement following REST endpoints to manage requests with Sinequa API. **General Endpoints** - [ ] audit.notify -- [ ] admin.config -- [ ] dev.plugin -- [ ] multi +- [ ] admin.config +- [ ] dev.plugin +- [ ] multi ## Development -Check [DEVELOPERS GUIDE](DEVELOPMENT.md) for details. +Check [DEVELOPERS GUIDE](DEVELOPMENT.md) for details. ## Contributing -When contributing to this repository, please first discuss the change you wish to make via issue, email, or any other method with the authors of this repository before making a change. +When contributing to this repository, please first discuss the change you wish to make via issue, email, or any other method with the authors of this repository before making a change. ## License diff --git a/pynequa/api/api.py b/pynequa/api/api.py index 6e49f47..30d0bf0 100644 --- a/pynequa/api/api.py +++ b/pynequa/api/api.py @@ -1,22 +1,24 @@ -import requests -import os from typing import Dict +import os +import requests + class API: ''' - API Class handles all HTTP Requests + API Class handles all HTTP Requests - Attributes: + Attributes: base_url(string): REST API base URL for Sinequa instance access_token(string): token for Sinequa authentication ''' - base_url: str - access_token: str - def __init__(self, config) -> None: - self.access_token = config["access_token"] - self.base_url = config["base_url"] + def __init__(self, access_token: str, base_url: str) -> None: + if not access_token or not base_url: + raise ValueError("access_token and base_url must not be empty") + + self.access_token = access_token + self.base_url = base_url def _get_headers(self) -> Dict: headers = { diff --git a/pynequa/core.py b/pynequa/core.py index bbc2485..de330e1 100644 --- a/pynequa/core.py +++ b/pynequa/core.py @@ -1,5 +1,7 @@ -from pynequa.api import API +from __future__ import annotations from typing import Optional, List, Dict + +from pynequa.api import API from pynequa.models import QueryParams, TreeParams @@ -8,17 +10,47 @@ class Sinequa(API): Sinequa API Client for Python Attributes: + access_token(str): authentication token for Sinequa + base_url(str): base URL for hosted Sinequa instance app_name(str): name of Sinequa app query_name(str): name of search query web service ''' - app_name: str - query_name: str - def __init__(self, config: Dict) -> None: - super().__init__(config) - self.app_name = config["app_name"] # name of application + def __init__( + self, + access_token: str, + base_url: str, + app_name: str = "vanilla-search", + query_name: str = "query", + ) -> None: + super().__init__(access_token=access_token, base_url=base_url) + self.app_name = app_name # name of application # name of search query web service - self.query_name = config["query_name"] + self.query_name = query_name + + @classmethod + def from_config(cls, cfg: Dict) -> Sinequa: + ''' + This method creates an instance of Sinequa class from a configuration + dictionary. + + Args: + cfg (Dict): A dictionary containing configuration parameters. + It should include the following keys: + - "access_token": A valid access token for authentication. + - "base_url": The base URL for the Sinequa API. + - "app_name": Name of the Sinequa application. + - "query_name": Name of the search query service. + + Returns: + Sinequa: An instance of Sinequa class initialized with given configuration + ''' + return cls( + access_token=cfg["access_token"], + base_url=cfg["base_url"], + app_name=cfg["app_name"], + query_name=cfg["query_name"], + ) @staticmethod def _prepare_kwargs(payload: Dict, kwargs: Dict) -> Dict: diff --git a/tests/test_api.py b/tests/test_api.py index 86f4cbb..5566b26 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -18,7 +18,7 @@ def test_get(self, mock_get): mock_get.return_value.status_code = 200 mock_get.return_value.json.return_value = sample_response - api = API(mock_config) + api = API(mock_config["access_token"], mock_config["base_url"]) resp = api.get("search.query") self.assertEqual(resp.status_code, 200) @@ -29,7 +29,7 @@ def test_post(self, mock_post): mock_post.return_value.status_code = 200 mock_post.return_value.json.return_value = sample_response - api = API(mock_config) + api = API(mock_config["access_token"], mock_config["base_url"]) endpoint = "search.query" payload = { "app": "sba", diff --git a/tests/test_search_query.py b/tests/test_search_query.py index 072bcdd..a4814f3 100644 --- a/tests/test_search_query.py +++ b/tests/test_search_query.py @@ -9,7 +9,34 @@ class TestSinequaSearchQuery(unittest.TestCase): + """ + A unittest class for testing Sinequa search queries. + + This class contains two test methods that evaluate the behavior of Sinequa + search queries under different authentication scenarios. + + Test Methods: + - `test_search_query_without_auth`: Tests a search query without + authentication. It creates a Sinequa instance using provided + configuration, sends a search query, and checks the error code in + the response. + + - `test_search_query_with_auth`: Tests a search query with + authentication. It retrieves an access token from environment + variables, creates a Sinequa instance using provided configuration, + sends a search query, and checks the method result in the response. + + """ + def test_search_query_without_auth(self): + """ + Test a search query without authentication. + + This test case creates a Sinequa instance with the given configuration, + sets up query parameters, sends a search query, and asserts that the + expected error code is returned in the response. + """ + config = { "base_url": base_url, "app_name": app_name, @@ -19,11 +46,19 @@ def test_search_query_without_auth(self): query_params = QueryParams() query_params.search_text = "NASA" - sinequa = Sinequa(config=config) + sinequa = Sinequa.from_config(config) resp = sinequa.search_query(query_params=query_params) self.assertEqual(resp["ErrorCode"], 6) def test_search_query_with_auth(self): + """ + Test a search query with authentication. + + This test case retrieves an access token from environment variables, + creates a Sinequa instance with the given configuration, sets up query + parameters, sends a search query, and asserts that the method result in + the response is as expected. + """ access_token = os.environ.get("SINEQUA_ACCESS_TOKEN") config = { "base_url": base_url, @@ -34,7 +69,7 @@ def test_search_query_with_auth(self): query_params = QueryParams() query_params.search_text = "NASA" - sinequa = Sinequa(config=config) + sinequa = Sinequa.from_config(config) resp = sinequa.search_query(query_params=query_params) self.assertEqual(resp["methodresult"], "ok")