diff options
author | montellese <montellese@xbmc.org> | 2011-06-11 16:14:38 +0200 |
---|---|---|
committer | montellese <montellese@xbmc.org> | 2011-06-11 16:15:03 +0200 |
commit | f86d3a6d76d81a9790189dbfd2a5726b7781c1ad (patch) | |
tree | da291b859ea9f69fbf3486440e80acabceeb0a0c | |
parent | 28c4d78e91b13b569352d250c05e24a758529e36 (diff) | |
parent | 787890946aec4cdf3a6ec839f175c360b5ba9fdd (diff) |
jsonrpc: add "filter" parameter to JSONRPC.Introspect to allow the retrieval of method/namespace/type/notification-specific schema definitions
* montellese/jsonrpc_introspect_filtering:
added parsing of "returns" to CJSONServiceDescription
jsonrpc: add "filter" parameter to JSONRPC.Introspect to allow the retrieval of method/namespace/type/notification-specific schema definitions
-rw-r--r-- | xbmc/interfaces/json-rpc/JSONRPC.cpp | 10 | ||||
-rw-r--r-- | xbmc/interfaces/json-rpc/JSONServiceDescription.cpp | 224 | ||||
-rw-r--r-- | xbmc/interfaces/json-rpc/JSONServiceDescription.h | 11 | ||||
-rw-r--r-- | xbmc/interfaces/json-rpc/ServiceDescription.h | 9 | ||||
-rw-r--r-- | xbmc/interfaces/json-rpc/methods.json | 9 |
5 files changed, 212 insertions, 51 deletions
diff --git a/xbmc/interfaces/json-rpc/JSONRPC.cpp b/xbmc/interfaces/json-rpc/JSONRPC.cpp index 259c803d0e..be1215de6e 100644 --- a/xbmc/interfaces/json-rpc/JSONRPC.cpp +++ b/xbmc/interfaces/json-rpc/JSONRPC.cpp @@ -59,13 +59,9 @@ void CJSONRPC::Initialize() JSON_STATUS CJSONRPC::Introspect(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result) { - bool getDescriptions = parameterObject["getdescriptions"].asBoolean(); - bool getMetadata = parameterObject["getmetadata"].asBoolean(); - bool filterByTransport = parameterObject["filterbytransport"].asBoolean(); - - CJSONServiceDescription::Print(result, transport, client, getDescriptions, getMetadata, filterByTransport); - - return OK; + return CJSONServiceDescription::Print(result, transport, client, + parameterObject["getdescriptions"].asBoolean(), parameterObject["getmetadata"].asBoolean(), parameterObject["filterbytransport"].asBoolean(), + parameterObject["filter"]["id"].asString(), parameterObject["filter"]["type"].asString(), parameterObject["filter"]["getreferences"].asBoolean()); } JSON_STATUS CJSONRPC::Version(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant& parameterObject, CVariant &result) diff --git a/xbmc/interfaces/json-rpc/JSONServiceDescription.cpp b/xbmc/interfaces/json-rpc/JSONServiceDescription.cpp index d632c277e9..1c4df3e19f 100644 --- a/xbmc/interfaces/json-rpc/JSONServiceDescription.cpp +++ b/xbmc/interfaces/json-rpc/JSONServiceDescription.cpp @@ -421,16 +421,114 @@ int CJSONServiceDescription::GetVersion() return JSONRPC_SERVICE_VERSION; } -void CJSONServiceDescription::Print(CVariant &result, ITransportLayer *transport, IClient *client, bool printDescriptions, bool printMetadata, bool filterByTransport) +JSON_STATUS CJSONServiceDescription::Print(CVariant &result, ITransportLayer *transport, IClient *client, + bool printDescriptions /* = true */, bool printMetadata /* = false */, bool filterByTransport /* = true */, + std::string filterByName /* = "" */, std::string filterByType /* = "" */, bool printReferences /* = true */) { + std::map<std::string, JSONSchemaTypeDefinition> types; + CJsonRpcMethodMap methods; + std::map<std::string, CVariant> notifications; + + int clientPermissions = client->GetPermissionFlags(); + int transportCapabilities = transport->GetCapabilities(); + + if (filterByName.size() > 0) + { + CStdString name = filterByName; + + if (filterByType == "method") + { + name = name.ToLower(); + + CJsonRpcMethodMap::JsonRpcMethodIterator methodIterator = m_actionMap.find(name); + if (methodIterator != m_actionMap.end() && + (clientPermissions & methodIterator->second.permission) != 0 && ((transportCapabilities & methodIterator->second.transportneed) != 0 || !filterByTransport)) + methods.add(methodIterator->second); + else + return InvalidParams; + } + else if (filterByType == "namespace") + { + // append a . delimiter to make sure we check for a namespace + name = name.ToLower().append("."); + + CJsonRpcMethodMap::JsonRpcMethodIterator methodIterator; + CJsonRpcMethodMap::JsonRpcMethodIterator methodIteratorEnd = m_actionMap.end(); + for (methodIterator = m_actionMap.begin(); methodIterator != methodIteratorEnd; methodIterator++) + { + // Check if the given name is at the very beginning of the method name + if (methodIterator->first.find(name) == 0 && + (clientPermissions & methodIterator->second.permission) != 0 && ((transportCapabilities & methodIterator->second.transportneed) != 0 || !filterByTransport)) + methods.add(methodIterator->second); + } + + if (methods.begin() == methods.end()) + return InvalidParams; + } + else if (filterByType == "type") + { + std::map<std::string, JSONSchemaTypeDefinition>::const_iterator typeIterator = m_types.find(name); + if (typeIterator != m_types.end()) + types[typeIterator->first] = typeIterator->second; + else + return InvalidParams; + } + else if (filterByType == "notification") + { + std::map<std::string, CVariant>::const_iterator notificationIterator = m_notifications.find(name); + if (notificationIterator != m_notifications.end()) + notifications[notificationIterator->first] = notificationIterator->second; + else + return InvalidParams; + } + else + return InvalidParams; + + // If we need to print all referenced types we have to go through all parameters etc + if (printReferences) + { + std::vector<std::string> referencedTypes; + + // Loop through all printed types to get all referenced types + std::map<std::string, JSONSchemaTypeDefinition>::const_iterator typeIterator; + std::map<std::string, JSONSchemaTypeDefinition>::const_iterator typeIteratorEnd = types.end(); + for (typeIterator = types.begin(); typeIterator != typeIteratorEnd; typeIterator++) + getReferencedTypes(typeIterator->second, referencedTypes); + + // Loop through all printed method's parameters and return value to get all referenced types + CJsonRpcMethodMap::JsonRpcMethodIterator methodIterator; + CJsonRpcMethodMap::JsonRpcMethodIterator methodIteratorEnd = methods.end(); + for (methodIterator = methods.begin(); methodIterator != methodIteratorEnd; methodIterator++) + { + for (unsigned int index = 0; index < methodIterator->second.parameters.size(); index++) + getReferencedTypes(methodIterator->second.parameters.at(index), referencedTypes); + + getReferencedTypes(methodIterator->second.returns, referencedTypes); + } + + for (unsigned int index = 0; index < referencedTypes.size(); index++) + { + std::map<std::string, JSONSchemaTypeDefinition>::const_iterator typeIterator = m_types.find(referencedTypes.at(index)); + if (typeIterator != m_types.end()) + types[typeIterator->first] = typeIterator->second; + } + } + } + else + { + types = m_types; + methods = m_actionMap; + notifications = m_notifications; + } + // Print the header result["id"] = JSONRPC_SERVICE_ID; result["version"] = JSONRPC_SERVICE_VERSION; result["description"] = JSONRPC_SERVICE_DESCRIPTION; std::map<std::string, JSONSchemaTypeDefinition>::const_iterator typeIterator; - std::map<std::string, JSONSchemaTypeDefinition>::const_iterator typeIteratorEnd = m_types.end(); - for (typeIterator = m_types.begin(); typeIterator != typeIteratorEnd; typeIterator++) + std::map<std::string, JSONSchemaTypeDefinition>::const_iterator typeIteratorEnd = types.end(); + for (typeIterator = types.begin(); typeIterator != typeIteratorEnd; typeIterator++) { CVariant currentType = CVariant(CVariant::VariantTypeObject); printType(typeIterator->second, false, true, true, printDescriptions, currentType); @@ -439,12 +537,9 @@ void CJSONServiceDescription::Print(CVariant &result, ITransportLayer *transport } // Iterate through all json rpc methods - int clientPermissions = client->GetPermissionFlags(); - int transportCapabilities = transport->GetCapabilities(); - CJsonRpcMethodMap::JsonRpcMethodIterator methodIterator; - CJsonRpcMethodMap::JsonRpcMethodIterator methodIteratorEnd = m_actionMap.end(); - for (methodIterator = m_actionMap.begin(); methodIterator != methodIteratorEnd; methodIterator++) + CJsonRpcMethodMap::JsonRpcMethodIterator methodIteratorEnd = methods.end(); + for (methodIterator = methods.begin(); methodIterator != methodIteratorEnd; methodIterator++) { if ((clientPermissions & methodIterator->second.permission) == 0 || ((transportCapabilities & methodIterator->second.transportneed) == 0 && filterByTransport)) continue; @@ -467,16 +562,18 @@ void CJSONServiceDescription::Print(CVariant &result, ITransportLayer *transport currentMethod["params"].append(param); } - currentMethod["returns"] = methodIterator->second.returns; + printType(methodIterator->second.returns, false, false, false, printDescriptions, currentMethod["returns"]); result["methods"][methodIterator->second.name] = currentMethod; } // Print notification description std::map<std::string, CVariant>::const_iterator notificationIterator; - std::map<std::string, CVariant>::const_iterator notificationIteratorEnd = m_notifications.end(); - for (notificationIterator = m_notifications.begin(); notificationIterator != notificationIteratorEnd; notificationIterator++) + std::map<std::string, CVariant>::const_iterator notificationIteratorEnd = notifications.end(); + for (notificationIterator = notifications.begin(); notificationIterator != notificationIteratorEnd; notificationIterator++) result["notifications"][notificationIterator->first] = notificationIterator->second; + + return OK; } JSON_STATUS CJSONServiceDescription::CheckCall(const char* const method, const CVariant &requestParameters, IClient *client, bool notification, MethodCall &methodCall, CVariant &outputParameters) @@ -1040,29 +1137,7 @@ bool CJSONServiceDescription::parseTypeDefinition(const CVariant &value, JSONSch } // Get the defined type of the parameter - if (value["type"].isArray()) - { - int parsedType = 0; - // If the defined type is an array, we have - // to handle a union type - for (unsigned int typeIndex = 0; typeIndex < value["type"].size(); typeIndex++) - { - // If the type is a string try to parse it - if (value["type"][typeIndex].isString()) - parsedType |= StringToSchemaValueType(value["type"][typeIndex].asString()); - else - CLog::Log(LOGWARNING, "JSONRPC: Invalid type in union type definition of type %s", type.name.c_str()); - } - - type.type = (JSONSchemaType)parsedType; - - // If the type has not been set yet - // set it to "any" - if (type.type == 0) - type.type = AnyValue; - } - else - type.type = value["type"].isString() ? StringToSchemaValueType(value["type"].asString()) : AnyValue; + type.type = parseJSONSchemaType(value["type"]); if (type.type == ObjectValue) { @@ -1254,11 +1329,49 @@ bool CJSONServiceDescription::parseTypeDefinition(const CVariant &value, JSONSch return true; } -void CJSONServiceDescription::parseReturn(const CVariant &value, CVariant &returns) +void CJSONServiceDescription::parseReturn(const CVariant &value, JSONSchemaTypeDefinition &returns) { // Only parse the "returns" definition if there is one - if (value.isMember("returns")) - returns = value["returns"]; + if (!value.isMember("returns")) + { + returns.type = NullValue; + return; + } + + // If the type of the return value is defined as a simple string we can parse it directly + if (value["returns"].isString()) + { + returns.type = parseJSONSchemaType(value["returns"]); + } + // otherwise we have to parse the whole type definition + else + parseTypeDefinition(value["returns"], returns, false); +} + +JSONSchemaType CJSONServiceDescription::parseJSONSchemaType(const CVariant &value) +{ + if (value.isArray()) + { + int parsedType = 0; + // If the defined type is an array, we have + // to handle a union type + for (unsigned int typeIndex = 0; typeIndex < value.size(); typeIndex++) + { + // If the type is a string try to parse it + if (value[typeIndex].isString()) + parsedType |= StringToSchemaValueType(value[typeIndex].asString()); + else + CLog::Log(LOGWARNING, "JSONRPC: Invalid type in union type definition"); + } + + // If the type has not been set yet set it to "any" + if (parsedType == 0) + return AnyValue; + + return (JSONSchemaType)parsedType; + } + else + return value.isString() ? StringToSchemaValueType(value.asString()) : AnyValue; } void CJSONServiceDescription::addReferenceTypeDefinition(JSONSchemaTypeDefinition &typeDefinition) @@ -1276,12 +1389,47 @@ void CJSONServiceDescription::addReferenceTypeDefinition(JSONSchemaTypeDefinitio m_types[typeDefinition.ID] = typeDefinition; } +void CJSONServiceDescription::getReferencedTypes(const JSONSchemaTypeDefinition &type, std::vector<std::string> &referencedTypes) +{ + // If the current type is a referenceable object, we can add it to the list + if (type.ID.size() > 0) + { + for (unsigned int index = 0; index < referencedTypes.size(); index++) + { + // The referenceable object has already been added to the list so we can just skip it + if (type.ID == referencedTypes.at(index)) + return; + } + + referencedTypes.push_back(type.ID); + } + + // If the current type is an object we need to check its properties + if (HasType(type.type, ObjectValue)) + { + JSONSchemaTypeDefinition::CJsonSchemaPropertiesMap::JSONSchemaPropertiesIterator iter; + JSONSchemaTypeDefinition::CJsonSchemaPropertiesMap::JSONSchemaPropertiesIterator iterEnd = type.properties.end(); + for (iter = type.properties.begin(); iter != iterEnd; iter++) + getReferencedTypes(iter->second, referencedTypes); + } + // If the current type is an array we need to check its items + if (HasType(type.type, ArrayValue)) + { + unsigned int index; + for (index = 0; index < type.items.size(); index++) + getReferencedTypes(type.items.at(index), referencedTypes); + + for (index = 0; index < type.additionalItems.size(); index++) + getReferencedTypes(type.additionalItems.at(index), referencedTypes); + } +} + CJSONServiceDescription::CJsonRpcMethodMap::CJsonRpcMethodMap() { m_actionmap = std::map<std::string, JsonRpcMethod>(); } -void CJSONServiceDescription::CJsonRpcMethodMap::add(JsonRpcMethod &method) +void CJSONServiceDescription::CJsonRpcMethodMap::add(const JsonRpcMethod &method) { CStdString name = method.name; name = name.ToLower(); diff --git a/xbmc/interfaces/json-rpc/JSONServiceDescription.h b/xbmc/interfaces/json-rpc/JSONServiceDescription.h index 9b42b60ccf..6b30eed190 100644 --- a/xbmc/interfaces/json-rpc/JSONServiceDescription.h +++ b/xbmc/interfaces/json-rpc/JSONServiceDescription.h @@ -205,7 +205,7 @@ namespace JSONRPC /*! \brief Definition of the return value */ - CVariant returns; + JSONSchemaTypeDefinition returns; } JsonRpcMethod; /*! @@ -292,7 +292,7 @@ namespace JSONRPC \param printMetadata Whether to print XBMC specific data or not \param filterByTransport Whether to filter by transport or not */ - static void Print(CVariant &result, ITransportLayer *transport, IClient *client, bool printDescriptions, bool printMetadata, bool filterByTransport); + static JSON_STATUS Print(CVariant &result, ITransportLayer *transport, IClient *client, bool printDescriptions = true, bool printMetadata = false, bool filterByTransport = true, std::string filterByName = "", std::string filterByType = "", bool printReferences = true); /*! \brief Checks the given parameters from the request against the @@ -322,15 +322,18 @@ namespace JSONRPC static bool parseMethod(const CVariant &value, JsonRpcMethod &method); static bool parseParameter(CVariant &value, JSONSchemaTypeDefinition ¶meter); static bool parseTypeDefinition(const CVariant &value, JSONSchemaTypeDefinition &type, bool isParameter); - static void parseReturn(const CVariant &value, CVariant &returns); + static void parseReturn(const CVariant &value, JSONSchemaTypeDefinition &returns); + static JSONSchemaType parseJSONSchemaType(const CVariant &value); static void addReferenceTypeDefinition(JSONSchemaTypeDefinition &typeDefinition); + static void getReferencedTypes(const JSONSchemaTypeDefinition &type, std::vector<std::string> &referencedTypes); + class CJsonRpcMethodMap { public: CJsonRpcMethodMap(); - void add(JsonRpcMethod &method); + void add(const JsonRpcMethod &method); typedef std::map<std::string, JsonRpcMethod>::const_iterator JsonRpcMethodIterator; JsonRpcMethodIterator begin() const; diff --git a/xbmc/interfaces/json-rpc/ServiceDescription.h b/xbmc/interfaces/json-rpc/ServiceDescription.h index 4610199929..49a066065a 100644 --- a/xbmc/interfaces/json-rpc/ServiceDescription.h +++ b/xbmc/interfaces/json-rpc/ServiceDescription.h @@ -770,7 +770,14 @@ namespace JSONRPC "\"params\": [" "{ \"name\": \"getdescriptions\", \"type\": \"boolean\", \"default\": true }," "{ \"name\": \"getmetadata\", \"type\": \"boolean\", \"default\": false }," - "{ \"name\": \"filterbytransport\", \"type\": \"boolean\", \"default\": true }" + "{ \"name\": \"filterbytransport\", \"type\": \"boolean\", \"default\": true }," + "{ \"name\": \"filter\", \"type\": \"object\"," + "\"properties\": {" + "\"id\": { \"type\": \"string\", \"required\": true, \"description\": \"Name of a namespace, method or type\" }," + "\"type\": { \"type\": \"string\", \"required\": true, \"enum\": [ \"method\", \"namespace\", \"type\", \"notification\" ], \"description\": \"Type of the given name\" }," + "\"getreferences\": { \"type\": \"boolean\", \"default\": true, \"description\": \"Whether or not to print the schema for referenced types\" }" + "}" + "}" "]," "\"returns\": \"object\"" "}", diff --git a/xbmc/interfaces/json-rpc/methods.json b/xbmc/interfaces/json-rpc/methods.json index 5b91ff77df..2e14d29898 100644 --- a/xbmc/interfaces/json-rpc/methods.json +++ b/xbmc/interfaces/json-rpc/methods.json @@ -7,7 +7,14 @@ "params": [ { "name": "getdescriptions", "type": "boolean", "default": true }, { "name": "getmetadata", "type": "boolean", "default": false }, - { "name": "filterbytransport", "type": "boolean", "default": true } + { "name": "filterbytransport", "type": "boolean", "default": true }, + { "name": "filter", "type": "object", + "properties": { + "id": { "type": "string", "required": true, "description": "Name of a namespace, method or type" }, + "type": { "type": "string", "required": true, "enum": [ "method", "namespace", "type", "notification" ], "description": "Type of the given name" }, + "getreferences": { "type": "boolean", "default": true, "description": "Whether or not to print the schema for referenced types" } + } + } ], "returns": "object" }, |