22#include <fmt/format.h>
23#include <wx/translation.h>
26#include <boost/algorithm/string.hpp>
41 auto curl = std::make_shared<KICAD_CURL_EASY>();
42 curl->SetHeader(
"Accept",
"application/json" );
43 curl->SetHeader(
"Authorization",
"Token " + aSource.
token );
44 curl->SetFollowRedirects(
true );
46 return [curl](
const std::string& aUrl,
int& aStatusCode, std::string& aBody, std::string& aError )
48 if( !curl->SetURL( aUrl ) )
50 aError =
"Unable to set request URL.";
54 if(
const int result = curl->Perform();
result != CURLE_OK )
56 aError = curl->GetErrorText(
result );
60 aStatusCode = curl->GetResponseStatusCode();
61 aBody = curl->GetBuffer();
78 if( aTestConnectionNow )
100 if(
res.length() == 0 )
102 m_lastError += wxString::Format(
_(
"KiCad received an empty response!" ) +
"\n" );
106 nlohmann::json response = nlohmann::json::parse(
res );
109 if( !response.at(
"categories" ).empty() && !response.at(
"parts" ).empty() )
113 catch(
const std::exception& e )
115 m_lastError += wxString::Format(
_(
"Error: %s" ) +
"\n" +
_(
"API Response: %s" ) +
"\n",
118 wxLogTrace(
traceHTTPLib, wxT(
"validateHttpLibraryEndpoints: Exception while testing API connection: %s" ),
136 wxLogTrace(
traceHTTPLib, wxT(
"syncCategories: without valid connection!" ) );
140 std::string
res =
"";
152 nlohmann::json response = nlohmann::json::parse(
res );
155 for(
const auto& item : response.items() )
159 auto& value = item.value();
160 category.
id = value[
"id"].get<std::string>();
161 category.
name = value[
"name"].get<std::string>();
163 if( value.contains(
"description" ) )
165 category.
description = value[
"description"].get<std::string>();
172 catch(
const std::exception& e )
174 m_lastError += wxString::Format(
_(
"Error: %s" ) +
"\n" +
_(
"API Response: %s" ) +
"\n",
192 wxString strval( std::any_cast<std::string>( aVal ).c_str(), wxConvUTF8 );
194 if( strval.IsEmpty() )
195 return aDefaultValue;
199 for(
const auto& trueVal : { wxS(
"true" ), wxS(
"yes" ), wxS(
"y" ), wxS(
"1" ) } )
201 if( strval.Matches( trueVal ) )
205 for(
const auto& falseVal : { wxS(
"false" ), wxS(
"no" ), wxS(
"n" ), wxS(
"0" ) } )
207 if( strval.Matches( falseVal ) )
211 catch(
const std::bad_any_cast& )
215 return aDefaultValue;
223 aPart.
id = aPart_json.at(
"id" );
226 if( aPart_json.contains(
"name" ) )
227 aPart.
name = aPart_json.at(
"name" );
233 if( aPart_json.contains(
"description" ) )
234 aPart.
desc = aPart_json.at(
"description" );
236 if( aPart_json.contains(
"keywords" ) )
237 aPart.
keywords = aPart_json.at(
"keywords" );
239 if( aPart_json.contains(
"footprint_filters" ) )
241 nlohmann::json filters_json = aPart_json.at(
"footprint_filters" );
243 if( filters_json.is_array() )
245 for(
const auto& val : filters_json )
260 if( aValue.is_boolean() )
261 return aValue.get<
bool>();
263 if( aValue.is_string() )
272 if( aPartJson.contains(
"symbolIdStr" ) && aPartJson.at(
"symbolIdStr" ).is_string() )
273 aPart.
symbolIdStr = aPartJson.at(
"symbolIdStr" ).get<std::string>();
275 if( aPartJson.contains(
"exclude_from_bom" ) )
278 if( aPartJson.contains(
"exclude_from_board" ) )
281 if( aPartJson.contains(
"exclude_from_sim" ) )
284 if( !aPartJson.contains(
"fields" ) || !aPartJson.at(
"fields" ).is_object() )
289 for(
const auto& field : aPartJson.at(
"fields" ).items() )
291 const nlohmann::json& properties = field.value();
293 if( !properties.is_object() || !properties.contains(
"value" )
294 || !properties.at(
"value" ).is_string() )
299 std::string value = properties.at(
"value" ).get<std::string>();
302 if( properties.contains(
"visible" ) )
305 aPart.
fields.emplace_back( field.key(), std::make_tuple( value, visible ) );
318 wxLogTrace(
traceHTTPLib, wxT(
"SelectOne: without valid connection!" ) );
326 if( std::difftime( std::time(
nullptr ),
m_cachedParts[aPartID].lastCached ) <
m_source.timeout_parts )
333 std::string
res =
"";
334 std::string url =
m_source.root_url + fmt::format(
"parts/{}.json", aPartID );
346 nlohmann::ordered_json response = nlohmann::ordered_json::parse(
res );
349 aFetchedPart.
lastCached = std::time(
nullptr );
357 if( response.contains(
"pin_map" ) )
368 catch(
const std::exception& e )
370 m_lastError += wxString::Format(
_(
"Error: %s" ) +
"\n" +
_(
"API Response: %s" ) +
"\n",
390 wxLogTrace(
traceHTTPLib, wxT(
"SelectAll: without valid connection!" ) );
394 std::string
res =
"";
395 std::string url =
m_source.root_url + fmt::format(
"parts/category/{}.json", aCategory.
id );
404 nlohmann::json response = nlohmann::json::parse(
res );
406 for( nlohmann::json& item : response )
424 aParts.emplace_back( std::move( part ) );
427 catch(
const std::exception& e )
429 m_lastError += wxString::Format(
_(
"Error: %s" ) +
"\n" +
_(
"API Response: %s" ) +
"\n",
445 if( !
m_retriever( aUrl, aStatusCode, aBody, error ) )
457 if( aStatusCode != 200 )
459 m_lastError += wxString::Format(
_(
"API responded with error code: %s" ) +
"\n",
470 auto codeDescription =
471 []( uint16_t aCode ) -> wxString
475 case 100:
return wxS(
"Continue" );
476 case 101:
return wxS(
"Switching Protocols" );
477 case 102:
return wxS(
"Processing" );
478 case 103:
return wxS(
"Early Hints" );
480 case 200:
return wxS(
"OK" );
481 case 201:
return wxS(
"Created" );
482 case 203:
return wxS(
"Non-Authoritative Information" );
483 case 204:
return wxS(
"No Content" );
484 case 205:
return wxS(
"Reset Content" );
485 case 206:
return wxS(
"Partial Content" );
486 case 207:
return wxS(
"Multi-Status" );
487 case 208:
return wxS(
"Already Reported" );
488 case 226:
return wxS(
"IM Used" );
490 case 300:
return wxS(
"Multiple Choices" );
491 case 301:
return wxS(
"Moved Permanently" );
492 case 302:
return wxS(
"Found" );
493 case 303:
return wxS(
"See Other" );
494 case 304:
return wxS(
"Not Modified" );
495 case 305:
return wxS(
"Use Proxy (Deprecated)" );
496 case 306:
return wxS(
"Unused" );
497 case 307:
return wxS(
"Temporary Redirect" );
498 case 308:
return wxS(
"Permanent Redirect" );
500 case 400:
return wxS(
"Bad Request" );
501 case 401:
return wxS(
"Unauthorized" );
502 case 402:
return wxS(
"Payment Required (Experimental)" );
503 case 403:
return wxS(
"Forbidden" );
504 case 404:
return wxS(
"Not Found" );
505 case 405:
return wxS(
"Method Not Allowed" );
506 case 406:
return wxS(
"Not Acceptable" );
507 case 407:
return wxS(
"Proxy Authentication Required" );
508 case 408:
return wxS(
"Request Timeout" );
509 case 409:
return wxS(
"Conflict" );
510 case 410:
return wxS(
"Gone" );
511 case 411:
return wxS(
"Length Required" );
512 case 412:
return wxS(
"Payload Too Large" );
513 case 414:
return wxS(
"URI Too Long" );
514 case 415:
return wxS(
"Unsupported Media Type" );
515 case 416:
return wxS(
"Range Not Satisfiable" );
516 case 417:
return wxS(
"Expectation Failed" );
517 case 418:
return wxS(
"I'm a teapot" );
518 case 421:
return wxS(
"Misdirected Request" );
519 case 422:
return wxS(
"Unprocessable Content" );
520 case 423:
return wxS(
"Locked" );
521 case 424:
return wxS(
"Failed Dependency" );
522 case 425:
return wxS(
"Too Early (Experimental)" );
523 case 426:
return wxS(
"Upgrade Required" );
524 case 428:
return wxS(
"Precondition Required" );
525 case 429:
return wxS(
"Too Many Requests" );
526 case 431:
return wxS(
"Request Header Fields Too Large" );
527 case 451:
return wxS(
"Unavailable For Legal Reasons" );
529 case 500:
return wxS(
"Internal Server Error" );
530 case 501:
return wxS(
"Not Implemented" );
531 case 502:
return wxS(
"Bad Gateway" );
532 case 503:
return wxS(
"Service Unavailable" );
533 case 504:
return wxS(
"Gateway Timeout" );
534 case 505:
return wxS(
"HTTP Version Not Supported" );
535 case 506:
return wxS(
"Variant Also Negotiates" );
536 case 507:
return wxS(
"Insufficient Storage" );
537 case 508:
return wxS(
"Loop Detected" );
538 case 510:
return wxS(
"Not Extended" );
539 case 511:
return wxS(
"Network Authentication Required" );
540 default:
return wxS(
"Unknown" );
544 return wxString::Format( wxS(
"%d: %s" ), aHttpCode, codeDescription( aHttpCode ) );
bool validateHttpLibraryEndpoints()
std::map< std::string, HTTP_LIB_PART > m_cachedParts
std::map< std::string, std::string > m_categoryDescriptions
bool checkServerResponse(int aStatusCode)
bool retrieve(const std::string &aUrl, int &aStatusCode, std::string &aBody)
Fetch aUrl through the transport.
bool SelectAll(const HTTP_LIB_CATEGORY &aCategory, std::vector< HTTP_LIB_PART > &aParts)
Retrieve all parts from a specific category from the HTTP library.
HTTP_LIB_CONNECTION(const HTTP_LIB_SOURCE &aSource, bool aTestConnectionNow)
std::map< std::string, std::tuple< std::string, std::string > > m_cache
std::vector< HTTP_LIB_CATEGORY > m_categories
bool IsValidEndpoint() const
std::function< bool(const std::string &aUrl, int &aStatusCode, std::string &aBody, std::string &aError)> RETRIEVER
Allows replacing CURL fetches with a mock for testing.
bool SelectOne(const std::string &aPartID, HTTP_LIB_PART &aFetchedPart)
Retrieve a single part with full details from the HTTP library.
wxString httpErrorCodeDescription(uint16_t aHttpCode)
HTTP response status codes indicate whether a specific HTTP request has been successfully completed.
static UTF8 FixIllegalChars(const UTF8 &aLibItemName, bool aLib)
Replace illegal LIB_ID item name characters with underscores '_'.
const char * c_str() const
void setPartIdNameAndMetadata(const nlohmann::json &aPart_json, HTTP_LIB_PART &aPart)
const char *const traceHTTPLib
bool setPartExtendedData(const nlohmann::json &aPartJson, HTTP_LIB_PART &aPart)
Parse the exclusion flags and field content from a part's JSON record into aPart.
bool boolFromString(const std::any &aVal, bool aDefaultValue)
static bool jsonBoolField(const nlohmann::json &aValue, bool aDefault)
static HTTP_LIB_CONNECTION::RETRIEVER makeDefaultRetriever(const HTTP_LIB_SOURCE &aSource)
const char *const traceHTTPLib
bool setPartExtendedData(const nlohmann::json &aPartJson, HTTP_LIB_PART &aPart)
Parse the exclusion flags and field content from a part's JSON record into aPart.
std::vector< ASSOCIATED_FOOTPRINT > ParseAssociatedFootprints(const nlohmann::json &aParent)
Parse the spec-form footprint associations from aParent["associated_footprints"] (issue #2282).
std::unordered_map< wxString, std::vector< wxString > > ParseLegacyPinAssignments(const nlohmann::json &aArray)
Parse the legacy flat pin-assignment JSON array (MR !2540 form) into a symbol-pin to footprint-pad(s)...
PIN_MAP_SET ParsePinMapSet(const nlohmann::json &aParent)
Parse the spec-form named maps from aParent["pin_maps"] into a PIN_MAP_SET (issue #2282).
std::string id
id of category
std::string name
name of category
std::string description
description of category
std::vector< ASSOCIATED_FOOTPRINT > associated_footprints
std::unordered_map< wxString, std::vector< wxString > > pin_map
Legacy flat MR !2540 pin assignment table (read for one release; issue #2282).
std::vector< std::string > fp_filters
std::vector< std::pair< std::string, field_type > > fields
PIN_MAP_SET named_pin_maps
Spec-form named pin maps and their footprint associations (issue #2282).
Connection parameters for one HTTP library.
wxString result
Test unit parsing edge cases and error handling.