db = $db; } /** * @param class-string|DbObject $implementation * @param stdClass $plain * @return DbObject * @throws JsonDecodeException */ public function import(string $implementation, stdClass $plain): DbObject { $this->assertTemplate($implementation, $plain); $this->fixRelations($implementation, $plain); $this->applyOtherWorkarounds($implementation, $plain); $this->fixLegacyBaskets($implementation, $plain); $this->fixSubObjects($implementation, $plain); $object = $this->loadExistingObject($implementation, $plain); if ($object === null) { $object = $implementation::create([], $this->db); } $properties = (array) $plain; unset($properties['fields']); unset($properties['originalId']); if ($implementation === Basket::class) { if (isset($properties['objects']) && is_string($properties['objects'])) { $properties['objects'] = JsonString::decode($properties['objects']); } } $object->setProperties($properties); return $object; } protected function fixLegacyBaskets(string $implementation, stdClass $plain) { // TODO: Check, whether current export sets modifiers = [] in case there is none if ($implementation == ImportSource::class) { if (!isset($plain->modifiers)) { $plain->modifiers = []; } } } protected function applyOtherWorkarounds(string $implementation, stdClass $plain) { if ($implementation === SyncRule::class) { if (isset($plain->properties)) { $plain->syncProperties = $plain->properties; unset($plain->properties); } } } protected function fixSubObjects(string $implementation, stdClass $plain) { if ($implementation === IcingaServiceSet::class) { foreach ($plain->services as $service) { unset($service->fields); } // Hint: legacy baskets are carrying service names as object keys, new baskets have arrays $plain->services = array_values((array) $plain->services); } } protected function fixRelations(string $implementation, stdClass $plain) { if ($implementation === DirectorJob::class) { $settings = $plain->settings; $source = $settings->source ?? null; if ($source && !isset($settings->source_id)) { $settings->source_id = ImportSource::load($source, $this->db)->get('id'); unset($settings->source); } $rule = $settings->rule ?? null; if ($rule && !isset($settings->rule_id)) { $settings->rule_id = SyncRule::load($rule, $this->db)->get('id'); unset($settings->rule); } } } /** * @param class-string $implementation * @param stdClass $plain * @return DbObject|null */ protected function loadExistingObject(string $implementation, stdClass $plain): ?DbObject { if (isset($plain->uuid) && $instance = $implementation::loadWithUniqueId(Uuid::fromString($plain->uuid), $this->db) ) { return $instance; } if ($implementation === IcingaService::class) { $key = [ 'object_type' => 'template', 'object_name' => $plain->object_name ]; } else { $dummy = $implementation::create(); $keyColumn = $dummy->getKeyName(); if (is_array($keyColumn)) { if (empty($keyColumn)) { throw new \RuntimeException("$implementation has an empty keyColumn array"); } $key = []; foreach ($keyColumn as $column) { if (isset($plain->$column)) { $key[$column] = $plain->$column; } } } else { $key = $plain->$keyColumn; } } return $implementation::loadOptional($key, $this->db); } protected function assertTemplate(string $implementation, stdClass $plain) { if (! in_array($implementation, self::$templatesOnly)) { return; } if ($plain->object_type !== 'template') { throw new InvalidArgumentException(sprintf( 'Can import only Templates, got "%s" for "%s"', $plain->object_type, $plain->name )); } } }