Ejemplo n.º 1
0
 void handleCreateUserAnswer(JsonObjectPtr aJsonResponse, ErrorPtr aError)
 {
   if (Error::isOK(aError)) {
     FOCUSLOG("Received success answer:\n%s", aJsonResponse->json_c_str());
     JsonObjectPtr s = HueComm::getSuccessItem(aJsonResponse);
     // apparently successful, extract user name
     if (s) {
       JsonObjectPtr u = s->get("username");
       if (u) {
         hueComm.userName = u->stringValue();
         hueComm.uuid = currentAuthCandidate->first;
         hueComm.baseURL = currentAuthCandidate->second;
         hueComm.apiReady = true; // can use API now
         FOCUSLOG("hue Bridge %s @ %s: successfully registered as user %s", hueComm.uuid.c_str(), hueComm.baseURL.c_str(), hueComm.userName.c_str());
         // successfully registered with hue bridge, let caller know
         callback(ErrorPtr());
         // done!
         keepAlive.reset(); // will delete object if nobody else keeps it
         return;
       }
     }
   }
   else {
     LOG(LOG_ERR, "hue Bridge: Error creating user: %s", aError->description().c_str());
   }
   // try next
   ++currentAuthCandidate;
   processCurrentAuthCandidate(); // process next, if any
 }
Ejemplo n.º 2
0
static JsonObjectPtr jsonRPCObj()
{
  JsonObjectPtr obj = JsonObject::newObj();
  // the mandatory version string all objects need to have
  obj->add("jsonrpc", JsonObject::newString("2.0"));
  return obj;
}
Ejemplo n.º 3
0
 void processCurrentAuthCandidate()
 {
   if (currentAuthCandidate!=authCandidates.end() && hueComm.findInProgress) {
     // try to authorize
     FOCUSLOG("Auth candidate: uuid=%s, baseURL=%s -> try creating user", currentAuthCandidate->first.c_str(), currentAuthCandidate->second.c_str());
     JsonObjectPtr request = JsonObject::newObj();
     request->add("username", JsonObject::newString(userName));
     request->add("devicetype", JsonObject::newString(deviceType));
     hueComm.apiAction(httpMethodPOST, currentAuthCandidate->second.c_str(), request, boost::bind(&BridgeFinder::handleCreateUserAnswer, this, _1, _2), true);
   }
   else {
     // done with all candidates (or find aborted in hueComm)
     if (authCandidates.size()>0 && MainLoop::now()<startedAuth+authTimeWindow && hueComm.findInProgress) {
       // we have still candidates and time to do a retry in a second, and find is not aborted
       retryLoginTicket = MainLoop::currentMainLoop().executeOnce(boost::bind(&BridgeFinder::attemptPairingWithCandidates, this), 1*Second);
       return;
     }
     else {
       // all candidates tried, nothing found in given time
       LOG(LOG_NOTICE, "Could not register with a hue bridge");
       hueComm.findInProgress = false;
       callback(ErrorPtr(new HueCommError(HueCommErrorNoRegistration, "No hue bridge found ready to register")));
       // done!
       keepAlive.reset(); // will delete object if nobody else keeps it
       return;
     }
   }
 }
Ejemplo n.º 4
0
const char *JsonObject::getCString(const char *aKey)
{
  JsonObjectPtr p = get(aKey);
  if (p)
    return p->c_strValue();
  return NULL;
}
Ejemplo n.º 5
0
ErrorPtr JsonRpcComm::sendResult(const char *aJsonRpcId, JsonObjectPtr aResult)
{
  JsonObjectPtr response = jsonRPCObj();
  // add the result, can be NULL
  response->add("result", aResult);
  // add the ID so the caller can associate with a previous request
  response->add("id", JsonObject::newString(aJsonRpcId));
  // now send
  FOCUSLOG("Sending JSON-RPC 2.0 result message:\n  %s\n", response->c_strValue());
  return sendMessage(response);
}
Ejemplo n.º 6
0
JsonObjectPtr HueComm::getSuccessItem(JsonObjectPtr aResult, int aIndex)
{
  if (aResult && aIndex<aResult->arrayLength()) {
    JsonObjectPtr responseItem = aResult->arrayGet(aIndex);
    JsonObjectPtr successItem;
    if (responseItem && responseItem->get("success", successItem)) {
      return successItem;
    }
  }
  return JsonObjectPtr();
}
Ejemplo n.º 7
0
void HomeConnectDeviceOven::handleEventTypeNotify(const string& aKey, JsonObjectPtr aValue)
{
  ALOG(LOG_INFO, "Oven Event 'NOTIFY' - item: %s, %s", aKey.c_str(), aValue ? aValue->c_strValue() : "<none>");

  if (aKey == "Cooking.Oven.Option.SetpointTemperature") {
    int32_t value = (aValue != NULL) ? aValue->int32Value() : 0;
    targetTemperatureProp->setInt32Value(value);
    return;
  }

  inherited::handleEventTypeNotify(aKey, aValue);
}
Ejemplo n.º 8
0
ErrorPtr JsonRpcComm::sendError(const char *aJsonRpcId, uint32_t aErrorCode, const char *aErrorMessage, JsonObjectPtr aErrorData)
{
  JsonObjectPtr response = jsonRPCObj();
  // create the error object
  JsonObjectPtr errorObj = JsonObject::newObj();
  errorObj->add("code", JsonObject::newInt32(aErrorCode));
  string errMsg;
  if (aErrorMessage) {
    errMsg = aErrorMessage;
  }
  else {
    errMsg = string_format("Error code %d (0x%X)", aErrorCode, aErrorCode);
  }
  errorObj->add("message", JsonObject::newString(errMsg));
  // add the data object if any
  if (aErrorData) {
    errorObj->add("data", aErrorData);
  }
  // add the error object
  response->add("error", errorObj);
  // add the ID so the caller can associate with a previous request
  response->add("id", aJsonRpcId ? JsonObject::newString(aJsonRpcId) : JsonObjectPtr());
  // now send
  FOCUSLOG("Sending JSON-RPC 2.0 error message:\n  %s\n", response->c_strValue());
  return sendMessage(response);
}
Ejemplo n.º 9
0
JsonObjectPtr NetatmoDevice::findDeviceJson(JsonObjectPtr aJsonArray, const string& aDeviceId)
{
  if (aJsonArray) {
    for (int index=0; auto device = aJsonArray->arrayGet(index); index++) {
      if (auto id = device->get("_id")) {
        if (id->stringValue() == aDeviceId) return device;
      }
    }
  }
  return nullptr;
}
Ejemplo n.º 10
0
void NetatmoDevice::updateData(JsonObjectPtr aJson)
{
  if (aJson) {
    if (auto swVersionJson = aJson->get("firmware")) {
      swVersion->setStringValue(swVersionJson->stringValue());
    }

    if (auto dashBoardData = aJson->get("dashboard_data")) {
      if (auto tempJson = dashBoardData->get("Temperature")) {
        sensorTemperature->updateSensorValue(tempJson->doubleValue());
      }

      if (auto humidityJson = dashBoardData->get("Humidity")) {
        sensorHumidity->updateSensorValue(humidityJson->doubleValue());
      }

      if (auto tempTrendJson = dashBoardData->get("temp_trend")) {
        auto statusTrend = getStatusTrend(tempTrendJson->stringValue());
        if (statusTrend < StatusTrend::_num){
          if (statusTempTrend->value()->setInt32Value(static_cast<int>(statusTrend))){
            statusTempTrend->push();
          }
        }
      }

      if (auto timestampJson = dashBoardData->get("time_utc")){
        measurementAbsloluteTimestamp = timestampJson->int64Value();
        auto secondsToday = getSecondsFromMidnight(measurementAbsloluteTimestamp);
        measurementTimestamp->setInt32Value(secondsToday.count());
      }

    }
  }

  bool wasPresent = isPresent;
  isPresent = getElapsedHoursFromLastMeasure(measurementAbsloluteTimestamp).count() < LAST_MEASUREMENT_ELAPSED_HOURS_MAX;

  // if last measurement was measured longer than 12 hrs ago, set device to inactive
  if (wasPresent != isPresent && !isPresent) reportVanished();

}
Ejemplo n.º 11
0
void NetatmoDevice::setIdentificationData(JsonObjectPtr aJson)
{
  if (aJson) {
    if (auto typeJson = aJson->get("type")) {
      netatmoType = typeJson->stringValue();
    }

    if (auto idJson = aJson->get("_id")) {
      netatmoId = idJson->stringValue();
    }

    if (auto nameJson = aJson->get("module_name")) {
      netatmoName = nameJson->stringValue();
      initializeName(netatmoName);
    }

    if (auto fwJson = aJson->get("firmware")) {
      netatmoFw = fwJson->stringValue();
    }
  }
}
Ejemplo n.º 12
0
ErrorPtr JsonRpcComm::sendRequest(const char *aMethod, JsonObjectPtr aParams, JsonRpcResponseCB aResponseHandler)
{
  JsonObjectPtr request = jsonRPCObj();
  // the method or notification name
  request->add("method", JsonObject::newString(aMethod));
  // the optional parameters
  if (aParams) {
    request->add("params", aParams);
  }
  // in any case, count this call (even if it is a notification)
  requestIdCounter++;
  // in case this is a method call (i.e. a answer handler is specified), add the call ID
  if (aResponseHandler) {
    // add the ID so the callee can include it in the response
    request->add("id", JsonObject::newInt32(requestIdCounter));
    // remember it in our map
    pendingAnswers[requestIdCounter] = aResponseHandler;
  }
  // now send
  FOCUSLOG("Sending JSON-RPC 2.0 request message:\n  %s\n", request->c_strValue());
  return sendMessage(request);
}
Ejemplo n.º 13
0
void P44VdcHost::configApiRequestHandler(JsonCommPtr aJsonComm, ErrorPtr aError, JsonObjectPtr aJsonObject)
{
  ErrorPtr err;
  // when coming from mg44, requests have the following form
  // - for GET requests like http://localhost:8080/api/json/myuri?foo=bar&this=that
  //   {"method":"GET","uri":"myuri","uri_params":{"foo":"bar","this":"that"}}
  // - for POST requests like
  //   curl "http://localhost:8080/api/json/myuri?foo=bar&this=that" --data-ascii "{ \"content\":\"data\", \"important\":false }"
  //   {"method":"POST","uri":"myuri","uri_params":{"foo":"bar","this":"that"},"data":{"content":"data","important":false}}
  //   curl "http://localhost:8080/api/json/myuri" --data-ascii "{ \"content\":\"data\", \"important\":false }"
  //   {"method":"POST","uri":"myuri","data":{"content":"data","important":false}}
  // processing:
  // - a JSON request must be either specified in the URL or in the POST data, not both
  // - if POST data ("data" member in the incoming request) is present, "uri_params" is ignored
  // - "uri" selects one of possibly multiple APIs
  if (Error::isOK(aError)) {
    // not JSON level error, try to process
    LOG(LOG_DEBUG, "cfg -> vdcd (JSON) request received: %s", aJsonObject->c_strValue());
    // find out which one is our actual JSON request
    // - try POST data first
    JsonObjectPtr request = aJsonObject->get("data");
    if (!request) {
      // no POST data, try uri_params
      request = aJsonObject->get("uri_params");
    }
    if (!request) {
      // empty query, that's an error
      aError = Error::err<P44VdcError>(415, "empty request");
    }
    else {
      // have the request processed
      string apiselector;
      JsonObjectPtr uri = aJsonObject->get("uri");
      if (uri) apiselector = uri->stringValue();
      // dispatch according to API
      if (apiselector=="vdc") {
        // process request that basically is a vdc API request, but as simple webbish JSON, not as JSON-RPC 2.0
        // and without the need to start a vdc session
        // Notes:
        // - if dSUID is specified invalid or empty, the vdc host itself is addressed.
        // - use x-p44-vdcs and x-p44-devices properties to find dsuids
        aError = processVdcRequest(aJsonComm, request);
      }
      else if (apiselector=="p44") {
        // process p44 specific requests
        aError = processP44Request(aJsonComm, request);
      }
      else {
        // unknown API selector
        aError = Error::err<P44VdcError>(400, "invalid URI, unknown API");
      }
    }
  }
  // if error or explicit OK, send response now. Otherwise, request processing will create and send the response
  if (aError) {
    sendCfgApiResponse(aJsonComm, JsonObjectPtr(), aError);
  }
}
Ejemplo n.º 14
0
void P44VdcHost::sendCfgApiResponse(JsonCommPtr aJsonComm, JsonObjectPtr aResult, ErrorPtr aError)
{
  // create response
  JsonObjectPtr response = JsonObject::newObj();
  if (!Error::isOK(aError)) {
    // error, return error response
    response->add("error", JsonObject::newInt32((int32_t)aError->getErrorCode()));
    response->add("errormessage", JsonObject::newString(aError->getErrorMessage()));
    response->add("errordomain", JsonObject::newString(aError->getErrorDomain()));
    VdcApiErrorPtr ve = boost::dynamic_pointer_cast<VdcApiError>(aError);
    if (ve) {
      response->add("errortype", JsonObject::newInt32(ve->getErrorType()));
      response->add("userfacingmessage", JsonObject::newString(ve->getUserFacingMessage()));
    }
  }
  else {
    // no error, return result (if any)
    response->add("result", aResult);
  }
  LOG(LOG_DEBUG, "Config API response: %s", response->c_strValue());
  aJsonComm->sendMessage(response);
}
Ejemplo n.º 15
0
// access to plan44 extras that are not part of the vdc API
ErrorPtr P44VdcHost::processP44Request(JsonCommPtr aJsonComm, JsonObjectPtr aRequest)
{
  ErrorPtr err;
  JsonObjectPtr m = aRequest->get("method");
  if (!m) {
    err = Error::err<P44VdcError>(400, "missing 'method'");
  }
  else {
    string method = m->stringValue();
    if (method=="learn") {
      // check proximity check disabling
      bool disableProximity = false;
      JsonObjectPtr o = aRequest->get("disableProximityCheck");
      if (o) {
        disableProximity = o->boolValue();
      }
      // get timeout
      o = aRequest->get("seconds");
      int seconds = 30; // default to 30
      if (o) seconds = o->int32Value();
      if (seconds==0) {
        // end learning prematurely
        stopLearning();
        MainLoop::currentMainLoop().cancelExecutionTicket(learnIdentifyTicket);
        // - close still running learn request
        if (learnIdentifyRequest) {
          learnIdentifyRequest->closeConnection();
          learnIdentifyRequest.reset();
        }
        // - confirm abort with no result
        sendCfgApiResponse(aJsonComm, JsonObjectPtr(), ErrorPtr());
      }
      else {
        // start learning
        learnIdentifyRequest = aJsonComm; // remember so we can cancel it when we receive a separate cancel request
        startLearning(boost::bind(&P44VdcHost::learnHandler, this, aJsonComm, _1, _2), disableProximity);
        learnIdentifyTicket = MainLoop::currentMainLoop().executeOnce(boost::bind(&P44VdcHost::learnHandler, this, aJsonComm, false, Error::err<P44VdcError>(408, "learn timeout")), seconds*Second);
      }
    }
    else if (method=="identify") {
      // get timeout
      JsonObjectPtr o = aRequest->get("seconds");
      int seconds = 30; // default to 30
      if (o) seconds = o->int32Value();
      if (seconds==0) {
        // end reporting user activity
        setUserActionMonitor(NULL);
        MainLoop::currentMainLoop().cancelExecutionTicket(learnIdentifyTicket);
        // - close still running identify request
        if (learnIdentifyRequest) {
          learnIdentifyRequest->closeConnection();
          learnIdentifyRequest.reset();
        }
        // - confirm abort with no result
        sendCfgApiResponse(aJsonComm, JsonObjectPtr(), ErrorPtr());
      }
      else {
        // wait for next user activity
        learnIdentifyRequest = aJsonComm; // remember so we can cancel it when we receive a separate cancel request
        setUserActionMonitor(boost::bind(&P44VdcHost::identifyHandler, this, aJsonComm, _1));
        learnIdentifyTicket = MainLoop::currentMainLoop().executeOnce(boost::bind(&P44VdcHost::identifyHandler, this, aJsonComm, DevicePtr()), seconds*Second);
      }
    }
    else if (method=="logLevel") {
      // get or set logging level for vdcd
      JsonObjectPtr o = aRequest->get("value");
      if (o) {
        // set new value first
        int newLevel = o->int32Value();
        int oldLevel = LOGLEVEL;
        SETLOGLEVEL(newLevel);
        LOG(LOG_WARNING, "\n\n========== changed log level from %d to %d ===============", oldLevel, newLevel);
      }
      // anyway: return current value
      sendCfgApiResponse(aJsonComm, JsonObject::newInt32(LOGLEVEL), ErrorPtr());
    }
    else {
      err = Error::err<P44VdcError>(400, "unknown method");
    }
  }
  return err;
}
Ejemplo n.º 16
0
void HueApiOperation::processAnswer(JsonObjectPtr aJsonResponse, ErrorPtr aError)
{
  error = aError;
  if (Error::isOK(error)) {
    // pre-process response in case of non-GET
    if (method!=httpMethodGET) {
      // Expected:
      //  [{"error":{"type":xxx,"address":"yyy","description":"zzz"}}]
      // or
      //  [{"success": { "xxx": "xxxxxxxx" }]
      int errCode = HueCommErrorInvalidResponse;
      string errMessage = "invalid response";
      for (int i=0; i<aJsonResponse->arrayLength(); i++) {
        JsonObjectPtr responseItem = aJsonResponse->arrayGet(i);
        responseItem->resetKeyIteration();
        JsonObjectPtr responseParams;
        string statusToken;
        if (responseItem->nextKeyValue(statusToken, responseParams)) {
          if (statusToken=="success" && responseParams) {
            // apparently successful, return entire response
            // Note: use getSuccessItem() to get success details
            data = aJsonResponse;
            errCode = HueCommErrorOK; // ok
            break;
          }
          else if (statusToken=="error" && responseParams) {
            // make Error object out of it
            JsonObjectPtr e = responseParams->get("type");
            if (e)
              errCode = e->int32Value();
            e = responseParams->get("description");
            if (e)
              errMessage = e->stringValue();
            break;
          }
        }
      } // for
      if (errCode!=HueCommErrorOK) {
        error = ErrorPtr(new HueCommError(errCode, errMessage));
      }
    }
    else {
      // GET, just return entire data
      data = aJsonResponse;
    }
  }
  // done
  completed = true;
  // have queue reprocessed
  hueComm.processOperations();
}
Ejemplo n.º 17
0
void JsonRpcComm::gotJson(ErrorPtr aError, JsonObjectPtr aJsonObject)
{
  JsonRpcCommPtr keepMeAlive(this); // make sure this object lives until routine terminates
  ErrorPtr respErr;
  bool safeError = false; // set when reporting error is safe (i.e. not error possibly generated by malformed error, to prevent error loops)
  JsonObjectPtr idObj;
  const char *idString = NULL;
  if (Error::isOK(aError)) {
    // received proper JSON, now check JSON-RPC specifics
    FOCUSLOG("Received JSON message:\n  %s\n", aJsonObject->c_strValue());
    if (aJsonObject->isType(json_type_array)) {
      respErr = ErrorPtr(new JsonRpcError(JSONRPC_INVALID_REQUEST, "Invalid Request - batch mode not supported by this implementation"));
    }
    else if (!aJsonObject->isType(json_type_object)) {
      respErr = ErrorPtr(new JsonRpcError(JSONRPC_INVALID_REQUEST, "Invalid Request - request must be JSON object"));
    }
    else {
      // check request object fields
      const char *method = NULL;
      JsonObjectPtr o = aJsonObject->get("jsonrpc");
      if (!o)
        respErr = ErrorPtr(new JsonRpcError(JSONRPC_INVALID_REQUEST, "Invalid Request - missing 'jsonrpc'"));
      else if (o->stringValue()!="2.0")
        respErr = ErrorPtr(new JsonRpcError(JSONRPC_INVALID_REQUEST, "Invalid Request - wrong version in 'jsonrpc'"));
      else {
        // get ID param (must be present for all messages except notification)
        idObj = aJsonObject->get("id");
        if (idObj) idString = idObj->c_strValue();
        JsonObjectPtr paramsObj = aJsonObject->get("params");
        // JSON-RPC version is correct, check other params
        method = aJsonObject->getCString("method");
        if (method) {
          // this is a request (responses don't have the method member)
          safeError = idObj!=NULL; // reporting error is safe if this is a method call. Other errors are reported only when reportAllErrors is set
          if (*method==0)
            respErr = ErrorPtr(new JsonRpcError(JSONRPC_INVALID_REQUEST, "Invalid Request - empty 'method'"));
          else {
            // looks like a valid method or notification call
            if (!jsonRequestHandler) {
              // no handler -> method cannot be executed
              respErr = ErrorPtr(new JsonRpcError(JSONRPC_METHOD_NOT_FOUND, "Method not found"));
            }
            else {
              if (paramsObj && !paramsObj->isType(json_type_array) && !paramsObj->isType(json_type_object)) {
                // invalid param object
                respErr = ErrorPtr(new JsonRpcError(JSONRPC_INVALID_REQUEST, "Invalid Request - 'params' must be object or array"));
              }
              else {
                // call handler to execute method or notification
                jsonRequestHandler(method, idString, paramsObj);
              }
            }
          }
        }
        else {
          // this is a response (requests always have a method member)
          // - check if result or error
          JsonObjectPtr respObj;
          if (!aJsonObject->get("result", respObj)) {
            // must be error, need further decoding
            respObj = aJsonObject->get("error");
            if (!respObj)
              respErr = ErrorPtr(new JsonRpcError(JSONRPC_INTERNAL_ERROR, "Internal JSON-RPC error - response with neither 'result' nor 'error'"));
            else {
              // dissect error object
              ErrorCode errCode = JSONRPC_INTERNAL_ERROR; // Internal RPC error
              const char *errMsg = "malformed Error response";
              // - try to get error code
              JsonObjectPtr o = respObj->get("code");
              if (o) errCode = o->int32Value();
              // - try to get error message
              o = respObj->get("message");
              if (o) errMsg = o->c_strValue();
              // compose error object from this
              respErr = ErrorPtr(new JsonRpcError(errCode, errMsg));
              // also get optional data element
              respObj = respObj->get("data");
            }
          }
          // Now we have either result or error.data in respObj, and respErr is Ok or contains the error code + message
          if (!idObj) {
            // errors without ID cannot be associated with calls made earlier, so just log the error
            LOG(LOG_WARNING,"JSON-RPC 2.0 warning: Received response with no or NULL 'id' that cannot be dispatched:\n  %s\n", aJsonObject->c_strValue());
          }
          else {
            // dispatch by ID
            uint32_t requestId = idObj->int32Value();
            PendingAnswerMap::iterator pos = pendingAnswers.find(requestId);
            if (pos==pendingAnswers.end()) {
              // errors without ID cannot be associated with calls made earlier, so just log the error
              LOG(LOG_WARNING,"JSON-RPC 2.0 error: Received response with unknown 'id'=%d : %s\n", requestId, aJsonObject->c_strValue());
            }
            else {
              // found callback
              JsonRpcResponseCB cb = pos->second;
              pendingAnswers.erase(pos); // erase
              cb(requestId, respErr, respObj); // call
            }
            respErr.reset(); // handled
          }
        }
      }
    }
  }
  else {
    // no proper JSON received, create error response
    if (aError->isDomain(JsonError::domain())) {
      // some kind of parsing error
      respErr = ErrorPtr(new JsonRpcError(JSONRPC_PARSE_ERROR, aError->description()));
    }
    else {
      // some other type of server error
      respErr = ErrorPtr(new JsonRpcError(JSONRPC_SERVER_ERROR, aError->description()));
    }
  }
  // auto-generate error response for internally created errors
  if (!Error::isOK(respErr)) {
    if (safeError || reportAllErrors)
      sendError(idString, respErr);
    else
      LOG(LOG_WARNING,"Received data that generated error which can't be sent back: Code=%d, Message='%s'\n", respErr->getErrorCode(), respErr->description().c_str());
  }
}
Ejemplo n.º 18
0
// access to vdc API methods and notifications via web requests
ErrorPtr P44VdcHost::processVdcRequest(JsonCommPtr aJsonComm, JsonObjectPtr aRequest)
{
  ErrorPtr err;
  string cmd;
  bool isMethod = false;
  // get method/notification and params
  JsonObjectPtr m = aRequest->get("method");
  if (m) {
    // is a method call, expects answer
    isMethod = true;
  }
  else {
    // not method, may be notification
    m = aRequest->get("notification");
  }
  if (!m) {
    err = Error::err<P44VdcError>(400, "invalid request, must specify 'method' or 'notification'");
  }
  else {
    // get method/notification name
    cmd = m->stringValue();
    // get params
    // Note: the "method" or "notification" param will also be in the params, but should not cause any problem
    ApiValuePtr params = JsonApiValue::newValueFromJson(aRequest);
    P44JsonApiRequestPtr request = P44JsonApiRequestPtr(new P44JsonApiRequest(aJsonComm));
    if (Error::isOK(err)) {
      // operation method
      if (isMethod) {
        // create request
        // check for old-style name/index and generate basic query (1 or 2 levels)
        ApiValuePtr query = params->newObject();
        ApiValuePtr name = params->get("name");
        if (name) {
          ApiValuePtr index = params->get("index");
          ApiValuePtr subquery = params->newNull();
          if (index) {
            // subquery
            subquery->setType(apivalue_object);
            subquery->add(index->stringValue(), subquery->newNull());
          }
          string nm = trimWhiteSpace(name->stringValue()); // to allow a single space for deep recursing wildcard
          query->add(nm, subquery);
          params->add("query", query);
        }
        // have method handled
        err = handleMethodForParams(request, cmd, params);
        // Note: if method returns NULL, it has sent or will send results itself.
        //   Otherwise, even if Error is ErrorOK we must send a generic response
      }
      else {
        // handle notification
        err = handleNotificationForParams(request->connection(), cmd, params);
        // Notifications are always immediately confirmed, so make sure there's an explicit ErrorOK
        if (!err) {
          err = ErrorPtr(new Error(Error::OK));
        }
      }
    }
  }
  // returning NULL means caller should not do anything more
  // returning an Error object (even ErrorOK) means caller should return status
  return err;
}