mirror of
https://github.com/Telecominfraproject/wlan-cloud-owprov.git
synced 2025-10-30 18:18:03 +00:00
542 lines
18 KiB
C++
542 lines
18 KiB
C++
//
|
|
// License type: BSD 3-Clause License
|
|
// License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE
|
|
//
|
|
// Created by Stephane Bourque on 2021-03-04.
|
|
// Arilia Wireless Inc.
|
|
//
|
|
|
|
#include "RESTAPI_venue_handler.h"
|
|
|
|
#include "RESTAPI/RESTAPI_db_helpers.h"
|
|
#include "RESTObjects/RESTAPI_ProvObjects.h"
|
|
#include "StorageService.h"
|
|
#include "Tasks/VenueConfigUpdater.h"
|
|
#include "Tasks/VenueRebooter.h"
|
|
#include "Tasks/VenueUpgrade.h"
|
|
#include "framework/CIDR.h"
|
|
#include "framework/MicroServiceFuncs.h"
|
|
|
|
#include "Kafka_ProvUpdater.h"
|
|
|
|
namespace OpenWifi {
|
|
|
|
static Types::UUIDvec_t GetDevices(const ProvObjects::Venue &V, bool GetChildren) {
|
|
Types::UUIDvec_t R;
|
|
std::copy(V.devices.begin(), V.devices.end(), std::back_inserter(R));
|
|
if (GetChildren) {
|
|
for (const auto &i : V.children) {
|
|
ProvObjects::Venue V2;
|
|
if (StorageService()->VenueDB().GetRecord("id", i, V2)) {
|
|
std::copy(V2.devices.begin(), V2.devices.end(), std::back_inserter(R));
|
|
auto LowerDevs = GetDevices(V2, GetChildren);
|
|
std::copy(LowerDevs.begin(), LowerDevs.end(), std::back_inserter(R));
|
|
}
|
|
}
|
|
}
|
|
|
|
std::sort(R.begin(), R.end());
|
|
auto Last = std::unique(R.begin(), R.end());
|
|
R.erase(Last, R.end());
|
|
|
|
std::vector<std::string> SerialNumbers;
|
|
|
|
for (const auto &device : R) {
|
|
ProvObjects::InventoryTag IT;
|
|
if (StorageService()->InventoryDB().GetRecord("id", device, IT)) {
|
|
SerialNumbers.push_back(IT.serialNumber);
|
|
}
|
|
}
|
|
|
|
return SerialNumbers;
|
|
}
|
|
|
|
void RESTAPI_venue_handler::DoGet() {
|
|
std::string UUID = GetBinding("uuid", "");
|
|
ProvObjects::Venue Existing;
|
|
if (UUID.empty() || !DB_.GetRecord("id", UUID, Existing)) {
|
|
return NotFound();
|
|
}
|
|
|
|
if (GetBoolParameter("getDevices")) {
|
|
ProvObjects::VenueDeviceList VDL;
|
|
VDL.id = Existing.info.id;
|
|
VDL.name = Existing.info.name;
|
|
VDL.description = Existing.info.description;
|
|
auto GetChildren = GetBoolParameter("getChildren");
|
|
VDL.devices = GetDevices(Existing, GetChildren);
|
|
Poco::JSON::Object Answer;
|
|
VDL.to_json(Answer);
|
|
return ReturnObject(Answer);
|
|
}
|
|
|
|
Poco::JSON::Object Answer;
|
|
if (QB_.AdditionalInfo)
|
|
AddExtendedInfo(Existing, Answer);
|
|
|
|
Existing.to_json(Answer);
|
|
ReturnObject(Answer);
|
|
}
|
|
|
|
void RESTAPI_venue_handler::DoDelete() {
|
|
std::string UUID = GetBinding("uuid", "");
|
|
ProvObjects::Venue Existing;
|
|
if (UUID.empty() || !DB_.GetRecord("id", UUID, Existing)) {
|
|
return NotFound();
|
|
}
|
|
|
|
if (!Existing.children.empty() || !Existing.devices.empty()) {
|
|
return BadRequest(RESTAPI::Errors::StillInUse);
|
|
}
|
|
|
|
if (!Existing.contacts.empty()) {
|
|
for (const auto &contact_uuid : Existing.contacts)
|
|
StorageService()->ContactDB().DeleteInUse(
|
|
"id", contact_uuid, StorageService()->VenueDB().Prefix(), UUID);
|
|
}
|
|
if (!Existing.location.empty())
|
|
StorageService()->LocationDB().DeleteInUse("id", Existing.location,
|
|
StorageService()->VenueDB().Prefix(), UUID);
|
|
if (!Existing.managementPolicy.empty())
|
|
StorageService()->PolicyDB().DeleteInUse("id", Existing.managementPolicy,
|
|
StorageService()->VenueDB().Prefix(), UUID);
|
|
if (!Existing.deviceConfiguration.empty()) {
|
|
for (auto &configuration_uuid : Existing.deviceConfiguration)
|
|
StorageService()->ConfigurationDB().DeleteInUse(
|
|
"id", configuration_uuid, StorageService()->VenueDB().Prefix(), UUID);
|
|
}
|
|
if (!Existing.parent.empty())
|
|
StorageService()->VenueDB().DeleteChild("id", Existing.parent, UUID);
|
|
if (!Existing.entity.empty())
|
|
StorageService()->EntityDB().DeleteVenue("id", Existing.entity, UUID);
|
|
DB_.DeleteRecord("id", UUID);
|
|
|
|
UpdateKafkaProvisioningObject(ProvisioningOperation::removal, Existing);
|
|
|
|
return OK();
|
|
}
|
|
|
|
void RESTAPI_venue_handler::DoPost() {
|
|
std::string UUID = GetBinding("uuid", "");
|
|
if (UUID.empty()) {
|
|
return BadRequest(RESTAPI::Errors::MissingUUID);
|
|
}
|
|
|
|
const auto &RawObject = ParsedBody_;
|
|
ProvObjects::Venue NewObject;
|
|
if (!NewObject.from_json(RawObject)) {
|
|
return BadRequest(RESTAPI::Errors::InvalidJSONDocument);
|
|
}
|
|
|
|
if ((RawObject->has("deviceRules") && !ValidDeviceRules(NewObject.deviceRules, *this))) {
|
|
return;
|
|
}
|
|
|
|
if (!CreateObjectInfo(RawObject, UserInfo_.userinfo, NewObject.info)) {
|
|
return BadRequest(RESTAPI::Errors::NameMustBeSet);
|
|
}
|
|
|
|
if (NewObject.parent.empty() && NewObject.entity.empty()) {
|
|
return BadRequest(RESTAPI::Errors::ParentOrEntityMustBeSet);
|
|
}
|
|
|
|
if (!NewObject.parent.empty() && !NewObject.entity.empty()) {
|
|
return BadRequest(RESTAPI::Errors::NotBoth);
|
|
}
|
|
|
|
if (!NewObject.parent.empty() && !DB_.Exists("id", NewObject.parent)) {
|
|
return BadRequest(RESTAPI::Errors::VenueMustExist);
|
|
}
|
|
|
|
if (NewObject.entity == EntityDB::RootUUID()) {
|
|
return BadRequest(RESTAPI::Errors::ValidNonRootUUID);
|
|
}
|
|
|
|
if (!NewObject.entity.empty() &&
|
|
!StorageService()->EntityDB().Exists("id", NewObject.entity)) {
|
|
return BadRequest(RESTAPI::Errors::EntityMustExist);
|
|
}
|
|
|
|
if(StorageService()->VenueDB().DoesVenueNameAlreadyExist(NewObject.info.name,NewObject.entity, NewObject.parent)) {
|
|
return BadRequest(RESTAPI::Errors::VenuesNameAlreadyExists);
|
|
}
|
|
|
|
if (!NewObject.contacts.empty()) {
|
|
for (const auto &i : NewObject.contacts) {
|
|
if (!StorageService()->ContactDB().Exists("id", i)) {
|
|
return BadRequest(RESTAPI::Errors::ContactMustExist);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!NewObject.location.empty() &&
|
|
!StorageService()->LocationDB().Exists("id", NewObject.location)) {
|
|
return BadRequest(RESTAPI::Errors::LocationMustExist);
|
|
}
|
|
|
|
if (!NewObject.managementPolicy.empty() &&
|
|
!StorageService()->PolicyDB().Exists("id", NewObject.managementPolicy)) {
|
|
return BadRequest(RESTAPI::Errors::UnknownManagementPolicyUUID);
|
|
}
|
|
|
|
if (!NewObject.deviceConfiguration.empty()) {
|
|
for (auto &i : NewObject.deviceConfiguration) {
|
|
if (!StorageService()->ConfigurationDB().Exists("id", i)) {
|
|
return BadRequest(RESTAPI::Errors::UnknownManagementPolicyUUID);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!NewObject.sourceIP.empty() && !CIDR::ValidateIpRanges(NewObject.sourceIP)) {
|
|
return BadRequest(RESTAPI::Errors::InvalidIPRanges);
|
|
}
|
|
|
|
NewObject.children.clear();
|
|
|
|
RESTAPI::Errors::msg Error = RESTAPI::Errors::SUCCESS;
|
|
std::vector<std::string> Errors;
|
|
auto ObjectsCreated = CreateObjects(NewObject, *this, Errors);
|
|
if (Error.err_num != 0) {
|
|
return BadRequest(RESTAPI::Errors::InternalError);
|
|
}
|
|
|
|
if (DB_.CreateRecord(NewObject)) {
|
|
MoveUsage(StorageService()->ContactDB(), DB_, {}, NewObject.contacts,
|
|
NewObject.info.id);
|
|
MoveUsage(StorageService()->LocationDB(), DB_, "", NewObject.location,
|
|
NewObject.info.id);
|
|
MoveUsage(StorageService()->PolicyDB(), DB_, "", NewObject.managementPolicy,
|
|
NewObject.info.id);
|
|
ManageMembership(StorageService()->EntityDB(), &ProvObjects::Entity::venues, "",
|
|
NewObject.entity, NewObject.info.id);
|
|
ManageMembership(StorageService()->VenueDB(), &ProvObjects::Venue::children, "",
|
|
NewObject.parent, NewObject.info.id);
|
|
MoveUsage(StorageService()->ConfigurationDB(), DB_, {}, NewObject.deviceConfiguration,
|
|
NewObject.info.id);
|
|
|
|
ProvObjects::Venue NewRecord;
|
|
DB_.GetRecord("id", NewObject.info.id, NewRecord);
|
|
Poco::JSON::Object Answer;
|
|
NewRecord.to_json(Answer);
|
|
return ReturnObject(Answer);
|
|
}
|
|
InternalError(RESTAPI::Errors::RecordNotCreated);
|
|
}
|
|
|
|
/*
|
|
* Get all the device types for the devices in the venue
|
|
* For each unique device type, get all the firmwares that apply for it
|
|
* create the intersection of all resulting firmwares
|
|
* create a list ordered in 3 types
|
|
* - Releases
|
|
* - RC of releases
|
|
* - Dev
|
|
*
|
|
* We obtain the revision by stripping and trimming the revision field previous to the "/"
|
|
* The sets should be built on the revision and the URI of the images, all per devicetype
|
|
*/
|
|
|
|
enum class RevisionTypes { release, release_candidate, development };
|
|
|
|
RevisionTypes RevisionIdentify(const std::string &r) {
|
|
auto it = r.find_first_of('/');
|
|
if (it == std::string::npos) {
|
|
return RevisionTypes::development;
|
|
}
|
|
++it;
|
|
if (r.find("TIP-devel-", it) != std::string::npos) {
|
|
return RevisionTypes::development;
|
|
}
|
|
if (r.find("-rc", it) != std::string::npos) {
|
|
return RevisionTypes::release_candidate;
|
|
}
|
|
if (r.find("TIP-v", it) != std::string::npos) {
|
|
return RevisionTypes::release;
|
|
}
|
|
return RevisionTypes::development;
|
|
}
|
|
|
|
std::uint64_t RevisionDate(const std::string &R, const std::vector<FMSObjects::Firmware> &F) {
|
|
|
|
for (const auto &f : F) {
|
|
if (f.revision == R)
|
|
return f.imageDate;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void RESTAPI_venue_handler::DoPut() {
|
|
std::string UUID = GetBinding("uuid", "");
|
|
|
|
ProvObjects::Venue Existing;
|
|
if (UUID.empty() || !DB_.GetRecord("id", UUID, Existing)) {
|
|
return NotFound();
|
|
}
|
|
|
|
auto testUpdateOnly = GetBoolParameter("testUpdateOnly");
|
|
if (testUpdateOnly) {
|
|
ProvObjects::SerialNumberList SNL;
|
|
StorageService()->InventoryDB().GetDevicesForVenue(UUID, SNL.serialNumbers);
|
|
Poco::JSON::Object Answer;
|
|
SNL.to_json(Answer);
|
|
return ReturnObject(Answer);
|
|
}
|
|
|
|
if (GetBoolParameter("updateAllDevices")) {
|
|
ProvObjects::SerialNumberList SNL;
|
|
StorageService()->InventoryDB().GetDevicesForVenue(UUID, SNL.serialNumbers);
|
|
|
|
Poco::JSON::Object Answer;
|
|
auto JobId = MicroServiceCreateUUID();
|
|
Types::StringVec Parameters{UUID};
|
|
auto NewJob = new VenueConfigUpdater(JobId, "VenueConfigurationUpdater", Parameters, 0,
|
|
UserInfo_.userinfo, Logger());
|
|
JobController()->AddJob(dynamic_cast<Job *>(NewJob));
|
|
SNL.to_json(Answer);
|
|
Answer.set("jobId", JobId);
|
|
return ReturnObject(Answer);
|
|
}
|
|
|
|
if (GetBoolParameter("upgradeAllDevices")) {
|
|
if (GetBoolParameter("revisionsAvailable")) {
|
|
std::set<std::string> DeviceTypes;
|
|
std::vector<ProvObjects::InventoryTag> ExistingDevices;
|
|
StorageService()->InventoryDB().GetDevicesForVenue(UUID, ExistingDevices);
|
|
for (const auto &device : ExistingDevices) {
|
|
DeviceTypes.insert(device.deviceType);
|
|
}
|
|
|
|
// Get all the revisions for all the device types
|
|
using FirmwareList = std::vector<FMSObjects::Firmware>;
|
|
using FirmwareRevisions = std::set<std::string>;
|
|
|
|
std::map<std::string, FirmwareList> AllFMs;
|
|
FirmwareRevisions AllRevisions;
|
|
bool first_pass = true;
|
|
for (const auto &device_type : DeviceTypes) {
|
|
FirmwareList list;
|
|
if (SDK::FMS::Firmware::GetDeviceTypeFirmwares(device_type, list)) {
|
|
AllFMs[device_type] = list;
|
|
FirmwareRevisions DeviceRevisions;
|
|
if (first_pass) {
|
|
for (const auto &revision : list) {
|
|
AllRevisions.insert(revision.revision);
|
|
}
|
|
} else {
|
|
FirmwareRevisions DeviceTypeRevisions;
|
|
for (const auto &revision : list) {
|
|
DeviceTypeRevisions.insert(revision.revision);
|
|
}
|
|
FirmwareRevisions NewRevisions;
|
|
std::set_intersection(
|
|
AllRevisions.begin(), AllRevisions.end(),
|
|
DeviceTypeRevisions.begin(), DeviceTypeRevisions.end(),
|
|
std::inserter(NewRevisions, NewRevisions.begin()));
|
|
AllRevisions = NewRevisions;
|
|
}
|
|
}
|
|
first_pass = false;
|
|
}
|
|
|
|
// OK we start the solution map with Our first device, and then we
|
|
Poco::JSON::Array Releases, ReleaseCandidates, DevelReleases;
|
|
for (const auto &revision : AllRevisions) {
|
|
auto Date = RevisionDate(revision, AllFMs.begin()->second);
|
|
switch (RevisionIdentify(revision)) {
|
|
case RevisionTypes::release_candidate: {
|
|
Poco::JSON::Object E;
|
|
E.set("revision", revision);
|
|
E.set("date", Date);
|
|
ReleaseCandidates.add(E);
|
|
} break;
|
|
case RevisionTypes::release: {
|
|
Poco::JSON::Object E;
|
|
E.set("revision", revision);
|
|
E.set("date", Date);
|
|
Releases.add(E);
|
|
} break;
|
|
case RevisionTypes::development:
|
|
default: {
|
|
Poco::JSON::Object E;
|
|
E.set("revision", revision);
|
|
E.set("date", Date);
|
|
DevelReleases.add(E);
|
|
}
|
|
}
|
|
}
|
|
Poco::JSON::Object Answer;
|
|
Answer.set("releases", Releases);
|
|
Answer.set("releasesCandidates", ReleaseCandidates);
|
|
Answer.set("developmentReleases", DevelReleases);
|
|
return ReturnObject(Answer);
|
|
}
|
|
|
|
auto Revision = GetParameter("revision", "");
|
|
if (Revision.empty()) {
|
|
return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
|
|
}
|
|
|
|
ProvObjects::SerialNumberList SNL;
|
|
StorageService()->InventoryDB().GetDevicesForVenue(UUID, SNL.serialNumbers);
|
|
|
|
Poco::JSON::Object Answer;
|
|
auto JobId = MicroServiceCreateUUID();
|
|
Types::StringVec Parameters{UUID, Revision};
|
|
auto NewJob = new VenueUpgrade(JobId, "VenueFirmwareUpgrade", Parameters, 0,
|
|
UserInfo_.userinfo, Logger());
|
|
JobController()->AddJob(dynamic_cast<Job *>(NewJob));
|
|
SNL.to_json(Answer);
|
|
Answer.set("jobId", JobId);
|
|
return ReturnObject(Answer);
|
|
}
|
|
|
|
if (GetBoolParameter("rebootAllDevices")) {
|
|
ProvObjects::SerialNumberList SNL;
|
|
StorageService()->InventoryDB().GetDevicesForVenue(UUID, SNL.serialNumbers);
|
|
|
|
Poco::JSON::Object Answer;
|
|
auto JobId = MicroServiceCreateUUID();
|
|
Types::StringVec Parameters{UUID};
|
|
;
|
|
auto NewJob = new VenueRebooter(JobId, "VenueRebooter", Parameters, 0,
|
|
UserInfo_.userinfo, Logger());
|
|
JobController()->AddJob(dynamic_cast<Job *>(NewJob));
|
|
SNL.to_json(Answer);
|
|
Answer.set("jobId", JobId);
|
|
return ReturnObject(Answer);
|
|
}
|
|
|
|
const auto &RawObject = ParsedBody_;
|
|
ProvObjects::Venue NewObject;
|
|
if (!NewObject.from_json(RawObject)) {
|
|
return BadRequest(RESTAPI::Errors::InvalidJSONDocument);
|
|
}
|
|
|
|
if ((RawObject->has("deviceRules") && !ValidDeviceRules(NewObject.deviceRules, *this))) {
|
|
return;
|
|
}
|
|
|
|
if (!UpdateObjectInfo(RawObject, UserInfo_.userinfo, Existing.info)) {
|
|
return BadRequest(RESTAPI::Errors::NameMustBeSet);
|
|
}
|
|
|
|
if (RawObject->has("deviceRules"))
|
|
Existing.deviceRules = NewObject.deviceRules;
|
|
|
|
if (RawObject->has("sourceIP")) {
|
|
if (!NewObject.sourceIP.empty() && !CIDR::ValidateIpRanges(NewObject.sourceIP)) {
|
|
return BadRequest(RESTAPI::Errors::InvalidIPRanges);
|
|
}
|
|
Existing.sourceIP = NewObject.sourceIP;
|
|
}
|
|
|
|
std::string MoveFromEntity, MoveToEntity;
|
|
if (AssignIfPresent(RawObject, "entity", MoveToEntity)) {
|
|
if (MoveToEntity.empty() || !StorageService()->EntityDB().Exists("id", MoveToEntity)) {
|
|
return BadRequest(RESTAPI::Errors::EntityMustExist);
|
|
}
|
|
MoveFromEntity = Existing.entity;
|
|
Existing.entity = MoveToEntity;
|
|
}
|
|
|
|
std::string MoveToVenue, MoveFromVenue;
|
|
if (AssignIfPresent(RawObject, "venue", MoveToVenue)) {
|
|
if (MoveToVenue.empty() || !StorageService()->VenueDB().Exists("id", MoveToVenue)) {
|
|
return BadRequest(RESTAPI::Errors::VenueMustExist);
|
|
}
|
|
MoveFromVenue = Existing.parent;
|
|
Existing.parent = MoveToVenue;
|
|
}
|
|
|
|
std::string MoveFromLocation, MoveToLocation;
|
|
if (AssignIfPresent(RawObject, "location", MoveToLocation)) {
|
|
if (MoveToLocation.empty() ||
|
|
!StorageService()->LocationDB().Exists("id", MoveToLocation)) {
|
|
return BadRequest(RESTAPI::Errors::LocationMustExist);
|
|
}
|
|
MoveFromLocation = Existing.location;
|
|
Existing.location = MoveToLocation;
|
|
}
|
|
|
|
Types::UUIDvec_t MoveFromContacts, MoveToContacts;
|
|
if (AssignIfPresent(RawObject, "contacts", MoveToContacts)) {
|
|
for (const auto &contact : NewObject.contacts) {
|
|
if (!StorageService()->ContactDB().Exists("id", contact)) {
|
|
return BadRequest(RESTAPI::Errors::ContactMustExist);
|
|
}
|
|
}
|
|
MoveFromContacts = Existing.contacts;
|
|
Existing.contacts = MoveToContacts;
|
|
}
|
|
|
|
std::string MoveFromPolicy, MoveToPolicy;
|
|
if (AssignIfPresent(RawObject, "managementPolicy", MoveToPolicy)) {
|
|
if (MoveToPolicy.empty() || !StorageService()->PolicyDB().Exists("id", MoveToPolicy)) {
|
|
return BadRequest(RESTAPI::Errors::UnknownManagementPolicyUUID);
|
|
}
|
|
MoveFromPolicy = Existing.managementPolicy;
|
|
Existing.managementPolicy = MoveToPolicy;
|
|
}
|
|
|
|
Types::UUIDvec_t MoveToConfigurations, MoveFromConfigurations;
|
|
if (RawObject->has("deviceConfiguration")) {
|
|
MoveToConfigurations = NewObject.deviceConfiguration;
|
|
for (auto &configuration : MoveToConfigurations) {
|
|
if (!StorageService()->ConfigurationDB().Exists("id", configuration)) {
|
|
return BadRequest(RESTAPI::Errors::ConfigurationMustExist);
|
|
}
|
|
}
|
|
MoveToConfigurations = NewObject.deviceConfiguration;
|
|
MoveFromConfigurations = Existing.deviceConfiguration;
|
|
Existing.deviceConfiguration = MoveToConfigurations;
|
|
}
|
|
|
|
std::string ErrorText;
|
|
NewObject.parent = Existing.parent;
|
|
NewObject.entity = Existing.entity;
|
|
|
|
std::vector<std::string> Errors;
|
|
auto ObjectsCreated = CreateObjects(NewObject, *this, Errors);
|
|
if (!Errors.empty()) {
|
|
return BadRequest(RESTAPI::Errors::ConfigBlockInvalid);
|
|
}
|
|
|
|
if (!ObjectsCreated.empty()) {
|
|
if (!ObjectsCreated.empty()) {
|
|
auto it = ObjectsCreated.find("location");
|
|
if (it != ObjectsCreated.end()) {
|
|
MoveFromLocation = "";
|
|
MoveToLocation = it->second;
|
|
Existing.location = MoveToLocation;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (RawObject->has("boards")) {
|
|
Existing.boards = NewObject.boards;
|
|
}
|
|
|
|
if (StorageService()->VenueDB().UpdateRecord("id", UUID, Existing)) {
|
|
UpdateKafkaProvisioningObject(ProvisioningOperation::modification, Existing);
|
|
MoveUsage(StorageService()->ContactDB(), DB_, MoveFromContacts, MoveToContacts,
|
|
Existing.info.id);
|
|
MoveUsage(StorageService()->LocationDB(), DB_, MoveFromLocation, MoveToLocation,
|
|
Existing.info.id);
|
|
MoveUsage(StorageService()->PolicyDB(), DB_, MoveFromPolicy, MoveToPolicy,
|
|
Existing.info.id);
|
|
ManageMembership(StorageService()->EntityDB(), &ProvObjects::Entity::venues,
|
|
MoveFromEntity, MoveToEntity, Existing.info.id);
|
|
ManageMembership(StorageService()->VenueDB(), &ProvObjects::Venue::children,
|
|
MoveFromVenue, MoveToVenue, Existing.info.id);
|
|
MoveUsage(StorageService()->ConfigurationDB(), DB_, MoveFromConfigurations,
|
|
MoveToConfigurations, Existing.info.id);
|
|
|
|
ProvObjects::Venue AddedRecord;
|
|
DB_.GetRecord("id", UUID, AddedRecord);
|
|
Poco::JSON::Object Answer;
|
|
AddedRecord.to_json(Answer);
|
|
return ReturnObject(Answer);
|
|
}
|
|
InternalError(RESTAPI::Errors::RecordNotUpdated);
|
|
}
|
|
} // namespace OpenWifi
|