6 |
7 |
8 |
14 |
15 |
32 |
--------------------------------------------------------------------------------
/docs/METAKG.md:
--------------------------------------------------------------------------------
1 | ##### What is Meta-KG?
2 |
3 | The SmartAPI Meta Knowledge Graph (Meta-KG) represents how biomedical concepts can be connected through APIs. Each node in the meta-KG represents a biolink entity type, e.g. Gene, SequenceVariant, ChemicalSubstance. Each edge in the meta-KG represents a unique combination of a biolink predicate, e.g. targets, treats, and an API which delivers the association.
4 |
5 | The Meta-KG is constructed using the collection of SmartAPI specifications currently registered in SmartAPI. All APIs registered with x-smartapi field will be integrated into the meta-KG. In addition, all ReasonerStdAPIs registered in SmartAPI and implemented the **/predicate** endpoint will also be integrated.
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/schemas.ini:
--------------------------------------------------------------------------------
1 | [swagger]
2 | swagger_v2: https://raw.githubusercontent.com/swagger-api/swagger-editor/v3.6.1/src/plugins/validate-json-schema/structural-validation/swagger2-schema.js
3 |
4 | [openapi]
5 | openapi_v3.0: https://raw.githubusercontent.com/swagger-api/swagger-editor/v3.15.6/src/plugins/json-schema-validator/oas3-schema.yaml
6 | openapi_v3.1: https://raw.githubusercontent.com/swagger-api/validator-badge/v2.1.5/src/main/resources/schemas/31/official.json
7 | x-translator: https://raw.githubusercontent.com/NCATSTranslator/translator_extensions/production/x-translator/smartapi_x-translator_schema.json
8 | x-trapi: https://raw.githubusercontent.com/NCATSTranslator/translator_extensions/production/x-trapi/smartapi_x-trapi_schema.json
9 |
--------------------------------------------------------------------------------
/web-app/src/store/modules/portals.js:
--------------------------------------------------------------------------------
1 | import translator_img from '@/assets/img/TranslatorLogo.jpg';
2 |
3 | export const portals = {
4 | state: () => ({
5 | portals: [
6 | {
7 | name: 'translator',
8 | title: 'Translator',
9 | link: '/portal/translator',
10 | image: translator_img,
11 | description:
12 | 'This program focuses on building tools for massive knowledge integration in support of biomedical and translational science. Learn more'
13 | }
14 | ]
15 | }),
16 | strict: true,
17 | getters: {
18 | portals: (state) => {
19 | return state.portals;
20 | }
21 | }
22 | };
23 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug-issue-report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug/Issue Report
3 | about: Report an issue with SmartAPI
4 | title: ''
5 | labels: bug
6 | assignees: marcodarko
7 |
8 | ---
9 |
10 | # Expected Behavior
11 |
12 | Please describe the behavior you are expecting
13 |
14 | # Current Behavior
15 |
16 | What is the current behavior?
17 |
18 | # Failure Information (for bugs)
19 |
20 | Please help provide information about the failure if this is a bug. If it is not a bug, please remove the rest of this template.
21 |
22 | ## Steps to Reproduce
23 |
24 | Please provide detailed steps for reproducing the issue.
25 |
26 | 1. step 1
27 | 2. step 2
28 | 3. you get it...
29 |
30 | ## Failure Logs
31 |
32 | Please include any relevant log snippets or files here.
33 |
--------------------------------------------------------------------------------
/web-app/cypress/support/e2e.js:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example support/index.js is processed and
3 | // loaded automatically before your test files.
4 | //
5 | // This is a great place to put global configuration and
6 | // behavior that modifies Cypress.
7 | //
8 | // You can change the location of this file or turn off
9 | // automatically serving support files with the
10 | // 'supportFile' configuration option.
11 | //
12 | // You can read more here:
13 | // https://on.cypress.io/configuration
14 | // ***********************************************************
15 |
16 | // Import commands.js using ES2015 syntax:
17 | import './commands'
18 |
19 | // Alternatively you can use CommonJS syntax:
20 | // require('./commands')
21 |
--------------------------------------------------------------------------------
/src/tests/_utils/metakg/integration/parser/query_operation_test.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 | from utils.metakg.query_operation import QueryOperationObject
4 |
5 |
6 | class TestQueryOperationObjectClass(unittest.TestCase):
7 | def test_missing_fiels_should_return_none(self):
8 | op = {
9 | "parameters": {"gene": "{inputs[0]}"},
10 | "requestBody": {id: "{inputs[1]"},
11 | "supportBatch": "false",
12 | "inputs": [{id: "NCBIGene", "semantic": "Gene"}],
13 | "outputs": [{"id": "NCBIGene", "semantic": "Gene"}],
14 | "predicate": "related_to",
15 | "response_mapping": {},
16 | }
17 |
18 | obj = QueryOperationObject()
19 | obj.xBTEKGSOperation = op
20 | self.assertIsNone(obj.input_separator)
21 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | biothings[web_extra]==1.0.1
2 | # git+https://github.com/biothings/biothings.api.git@1.0.x#egg=biothings[web_extra]
3 |
4 | # document validation
5 | jsonschema>=4.4.0
6 |
7 | # web handling
8 | pycurl==7.45.2 # to use curl_httpclient in tornado
9 | #chardet==3.0.4
10 | aiocron==1.8
11 |
12 | # gitdb version specified because gitdb.utils.compat not available in newest version
13 | gitdb==4.0.11
14 |
15 | # local issuer certificate
16 | certifi
17 |
18 | # pytest
19 |
20 | # used in admin.py to lock file
21 | filelock
22 | # used in admin.py to upload file to s3
23 | boto3
24 |
25 | # Biolink Model Toolkit, used in /api/metakg endpoint
26 | bmt-lite-v3.1.0==2.2.2; python_version >= "3.7.0" and python_version < "3.9.0"
27 | bmt-lite-v3.6.0==2.3.0; python_version >= "3.9.0"
28 | networkx==3.1.0
29 |
--------------------------------------------------------------------------------
/src/utils/metakg/biolink_helpers.py:
--------------------------------------------------------------------------------
1 | from typing import List, Union
2 |
3 | import bmt
4 |
5 | # Initialize the Biolink Model Toolkit instance globally if it's used frequently
6 | # or pass it as a parameter to functions that require it.
7 | toolkit = bmt.Toolkit()
8 |
9 |
10 | def get_expanded_values(value: Union[str, List[str]], toolkit_instance=toolkit) -> List[str]:
11 | """Return expanded value list for a given Biolink class name."""
12 | if isinstance(value, str):
13 | value = [value]
14 | _out = []
15 | for v in value:
16 | try:
17 | v = toolkit_instance.get_descendants(v, reflexive=True, formatted=True)
18 | v = [x.split(":")[-1] for x in v] # Remove 'biolink:' prefix
19 | except ValueError:
20 | v = [v]
21 | _out.extend(v)
22 | return _out
23 |
--------------------------------------------------------------------------------
/src/utils/metakg/component.py:
--------------------------------------------------------------------------------
1 | class Components:
2 | components = {}
3 |
4 | def __init__(self, components):
5 | self.components = components
6 |
7 | def fetch(self, obj, key):
8 | if key in ["#", "components"]:
9 | return obj
10 | if obj and key in obj:
11 | return obj[key]
12 | return None
13 |
14 | def fetch_component_by_ref(self, ref):
15 | if ref.startswith("#/components/"):
16 | if ref[-1] == "/":
17 | ref = ref[0:-1]
18 | res = self.components
19 | paths = ref.split("/")
20 | try:
21 | for ele in paths:
22 | res = self.fetch(res, ele)
23 | except Exception:
24 | return None
25 | return res
26 | return None
27 |
--------------------------------------------------------------------------------
/web-app/src/assets/img/icon-summary.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
15 |
--------------------------------------------------------------------------------
/web-app/cypress/support/commands.js:
--------------------------------------------------------------------------------
1 | // ***********************************************
2 | // This example commands.js shows you how to
3 | // create various custom commands and overwrite
4 | // existing commands.
5 | //
6 | // For more comprehensive examples of custom
7 | // commands please read more here:
8 | // https://on.cypress.io/custom-commands
9 | // ***********************************************
10 | //
11 | //
12 | // -- This is a parent command --
13 | // Cypress.Commands.add('login', (email, password) => { ... })
14 | //
15 | //
16 | // -- This is a child command --
17 | // Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
18 | //
19 | //
20 | // -- This is a dual command --
21 | // Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
22 | //
23 | //
24 | // -- This will overwrite an existing command --
25 | // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
26 |
--------------------------------------------------------------------------------
/web-app/src/assets/img/ok.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
17 |
--------------------------------------------------------------------------------
/web-app/src/assets/img/icon-registry.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
17 |
--------------------------------------------------------------------------------
/web-app/src/views/PortalHome.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
SmartAPI Portals
8 |
9 |
10 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
36 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/report-broken.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Report Broken/Down API
3 | about: Report a broken API or an issue with any API
4 | title: ''
5 | labels: help wanted
6 | assignees: marcodarko
7 |
8 | ---
9 |
10 | ## Report a broken API
11 |
12 | Fill this out if you would like to report an issue with an API. Please include as much detail as possible to make sure we address your issue as soon as possible.
13 |
14 |
15 |
16 | *Let's get some information first*
17 |
18 | **I'm am reporting...**
19 |
20 | - [ ] An API with broken source URL
21 |
22 | - [ ] An API Down
23 |
24 | - [ ] An API with FAIL uptime
25 |
26 | - [ ] A Different issue. Please specify below.
27 |
28 |
29 | * Details *
30 |
31 |
32 | * What is the ***name*** and ***id*** of the API? (You can find the ID under the Details section of the API) *eg. TheAPI : 124094327094378*
33 |
34 |
35 |
36 |
37 | * How is this issue affecting your work? This can help us prioritize our issues.
38 |
39 |
40 |
41 |
42 | Thank you! We will review this request ASAP and will inform you of any progress.
43 |
--------------------------------------------------------------------------------
/web-app/src/components/Card.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{ title }}
6 |
7 |
8 |
9 | Continue
10 |
11 |
12 |
13 |
14 |
46 |
--------------------------------------------------------------------------------
/web-app/src/assets/img/v0.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
20 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 The Network of BioThings Consortium
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/.github/workflows/check_backup.yml:
--------------------------------------------------------------------------------
1 | name: Check S3 Backup and Notify Slack
2 |
3 | on:
4 | workflow_dispatch: # Allows manual trigger from GitHub Actions UI
5 | schedule:
6 | - cron: '0 13 * * *' # 5:00 AM PST (UTC-8)
7 |
8 | jobs:
9 | check-backup:
10 | runs-on: ubuntu-latest
11 |
12 | steps:
13 | - name: Checkout repository
14 | uses: actions/checkout@v2
15 |
16 | - name: Set up Python
17 | uses: actions/setup-python@v4
18 | with:
19 | python-version: '3.x'
20 |
21 | - name: Install boto3 (AWS SDK for Python)
22 | run: |
23 | python -m pip install --upgrade pip
24 | pip install boto3 requests
25 |
26 | - name: Check if backup exists in S3
27 | run: python .github/scripts/check_backup.py
28 | env:
29 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
30 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
31 | AWS_REGION: ${{ secrets.AWS_REGION }}
32 | BACKUP_BUCKET_NAME: "${{ secrets.BACKUP_BUCKET_NAME }}"
33 | S3_FOLDER: "db_backup/"
34 | SLACK_CHANNEL: "#ncats-translator"
35 | SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
36 |
--------------------------------------------------------------------------------
/web-app/public/img/logo-small.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
25 |
--------------------------------------------------------------------------------
/web-app/src/assets/img/logo-small.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
25 |
--------------------------------------------------------------------------------
/src/utils/indices.py:
--------------------------------------------------------------------------------
1 | from elasticsearch_dsl import Index
2 | from model import SmartAPIDoc
3 |
4 |
5 | def exists(model_class=SmartAPIDoc, index=None):
6 | return Index(index or model_class.Index.name).exists()
7 |
8 |
9 | def setup(model_class=SmartAPIDoc, index=None):
10 | """
11 | Setup Elasticsearch Index with dynamic template.
12 | Run it on an open index to update dynamic mapping.
13 | """
14 |
15 | if not exists(model_class, index=index):
16 | model_class.init(index=index)
17 |
18 | # if model_class == SmartAPIDoc:
19 | # # set up custom mapping for SmartAPI index
20 | # _dirname = os.path.dirname(__file__)
21 | # with open(os.path.join(_dirname, "mapping.json"), "r") as file:
22 | # mapping = json.load(file)
23 | # Index(model_class.Index.name).put_mapping(body=mapping)
24 |
25 |
26 | def delete(model_class=SmartAPIDoc, index=None):
27 | Index(index or model_class.Index.name).delete()
28 |
29 |
30 | def reset(model_class=SmartAPIDoc, index=None):
31 | if exists(model_class, index=index):
32 | delete(model_class, index=index)
33 |
34 | setup(model_class, index=index)
35 |
36 |
37 | def refresh(model_class=SmartAPIDoc, index=None):
38 | idx = Index(index or model_class.Index.name)
39 | idx.refresh()
40 |
--------------------------------------------------------------------------------
/web-app/src/assets/img/icon-metakg.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
23 |
--------------------------------------------------------------------------------
/src/tests/_utils/metakg/integration/parser/component_test.py:
--------------------------------------------------------------------------------
1 | import json
2 | import os
3 | import unittest
4 |
5 | from utils.metakg.component import Components
6 |
7 |
8 | class TestComponent(unittest.TestCase):
9 | def test_ref_with_trailing_slash(self):
10 | with open(os.path.abspath(os.path.join(os.path.dirname(__file__), "components.json"))) as f:
11 | components = json.load(f)
12 | cp_obj = Components(components)
13 | rec = cp_obj.fetch_component_by_ref("#/components/x-bte-kgs-operations/enablesMF/")
14 | self.assertEqual(rec[0]["source"], "entrez")
15 |
16 | def test_wrong_ref(self):
17 | with open(os.path.abspath(os.path.join(os.path.dirname(__file__), "components.json"))) as f:
18 | components = json.load(f)
19 | cp_obj = Components(components)
20 | rec = cp_obj.fetch_component_by_ref("/components/x-bte-response-mapping")
21 | self.assertEqual(rec, None)
22 |
23 | def test_wrong_ref_2(self):
24 | with open(os.path.abspath(os.path.join(os.path.dirname(__file__), "components.json"))) as f:
25 | components = json.load(f)
26 | cp_obj = Components(components)
27 | rec = cp_obj.fetch_component_by_ref("#/components/x-bte-response-mapping/hello/world")
28 | self.assertEqual(rec, None)
29 |
--------------------------------------------------------------------------------
/web-app/README.md:
--------------------------------------------------------------------------------
1 | # SmartAPI web app
2 |
3 | Developed with Vue 3 in Vite.
4 |
5 | ## Recommended IDE Setup
6 |
7 | [VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
8 |
9 | ## Customize configuration
10 |
11 | See [Vite Configuration Reference](https://vitejs.dev/config/).
12 |
13 | ## Project Setup
14 |
15 | ```sh
16 | npm install
17 | ```
18 |
19 | ### Compile and Hot-Reload for Development
20 |
21 | ```sh
22 | npm run dev
23 | ```
24 |
25 | ### Compile and Minify for Production
26 |
27 | ```sh
28 | npm run build
29 | ```
30 |
31 | ### Run Unit Tests with [Vitest](https://vitest.dev/)
32 |
33 | ```sh
34 | npm run test:unit
35 | ```
36 |
37 | ### Run End-to-End Tests with [Cypress](https://www.cypress.io/)
38 |
39 | ```sh
40 | npm run test:e2e:dev
41 | ```
42 |
43 | This runs the end-to-end tests against the Vite development server.
44 | It is much faster than the production build.
45 |
46 | But it's still recommended to test the production build with `test:e2e` before deploying (e.g. in CI environments):
47 |
48 | ```sh
49 | npm run build
50 | npm run test:e2e
51 | ```
52 |
53 | ### Lint with [ESLint](https://eslint.org/)
54 |
55 | ```sh
56 | npm run lint
57 | ```
58 |
--------------------------------------------------------------------------------
/web-app/src/store/modules/authentication.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 |
3 | export const authentication = {
4 | state: () => ({
5 | userInfo: {},
6 | loggedIn: false
7 | }),
8 | mutations: {
9 | saveUser(state, payload) {
10 | state.userInfo = payload['user'];
11 | state.loggedIn = Object.prototype.hasOwnProperty.call(state.userInfo, 'login') ? true : false;
12 | },
13 | resetUser(state) {
14 | state.userInfo = {};
15 | state.loggedIn = false;
16 | }
17 | },
18 | actions: {
19 | checkUser({ commit }) {
20 | if (process.env.NODE_ENV == 'development') {
21 | // for dev only
22 | commit('saveUser', {
23 | user: {
24 | name: 'Marco Cano',
25 | email: 'artofmarco@gmail.com',
26 | login: 'marcodarko',
27 | avatar_url: 'https://avatars.githubusercontent.com/u/23092057?v=4'
28 | }
29 | });
30 | } else {
31 | axios
32 | .get('/user')
33 | .then((response) => {
34 | commit('saveUser', { user: response.data });
35 | })
36 | .catch((err) => {
37 | commit('resetUser');
38 | throw err;
39 | });
40 | }
41 | }
42 | },
43 | getters: {
44 | userInfo: (state) => {
45 | return state.userInfo;
46 | },
47 | loggedIn: (state) => {
48 | return state.loggedIn;
49 | }
50 | }
51 | };
52 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/transfer-of-ownership.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Transfer of Ownership
3 | about: Transfer a registration to another person
4 | title: ''
5 | labels: help wanted
6 | assignees: marcodarko
7 |
8 | ---
9 |
10 | ## Transfer of Ownership
11 |
12 | Fill this out if you would like to transfer ownership of an API entry to someone else or claim ownership of one.
13 |
14 | **Note: This transfer of ownership requests can only come from a registered user and go to another registered user on SmartAPI**.
15 |
16 |
17 |
18 |
19 | *Let's start! First, tell us who you are*
20 |
21 | * **I'm am ...**
22 |
23 | - [ ] the person transferring ownership of my API
24 |
25 | - [ ] the person that this API is being transferred to
26 |
27 | - [ ] a third party requesting a special transfer (please provide details)
28 |
29 |
30 |
31 | * What is the ***name*** and ***id*** of the API you would like to transfer? *eg. MyAPI : 124094327094378*
32 |
33 |
34 |
35 |
36 | * What is the ***name*** and ***username*** of the person that this API currently belongs to? *eg. Jane Doe @janedoe1
37 |
38 |
39 |
40 |
41 | * What is the ***name*** and ***username*** of the person that this API is being transferred to? *eg. John Doe @johndoe1*
42 |
43 |
44 |
45 |
46 | * What is the reason this transfer of ownership is being requested?
47 |
48 |
49 |
50 |
51 | Thank you! We will review this request ASAP and will inform you of the status of the transfer.
52 |
--------------------------------------------------------------------------------
/web-app/src/components/Login.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | dashboard
6 |
7 |
8 | Logout
9 |
10 |
11 |
12 |
13 | Login
14 |
15 |
16 |
17 |
18 |
36 |
37 |
62 |
--------------------------------------------------------------------------------
/.github/workflows/app_tests.yml:
--------------------------------------------------------------------------------
1 | name: App Tests
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 | branches:
9 | - main
10 |
11 | jobs:
12 | run_app_tests:
13 | runs-on: ubuntu-latest
14 | strategy:
15 | fail-fast: false
16 | matrix:
17 | python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
18 | steps:
19 | - name: Checkout source
20 | uses: actions/checkout@v4
21 | - name: Set up Python ${{ matrix.python-version }}
22 | uses: actions/setup-python@v5
23 | with:
24 | python-version: ${{ matrix.python-version }}
25 | - name: Update
26 | run: sudo apt-get update
27 | - name: Install apt-get packages
28 | run: sudo apt-get install libssl-dev libcurl4-openssl-dev
29 | - name: Upgrade pip
30 | run: pip install --upgrade pip
31 | - name: Install dependencies
32 | run: pip install -r requirements.txt
33 | - name: Install PyTest
34 | run: pip install pytest
35 | - name: Run App Tests
36 | run: pytest tests
37 | working-directory: src
38 | services:
39 | Elasticsearch:
40 | image: docker.elastic.co/elasticsearch/elasticsearch:8.17.0
41 | env:
42 | "discovery.type": single-node
43 | "xpack.security.enabled": false
44 | "xpack.security.http.ssl.enabled": false
45 | "xpack.security.transport.ssl.enabled": false
46 | ports:
47 | - 9200:9200
48 |
--------------------------------------------------------------------------------
/web-app/src/components/VModal.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
24 |
25 |
65 |
--------------------------------------------------------------------------------
/src/utils/http_error.py:
--------------------------------------------------------------------------------
1 | from typing import Optional, Any
2 | from tornado.web import HTTPError
3 | import re
4 |
5 |
6 | class SmartAPIHTTPError(HTTPError):
7 | """An extended HTTPError class with additional details and message sanitization.
8 |
9 | Adds the following enhancements:
10 | - A `details` parameter for including extra context about the error.
11 | - A `clean_error_message` method for sanitizing log messages and details.
12 |
13 | :arg str details: Additional information about the error.
14 | """
15 |
16 | def __init__(
17 | self,
18 | status_code: int = 500,
19 | log_message: Optional[str] = None,
20 | *args: Any,
21 | **kwargs: Any,
22 | ) -> None:
23 | super().__init__(status_code, log_message, *args, **kwargs)
24 | if self.reason:
25 | self.reason = self.clean_error_message(self.reason)
26 | if self.log_message:
27 | self.log_message = self.clean_error_message(self.log_message)
28 |
29 | @staticmethod
30 | def clean_error_message(message: str) -> str:
31 | """
32 | Sanitizes an error message by replacing newlines, tabs, and reducing multiple spaces.
33 |
34 | :param message: The error message to sanitize.
35 | :return: A cleaned and sanitized version of the message.
36 | """
37 | message = message.replace("\n", " ") # Replace actual newlines with spaces
38 | message = re.sub(r'\s+', ' ', message) # Normalize spaces
39 | return message.strip()
40 |
--------------------------------------------------------------------------------
/web-app/src/components/EntityPill.vue:
--------------------------------------------------------------------------------
1 |
2 |