Prevent duplicates
Prerequisite: You must be familiar with the Syntax used in Tutorials and have already created an extension.
- learning:
- Configure uniqueness rules
- level:
- Beginner
- domains:
- XML, Constrain, Dictionary
- min version:
- 2.6.0
By customizing iTop, you can specify on a class, uniqueness
rules, to prevent duplicate entries.
In this Tutorial we will see:
-
How uniqueness rules behave from a user perspective
-
How to define new uniqueness rules
-
Uniqueness rules defined on Person class in iTop out of the box
-
Removing an existing uniqueness rule
Behavior
-
A rule would specify the scope of objects on which the uniqueness must be checked. For example, you don't want to have two persons with the same employee number, unless that number is empty. The scope would then be every Person with
employee_number
not empty. -
During a single creation or modification made on the console or the portal, a rule can either
-
block the creation/update of the object
-
-
or just display a warning message after the creation/update.
-
A uniqueness rule applies on one or multiple fields of the class (including ExternalFields)
-
Uniqueness on attribute types such as File, Image, Dashboard, Stopwatch,… are not supported (incomplete list).
Organization
and Employee
number
, but this rule only applies to your company, then you
define the scope of the rule to be “Only Persons of organization
XXX”Default rules
-
Class Brand, id=
name
, fieldname
must be unique. -
Class Model, id=
name_brand
, fields:name + brand_id
must be unique. -
Class Person, id=
employee_number
fields:org_id + employee_number
must be unique only if employee_id is defined. -
Class Person, id=
name
, fields:org_id + first_name + name
should be unique (warning only).
Defining your own rule
For defining Uniqueness rules we only need to write XML.
Unique Server name
In this usecase we want to prevent any Server to have the same name as another Server only if they both belong to the same organization.
- itop-design / classes / class@Server / properties
-
<uniqueness_rules> <rule id="server_name" _delta="define"> <!-- field or combination of "Server" fields which must be unique --> <attributes> <attribute id="name"/> <attribute id="org_id"/> </attributes> <!-- ... -->
- itop-design / dictionaries / dictionary@EN US / entries
-
<entry id="Class:Server/UniquenessRule:server_name"> <![CDATA[There is already a server with the same name in the '$this->org_id_friendlyname$' organization]]> </entry>
finalclass
attribute in uniqueness rule is not
supportedUnique name across sub-classes
In this use case we want to prevent two FunctionalCIs to have the same name. Except if the FunctionalCI is in fact a SoftwareInstance, a MiddlewareInstance, a DatabaseSchema or an ApplicationSolution, in which case, we don't care.
Let's assume we have just two FunctionalCI in our iTop:
-
a NetworkDevice named “test.combodo.com”
-
and a ApplicationSolution named “iTop”
What we can and cannot do:
-
This rule will prevent creation of a Server named “test.combodo.com” as a NetworkDevice exists already with that same name
-
It will prevent the rename of a Server into “test.combodo.com” as a NetworkDevice exists already with that same name
-
It will allow creation of a Server named “iTop” as the existing ApplicationSolution named “iTop” is part of the final classes which are filtered out
- itop-design
-
<classes> <class id="FunctionalCI" _delta="must_exist"> <properties> <uniqueness_rules> <!-- For the "id" it must be a single word without special character but underscore --> <rule id="functionalci_name" _delta="define"> <!-- field or combination of "FunctionalCI" fields which must be unique --> <attributes> <attribute id="name"/> </attributes> <!-- Define an OQL WHERE clause with condition on FunctionalCI fields --> <!-- It will be combined this way "SELECT FunctionalCI WHERE " + <filter> --> <filter><![CDATA[ finalclass NOT IN ('DBServer','Middleware', 'OtherSoftware','WebServer','PCSoftware','MiddlewareInstance', 'DatabaseSchema','ApplicationSolution')]]> </filter> <!-- This flag allow you to keep the rule in the XML but desactivate it --> <disabled>false</disabled> <!-- If true or ommitted, a duplicate is blocking the creation/update --> <!-- otherwise it displays a warning message after creation/update --> <is_blocking>true</is_blocking> </rule> </uniqueness_rules> </properties> </class> </classes> <dictionaries> <dictionary id="EN US" _delta="must_exist"> <entries> <entry id="Class:FunctionalCI/UniquenessRule:functionalci_name" _delta="define"> <![CDATA[There are already a Functional CI with that name, please use another name.]]> </entry> <entry id="Class:FunctionalCI/UniquenessRule:functionalci_name+" _delta="define"> <![CDATA[FunctionalCI name should be unique]]> </entry> </entries> </dictionary> </dictionaries>
-
This entry is used for the error message related to the Uniqueness rule.
-
The same code with a + at the end, correspond to a description of the Uniqueness rule, not yet displayed anywhere, but probably one day, it will be displayed in the Datamodel viewer.
Existing Rules on Person
Let's look at the existing uniqueness rules on Person, provided by default in iTop. There are 2 rules:
employee_number
-
It's a blocking rule, it will prevent creation/update in case of error
-
It will prevent two persons belonging to the same organization to have the same employee number,
-
An empty employee number is allowed and will not generate an error, even if other persons exist with an empty employee number in the same organization.
-
Two persons can have the same non-empty employee number only if they belongs to two different organizations.
name
-
This rule is just a warning, it does not prevent creation/update, it just display a message.
-
It is generated if there is an homonym within the same organization
Here is how they have been defined:
- itop-design / classes / class@Person / properties
-
<uniqueness_rules> <rule id="employee_number"> <attributes> <attribute id="org_id"/> <attribute id="employee_number"/> </attributes> <!-- Empty employee_number is allowed and will not be treated as a duplicate --> <filter><![CDATA[employee_number != '']]></filter> <disabled>false</disabled> <is_blocking>true</is_blocking> </rule> <rule id="name"> <attributes> <attribute id="org_id"/> <attribute id="name"/> <attribute id="first_name"/> </attributes> <filter/> <disabled>false</disabled> <is_blocking>false</is_blocking> </rule> </rules> </uniqueness_rules>
Removing a Rule
In this example we will remove an existing uniqueness rule, which is in the default iTop datamodel. In order to remove a uniqueness rule, you need to know two things:
-
on which class it has been declared, here “Person”
-
and its
rule_id
here ''employee_number
Removing a rule requires a piece of XML embedded in an extension, just like adding a rule. The important piece here is _delta=“delete” within the upper node which must be removed with all its sub-nodes
- itop-design / classes
-
<class id="Person" _delta="must_exist"> <properties> <uniqueness_rules> <rule id="employee_number" _delta="delete"/> </uniqueness_rules> </properties> </class>
Partially disabling a rule
In this usecase we suppose that this rule was defined on FunctionalCI:
- classes / class@FunctionalCI / properties
-
<uniqueness_rules> <rule id="functionalci_name" _delta="define"> <!-- field or combination of "FunctionalCI" fields which must be unique --> <attributes> <attribute id="name"/> <attribute id="finalclass"/> </attributes> <!-- ... -->
and we want to change it for DatacenterDevice (which is a child class of FunctionalCI), for which we want a different behavior with a uniqueness of name across all sub-classes of DatacenterDevice.
For this we disable the parent class rule “functionalci_name” and define a new rule, here “datacenterdevice_name”.
- classes / class@DatacenterDevice / properties
-
<uniqueness_rules> <!-- here we use the same rule_id as the rule we want to stop --> <!-- We are on a child class, so the node is new, which explain the _delta="define" --> <rule id="functionalci_name" _delta="define"> <!-- 'disabled' is the only tag supported when overwriting an existing rule_id --> <!-- It disables the 'functionalci_name' rule that for DatacenterDevices --> <disabled>true</disabled> </rule> <!-- then we can add another rule or not... --> <rule id="datacenterdevice_name" _delta="define"> <attributes> <attribute id="name"/> </attributes> <!-- ... -->
Reusing one within a branch is only allowed to disable the parent rule