diff --git a/web/api/completly-rebuild-thrift.sh b/web/api/completly-rebuild-thrift.sh new file mode 100755 index 0000000000..c236f56a67 --- /dev/null +++ b/web/api/completly-rebuild-thrift.sh @@ -0,0 +1,45 @@ +#!/bin/bash + +set -e # Exit on any error + +echo "Starting CodeChecker Thrift rebuild process..." + +# Step 1: Build the API +echo "Step 1: Building API..." +cd "$HOME/codechecker/web/api" +make build + +# Step 2: Execute the main rebuild process +echo "Step 2: Executing main rebuild..." +cd "$HOME/codechecker" + +# Deactivation of virtual enviroment +echo "Deactivating current environment..." +if command -v deactivate &> /dev/null; then + deactivate 2>/dev/null || true +fi + +# Resetting the package-lock.json just in case. +echo "Resetting package-lock.json..." +git checkout master -- "$HOME/codechecker/web/server/vue-cli/package-lock.json" +git reset HEAD "$HOME/codechecker/web/server/vue-cli/package-lock.json" + +# Cleaning. +echo "Cleaning previous builds..." +make clean +make clean_venv_dev + +# Creating new virtual enviroment. +echo "Creating new virtual environment..." +make venv_dev + +echo "Building package..." +make package + +# Again just in case. +echo "Activating virtual environment and setting PATH..." +source "$HOME/codechecker/venv_dev/bin/activate" +export PATH="$HOME/codechecker/build/CodeChecker/bin:$PATH" + +echo "CodeChecker rebuild completed successfully!" +echo "You can now use CodeChecker commands." diff --git a/web/api/js/codechecker-api-node/dist/codechecker-api-6.66.0.tgz b/web/api/js/codechecker-api-node/dist/codechecker-api-6.66.0.tgz deleted file mode 100644 index 5cf45a53c6..0000000000 Binary files a/web/api/js/codechecker-api-node/dist/codechecker-api-6.66.0.tgz and /dev/null differ diff --git a/web/api/js/codechecker-api-node/dist/codechecker-api-6.67.0.tgz b/web/api/js/codechecker-api-node/dist/codechecker-api-6.67.0.tgz new file mode 100644 index 0000000000..01521b8fc8 Binary files /dev/null and b/web/api/js/codechecker-api-node/dist/codechecker-api-6.67.0.tgz differ diff --git a/web/api/js/codechecker-api-node/package.json b/web/api/js/codechecker-api-node/package.json index 262f3560ae..eb304081d8 100644 --- a/web/api/js/codechecker-api-node/package.json +++ b/web/api/js/codechecker-api-node/package.json @@ -1,6 +1,6 @@ { "name": "codechecker-api", - "version": "6.66.0", + "version": "6.67.0", "description": "Generated node.js compatible API stubs for CodeChecker server.", "main": "lib", "homepage": "https://github.com/Ericsson/codechecker", diff --git a/web/api/py/codechecker_api/dist/codechecker_api.tar.gz b/web/api/py/codechecker_api/dist/codechecker_api.tar.gz index e3e2712521..5d572a15c8 100644 Binary files a/web/api/py/codechecker_api/dist/codechecker_api.tar.gz and b/web/api/py/codechecker_api/dist/codechecker_api.tar.gz differ diff --git a/web/api/py/codechecker_api/setup.py b/web/api/py/codechecker_api/setup.py index 0cc7d8b380..4face1d377 100644 --- a/web/api/py/codechecker_api/setup.py +++ b/web/api/py/codechecker_api/setup.py @@ -8,7 +8,7 @@ with open('README.md', encoding='utf-8', errors="ignore") as f: long_description = f.read() -api_version = '6.66.0' +api_version = '6.67.0' setup( name='codechecker_api', diff --git a/web/api/py/codechecker_api_shared/dist/codechecker_api_shared.tar.gz b/web/api/py/codechecker_api_shared/dist/codechecker_api_shared.tar.gz index 1f6d692a70..2cc3f0deb9 100644 Binary files a/web/api/py/codechecker_api_shared/dist/codechecker_api_shared.tar.gz and b/web/api/py/codechecker_api_shared/dist/codechecker_api_shared.tar.gz differ diff --git a/web/api/py/codechecker_api_shared/setup.py b/web/api/py/codechecker_api_shared/setup.py index d11dda94d0..2986c3eeb2 100644 --- a/web/api/py/codechecker_api_shared/setup.py +++ b/web/api/py/codechecker_api_shared/setup.py @@ -8,7 +8,7 @@ with open('README.md', encoding='utf-8', errors="ignore") as f: long_description = f.read() -api_version = '6.66.0' +api_version = '6.67.0' setup( name='codechecker_api_shared', diff --git a/web/api/report_server.thrift b/web/api/report_server.thrift index a30274b420..30bf4f83bc 100644 --- a/web/api/report_server.thrift +++ b/web/api/report_server.thrift @@ -404,6 +404,12 @@ struct ReportFilter { 23: optional list reportStatus, // Specifying the status of the filtered reports. } +struct FilterPreset { + 1: i64 id, // Unique ID of filter preset. + 2: string name, // Human readable name of preset. + 3: ReportFilter reportFilter // Uniquely configured ReportFilter. +} + struct RunReportCount { 1: i64 runId, // Unique ID of the run. 2: string name, // Human readable name of the run. @@ -581,6 +587,40 @@ service codeCheckerDBAccess { 4: optional RunSortMode sortMode) throws (1: codechecker_api_shared.RequestFailed requestError), + //============================================ + // Filter grouping api calls. + //============================================ + + // Stores the given FilterPreset with the given id + // If the preset exists, it overwrites the name, and all preset values + // if the preset does not exist yet, it creates it with + // if the id is -1 a new preset filter is created + // the filter preset name must be unique. + // An error must be thrown if another preset exists with the same name. + // The encoding of the name must be unicode. (whitespaces allowed?) + // Returns: the id of the modified or created preset, -1 in case of error + // PERMISSION: PRODUCT_ADMIN + i64 storeFilterPreset(1: FilterPreset preset) + throws (1: codechecker_api_shared.RequestFailed requestError); + + // Returns the FilterPreset identified by id + // Returns -1 in case there is no preset with the given id + // PERMISSION: PRODUCT_VIEW + FilterPreset getFilterPreset(1: i64 id) + throws (1: codechecker_api_shared.RequestFailed requestError); + + // Removes the filterpreset with the given id + // returns the id of the filter preset removed + // and -1 in case of error. + // PERMISSION: PRODUCT_ADMIN + i64 deleteFilterPreset(1: i64 id) + throws (1: codechecker_api_shared.RequestFailed requestError); + + // Returns all filter presets stored for the product repository + // PERMISSION: PRODUCT_VIEW + list listFilterPreset() + throws (1: codechecker_api_shared.RequestFailed requestError); + // Returns the number of available runs based on the run filter parameter. // PERMISSION: PRODUCT_VIEW i64 getRunCount(1: RunFilter runFilter) @@ -1143,4 +1183,10 @@ service codeCheckerDBAccess { bool unsetCleanupPlan(1: i64 cleanupPlanId, 2: list reportHashes) throws (1: codechecker_api_shared.RequestFailed requestError), + + // return name based on mapping based on value and classname. + // PERMISSION: PRODUCT_VIEW + string getNameByValueForFilter(1: string classname, + 2: i64 value) + throws (1: codechecker_api_shared.RequestFailed requestError), } diff --git a/web/client/codechecker_client/helpers/results.py b/web/client/codechecker_client/helpers/results.py index dd6978ee9c..3d9f7d02f1 100644 --- a/web/client/codechecker_client/helpers/results.py +++ b/web/client/codechecker_client/helpers/results.py @@ -33,6 +33,22 @@ def __init__(self, protocol, host, port, uri, session_token=None, def getRunData(self, run_name_filter, limit, offset, sort_mode): pass + @thrift_client_call + def storeFilterPreset(self, preset): + pass + + @thrift_client_call + def getFilterPreset(self, id): + pass + + @thrift_client_call + def deleteFilterPreset(self, id): + pass + + @thrift_client_call + def listFilterPreset(self): + pass + @thrift_client_call def getRunHistory(self, run_ids, limit, offset, run_history_filter): pass diff --git a/web/codechecker_web/shared/version.py b/web/codechecker_web/shared/version.py index 9abaa27c6c..fa2de94d4c 100644 --- a/web/codechecker_web/shared/version.py +++ b/web/codechecker_web/shared/version.py @@ -20,7 +20,7 @@ # The newest supported minor version (value) for each supported major version # (key) in this particular build. SUPPORTED_VERSIONS = { - 6: 66 + 6: 67 } # Used by the client to automatically identify the latest major and minor diff --git a/web/server/codechecker_server/api/report_server.py b/web/server/codechecker_server/api/report_server.py index 249c8d4db0..9dd90d1e9f 100644 --- a/web/server/codechecker_server/api/report_server.py +++ b/web/server/codechecker_server/api/report_server.py @@ -69,7 +69,7 @@ File, FileContent, \ Report, ReportAnnotations, ReportAnalysisInfo, ReviewStatus, \ Run, RunHistory, RunHistoryAnalysisInfo, RunLock, \ - SourceComponent + SourceComponent , FilterPreset from .common import exc_to_thrift_reqfail from .thrift_enum_helper import detection_status_enum, \ @@ -1539,6 +1539,385 @@ def getRunData(self, run_filter, limit, offset, sort_mode): analyzerStatistics=analyzer_stats, description=description)) return results + # Stores the given FilterPreset with the given id + # If the preset exists, it overwrites the name, and all preset values + # if the preset does not exist yet, it creates it with + # if the id is -1 a new preset filter is created + # the filter preset name must be unique. + # An error must be thrown if another preset exists with the same name. + # The encoding of the name must be unicode. (whitespaces allowed?) + # Returns: the id of the modified or created preset, -1 in case of error + # PERMISSION: PRODUCT_ADMIN + @exc_to_thrift_reqfail + @timeit + def storeFilterPreset(self, filterpreset): + """ + Store a configured ReportFilter in corresponding + product row. + args: + int id : Numerical identifier. + str name : Human readable name of preset. + ReportFilter: ReportFilter obj itself. + """ + self.__require_admin() + LOG.info("got to store filter preset in backend") + id = filterpreset.id + name = filterpreset.name + report_filter = json.dumps(filterpreset.reportFilter.__dict__) + # If the preset exists, it overwrites the name, and all preset values + # if the preset does not exist yet, it creates it with + with DBSession(self._Session) as session: + LOG.debug(f"Preset:{name}, is being inserted in database") + preset_entry = FilterPreset(preset_id = id, + preset_name = name, + report_filter=report_filter) + session.add(preset_entry) + session.commit() + return id + + # ReportFilter(filepath=None, + # checkerMsg=None, + # checkerName=None, + # reportHash=None, + # severity=None, + # reviewStatus=[0, 1], + # detectionStatus=[0, 3, 2], + # runHistoryTag=None, + # firstDetectionDate=None, + # fixDate=None, + # isUnique=False, + # runName=None, + # runTag=None, + # componentNames=None, + # bugPathLength=None, + # date=None, + # analyzerNames=None, + # openReportsDate=None, + # cleanupPlanNames=None, + # fileMatchesAnyPoint=None, + # componentMatchesAnyPoint=None, + # annotations=None, + # reportStatus=None) + # `alembic --name config_db revision --autogenerate -m "Change description"` + # PYTHONPATH=/build/CodeChecker/lib/python3 alembic --name config_db revision --autogenerate -m "Change description" + + @exc_to_thrift_reqfail + @timeit + def deleteFilterPreset(self, preset_id): + """ + Delete preset from products list based on preset_id. + """ + self.__require_admin() + LOG.info("deleting filter preset by id") + try: + with DBSession(self._Session) as session: + session.query(FilterPreset) \ + .filter(FilterPreset.preset_id == preset_id) \ + .delete() + session.commit() + return preset_id + except Exception: + return -1 + # raise codechecker_api_shared.ttypes.RequestFailed( + # codechecker_api_shared.ttypes.ErrorCode.DATABASE, + # "CodeChecker could not remove a preset ") + + + @exc_to_thrift_reqfail + @timeit + def getFilterPreset(self, preset_id): + """ + Returns the FilterPreset identified by id. + """ + self.__require_view() + LOG.info("Returning filter Preset by id") + try: + with DBSession(self._Session) as session: + preset = ( + session.query(FilterPreset) + .filter(FilterPreset.preset_id == preset_id) + .one_or_none() + ) + + if preset is None: + return None + + # convert ORM to dict for ReportFilter + rf = preset.report_filter + if isinstance(rf, str): + rf = json.loads(rf) + + # NOTE: Update this mapping if new fields are added to the ReportFilter struct. + report_filter = ttypes.ReportFilter( + filepath = rf.get("filepath"), + checkerMsg = rf.get("checkerMsg"), + checkerName = rf.get("checkerName"), + reportHash = rf.get("reportHash"), + severity = rf.get("severity"), + reviewStatus = rf.get("reviewStatus"), + detectionStatus = rf.get("detectionStatus"), + runHistoryTag = rf.get("runHistoryTag"), + firstDetectionDate = rf.get("firstDetectionDate"), + fixDate = rf.get("fixDate"), + isUnique = rf.get("isUnique"), + runName = rf.get("runName"), + runTag = rf.get("runTag"), + componentNames = rf.get("componentNames"), + bugPathLength = ttypes.BugPathLengthRange(**rf["bugPathLength"]) + if isinstance(rf.get("bugPathLength"), dict) else None, + date = ttypes.ReportDate(**rf["date"]) + if isinstance(rf.get("date"), dict) else None, + analyzerNames = rf.get("analyzerNames", None), + openReportsDate = rf.get("openReportsDate"), + cleanupPlanNames = rf.get("cleanupPlanNames"), + fileMatchesAnyPoint = rf.get("fileMatchesAnyPoint"), + componentMatchesAnyPoint = rf.get("componentMatchesAnyPoint"), + annotations = [ttypes.Pair(**a) for a in rf.get("annotations", [])] + if isinstance(rf.get("annotations"), list) else None, + reportStatus = rf.get("reportStatus"), + ) + result = ttypes.FilterPreset(preset.preset_id, + preset.preset_name, + report_filter + ) + return result + except Exception as ex: + raise codechecker_api_shared.ttypes.RequestFailed( + codechecker_api_shared.ttypes.ErrorCode.DATABASE, + "CodeChecker could not list a preset :", ex) + + @exc_to_thrift_reqfail + @timeit + def listFilterPreset(self): + """ + Returns all filter presets stored for the product repository + """ + self.__require_view() + LOG.info("List back filter presets") + + try: + with DBSession(self._Session) as session: + all_presets = ( + session.query(FilterPreset).all() + ) + # create a list of id's to use getFilterPreset to consturct a list of all presets + list_of_all_presets = [self.getFilterPreset(preset.preset_id) for preset in all_presets] + + return list_of_all_presets + except Exception as ex: + raise codechecker_api_shared.ttypes.RequestFailed( + codechecker_api_shared.ttypes.ErrorCode.DATABASE, + "CodeChecker could not list a preset :", ex) + + # Stores the given FilterPreset with the given id + # If the preset exists, it overwrites the name, and all preset values + # if the preset does not exist yet, it creates it with + # if the id is -1 a new preset filter is created + # the filter preset name must be unique. + # An error must be thrown if another preset exists with the same name. + # The encoding of the name must be unicode. (whitespaces allowed?) + # Returns: the id of the modified or created preset, -1 in case of error + # PERMISSION: PRODUCT_ADMIN + + @exc_to_thrift_reqfail + @timeit + def storeFilterPreset(self, filterpreset): + """ + Store a configured ReportFilter in corresponding + product row. + args: + int id : Numerical identifier. + str name : Human readable name of preset. + ReportFilter: ReportFilter obj itself. + """ + self.__require_admin() + LOG.info("got to store filter preset in backend") + id = filterpreset.id + name = filterpreset.name + report_filter = json.dumps(filterpreset.reportFilter.__dict__) + + # If the preset exists, it overwrites the name, and all preset values + # if the preset does not exist yet, it creates it with + #TODO make it so new preset with same name overrides the stored one + + with DBSession(self._Session) as session: + LOG.debug(f"Preset:{name}, is being inserted in database") + preset_entry = FilterPreset(preset_id = id, + preset_name = name, + report_filter=report_filter) + session.add(preset_entry) + session.commit() + return id + + @exc_to_thrift_reqfail + @timeit + def deleteFilterPreset(self, preset_id): + """ + Delete preset from products list based on preset_id. + """ + self.__require_admin() + LOG.info("deleting filter preset by id") + try: + with DBSession(self._Session) as session: + session.query(FilterPreset) \ + .filter(FilterPreset.preset_id == preset_id) \ + .delete() + session.commit() + return preset_id + except Exception: + return -1 + # raise codechecker_api_shared.ttypes.RequestFailed( + # codechecker_api_shared.ttypes.ErrorCode.DATABASE, + # "CodeChecker could not remove a preset ") + + + @exc_to_thrift_reqfail + @timeit + def getFilterPreset(self, preset_id): + """ + Returns the FilterPreset identified by id. + """ + self.__require_view() + LOG.info("Returning filter Preset by id") + try: + with DBSession(self._Session) as session: + preset = ( + session.query(FilterPreset) + .filter(FilterPreset.preset_id == preset_id) + .one_or_none() + ) + + if preset is None: + return None + + # convert ORM to dict for ReportFilter + rf = preset.report_filter + if isinstance(rf, str): + rf = json.loads(rf) + + # NOTE: Update this mapping if new fields are added to the ReportFilter struct. + report_filter = ttypes.ReportFilter( + filepath = rf.get("filepath"), + checkerMsg = rf.get("checkerMsg"), + checkerName = rf.get("checkerName"), + reportHash = rf.get("reportHash"), + severity = rf.get("severity"), + reviewStatus = rf.get("reviewStatus"), + detectionStatus = rf.get("detectionStatus"), + runHistoryTag = rf.get("runHistoryTag"), + firstDetectionDate = rf.get("firstDetectionDate"), + fixDate = rf.get("fixDate"), + isUnique = rf.get("isUnique"), + runName = rf.get("runName"), + runTag = rf.get("runTag"), + componentNames = rf.get("componentNames"), + bugPathLength = ttypes.BugPathLengthRange(**rf["bugPathLength"]) + if isinstance(rf.get("bugPathLength"), dict) else None, + date = ttypes.ReportDate(**rf["date"]) + if isinstance(rf.get("date"), dict) else None, + analyzerNames = rf.get("analyzerNames", None), + openReportsDate = rf.get("openReportsDate"), + cleanupPlanNames = rf.get("cleanupPlanNames"), + fileMatchesAnyPoint = rf.get("fileMatchesAnyPoint"), + componentMatchesAnyPoint = rf.get("componentMatchesAnyPoint"), + annotations = [ttypes.Pair(**a) for a in rf.get("annotations", [])] + if isinstance(rf.get("annotations"), list) else None, + reportStatus = rf.get("reportStatus"), + ) + result = ttypes.FilterPreset(preset.preset_id, + preset.preset_name, + report_filter + ) + return result + except Exception as ex: + raise codechecker_api_shared.ttypes.RequestFailed( + codechecker_api_shared.ttypes.ErrorCode.DATABASE, + "CodeChecker could not list a preset :", ex) + + @exc_to_thrift_reqfail + @timeit + def listFilterPreset(self): + """ + Returns all filter presets stored for the product repository + """ + self.__require_view() + LOG.info("List back filter presets") + + try: + # with DBSession(self._Session) as session: + # all_presets = ( + # session.query(FilterPreset.preset_id, FilterPreset.preset_name).all() + # ) + + # return all_presets + with DBSession(self._Session) as session: + all_presets = ( + session.query(FilterPreset).all() + ) + # create a list of id's to use getFilterPreset to consturct a list of all presets + list_of_all_presets = [self.getFilterPreset(preset.preset_id) for preset in all_presets] + + return list_of_all_presets + except Exception as ex: + raise codechecker_api_shared.ttypes.RequestFailed( + codechecker_api_shared.ttypes.ErrorCode.DATABASE, + "CodeChecker could not list a preset :", ex) + + @exc_to_thrift_reqfail + @timeit + def getNameByValueForFilter(self, filter_type, value): + """ + Convert enum integer value to a UI-recognizable string. + Returns "" when not convertible / not allowed. + """ + + self.__require_view() + LOG.info("Getting name by value for filter") + + type_name = filter_type[0].upper() + filter_type[1:] + + ALLOWED_ENUMS = { + "DetectionStatus", + "DiffType", + "Encoding", + "Order", + "ReportStatus", + "ReviewStatus", + "Severity", + "SortType", + "RunSortType", + "StoreLimitKind", + "ExtendedReportDataType", + "CommentKind", + "ReviewStatusRuleSortType", + } + + if type_name not in ALLOWED_ENUMS: + return "" + + enum_cls = getattr(ttypes, type_name, None) + if not enum_cls or not hasattr(enum_cls, "_VALUES_TO_NAMES"): + return "" + + try: + value_int = int(value) + token = enum_cls._VALUES_TO_NAMES.get(value_int) # e.g. "CONFIRMED" + if not token: + return "" + + # special case for formatting confirmed bug. IN UI for some reason + # it is shown as "Confirmed bug" instead of just "Confirmed". + if type_name == "ReviewStatus" and token == "CONFIRMED": + return "Confirmed bug" + + # # Default formatting: "FALSE_POSITIVE" → "False positive" + return token.replace("_", " ").lower().capitalize() + + except Exception as ex: + raise codechecker_api_shared.ttypes.RequestFailed( + codechecker_api_shared.ttypes.ErrorCode.DATABASE, + f"CodeChecker could not convert {type_name} value to name: {ex}" + ) @exc_to_thrift_reqfail @timeit diff --git a/web/server/codechecker_server/database/run_db_model.py b/web/server/codechecker_server/database/run_db_model.py index c8eaec963f..67ee80b7ae 100644 --- a/web/server/codechecker_server/database/run_db_model.py +++ b/web/server/codechecker_server/database/run_db_model.py @@ -14,7 +14,7 @@ from typing import Optional from sqlalchemy import Boolean, Column, DateTime, Enum, ForeignKey, Integer, \ - LargeBinary, MetaData, String, UniqueConstraint, Table, Text + LargeBinary, MetaData, String, UniqueConstraint, Table, Text, JSON from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import relationship from sqlalchemy.sql.expression import true, false @@ -603,3 +603,16 @@ class CleanupPlanReportHash(Base): 'identifier': "RunDatabase", 'orm_meta': CC_META } + +class FilterPreset(Base): + __tablename__ = 'filter_presets' + + id = Column(Integer, autoincrement=True, primary_key=True) + preset_id = Column(Integer, nullable=False ) + preset_name = Column(String, nullable=False, unique=True) + report_filter = Column(JSON, nullable=False) + + def __init__(self, preset_id, preset_name, report_filter): + self.preset_id = preset_id + self.preset_name = preset_name + self.report_filter = report_filter \ No newline at end of file diff --git a/web/server/vue-cli/package-lock.json b/web/server/vue-cli/package-lock.json index 95a4ea1fd9..5f285ef8f9 100644 --- a/web/server/vue-cli/package-lock.json +++ b/web/server/vue-cli/package-lock.json @@ -11,7 +11,7 @@ "@mdi/font": "^6.5.95", "chart.js": "^2.9.4", "chartjs-plugin-datalabels": "^0.7.0", - "codechecker-api": "file:../../api/js/codechecker-api-node/dist/codechecker-api-6.66.0.tgz", + "codechecker-api": "file:../../api/js/codechecker-api-node/dist/codechecker-api-6.67.0.tgz", "codemirror": "^5.65.0", "date-fns": "^2.28.0", "js-cookie": "^3.0.1", @@ -5059,9 +5059,9 @@ } }, "node_modules/codechecker-api": { - "version": "6.66.0", - "resolved": "file:../../api/js/codechecker-api-node/dist/codechecker-api-6.66.0.tgz", - "integrity": "sha512-wPci6y6cCnzjpJCBRbWO0abEA/u/NSQqd37P1oAZ3l2rViiusFjfhY6vehQaVbD5QBPLAj0n0owKYoMIlK2wOQ==", + "version": "6.67.0", + "resolved": "file:../../api/js/codechecker-api-node/dist/codechecker-api-6.67.0.tgz", + "integrity": "sha512-0GyDYq5WLT6o6hg8Mpk+aIbZhe0Gn3B8gmXcVebO0Zwn/Ewk7BKOa6hCqb3iVIzaFpKyIBetJLln7/eqWITlbg==", "license": "SEE LICENSE IN LICENSE", "dependencies": { "thrift": "0.13.0-hotfix.1" diff --git a/web/server/vue-cli/package.json b/web/server/vue-cli/package.json index fccef01938..82e3467f0d 100644 --- a/web/server/vue-cli/package.json +++ b/web/server/vue-cli/package.json @@ -29,7 +29,7 @@ "@mdi/font": "^6.5.95", "chart.js": "^2.9.4", "chartjs-plugin-datalabels": "^0.7.0", - "codechecker-api": "file:../../api/js/codechecker-api-node/dist/codechecker-api-6.66.0.tgz", + "codechecker-api": "file:../../api/js/codechecker-api-node/dist/codechecker-api-6.67.0.tgz", "codemirror": "^5.65.0", "date-fns": "^2.28.0", "js-cookie": "^3.0.1", diff --git a/web/server/vue-cli/src/components/Report/ReportFilter/Filters/PresetMenu.vue b/web/server/vue-cli/src/components/Report/ReportFilter/Filters/PresetMenu.vue new file mode 100644 index 0000000000..803ff4efb9 --- /dev/null +++ b/web/server/vue-cli/src/components/Report/ReportFilter/Filters/PresetMenu.vue @@ -0,0 +1,234 @@ + + + diff --git a/web/server/vue-cli/src/components/Report/ReportFilter/ReportFilter.vue b/web/server/vue-cli/src/components/Report/ReportFilter/ReportFilter.vue index 2490d3cb49..677f961c65 100644 --- a/web/server/vue-cli/src/components/Report/ReportFilter/ReportFilter.vue +++ b/web/server/vue-cli/src/components/Report/ReportFilter/ReportFilter.vue @@ -26,6 +26,39 @@ + + + + + + + + + + + + + Save + + + diff --git a/web/server/vue-cli/src/views/Login.vue b/web/server/vue-cli/src/views/Login.vue index bbd5e2190a..370f174c8b 100644 --- a/web/server/vue-cli/src/views/Login.vue +++ b/web/server/vue-cli/src/views/Login.vue @@ -141,6 +141,7 @@ import { mapGetters } from "vuex"; import { LOGIN } from "@/store/actions.type"; import { authService, handleThriftError } from "@cc-api"; + import Alerts from "@/components/Alerts"; export default {