From 7f137c22d609fd15a1ad60bf138f7c0f4c5c0d92 Mon Sep 17 00:00:00 2001 From: Benn Oshrin Date: Wed, 6 Mar 2024 11:33:52 -0600 Subject: [PATCH] Initial implementation of SqlAssigner (CFM-379 --- .../ApiConnector/config/routes.php | 1 - .../src/Model/Table/SqlProvisionersTable.php | 1 - .../resources/locales/en_US/core_assigner.po | 18 ++ .../src/Controller/SqlAssignersController.php | 40 ++++ .../src/Model/Entity/SqlAssigner.php | 49 +++++ .../src/Model/Table/SqlAssignersTable.php | 177 ++++++++++++++++++ .../CoreAssigner/src/config/plugin.json | 21 ++- .../templates/SqlAssigners/fields.inc | 35 ++++ .../src/Model/Table/SqlServersTable.php | 4 +- .../ProvisioningHistoryRecordsController.php | 19 -- app/src/View/Helper/FieldHelper.php | 14 +- .../IdentifierAssignments/columns.inc | 4 + 12 files changed, 352 insertions(+), 31 deletions(-) create mode 100644 app/plugins/CoreAssigner/src/Controller/SqlAssignersController.php create mode 100644 app/plugins/CoreAssigner/src/Model/Entity/SqlAssigner.php create mode 100644 app/plugins/CoreAssigner/src/Model/Table/SqlAssignersTable.php create mode 100644 app/plugins/CoreAssigner/templates/SqlAssigners/fields.inc diff --git a/app/availableplugins/ApiConnector/config/routes.php b/app/availableplugins/ApiConnector/config/routes.php index 2f470cb73..a7af3b212 100644 --- a/app/availableplugins/ApiConnector/config/routes.php +++ b/app/availableplugins/ApiConnector/config/routes.php @@ -36,7 +36,6 @@ // of Cake routes, so either can be specified here. // ApiSource API routes -// We place these under /v2 since the Registry v4 plugin essentially implemented v1 $routes->scope('/api/apisource', function (RouteBuilder $builder) { // Register scoped middleware for in scopes. diff --git a/app/availableplugins/SqlConnector/src/Model/Table/SqlProvisionersTable.php b/app/availableplugins/SqlConnector/src/Model/Table/SqlProvisionersTable.php index 8075bc794..3fff9f39b 100644 --- a/app/availableplugins/SqlConnector/src/Model/Table/SqlProvisionersTable.php +++ b/app/availableplugins/SqlConnector/src/Model/Table/SqlProvisionersTable.php @@ -61,7 +61,6 @@ class SqlProvisionersTable extends Table { 'source' => 'People', 'source_table' => 'people', 'related' => [ -// XXX partial reversion of CFM-363 'AdHocAttributes', 'Addresses', 'EmailAddresses', diff --git a/app/plugins/CoreAssigner/resources/locales/en_US/core_assigner.po b/app/plugins/CoreAssigner/resources/locales/en_US/core_assigner.po index 0e3112e84..68dbb5f33 100644 --- a/app/plugins/CoreAssigner/resources/locales/en_US/core_assigner.po +++ b/app/plugins/CoreAssigner/resources/locales/en_US/core_assigner.po @@ -25,6 +25,9 @@ msgid "controller.FormatAssigners" msgstr "{0,plural,=1{Format Assigner} other{Format Assigners}}" +msgid "controller.SqlAssigners" +msgstr "{0,plural,=1{SQL Assigner} other{SQL Assigners}}" + msgid "enumeration.CollisionModeEnum.R" msgstr "Random" @@ -43,6 +46,12 @@ msgstr "AlphaNumeric and Dot, Dash, Underscore, Apostrophe" msgid "enumeration.PermittedCharactersEnum.AL" msgstr "Any" +msgid "error.SqlAssigners.failed" +msgstr "Could not map key Identifier to target Identifier" + +msgid "error.SqlAssigners.key.none" +msgstr "Could not find key Identifier in Person record" + msgid "field.FormatAssigners.collision_mode" msgstr "Collision Mode" @@ -72,3 +81,12 @@ msgstr "Permitted Characters" msgid "field.FormatAssigners.permitted_characters.desc" msgstr "When substituting parameters in a format, only permit these characters to be used" + +msgid "field.SqlAssigners.source_table" +msgstr "Source Table Name" + +msgid "field.SqlAssigners.type_id" +msgstr "Key Identifier Type" + +msgid "field.SqlAssigners.type_id.desc" +msgstr "Type of existing Identifier used to query the Source Table" \ No newline at end of file diff --git a/app/plugins/CoreAssigner/src/Controller/SqlAssignersController.php b/app/plugins/CoreAssigner/src/Controller/SqlAssignersController.php new file mode 100644 index 000000000..22609df97 --- /dev/null +++ b/app/plugins/CoreAssigner/src/Controller/SqlAssignersController.php @@ -0,0 +1,40 @@ + [ + 'SqlAssigners.server_id' => 'asc' + ] + ]; +} diff --git a/app/plugins/CoreAssigner/src/Model/Entity/SqlAssigner.php b/app/plugins/CoreAssigner/src/Model/Entity/SqlAssigner.php new file mode 100644 index 000000000..3e8e70149 --- /dev/null +++ b/app/plugins/CoreAssigner/src/Model/Entity/SqlAssigner.php @@ -0,0 +1,49 @@ + + */ + protected $_accessible = [ + '*' => true, + 'id' => false, + 'slug' => false, + ]; +} diff --git a/app/plugins/CoreAssigner/src/Model/Table/SqlAssignersTable.php b/app/plugins/CoreAssigner/src/Model/Table/SqlAssignersTable.php new file mode 100644 index 000000000..3ac0a5b25 --- /dev/null +++ b/app/plugins/CoreAssigner/src/Model/Table/SqlAssignersTable.php @@ -0,0 +1,177 @@ +addBehavior('Changelog'); + $this->addBehavior('Log'); + // Timestamp behavior handles created/modified updates + $this->addBehavior('Timestamp'); + + $this->setTableType(\App\Lib\Enum\TableTypeEnum::Configuration); + + // Define associations + $this->belongsTo('IdentifierAssignments'); + $this->belongsTo('Servers'); + $this->belongsTo('Types'); + + $this->setDisplayField('source_table'); + + $this->setPrimaryLink('identifier_assignment_id'); + $this->setRequiresCO(true); + + $this->setAutoViewVars([ + 'servers' => [ + 'type' => 'select', + 'model' => 'Servers', + 'where' => ['plugin' => 'CoreServer.SqlServers'] + ], + 'types' => [ + 'type' => 'type', + 'attribute' => 'Identifiers.type' + ] + ]); + + $this->setPermissions([ + // Actions that operate over an entity (ie: require an $id) + 'entity' => [ + 'delete' => false, // Delete the pluggable object instead + 'edit' => ['platformAdmin', 'coAdmin'], + 'view' => ['platformAdmin', 'coAdmin'] + ], + // Actions that operate over a table (ie: do not require an $id) + 'table' => [ + 'add' => false, // This is added by the parent model + 'index' => ['platformAdmin', 'coAdmin'] + ] + ]); + } + + /** + * Assign an identifier. + * + * @since COmanage Registry v5.0.0 + * @param IdentifierAssignment $ia Identifier Assignment describing the requested configuration + * @param object $entity The entity (Person, Group, Department) to assign an Identifier for + * @return string The newly proposed Identifier + * @throws InvalidArgumentException + * @throws RuntimeException + */ + + public function assign($ia, $entity): string { + // Find the key identifier type in the $entity data + $keyIdentifier = Hash::extract($entity->identifiers, '{n}[type_id='.$ia->sql_assigner->type_id.']'); + + if(empty($keyIdentifier)) { + throw new \InvalidArgumentException(__d('core_assigner', 'error.SqlAssigners.key.none')); + } + + $SqlServer = TableRegistry::getTableLocator()->get('CoreServer.SqlServers'); + + $SqlServer->connect($ia->sql_assigner->server_id, 'sqlassigner'); + + $options = [ + 'table' => $ia->sql_assigner->source_table, + 'alias' => 'SourceIdentifiers', + 'connection' => ConnectionManager::get('sqlassigner') + ]; + + $SourceTable = TableRegistry::getTableLocator()->get( + alias: 'SourceIdentifiers', + options: $options + ); + + $identifier = $SourceTable->find() + ->where(['key' => $keyIdentifier[0]->identifier]) + ->first(); + + if(!empty($identifier->identifier)) { + return $identifier->identifier; + } + + throw new \InvalidArgumentException(__d('core_assigner', 'error.SqlAssigners.failed')); + } + + /** + * Set validation rules. + * + * @since COmanage Registry v5.0.0 + * @param Validator $validator Validator + * @return Validator Validator + */ + + public function validationDefault(Validator $validator): Validator { + $schema = $this->getSchema(); + + $validator->add('identifier_assignment_id', [ + 'content' => ['rule' => 'isInteger'] + ]); + $validator->notEmptyString('identifier_assignment_id'); + + $validator->add('server_id', [ + 'content' => ['rule' => 'isInteger'] + ]); + $validator->notEmptyString('server_id'); + + $this->registerStringValidation($validator, $schema, 'source_table', true); + + $validator->add('type_id', [ + 'content' => ['rule' => 'isInteger'] + ]); + $validator->notEmptyString('type_id'); + + return $validator; + } +} diff --git a/app/plugins/CoreAssigner/src/config/plugin.json b/app/plugins/CoreAssigner/src/config/plugin.json index 66155266a..0ea282c32 100644 --- a/app/plugins/CoreAssigner/src/config/plugin.json +++ b/app/plugins/CoreAssigner/src/config/plugin.json @@ -1,7 +1,8 @@ { "types": { "assigner": [ - "FormatAssigners" + "FormatAssigners", + "SqlAssigners" ] }, "schema": { @@ -32,6 +33,24 @@ "format_assigner_sequences_i2": { "needed": false, "columns": [ "format_assigner_id" ] } }, "changelog": false + }, + "sql_assigners": { + "columns": { + "id": {}, + "identifier_assignment_id": {}, + "server_id": { + "notnull": false, + "comment": "type_id isn't available on the initial row insert by StandardPluggableController::instantiatePlugin" + }, + "source_table": { "type": "string", "size": 80 }, + "type_id": { + "notnull": false, + "comment": "type_id isn't available on the initial row insert by StandardPluggableController::instantiatePlugin" + } + }, + "indexes": { + "sql_assigners": { "columns": [ "identifier_assignment_id" ] } + } } } } diff --git a/app/plugins/CoreAssigner/templates/SqlAssigners/fields.inc b/app/plugins/CoreAssigner/templates/SqlAssigners/fields.inc new file mode 100644 index 000000000..d0b2f5942 --- /dev/null +++ b/app/plugins/CoreAssigner/templates/SqlAssigners/fields.inc @@ -0,0 +1,35 @@ +Field->control('server_id'); + + print $this->Field->control('source_table'); + + print $this->Field->control('type_id'); +} diff --git a/app/plugins/CoreServer/src/Model/Table/SqlServersTable.php b/app/plugins/CoreServer/src/Model/Table/SqlServersTable.php index f4d9851fc..98046d2d0 100644 --- a/app/plugins/CoreServer/src/Model/Table/SqlServersTable.php +++ b/app/plugins/CoreServer/src/Model/Table/SqlServersTable.php @@ -134,9 +134,9 @@ public function connect(int $serverId, string $name): bool { ]; // We need to drop the existing configuration before we can reconfigure it - ConnectionManager::drop('targetdb'); + ConnectionManager::drop($name); - ConnectionManager::setConfig('targetdb', $dbconfig); + ConnectionManager::setConfig($name, $dbconfig); return true; } diff --git a/app/src/Controller/ProvisioningHistoryRecordsController.php b/app/src/Controller/ProvisioningHistoryRecordsController.php index 888e2f031..4a6aa9b1f 100644 --- a/app/src/Controller/ProvisioningHistoryRecordsController.php +++ b/app/src/Controller/ProvisioningHistoryRecordsController.php @@ -39,23 +39,4 @@ class ProvisioningHistoryRecordsController extends StandardController { 'ProvisioningHistoryRecords.id' => 'desc' ] ]; - - /** - * Callback run prior to the request action. - * - * @since COmanage Registry v5.0.0 - * @param EventInterface $event Cake Event - * @return \Cake\Http\Response HTTP Response - */ - - public function beforeFilter(\Cake\Event\EventInterface $event) { - if(!$this->request->is('restful')) { - // Provide additional hints to BreadcrumbsComponent. This needs to be here - // and not in beforeRender because the component beforeRender will run first. - - $this->Breadcrumb->injectPrimaryLink($this->getPrimaryLink(true)); - } - - return parent::beforeFilter($event); - } } \ No newline at end of file diff --git a/app/src/View/Helper/FieldHelper.php b/app/src/View/Helper/FieldHelper.php index 961eaf862..c8bb4e409 100644 --- a/app/src/View/Helper/FieldHelper.php +++ b/app/src/View/Helper/FieldHelper.php @@ -451,14 +451,14 @@ protected function formNameDiv(string $fieldName, string $labelText=null, string if($key != $label) { break; } - } else { - // Just look up the key - $key = (!$core ? "field." : "") . $fn; - $label = __d(($core ? 'field' : $pluginDomain), $key); + } + + // Just look up the key + $key = (!$core ? "field." : "") . $fn; + $label = __d(($core ? 'field' : $pluginDomain), $key); - if($key != $label) { - break; - } + if($key != $label) { + break; } } else { // If we found a key, break the loop diff --git a/app/templates/IdentifierAssignments/columns.inc b/app/templates/IdentifierAssignments/columns.inc index 835530014..e3b8827fd 100644 --- a/app/templates/IdentifierAssignments/columns.inc +++ b/app/templates/IdentifierAssignments/columns.inc @@ -30,6 +30,10 @@ $indexColumns = [ 'type' => 'link', 'sortable' => true ], + 'identifier_type_id' => [ + 'type' => 'fk', + 'label' => __d('field', 'type'), + ], 'plugin' => [ 'type' => 'echo', 'sortable' => true