7
mirror of https://gitlab.com/kicad/code/kicad.git synced 2025-03-30 05:46:55 +00:00

API: add a schema for plugin config files

Also flip the dependence between json_schema_validator and kicommon,
and create a shared JSON_SCHEMA_VALIDATOR so that we don't have to
copy/paste the schema loading code as much
This commit is contained in:
Jon Evans 2025-01-05 16:34:41 -05:00
parent 6be3401b92
commit 3f7e459d62
15 changed files with 290 additions and 43 deletions

View File

@ -132,3 +132,19 @@ if( APPLE )
"@executable_path/../Frameworks" )
set_target_properties( kiapi PROPERTIES BUILD_WITH_INSTALL_RPATH 1 )
endif()
if( NOT (${CMAKE_CURRENT_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_BINARY_DIR} ) )
file( GLOB SCHEMA_FILES ${CMAKE_CURRENT_SOURCE_DIR}/schemas/*.json )
add_custom_target( api_schema_build_copy ALL
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/schemas ${CMAKE_BINARY_DIR}/schemas
DEPENDS ${SCHEMA_FILES}
COMMENT "Copying API schema files into build directory"
)
endif()
INSTALL( DIRECTORY
schemas
DESTINATION ${KICAD_DATA}
COMPONENT Runtime
)

View File

@ -0,0 +1,108 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://go.kicad.org/api/schemas/v1",
"title": "KiCad API Plugin Schema",
"description": "KiCad IPC API Plugin and Action schema",
"$ref": "#/definitions/Plugin",
"definitions": {
"Plugin": {
"type": "object",
"properties": {
"identifier": {
"type": "string",
"description": "Plugin identifier",
"pattern": "^[a-zA-Z][-_a-zA-Z0-9.]{0,98}[a-zA-Z0-9]$"
},
"name": {
"type": "string",
"maxLength": 200
},
"description": {
"type": "string",
"maxLength": 500
},
"runtime": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": [
"python",
"exec"
],
"description": "How KiCad should launch the plugin"
},
"min_version": {
"type": "string"
}
},
"required": [
"type"
]
},
"actions": {
"type": "array",
"items": {
"type": "object",
"properties": {
"identifier": {
"type": "string"
},
"name": {
"type": "string"
},
"description": {
"type": "string"
},
"show-button": {
"type": "boolean"
},
"scopes": {
"type": "array",
"items": {
"type": "string",
"enum": [
"pcb",
"schematic",
"footprint",
"symbol",
"project_manager"
]
}
},
"entrypoint": {
"type": "string"
},
"icons-light": {
"type": "array",
"items": {
"type": "string"
}
},
"icons-dark": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [
"identifier",
"name",
"description",
"show-button",
"entrypoint"
]
}
}
},
"required": [
"identifier",
"name",
"description",
"runtime",
"actions"
]
}
}
}

View File

@ -161,6 +161,7 @@ set( KICOMMON_SRCS
gestfich.cpp
increment.cpp
json_conversions.cpp
json_schema_validator.cpp
kidialog.cpp
kiid.cpp
kiway.cpp
@ -226,6 +227,7 @@ target_link_libraries( kicommon
kimath
kiplatform
nlohmann_json
nlohmann_json_schema_validator
fmt::fmt
CURL::libcurl
picosha2
@ -304,6 +306,7 @@ target_include_directories( kicommon
$<TARGET_PROPERTY:expected,INTERFACE_INCLUDE_DIRECTORIES>
$<TARGET_PROPERTY:kiapi,INTERFACE_INCLUDE_DIRECTORIES>
$<TARGET_PROPERTY:picosha2,INTERFACE_INCLUDE_DIRECTORIES>
$<TARGET_PROPERTY:nlohmann_json_schema_validator,INTERFACE_INCLUDE_DIRECTORIES>
)
add_dependencies( kicommon pegtl version_header )

View File

@ -29,6 +29,28 @@
#include <api/api_plugin_manager.h>
#include <api/api_utils.h>
#include <json_conversions.h>
#include <json_schema_validator.h>
class LOGGING_ERROR_HANDLER : public nlohmann::json_schema::error_handler
{
public:
LOGGING_ERROR_HANDLER() : m_hasError( false ) {}
bool HasError() const { return m_hasError; }
void error( const nlohmann::json::json_pointer& ptr, const nlohmann::json& instance,
const std::string& message ) override
{
m_hasError = true;
wxLogTrace( traceApi,
wxString::Format( wxS( "JSON error: at %s, value:\n%s\n%s" ),
ptr.to_string(), instance.dump(), message ) );
}
private:
bool m_hasError;
};
bool PLUGIN_RUNTIME::FromJson( const nlohmann::json& aJson )
@ -54,7 +76,8 @@ bool PLUGIN_RUNTIME::FromJson( const nlohmann::json& aJson )
struct API_PLUGIN_CONFIG
{
API_PLUGIN_CONFIG( API_PLUGIN& aParent, const wxFileName& aConfigFile );
API_PLUGIN_CONFIG( API_PLUGIN& aParent, const wxFileName& aConfigFile,
const JSON_SCHEMA_VALIDATOR& aValidator );
bool valid;
wxString identifier;
@ -67,7 +90,8 @@ struct API_PLUGIN_CONFIG
};
API_PLUGIN_CONFIG::API_PLUGIN_CONFIG( API_PLUGIN& aParent, const wxFileName& aConfigFile ) :
API_PLUGIN_CONFIG::API_PLUGIN_CONFIG( API_PLUGIN& aParent, const wxFileName& aConfigFile,
const JSON_SCHEMA_VALIDATOR& aValidator ) :
parent( aParent )
{
valid = false;
@ -94,7 +118,11 @@ API_PLUGIN_CONFIG::API_PLUGIN_CONFIG( API_PLUGIN& aParent, const wxFileName& aCo
return;
}
// TODO add schema and validate
LOGGING_ERROR_HANDLER handler;
aValidator.Validate( js, handler, nlohmann::json_uri( "#/definitions/Plugin" ) );
if( !handler.HasError() )
wxLogTrace( traceApi, "Plugin: schema validation successful" );
// All of these are required; any exceptions here leave us with valid == false
try
@ -151,9 +179,9 @@ API_PLUGIN_CONFIG::API_PLUGIN_CONFIG( API_PLUGIN& aParent, const wxFileName& aCo
}
API_PLUGIN::API_PLUGIN( const wxFileName& aConfigFile ) :
API_PLUGIN::API_PLUGIN( const wxFileName& aConfigFile, const JSON_SCHEMA_VALIDATOR& aValidator ) :
m_configFile( aConfigFile ),
m_config( std::make_unique<API_PLUGIN_CONFIG>( *this, aConfigFile ) )
m_config( std::make_unique<API_PLUGIN_CONFIG>( *this, aConfigFile, aValidator ) )
{
}

View File

@ -18,6 +18,8 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <fstream>
#include <env_vars.h>
#include <fmt/format.h>
#include <wx/dir.h>
@ -42,6 +44,13 @@ API_PLUGIN_MANAGER::API_PLUGIN_MANAGER( wxEvtHandler* aEvtHandler ) :
wxEvtHandler(),
m_parent( aEvtHandler )
{
// Read and store pcm schema
wxFileName schemaFile( PATHS::GetStockDataPath( true ), wxS( "api.v1.schema.json" ) );
schemaFile.Normalize( FN_NORMALIZE_FLAGS | wxPATH_NORM_ENV_VARS );
schemaFile.AppendDir( wxS( "schemas" ) );
m_schema_validator = std::make_unique<JSON_SCHEMA_VALIDATOR>( schemaFile );
Bind( EDA_EVT_PLUGIN_MANAGER_JOB_FINISHED, &API_PLUGIN_MANAGER::processNextJob, this );
}
@ -90,7 +99,7 @@ void API_PLUGIN_MANAGER::ReloadPlugins()
wxLogTrace( traceApi, wxString::Format( "Manager: loading plugin from %s",
aFile.GetFullPath() ) );
auto plugin = std::make_unique<API_PLUGIN>( aFile );
auto plugin = std::make_unique<API_PLUGIN>( aFile, *m_schema_validator );
if( plugin->IsOk() )
{

View File

@ -0,0 +1,64 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <fstream>
#include <wx/filename.h>
#include <wx/log.h>
#include <json_schema_validator.h>
#include <locale_io.h>
JSON_SCHEMA_VALIDATOR::JSON_SCHEMA_VALIDATOR( const wxFileName& aSchemaFile )
{
std::ifstream schema_stream( aSchemaFile.GetFullPath().fn_str() );
nlohmann::json schema;
try
{
// For some obscure reason on MINGW, using UCRT option,
// m_schema_validator.set_root_schema() hangs without switching to locale "C"
#if defined(__MINGW32__) && defined(_UCRT)
LOCALE_IO dummy;
#endif
schema_stream >> schema;
m_validator.set_root_schema( schema );
}
catch( std::exception& e )
{
if( !aSchemaFile.FileExists() )
{
wxLogError( wxString::Format( _( "schema file '%s' not found" ),
aSchemaFile.GetFullPath() ) );
}
else
{
wxLogError( wxString::Format( _( "Error loading schema: %s" ), e.what() ) );
}
}
}
nlohmann::json JSON_SCHEMA_VALIDATOR::Validate( const nlohmann::json& aJson,
nlohmann::json_schema::error_handler& aErrorHandler,
const nlohmann::json_uri& aInitialUri ) const
{
return m_validator.validate( aJson, aErrorHandler, aInitialUri );
}

View File

@ -34,6 +34,7 @@
struct API_PLUGIN_CONFIG;
class API_PLUGIN;
class JSON_SCHEMA_VALIDATOR;
struct PLUGIN_DEPENDENCY
@ -104,7 +105,7 @@ struct PLUGIN_ACTION
class KICOMMON_API API_PLUGIN
{
public:
API_PLUGIN( const wxFileName& aConfigFile );
API_PLUGIN( const wxFileName& aConfigFile, const JSON_SCHEMA_VALIDATOR& aValidator );
~API_PLUGIN();

View File

@ -25,6 +25,7 @@
#include <wx/event.h>
#include <api/api_plugin.h>
#include <json_schema_validator.h>
#include <kicommon.h>
/// Internal event used for handling async tasks
@ -98,4 +99,6 @@ private:
};
std::deque<JOB> m_jobs;
std::unique_ptr<JSON_SCHEMA_VALIDATOR> m_schema_validator;
};

View File

@ -0,0 +1,43 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef JSON_SCHEMA_VALIDATOR_H
#define JSON_SCHEMA_VALIDATOR_H
#include <wx/filename.h>
#include <kicommon.h>
#include <json_common.h>
#include <nlohmann/json-schema.hpp>
class KICOMMON_API JSON_SCHEMA_VALIDATOR
{
public:
JSON_SCHEMA_VALIDATOR( const wxFileName& aSchemaFile );
~JSON_SCHEMA_VALIDATOR() = default;
nlohmann::json Validate( const nlohmann::json& aJson,
nlohmann::json_schema::error_handler& aErrorHandler,
const nlohmann::json_uri& aInitialUri = nlohmann::json_uri("#") ) const;
private:
nlohmann::json_schema::json_validator m_validator;
};
#endif //JSON_SCHEMA_VALIDATOR_H

View File

@ -40,7 +40,6 @@ target_link_libraries( pcm
${wxWidgets_LIBRARIES}
common
picosha2
nlohmann_json_schema_validator
)
# Copy the schema to the build directory when building outside the source tree

View File

@ -26,6 +26,7 @@
#include "core/wx_stl_compat.h"
#include <env_vars.h>
#include <background_jobs_monitor.h>
#include <json_schema_validator.h>
#include "build_version.h"
#include "paths.h"
#include "pcm.h"
@ -81,32 +82,7 @@ PLUGIN_CONTENT_MANAGER::PLUGIN_CONTENT_MANAGER(
schema_file.Normalize( FN_NORMALIZE_FLAGS | wxPATH_NORM_ENV_VARS );
schema_file.AppendDir( wxS( "schemas" ) );
std::ifstream schema_stream( schema_file.GetFullPath().fn_str() );
nlohmann::json schema;
try
{
// For some obscure reason on MINGW, using UCRT option,
// m_schema_validator.set_root_schema() hangs without switching to locale "C"
#if defined(__MINGW32__) && defined(_UCRT)
LOCALE_IO dummy;
#endif
schema_stream >> schema;
m_schema_validator.set_root_schema( schema );
}
catch( std::exception& e )
{
if( !schema_file.FileExists() )
{
wxLogError( wxString::Format( _( "schema file '%s' not found" ),
schema_file.GetFullPath() ) );
}
else
{
wxLogError( wxString::Format( _( "Error loading schema: %s" ), e.what() ) );
}
}
m_schema_validator = std::make_unique<JSON_SCHEMA_VALIDATOR>( schema_file );
// Load currently installed packages
wxFileName f( PATHS::GetUserSettingsPath(), wxT( "installed_packages.json" ) );
@ -314,7 +290,7 @@ void PLUGIN_CONTENT_MANAGER::ValidateJson( const nlohmann::json& aJson,
const nlohmann::json_uri& aUri ) const
{
THROWING_ERROR_HANDLER error_handler;
m_schema_validator.validate( aJson, error_handler, aUri );
m_schema_validator->Validate( aJson, error_handler, aUri );
}

View File

@ -23,12 +23,12 @@
#include "core/wx_stl_compat.h"
#include "pcm_data.h"
#include <json_schema_validator.h>
#include "widgets/wx_progress_reporters.h"
#include <functional>
#include <iostream>
#include <map>
#include <json_common.h>
#include <nlohmann/json-schema.hpp>
#include <thread>
#include <tuple>
#include <unordered_map>
@ -393,7 +393,7 @@ private:
time_t getCurrentTimestamp() const;
wxWindow* m_dialog;
nlohmann::json_schema::json_validator m_schema_validator;
std::unique_ptr<JSON_SCHEMA_VALIDATOR> m_schema_validator;
wxString m_3rdparty_path;
wxString m_cache_path;
std::unordered_map<wxString, PCM_REPOSITORY> m_repository_cache;

View File

@ -6,15 +6,14 @@ add_library( nlohmann_json_schema_validator STATIC
string-format-check.cpp
)
add_dependencies( nlohmann_json_schema_validator kicommon )
target_include_directories( nlohmann_json_schema_validator
PUBLIC
$<INSTALL_INTERFACE:include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
)
target_include_directories( nlohmann_json_schema_validator INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} )
target_link_libraries( nlohmann_json_schema_validator
PUBLIC nlohmann_json
PRIVATE kicommon
)

View File

@ -6,7 +6,6 @@
* SPDX-License-Identifier: MIT
*
*/
#include <json_common.h>
#include <nlohmann/json.hpp>
namespace nlohmann

View File

@ -21,7 +21,6 @@
# define JSON_SCHEMA_VALIDATOR_API
#endif
#include <json_common.h>
#include <nlohmann/json.hpp>
#ifdef NLOHMANN_JSON_VERSION_MAJOR