pyxecm 2.0.1__py3-none-any.whl → 2.0.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pyxecm might be problematic. Click here for more details.
- pyxecm/__init__.py +3 -2
- pyxecm/avts.py +3 -1
- pyxecm/customizer/api/app.py +2 -2
- pyxecm/customizer/api/auth/functions.py +37 -30
- pyxecm/customizer/api/common/functions.py +54 -0
- pyxecm/customizer/api/common/router.py +50 -3
- pyxecm/customizer/api/settings.py +5 -3
- pyxecm/customizer/api/terminal/router.py +43 -18
- pyxecm/customizer/api/v1_csai/models.py +18 -0
- pyxecm/customizer/api/v1_csai/router.py +26 -1
- pyxecm/customizer/api/v1_payload/functions.py +9 -3
- pyxecm/customizer/browser_automation.py +508 -200
- pyxecm/customizer/customizer.py +123 -22
- pyxecm/customizer/guidewire.py +170 -37
- pyxecm/customizer/payload.py +614 -257
- pyxecm/customizer/settings.py +21 -3
- pyxecm/helper/xml.py +1 -1
- pyxecm/otawp.py +10 -6
- pyxecm/otca.py +187 -21
- pyxecm/otcs.py +496 -206
- pyxecm/otds.py +1 -0
- pyxecm/otkd.py +1369 -0
- pyxecm/otmm.py +190 -66
- {pyxecm-2.0.1.dist-info → pyxecm-2.0.3.dist-info}/METADATA +3 -6
- {pyxecm-2.0.1.dist-info → pyxecm-2.0.3.dist-info}/RECORD +28 -26
- {pyxecm-2.0.1.dist-info → pyxecm-2.0.3.dist-info}/WHEEL +1 -1
- {pyxecm-2.0.1.dist-info → pyxecm-2.0.3.dist-info}/licenses/LICENSE +0 -0
- {pyxecm-2.0.1.dist-info → pyxecm-2.0.3.dist-info}/top_level.txt +0 -0
pyxecm/customizer/payload.py
CHANGED
|
@@ -67,9 +67,9 @@ from dateutil.parser import parse
|
|
|
67
67
|
from lark import exceptions as lark_exceptions # used by hcl2
|
|
68
68
|
|
|
69
69
|
# OpenText specific modules:
|
|
70
|
-
from pyxecm import AVTS, OTAC, OTAWP, OTCS, OTDS, OTIV, OTMM, OTPD, CoreShare
|
|
70
|
+
from pyxecm import AVTS, OTAC, OTAWP, OTCA, OTCS, OTDS, OTIV, OTKD, OTMM, OTPD, CoreShare
|
|
71
71
|
from pyxecm.customizer.browser_automation import BrowserAutomation
|
|
72
|
-
from pyxecm.customizer.exceptions import StopOnError
|
|
72
|
+
from pyxecm.customizer.exceptions import PayloadImportError, StopOnError
|
|
73
73
|
from pyxecm.customizer.k8s import K8s
|
|
74
74
|
from pyxecm.customizer.m365 import M365
|
|
75
75
|
from pyxecm.customizer.pht import PHT
|
|
@@ -160,23 +160,18 @@ def load_payload(
|
|
|
160
160
|
except (
|
|
161
161
|
lark_exceptions.UnexpectedToken,
|
|
162
162
|
lark_exceptions.UnexpectedCharacters,
|
|
163
|
-
):
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
)
|
|
168
|
-
payload = {}
|
|
163
|
+
) as exc:
|
|
164
|
+
exception = f"Syntax error while reading Terraform payload file -> '{payload_source}'! --> {traceback.format_exception_only(exc)}"
|
|
165
|
+
raise PayloadImportError(exception) from exc
|
|
166
|
+
|
|
169
167
|
except (
|
|
170
168
|
FileNotFoundError,
|
|
171
169
|
ImportError,
|
|
172
170
|
ValueError,
|
|
173
171
|
SyntaxError,
|
|
174
|
-
):
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
payload_source,
|
|
178
|
-
)
|
|
179
|
-
payload = {}
|
|
172
|
+
) as exc:
|
|
173
|
+
exception = f"Error while reading Terraform payload file -> '{payload_source}'! --> {traceback.format_exception_only(exc)}"
|
|
174
|
+
raise PayloadImportError(exception) from exc
|
|
180
175
|
|
|
181
176
|
elif payload_source.endswith(".yml.gz.b64"):
|
|
182
177
|
logger.info("Open payload from base64-gz-YAML file -> '%s'", payload_source)
|
|
@@ -239,9 +234,11 @@ class Payload:
|
|
|
239
234
|
_successfactors: SuccessFactors | None
|
|
240
235
|
_salesforce: Salesforce | None
|
|
241
236
|
_servicenow: ServiceNow | None
|
|
242
|
-
_browser_automation: BrowserAutomation | None
|
|
243
237
|
_custom_settings_dir = ""
|
|
244
238
|
_otawp: OTAWP | None
|
|
239
|
+
_otca: OTCA | None
|
|
240
|
+
_otkd: OTKD | None
|
|
241
|
+
_avts: AVTS | None
|
|
245
242
|
|
|
246
243
|
# _payload_source (string): This is either path + filename of the yaml payload
|
|
247
244
|
# or an path + filename of the Terraform HCL payload
|
|
@@ -411,6 +408,7 @@ class Payload:
|
|
|
411
408
|
- title (str, optional, default = "")
|
|
412
409
|
- email (str, optional, default = "")
|
|
413
410
|
- base_group (str, optional, default = "DefaultGroup")
|
|
411
|
+
- user_type (str, optional, default = "User") - possible values are "User" and "ServiceUser"
|
|
414
412
|
- company (str, optional, default = "Innovate") - currently used for Salesforce users only
|
|
415
413
|
- privileges (list, optional, default = ["Login", "Public Access"])
|
|
416
414
|
- groups (list, optional)
|
|
@@ -567,7 +565,12 @@ class Payload:
|
|
|
567
565
|
* role (name)
|
|
568
566
|
* users (list, optional, default = [])
|
|
569
567
|
* groups (list, optional, default = [])
|
|
570
|
-
- relationships (list, optional, default = []) - list of
|
|
568
|
+
- relationships (list, optional, default = []) - list of related workspaces.
|
|
569
|
+
The elements of the list can be:
|
|
570
|
+
* string or integer with logical workspace ID
|
|
571
|
+
* string with nickname of the related workspace
|
|
572
|
+
* dictionaries with keys "type" and "name" of the related workspace
|
|
573
|
+
* list of strings with the top-down path in the Enterprise volume
|
|
571
574
|
"""
|
|
572
575
|
_workspaces = []
|
|
573
576
|
|
|
@@ -636,6 +639,8 @@ class Payload:
|
|
|
636
639
|
- name (str, mandatory)
|
|
637
640
|
- nodeid (int, mandatory if no volume is specified) - this is the technical OTCS ID - typically only known for some preinstalled items
|
|
638
641
|
- volume (int, mandatory if no nodeid is specified)
|
|
642
|
+
- path (list, optional) - can be combined with volume - to specify a top-down path in the volume to the item to be renamed
|
|
643
|
+
- nickname (str, optional) - the nickname of the node to rename - alternative to to volume/path or nodeid
|
|
639
644
|
"""
|
|
640
645
|
_renamings = []
|
|
641
646
|
|
|
@@ -747,7 +752,7 @@ class Payload:
|
|
|
747
752
|
- base_url (str, mandatory)
|
|
748
753
|
- user_name (str, optional)
|
|
749
754
|
- password (str, optional)
|
|
750
|
-
- wait_time (float, optional, default =
|
|
755
|
+
- wait_time (float, optional, default = 30.0) - wait time in seconds
|
|
751
756
|
- wait_until (str, optional) - the page load / navigation `wait until` strategy. Possible values: `load`, `networkidle`, `domcontentloaded`
|
|
752
757
|
- debug (bool, optional, default = False) - if True take screenshots and save to container
|
|
753
758
|
- automations (list, mandatory)
|
|
@@ -1189,6 +1194,8 @@ class Payload:
|
|
|
1189
1194
|
|
|
1190
1195
|
_bulk_classifications = []
|
|
1191
1196
|
|
|
1197
|
+
_nifi_flows = []
|
|
1198
|
+
|
|
1192
1199
|
_placeholder_values = {}
|
|
1193
1200
|
|
|
1194
1201
|
# Link to the method in customizer.py to restart the Content Server pods.
|
|
@@ -1224,7 +1231,6 @@ class Payload:
|
|
|
1224
1231
|
otpd_object: OTPD | None,
|
|
1225
1232
|
m365_object: M365 | None,
|
|
1226
1233
|
core_share_object: CoreShare | None,
|
|
1227
|
-
browser_automation_object: BrowserAutomation | None,
|
|
1228
1234
|
placeholder_values: dict,
|
|
1229
1235
|
log_header_callback: Callable,
|
|
1230
1236
|
browser_headless: bool = True,
|
|
@@ -1232,6 +1238,8 @@ class Payload:
|
|
|
1232
1238
|
aviator_enabled: bool = False,
|
|
1233
1239
|
upload_status_files: bool = True,
|
|
1234
1240
|
otawp_object: OTAWP | None = None,
|
|
1241
|
+
otca_object: OTCA | None = None,
|
|
1242
|
+
otkd_object: OTKD | None = None,
|
|
1235
1243
|
avts_object: AVTS | None = None,
|
|
1236
1244
|
logger: logging.Logger = default_logger,
|
|
1237
1245
|
) -> None:
|
|
@@ -1266,8 +1274,6 @@ class Payload:
|
|
|
1266
1274
|
The M365 object to talk to Microsoft Graph API.
|
|
1267
1275
|
core_share_object (CoreShare | None):
|
|
1268
1276
|
The Core Share object.
|
|
1269
|
-
browser_automation_object (BrowserAutomation):
|
|
1270
|
-
The BrowserAutomation object to automate things which don't have a REST API.
|
|
1271
1277
|
placeholder_values (dict):
|
|
1272
1278
|
A dictionary of placeholder values to be replaced in admin settings.
|
|
1273
1279
|
log_header_callback:
|
|
@@ -1284,6 +1290,10 @@ class Payload:
|
|
|
1284
1290
|
of the admin user in Content Server.
|
|
1285
1291
|
otawp_object (OTAWP):
|
|
1286
1292
|
An optional AppWorks Platform object.
|
|
1293
|
+
otca_object (OTCA):
|
|
1294
|
+
An optional Content Aviator object.
|
|
1295
|
+
otkd_object (OTKD):
|
|
1296
|
+
An optional Knowledge Discovery object.
|
|
1287
1297
|
avts_object (AVTS):
|
|
1288
1298
|
An optional Aviator Search object.
|
|
1289
1299
|
logger (logging.Logger, optional):
|
|
@@ -1316,8 +1326,9 @@ class Payload:
|
|
|
1316
1326
|
self._otcs_source = None
|
|
1317
1327
|
self._pht = None # the OpenText prodcut hierarchy
|
|
1318
1328
|
self._nhc = None # National Hurricane Center
|
|
1319
|
-
self.
|
|
1320
|
-
self.
|
|
1329
|
+
self._otca = otca_object # Content Aviator
|
|
1330
|
+
self._otkd = otkd_object # Knowledge Discovery
|
|
1331
|
+
self._avts = avts_object # Aviator Search
|
|
1321
1332
|
self._browser_headless = browser_headless
|
|
1322
1333
|
self._custom_settings_dir = custom_settings_dir
|
|
1323
1334
|
self._placeholder_values = placeholder_values
|
|
@@ -1504,6 +1515,7 @@ class Payload:
|
|
|
1504
1515
|
self._avts_repositories = self.get_payload_section("avtsRepositories")
|
|
1505
1516
|
self._avts_questions = self.get_payload_section("avtsQuestions")
|
|
1506
1517
|
self._embeddings = self.get_payload_section("embeddings")
|
|
1518
|
+
self._nifi_flows = self.get_payload_section("nifi")
|
|
1507
1519
|
|
|
1508
1520
|
return self._payload
|
|
1509
1521
|
|
|
@@ -2153,7 +2165,7 @@ class Payload:
|
|
|
2153
2165
|
case "customer":
|
|
2154
2166
|
customer = self._otawp.get_customer_by_name(name=entity.get("name"))
|
|
2155
2167
|
if customer:
|
|
2156
|
-
customer_id = self._otawp.get_entity_value(entity=
|
|
2168
|
+
customer_id = self._otawp.get_entity_value(entity=customer, key="id")
|
|
2157
2169
|
self.logger.info(
|
|
2158
2170
|
"Customer -> '%s' (%s) does already exist. Skipping...", entity.get("name"), str(customer_id)
|
|
2159
2171
|
)
|
|
@@ -2763,7 +2775,7 @@ class Payload:
|
|
|
2763
2775
|
return group["id"]
|
|
2764
2776
|
else:
|
|
2765
2777
|
self.logger.debug(
|
|
2766
|
-
"
|
|
2778
|
+
"Cannot find an existing group -> '%s'",
|
|
2767
2779
|
group_name,
|
|
2768
2780
|
)
|
|
2769
2781
|
return 0
|
|
@@ -2814,7 +2826,7 @@ class Payload:
|
|
|
2814
2826
|
return group["m365_id"]
|
|
2815
2827
|
else:
|
|
2816
2828
|
self.logger.debug(
|
|
2817
|
-
"
|
|
2829
|
+
"Cannot find an existing M365 group -> '%s'",
|
|
2818
2830
|
group_name,
|
|
2819
2831
|
)
|
|
2820
2832
|
return None
|
|
@@ -2868,7 +2880,7 @@ class Payload:
|
|
|
2868
2880
|
return group["core_share_id"]
|
|
2869
2881
|
else:
|
|
2870
2882
|
self.logger.debug(
|
|
2871
|
-
"
|
|
2883
|
+
"Cannot find an existing Core Share group -> '%s'",
|
|
2872
2884
|
group["name"],
|
|
2873
2885
|
)
|
|
2874
2886
|
return None
|
|
@@ -2903,7 +2915,10 @@ class Payload:
|
|
|
2903
2915
|
self.logger.error("User needs a login name to lookup the ID!")
|
|
2904
2916
|
return 0
|
|
2905
2917
|
|
|
2906
|
-
|
|
2918
|
+
user_type = 17 if user.get("type", "User") == "ServiceUser" else 0
|
|
2919
|
+
|
|
2920
|
+
response = self._otcs.get_user(name=user_name, user_type=user_type)
|
|
2921
|
+
|
|
2907
2922
|
# We use the lookup method here as get_user() could deliver more
|
|
2908
2923
|
# then 1 result element (in edge cases):
|
|
2909
2924
|
user_id = self._otcs.lookup_result_value(
|
|
@@ -2920,7 +2935,7 @@ class Payload:
|
|
|
2920
2935
|
return user["id"]
|
|
2921
2936
|
else:
|
|
2922
2937
|
self.logger.debug(
|
|
2923
|
-
"
|
|
2938
|
+
"Cannot find an existing user -> '%s'!",
|
|
2924
2939
|
user_name,
|
|
2925
2940
|
)
|
|
2926
2941
|
return 0
|
|
@@ -2969,7 +2984,7 @@ class Payload:
|
|
|
2969
2984
|
return user["m365_id"]
|
|
2970
2985
|
else:
|
|
2971
2986
|
self.logger.debug(
|
|
2972
|
-
"Did not find an existing M365 user
|
|
2987
|
+
"Did not find an existing M365 user -> '%s'",
|
|
2973
2988
|
user_name,
|
|
2974
2989
|
)
|
|
2975
2990
|
return None
|
|
@@ -3029,11 +3044,17 @@ class Payload:
|
|
|
3029
3044
|
user["core_share_id"] = core_share_user_id
|
|
3030
3045
|
return user["core_share_id"]
|
|
3031
3046
|
else:
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3047
|
+
if "email" in user:
|
|
3048
|
+
self.logger.debug(
|
|
3049
|
+
"Did not find an existing Core Share user with email -> '%s'",
|
|
3050
|
+
user["email"],
|
|
3051
|
+
)
|
|
3052
|
+
else:
|
|
3053
|
+
self.logger.debug(
|
|
3054
|
+
"Cannot find an existing Core Share user -> '%s %s'",
|
|
3055
|
+
user.get("firstname"),
|
|
3056
|
+
user.get("lastname"),
|
|
3057
|
+
)
|
|
3037
3058
|
return None
|
|
3038
3059
|
|
|
3039
3060
|
# end method definition
|
|
@@ -3650,6 +3671,9 @@ class Payload:
|
|
|
3650
3671
|
if not self._business_object_types:
|
|
3651
3672
|
self._log_header_callback(text="Process Business Object Types")
|
|
3652
3673
|
self.process_business_object_types()
|
|
3674
|
+
case "nifi":
|
|
3675
|
+
self._log_header_callback("Process Knowledge Discovery Nifi Flows")
|
|
3676
|
+
self.process_nifi_flows()
|
|
3653
3677
|
case _:
|
|
3654
3678
|
self.logger.error(
|
|
3655
3679
|
"Illegal payload section name -> '%s' in payloadSections!",
|
|
@@ -5397,7 +5421,7 @@ class Payload:
|
|
|
5397
5421
|
self.logger.error("Group -> '%s' does not have an ID.", group["name"])
|
|
5398
5422
|
success = False
|
|
5399
5423
|
continue
|
|
5400
|
-
parent_group_names = group
|
|
5424
|
+
parent_group_names = group.get("parent_groups", [])
|
|
5401
5425
|
for parent_group_name in parent_group_names:
|
|
5402
5426
|
# First, try to find parent group in payload by parent group name:
|
|
5403
5427
|
parent_group = next(
|
|
@@ -5460,8 +5484,9 @@ class Payload:
|
|
|
5460
5484
|
member_id=group["id"],
|
|
5461
5485
|
group_id=parent_group_id,
|
|
5462
5486
|
)
|
|
5487
|
+
# end for parent_group_name in parent_group_names:
|
|
5463
5488
|
|
|
5464
|
-
# Assign application roles to the new
|
|
5489
|
+
# Assign application roles to the new group:
|
|
5465
5490
|
application_roles = group.get("application_roles", [])
|
|
5466
5491
|
for role in application_roles:
|
|
5467
5492
|
group_partition = self._otcs.config()["partition"]
|
|
@@ -5999,7 +6024,7 @@ class Payload:
|
|
|
5999
6024
|
)
|
|
6000
6025
|
continue
|
|
6001
6026
|
self.logger.info(
|
|
6002
|
-
"
|
|
6027
|
+
"Cannot find an existing user -> '%s' - creating a new user...",
|
|
6003
6028
|
user_name,
|
|
6004
6029
|
)
|
|
6005
6030
|
|
|
@@ -6355,7 +6380,7 @@ class Payload:
|
|
|
6355
6380
|
user_name,
|
|
6356
6381
|
rfc_name,
|
|
6357
6382
|
rfc_description,
|
|
6358
|
-
rfc_params,
|
|
6383
|
+
str(rfc_params),
|
|
6359
6384
|
)
|
|
6360
6385
|
|
|
6361
6386
|
result = self._sap.call(
|
|
@@ -6837,7 +6862,7 @@ class Payload:
|
|
|
6837
6862
|
)
|
|
6838
6863
|
if not group:
|
|
6839
6864
|
self.logger.error(
|
|
6840
|
-
"Cannot find group
|
|
6865
|
+
"Cannot find group -> '%s'. Cannot establish membership in Salesforce. Skipping to next group...",
|
|
6841
6866
|
user_group,
|
|
6842
6867
|
)
|
|
6843
6868
|
success = False
|
|
@@ -7305,7 +7330,7 @@ class Payload:
|
|
|
7305
7330
|
)
|
|
7306
7331
|
if not group:
|
|
7307
7332
|
self.logger.error(
|
|
7308
|
-
"Cannot find group
|
|
7333
|
+
"Cannot find group -> '%s'. Cannot establish membership in Core Share. Skipping to next group...",
|
|
7309
7334
|
user_group,
|
|
7310
7335
|
)
|
|
7311
7336
|
success = False
|
|
@@ -7880,8 +7905,8 @@ class Payload:
|
|
|
7880
7905
|
"""Process groups in payload and create matching Teams in Microsoft 365.
|
|
7881
7906
|
|
|
7882
7907
|
We need to do this after the creation of the M365 users as we require
|
|
7883
|
-
Group Owners to create teams. These are NOT the teams for
|
|
7884
|
-
workspaces! Those are created by Scheduled Bots (Jobs) from
|
|
7908
|
+
Group Owners to create teams. These are NOT the teams for OTCS
|
|
7909
|
+
workspaces! Those are created by Scheduled Bots (Jobs) from OTCS!
|
|
7885
7910
|
|
|
7886
7911
|
Args:
|
|
7887
7912
|
section_name (str, optional):
|
|
@@ -8285,7 +8310,7 @@ class Payload:
|
|
|
8285
8310
|
|
|
8286
8311
|
workspace_name = workspace["name"]
|
|
8287
8312
|
self.logger.info(
|
|
8288
|
-
"Check if stale Microsoft 365 Teams
|
|
8313
|
+
"Check if stale Microsoft 365 Teams -> '%s' exist...",
|
|
8289
8314
|
workspace_name,
|
|
8290
8315
|
)
|
|
8291
8316
|
self._m365.delete_teams(name=workspace_name)
|
|
@@ -8381,8 +8406,8 @@ class Payload:
|
|
|
8381
8406
|
def process_sites_m365(self, section_name: str = "sitesM365") -> bool:
|
|
8382
8407
|
"""Process M365 groups in payload and configure SharePoint sites in Microsoft 365.
|
|
8383
8408
|
|
|
8384
|
-
These are NOT the SharePoint sites for
|
|
8385
|
-
by Scheduled Bots (Jobs) from
|
|
8409
|
+
These are NOT the SharePoint sites for Business Workspaces which are created
|
|
8410
|
+
by Scheduled Bots (Jobs) from OTCS via the creation of MS teams
|
|
8386
8411
|
(each MS Team has a SharePoint site behind it)!
|
|
8387
8412
|
|
|
8388
8413
|
The are the SharePoint sites for the departmental groups such as "Sales",
|
|
@@ -8882,6 +8907,19 @@ class Payload:
|
|
|
8882
8907
|
"A restart of the Content Server service is required.",
|
|
8883
8908
|
)
|
|
8884
8909
|
restart_required = True
|
|
8910
|
+
|
|
8911
|
+
if admin_setting.get("restart", False):
|
|
8912
|
+
self.logger.info(
|
|
8913
|
+
"Immediate restart requested - restart of OTCS services...",
|
|
8914
|
+
)
|
|
8915
|
+
# Restart OTCS frontend and backend pods:
|
|
8916
|
+
self._otcs_restart_callback(
|
|
8917
|
+
backend=self._otcs_backend,
|
|
8918
|
+
frontend=self._otcs_frontend,
|
|
8919
|
+
)
|
|
8920
|
+
|
|
8921
|
+
restart_required = False
|
|
8922
|
+
|
|
8885
8923
|
else:
|
|
8886
8924
|
self.logger.error(
|
|
8887
8925
|
"Admin settings file -> '%s' not found.",
|
|
@@ -9026,6 +9064,11 @@ class Payload:
|
|
|
9026
9064
|
continue
|
|
9027
9065
|
system_type = external_system["external_system_type"]
|
|
9028
9066
|
|
|
9067
|
+
self._log_header_callback(
|
|
9068
|
+
text="Process External System -> '{}' ({})".format(system_name, system_type),
|
|
9069
|
+
char="-",
|
|
9070
|
+
)
|
|
9071
|
+
|
|
9029
9072
|
# Check if external system has been explicitly disabled in payload
|
|
9030
9073
|
# (enabled = false). In this case we skip the element:
|
|
9031
9074
|
if not external_system.get("enabled", True):
|
|
@@ -9538,6 +9581,11 @@ class Payload:
|
|
|
9538
9581
|
)
|
|
9539
9582
|
continue
|
|
9540
9583
|
|
|
9584
|
+
# We skip also user of type "ServiceUser":
|
|
9585
|
+
if user.get("type", "User") == "ServiceUser":
|
|
9586
|
+
self.logger.info("Skipping service user -> '%s'...", user_name)
|
|
9587
|
+
continue
|
|
9588
|
+
|
|
9541
9589
|
user_id = user.get("id")
|
|
9542
9590
|
if not user_id:
|
|
9543
9591
|
self.logger.error(
|
|
@@ -9978,23 +10026,23 @@ class Payload:
|
|
|
9978
10026
|
# we assume the nickname of the photo item equals the login name of the user
|
|
9979
10027
|
# we also assume that the photos have been uploaded / transported into the target system
|
|
9980
10028
|
for user in self._users:
|
|
9981
|
-
if "
|
|
10029
|
+
if "name" not in user:
|
|
9982
10030
|
self.logger.error(
|
|
9983
|
-
"User is missing
|
|
10031
|
+
"User is missing login name. Skipping to next user...",
|
|
9984
10032
|
)
|
|
9985
10033
|
success = False
|
|
9986
10034
|
continue
|
|
9987
10035
|
user_login = user["name"]
|
|
9988
|
-
user_last_name = user
|
|
9989
|
-
user_first_name = user
|
|
9990
|
-
user_name =
|
|
10036
|
+
user_last_name = user.get("lastname", "")
|
|
10037
|
+
user_first_name = user.get("firstname", "")
|
|
10038
|
+
user_name = "{} {}".format(user_first_name, user_last_name).strip()
|
|
9991
10039
|
|
|
9992
10040
|
# Check if user has been explicitly disabled in payload
|
|
9993
10041
|
# (enabled = false). In this case we skip the element:
|
|
9994
10042
|
if not user.get("enabled", True):
|
|
9995
10043
|
self.logger.info(
|
|
9996
10044
|
"Payload for user -> '%s' is disabled. Skipping...",
|
|
9997
|
-
|
|
10045
|
+
user_login,
|
|
9998
10046
|
)
|
|
9999
10047
|
continue
|
|
10000
10048
|
|
|
@@ -10005,7 +10053,7 @@ class Payload:
|
|
|
10005
10053
|
if not user.get("enable_core_share", False):
|
|
10006
10054
|
self.logger.info(
|
|
10007
10055
|
"User -> '%s' is not enabled for Core Share. Skipping...",
|
|
10008
|
-
|
|
10056
|
+
user_login,
|
|
10009
10057
|
)
|
|
10010
10058
|
continue
|
|
10011
10059
|
|
|
@@ -10520,7 +10568,7 @@ class Payload:
|
|
|
10520
10568
|
|
|
10521
10569
|
if not self._business_object_types:
|
|
10522
10570
|
self.logger.warning(
|
|
10523
|
-
"List of business object types is empty / not initialized! Cannot lookup type
|
|
10571
|
+
"List of business object types is empty / not initialized! Cannot lookup type -> '%s'",
|
|
10524
10572
|
bo_type_name,
|
|
10525
10573
|
)
|
|
10526
10574
|
return None
|
|
@@ -10532,7 +10580,7 @@ class Payload:
|
|
|
10532
10580
|
)
|
|
10533
10581
|
if not business_object_type:
|
|
10534
10582
|
self.logger.warning(
|
|
10535
|
-
"Cannot find business object type
|
|
10583
|
+
"Cannot find business object type -> '%s'",
|
|
10536
10584
|
bo_type_name,
|
|
10537
10585
|
)
|
|
10538
10586
|
return None
|
|
@@ -10851,7 +10899,7 @@ class Payload:
|
|
|
10851
10899
|
if role_id is None:
|
|
10852
10900
|
# if member_role is None:
|
|
10853
10901
|
self.logger.error(
|
|
10854
|
-
"Workspace template -> '%s' does not have a role
|
|
10902
|
+
"Workspace template -> '%s' does not have a role -> '%s'",
|
|
10855
10903
|
template_name,
|
|
10856
10904
|
member_role_name,
|
|
10857
10905
|
)
|
|
@@ -12998,7 +13046,7 @@ class Payload:
|
|
|
12998
13046
|
workspace["type_name"],
|
|
12999
13047
|
)
|
|
13000
13048
|
|
|
13001
|
-
# now determine the actual node
|
|
13049
|
+
# now determine the actual node ID of the workspace (which should have been created before):
|
|
13002
13050
|
workspace_node_id = int(self.determine_workspace_id(workspace=workspace))
|
|
13003
13051
|
if not workspace_node_id:
|
|
13004
13052
|
self.logger.warning(
|
|
@@ -13016,64 +13064,97 @@ class Payload:
|
|
|
13016
13064
|
|
|
13017
13065
|
success: bool = True
|
|
13018
13066
|
|
|
13019
|
-
for
|
|
13067
|
+
for related_workspace in workspace["relationships"]:
|
|
13020
13068
|
# Initialize variable to determine if we found a related workspace:
|
|
13021
13069
|
related_workspace_node_id = None
|
|
13070
|
+
found_by = ""
|
|
13022
13071
|
|
|
13023
|
-
|
|
13024
|
-
|
|
13025
|
-
|
|
13026
|
-
|
|
13027
|
-
|
|
13028
|
-
|
|
13029
|
-
|
|
13030
|
-
|
|
13031
|
-
if
|
|
13032
|
-
|
|
13033
|
-
|
|
13034
|
-
|
|
13072
|
+
if isinstance(related_workspace, (str, int)):
|
|
13073
|
+
#
|
|
13074
|
+
# 1. Option: Find the related workspace with the logical ID given in the payload:
|
|
13075
|
+
#
|
|
13076
|
+
related_workspace_payload = next(
|
|
13077
|
+
(item for item in self._workspaces if str(item["id"]) == str(related_workspace)),
|
|
13078
|
+
None,
|
|
13079
|
+
)
|
|
13080
|
+
if related_workspace_payload:
|
|
13081
|
+
if not related_workspace_payload.get("enabled", True):
|
|
13082
|
+
self.logger.info(
|
|
13083
|
+
"Payload for Related Workspace -> '%s' is disabled. Skipping...",
|
|
13084
|
+
related_workspace_payload["name"],
|
|
13085
|
+
)
|
|
13086
|
+
continue
|
|
13087
|
+
|
|
13088
|
+
related_workspace_node_id = self.determine_workspace_id(
|
|
13089
|
+
workspace=related_workspace_payload,
|
|
13035
13090
|
)
|
|
13036
|
-
|
|
13091
|
+
if not related_workspace_node_id:
|
|
13092
|
+
self.logger.warning(
|
|
13093
|
+
"Related Workspace -> '%s' (type -> '%s') has no node ID (workspaces creation may have failed or name is different from payload). Skipping to next workspace...",
|
|
13094
|
+
related_workspace_payload["name"],
|
|
13095
|
+
related_workspace_payload["type_name"],
|
|
13096
|
+
)
|
|
13097
|
+
continue
|
|
13098
|
+
found_by = "logical ID -> '{}' in payload".format(related_workspace)
|
|
13099
|
+
# end if related_workspace_payload:
|
|
13037
13100
|
|
|
13038
|
-
|
|
13039
|
-
|
|
13040
|
-
|
|
13041
|
-
|
|
13042
|
-
|
|
13043
|
-
|
|
13044
|
-
|
|
13045
|
-
|
|
13101
|
+
#
|
|
13102
|
+
# 2. Option: Find the related workspace with nickname:
|
|
13103
|
+
#
|
|
13104
|
+
else:
|
|
13105
|
+
# See if a nickname exists the the provided related_workspace:
|
|
13106
|
+
response = self._otcs.get_node_from_nickname(nickname=related_workspace)
|
|
13107
|
+
related_workspace_node_id = self._otcs.get_result_value(
|
|
13108
|
+
response=response,
|
|
13109
|
+
key="id",
|
|
13046
13110
|
)
|
|
13047
|
-
|
|
13048
|
-
|
|
13049
|
-
|
|
13050
|
-
related_workspace_id,
|
|
13051
|
-
related_workspace_node_id,
|
|
13052
|
-
)
|
|
13053
|
-
# end if related_workspace is not None
|
|
13111
|
+
if related_workspace_node_id:
|
|
13112
|
+
found_by = "nickname -> '{}'".format(related_workspace)
|
|
13113
|
+
# end if isinstance(related_workspace_id, (str, int)):
|
|
13054
13114
|
|
|
13055
13115
|
#
|
|
13056
|
-
#
|
|
13116
|
+
# 3. Option: Find the related workspace type and name:
|
|
13057
13117
|
#
|
|
13058
|
-
|
|
13059
|
-
|
|
13060
|
-
|
|
13118
|
+
elif isinstance(related_workspace, dict):
|
|
13119
|
+
related_workspace_type = related_workspace.get("type", None)
|
|
13120
|
+
related_workspace_name = related_workspace.get("name", None)
|
|
13121
|
+
if related_workspace_type and related_workspace_name:
|
|
13122
|
+
response = self._otcs.get_workspace_by_type_and_name(
|
|
13123
|
+
type_name=related_workspace_type, name=related_workspace_name
|
|
13124
|
+
)
|
|
13125
|
+
related_workspace_node_id = self._otcs.get_result_value(
|
|
13126
|
+
response=response,
|
|
13127
|
+
key="id",
|
|
13128
|
+
)
|
|
13129
|
+
if related_workspace_node_id:
|
|
13130
|
+
found_by = "type -> '{}' and name -> '{}'".format(
|
|
13131
|
+
related_workspace_type, related_workspace_name
|
|
13132
|
+
)
|
|
13133
|
+
#
|
|
13134
|
+
# 4. Option: Find the related workspace volume and path:
|
|
13135
|
+
#
|
|
13136
|
+
elif isinstance(related_workspace, list):
|
|
13137
|
+
response = self._otcs.get_node_by_volume_and_path(
|
|
13138
|
+
volume_type=self._otcs.VOLUME_TYPE_ENTERPRISE_WORKSPACE, path=related_workspace
|
|
13139
|
+
)
|
|
13061
13140
|
related_workspace_node_id = self._otcs.get_result_value(
|
|
13062
13141
|
response=response,
|
|
13063
13142
|
key="id",
|
|
13064
13143
|
)
|
|
13144
|
+
if related_workspace_node_id:
|
|
13145
|
+
found_by = "path -> {}".format(related_workspace)
|
|
13065
13146
|
|
|
13066
13147
|
if related_workspace_node_id is None:
|
|
13067
13148
|
self.logger.error(
|
|
13068
|
-
"Related Workspace
|
|
13069
|
-
|
|
13149
|
+
"Related Workspace -> %s not found.",
|
|
13150
|
+
related_workspace,
|
|
13070
13151
|
)
|
|
13071
13152
|
success = False
|
|
13072
13153
|
continue
|
|
13073
13154
|
|
|
13074
13155
|
self.logger.debug(
|
|
13075
|
-
"Related Workspace with
|
|
13076
|
-
|
|
13156
|
+
"Related Workspace with %s has node ID -> %s",
|
|
13157
|
+
found_by,
|
|
13077
13158
|
related_workspace_node_id,
|
|
13078
13159
|
)
|
|
13079
13160
|
|
|
@@ -13443,7 +13524,7 @@ class Payload:
|
|
|
13443
13524
|
if role_id is None:
|
|
13444
13525
|
# if member_role is None:
|
|
13445
13526
|
self.logger.error(
|
|
13446
|
-
"Workspace -> '%s' does not have a role
|
|
13527
|
+
"Workspace -> '%s' does not have a role -> '%s'",
|
|
13447
13528
|
workspace_name,
|
|
13448
13529
|
member_role_name,
|
|
13449
13530
|
)
|
|
@@ -13719,7 +13800,7 @@ class Payload:
|
|
|
13719
13800
|
)
|
|
13720
13801
|
if role_id is None:
|
|
13721
13802
|
self.logger.error(
|
|
13722
|
-
"Workspace -> '%s' does not have a role
|
|
13803
|
+
"Workspace -> '%s' does not have a role -> '%s'",
|
|
13723
13804
|
workspace_name,
|
|
13724
13805
|
member_role_name,
|
|
13725
13806
|
)
|
|
@@ -14197,7 +14278,12 @@ class Payload:
|
|
|
14197
14278
|
)
|
|
14198
14279
|
continue
|
|
14199
14280
|
|
|
14200
|
-
|
|
14281
|
+
self._log_header_callback(
|
|
14282
|
+
text="Process settings for user -> '{}'".format(user_name),
|
|
14283
|
+
char="-",
|
|
14284
|
+
)
|
|
14285
|
+
|
|
14286
|
+
user_partition = self._otcs.config().get("partition", None)
|
|
14201
14287
|
if not user_partition:
|
|
14202
14288
|
self.logger.error("User partition not found!")
|
|
14203
14289
|
success = False
|
|
@@ -14312,9 +14398,6 @@ class Payload:
|
|
|
14312
14398
|
# The following code (for loop) will change the authenticated user - we need to
|
|
14313
14399
|
# switch it back to admin user later so we safe the admin credentials for this:
|
|
14314
14400
|
|
|
14315
|
-
# save admin credentials for later switch back to admin user:
|
|
14316
|
-
# admin_credentials = self._otcs.credentials() if self._users else {}
|
|
14317
|
-
|
|
14318
14401
|
for user in self._users:
|
|
14319
14402
|
user_name = user.get("name")
|
|
14320
14403
|
if not user_name:
|
|
@@ -14323,6 +14406,11 @@ class Payload:
|
|
|
14323
14406
|
)
|
|
14324
14407
|
continue
|
|
14325
14408
|
|
|
14409
|
+
self._log_header_callback(
|
|
14410
|
+
text="Process Favorites and Profile for user -> '{}'".format(user_name),
|
|
14411
|
+
char="-",
|
|
14412
|
+
)
|
|
14413
|
+
|
|
14326
14414
|
# Check if user has been explicitly disabled in payload
|
|
14327
14415
|
# (enabled = false). In this case we skip the element:
|
|
14328
14416
|
if not user.get("enabled", True):
|
|
@@ -14332,6 +14420,11 @@ class Payload:
|
|
|
14332
14420
|
)
|
|
14333
14421
|
continue
|
|
14334
14422
|
|
|
14423
|
+
# We skip also user of type "ServiceUser":
|
|
14424
|
+
if user.get("type", "User") == "ServiceUser":
|
|
14425
|
+
self.logger.info("Skipping service user -> '%s'...", user_name)
|
|
14426
|
+
continue
|
|
14427
|
+
|
|
14335
14428
|
# Impersonate as the user:
|
|
14336
14429
|
self.logger.info("Impersonate user -> '%s'...", user_name)
|
|
14337
14430
|
result = self.start_impersonation(username=user_name)
|
|
@@ -15499,17 +15592,37 @@ class Payload:
|
|
|
15499
15592
|
if "name" not in renaming:
|
|
15500
15593
|
self.logger.error("Renamings require the new name!")
|
|
15501
15594
|
continue
|
|
15502
|
-
if "nodeid"
|
|
15503
|
-
|
|
15504
|
-
|
|
15505
|
-
|
|
15595
|
+
if "nodeid" in renaming:
|
|
15596
|
+
node_id = renaming["nodeid"]
|
|
15597
|
+
elif "volume" in renaming:
|
|
15598
|
+
path = renaming.get("path")
|
|
15599
|
+
volume = renaming.get("volume")
|
|
15600
|
+
if path:
|
|
15601
|
+
self.logger.info(
|
|
15602
|
+
"Found path -> '%s' in renaming payload. Determine node ID by volume and path...",
|
|
15603
|
+
path,
|
|
15506
15604
|
)
|
|
15507
|
-
|
|
15508
|
-
|
|
15509
|
-
|
|
15510
|
-
|
|
15605
|
+
node = self._otcs.get_node_by_volume_and_path(
|
|
15606
|
+
volume_type=volume,
|
|
15607
|
+
path=path,
|
|
15608
|
+
)
|
|
15609
|
+
else:
|
|
15610
|
+
# Determine object ID of volume:
|
|
15611
|
+
node = self._otcs.get_volume(volume_type=volume)
|
|
15612
|
+
node_id = self._otcs.get_result_value(response=node, key="id")
|
|
15613
|
+
elif "nickname" in renaming:
|
|
15614
|
+
nickname = renaming["nickname"]
|
|
15615
|
+
self.logger.info(
|
|
15616
|
+
"Found nickname -> '%s' in renaming payload. Determine node ID by nickname...",
|
|
15617
|
+
nickname,
|
|
15618
|
+
)
|
|
15619
|
+
node = self._otcs.get_node_from_nickname(nickname=nickname)
|
|
15620
|
+
node_id = self._otcs.get_result_value(response=node, key="id")
|
|
15511
15621
|
else:
|
|
15512
|
-
|
|
15622
|
+
self.logger.error(
|
|
15623
|
+
"Renamings require either a node ID or a volume (with an optional path) or a nickname! Skipping to next renaming...",
|
|
15624
|
+
)
|
|
15625
|
+
continue
|
|
15513
15626
|
|
|
15514
15627
|
# Check if renaming has been explicitly disabled in payload
|
|
15515
15628
|
# (enabled = false). In this case we skip this payload element:
|
|
@@ -15625,7 +15738,7 @@ class Payload:
|
|
|
15625
15738
|
)
|
|
15626
15739
|
success = False
|
|
15627
15740
|
continue
|
|
15628
|
-
|
|
15741
|
+
elif parent_path is not None: # parent_path can be [] which is valid for top-level items!
|
|
15629
15742
|
parent_volume = item.get("parent_volume", self._otcs.VOLUME_TYPE_ENTERPRISE_WORKSPACE)
|
|
15630
15743
|
parent_node = self._otcs.get_node_by_volume_and_path(
|
|
15631
15744
|
volume_type=parent_volume,
|
|
@@ -15636,11 +15749,17 @@ class Payload:
|
|
|
15636
15749
|
if not parent_id:
|
|
15637
15750
|
# if not parent_node:
|
|
15638
15751
|
self.logger.error(
|
|
15639
|
-
"Item -> '%s' has a parent path that does not exist. Skipping...",
|
|
15752
|
+
"Item -> '%s' has a parent path -> %s that does not exist and couldn't be created in volume -> %d. Skipping...",
|
|
15640
15753
|
item_name,
|
|
15754
|
+
parent_path,
|
|
15755
|
+
self._otcs.VOLUME_TYPE_ENTERPRISE_WORKSPACE,
|
|
15641
15756
|
)
|
|
15642
15757
|
success = False
|
|
15643
15758
|
continue
|
|
15759
|
+
else:
|
|
15760
|
+
self.logger.error("The parent for the item -> '%s' is not specified by nickname nor path!", item_name)
|
|
15761
|
+
success = False
|
|
15762
|
+
continue
|
|
15644
15763
|
|
|
15645
15764
|
# Handling for shortcut items that have an orginal node:
|
|
15646
15765
|
original_nickname = item.get("original_nickname")
|
|
@@ -15663,9 +15782,10 @@ class Payload:
|
|
|
15663
15782
|
)
|
|
15664
15783
|
success = False
|
|
15665
15784
|
continue
|
|
15666
|
-
elif original_path:
|
|
15785
|
+
elif original_path is not None: # original_path can be [] which is valid for top-level items!
|
|
15786
|
+
original_volume = item.get("original_volume", self._otcs.VOLUME_TYPE_ENTERPRISE_WORKSPACE)
|
|
15667
15787
|
original_node = self._otcs.get_node_by_volume_and_path(
|
|
15668
|
-
volume_type=
|
|
15788
|
+
volume_type=original_volume,
|
|
15669
15789
|
path=original_path,
|
|
15670
15790
|
)
|
|
15671
15791
|
original_id = self._otcs.get_result_value(
|
|
@@ -15759,14 +15879,23 @@ class Payload:
|
|
|
15759
15879
|
)
|
|
15760
15880
|
node_id = self._otcs.get_result_value(response=response, key="id")
|
|
15761
15881
|
if not node_id:
|
|
15762
|
-
self.logger.error(
|
|
15882
|
+
self.logger.error(
|
|
15883
|
+
"Failed to create item -> '%s' under parent%s.",
|
|
15884
|
+
item_name,
|
|
15885
|
+
" with nickname -> '{}'".format(parent_nickname)
|
|
15886
|
+
if parent_nickname
|
|
15887
|
+
else " path -> {} in volume -> {}".format(parent_path, parent_volume),
|
|
15888
|
+
)
|
|
15763
15889
|
success = False
|
|
15764
15890
|
continue
|
|
15765
15891
|
|
|
15766
15892
|
self.logger.info(
|
|
15767
|
-
"Successfully created item -> '%s' with ID -> %s.",
|
|
15893
|
+
"Successfully created item -> '%s' with ID -> %s under parent%s.",
|
|
15768
15894
|
item_name,
|
|
15769
15895
|
node_id,
|
|
15896
|
+
" with nickname -> '{}'".format(parent_nickname)
|
|
15897
|
+
if parent_nickname
|
|
15898
|
+
else " path -> {} in volume -> {}".format(parent_path, parent_volume),
|
|
15770
15899
|
)
|
|
15771
15900
|
|
|
15772
15901
|
# Special handling for scheduled bot items:
|
|
@@ -15829,14 +15958,20 @@ class Payload:
|
|
|
15829
15958
|
success = False
|
|
15830
15959
|
continue
|
|
15831
15960
|
|
|
15832
|
-
#
|
|
15833
|
-
|
|
15834
|
-
|
|
15835
|
-
|
|
15961
|
+
# Check if we want to execute an action immediately after creation, like "Runnow":
|
|
15962
|
+
actions = item_details.get("actions", [])
|
|
15963
|
+
for action in actions:
|
|
15964
|
+
self.logger.info("Execute action -> '%s' for scheduled bot -> '%s'...", action, item_name)
|
|
15965
|
+
response = self._otcs.update_item(node_id=node_id, body=False, actionName=action)
|
|
15836
15966
|
if not response:
|
|
15837
|
-
self.logger.error(
|
|
15967
|
+
self.logger.error(
|
|
15968
|
+
"Failed to execute action -> '%s' for scheduled bot item -> '%s'.", action, item_name
|
|
15969
|
+
)
|
|
15838
15970
|
success = False
|
|
15839
15971
|
continue
|
|
15972
|
+
if not actions:
|
|
15973
|
+
self.logger.info("No immediate actions specified for scheduled bot -> '%s'.", item_name)
|
|
15974
|
+
|
|
15840
15975
|
# end if item_type == self._otcs.ITEM_TYPE_SCHEDULED_BOT:
|
|
15841
15976
|
|
|
15842
15977
|
# Special handling for collection items:
|
|
@@ -16035,7 +16170,7 @@ class Payload:
|
|
|
16035
16170
|
user_id = self._otcs.get_result_value(response=response, key="id")
|
|
16036
16171
|
if not user_id:
|
|
16037
16172
|
self.logger.error(
|
|
16038
|
-
"Cannot find user
|
|
16173
|
+
"Cannot find user -> '%s'; cannot set user permissions. Skipping user...",
|
|
16039
16174
|
user_name,
|
|
16040
16175
|
)
|
|
16041
16176
|
return False
|
|
@@ -16090,7 +16225,7 @@ class Payload:
|
|
|
16090
16225
|
group_id = self._otcs.get_result_value(response=otcs_group, key="id")
|
|
16091
16226
|
if not group_id:
|
|
16092
16227
|
self.logger.error(
|
|
16093
|
-
"Cannot find group
|
|
16228
|
+
"Cannot find group -> '%s'; cannot set group permissions. Skipping group...",
|
|
16094
16229
|
group_name,
|
|
16095
16230
|
)
|
|
16096
16231
|
return False
|
|
@@ -16149,7 +16284,7 @@ class Payload:
|
|
|
16149
16284
|
)
|
|
16150
16285
|
if not role_id:
|
|
16151
16286
|
self.logger.error(
|
|
16152
|
-
"Cannot find role
|
|
16287
|
+
"Cannot find role -> '%s'; cannot set role permissions.",
|
|
16153
16288
|
role_name,
|
|
16154
16289
|
)
|
|
16155
16290
|
return False
|
|
@@ -17473,6 +17608,11 @@ class Payload:
|
|
|
17473
17608
|
continue
|
|
17474
17609
|
workspace_type = doc_generator["workspace_type"]
|
|
17475
17610
|
|
|
17611
|
+
self._log_header_callback(
|
|
17612
|
+
text="Process Document Generator for workspace type -> '{}'".format(workspace_type),
|
|
17613
|
+
char="-",
|
|
17614
|
+
)
|
|
17615
|
+
|
|
17476
17616
|
# Check if doc generator has been explicitly disabled in payload
|
|
17477
17617
|
# (enabled = false). In this case we skip the element:
|
|
17478
17618
|
if not doc_generator.get("enabled", True):
|
|
@@ -17715,7 +17855,7 @@ class Payload:
|
|
|
17715
17855
|
)
|
|
17716
17856
|
if response["results"]:
|
|
17717
17857
|
self.logger.warning(
|
|
17718
|
-
"Node
|
|
17858
|
+
"Node -> '%s' does already exist in workspace folder with ID -> %s",
|
|
17719
17859
|
document_name,
|
|
17720
17860
|
workspace_folder_id,
|
|
17721
17861
|
)
|
|
@@ -17730,7 +17870,7 @@ class Payload:
|
|
|
17730
17870
|
)
|
|
17731
17871
|
if not response:
|
|
17732
17872
|
self.logger.error(
|
|
17733
|
-
"Failed to generate document -> '%s' in workspace -> '%s' (%s) as user -> %s",
|
|
17873
|
+
"Failed to generate document -> '%s' in workspace -> '%s' (%s) as user -> '%s'",
|
|
17734
17874
|
document_name,
|
|
17735
17875
|
workspace_name,
|
|
17736
17876
|
workspace_id,
|
|
@@ -18288,10 +18428,9 @@ class Payload:
|
|
|
18288
18428
|
# (enabled = false). In this case we skip this payload element:
|
|
18289
18429
|
if not browser_automation.get("enabled", True):
|
|
18290
18430
|
self.logger.info(
|
|
18291
|
-
"Payload for %s automation -> '%s'
|
|
18431
|
+
"Payload for %s automation -> '%s' is disabled. Skipping...",
|
|
18292
18432
|
automation_type.lower(),
|
|
18293
18433
|
name,
|
|
18294
|
-
" ({})".format(description) if description else "",
|
|
18295
18434
|
)
|
|
18296
18435
|
continue
|
|
18297
18436
|
|
|
@@ -18300,6 +18439,7 @@ class Payload:
|
|
|
18300
18439
|
self.logger.error(
|
|
18301
18440
|
"%s automation -> '%s' is missing 'base_url' parameter. Skipping...", automation_type, name
|
|
18302
18441
|
)
|
|
18442
|
+
browser_automation["result"] = "failure"
|
|
18303
18443
|
success = False
|
|
18304
18444
|
continue
|
|
18305
18445
|
|
|
@@ -18318,6 +18458,7 @@ class Payload:
|
|
|
18318
18458
|
automation_type,
|
|
18319
18459
|
name,
|
|
18320
18460
|
)
|
|
18461
|
+
browser_automation["result"] = "failure"
|
|
18321
18462
|
success = False
|
|
18322
18463
|
continue
|
|
18323
18464
|
|
|
@@ -18343,18 +18484,19 @@ class Payload:
|
|
|
18343
18484
|
headless=self._browser_headless,
|
|
18344
18485
|
logger=self.logger,
|
|
18345
18486
|
wait_until=wait_until,
|
|
18487
|
+
browser=browser_automation.get("browser"), # None is acceptable
|
|
18346
18488
|
)
|
|
18347
|
-
# Wait time is a global setting (for whole
|
|
18489
|
+
# Wait time is a global setting (for whole browser session)
|
|
18348
18490
|
# This makes sure a page is fully loaded and elements are present
|
|
18349
|
-
# before accessing them. We set
|
|
18491
|
+
# before accessing them. We set 30.0 seconds as default if not
|
|
18350
18492
|
# otherwise specified by "wait_time" in the payload.
|
|
18351
|
-
wait_time = browser_automation.get("wait_time",
|
|
18493
|
+
wait_time = float(browser_automation.get("wait_time", 30.0))
|
|
18352
18494
|
browser_automation_object.set_timeout(wait_time=wait_time)
|
|
18353
18495
|
if "wait_time" in browser_automation:
|
|
18354
18496
|
self.logger.info(
|
|
18355
18497
|
"%s Automation wait time -> '%s' configured.",
|
|
18356
18498
|
automation_type,
|
|
18357
|
-
wait_time,
|
|
18499
|
+
str(wait_time),
|
|
18358
18500
|
)
|
|
18359
18501
|
|
|
18360
18502
|
# Initialize overall result status:
|
|
@@ -18362,11 +18504,17 @@ class Payload:
|
|
|
18362
18504
|
first_step = True
|
|
18363
18505
|
|
|
18364
18506
|
for automation_step in automation_steps:
|
|
18365
|
-
|
|
18366
|
-
|
|
18507
|
+
automation_step_type = automation_step.get("type", "")
|
|
18508
|
+
if not automation_step_type:
|
|
18509
|
+
self.logger.error(
|
|
18510
|
+
"%s automation step -> %s in browser automation -> '%s' is missing 'type' parameter. Stopping automation -> '%s'.",
|
|
18511
|
+
automation_type,
|
|
18512
|
+
str(automation_step),
|
|
18513
|
+
name,
|
|
18514
|
+
name,
|
|
18515
|
+
)
|
|
18367
18516
|
success = False
|
|
18368
18517
|
break
|
|
18369
|
-
automation_step_type = automation_step.get("type", "")
|
|
18370
18518
|
dependent = automation_step.get("dependent", True)
|
|
18371
18519
|
if not dependent and not result:
|
|
18372
18520
|
self.logger.warning(
|
|
@@ -18384,7 +18532,7 @@ class Payload:
|
|
|
18384
18532
|
)
|
|
18385
18533
|
continue
|
|
18386
18534
|
elif not first_step:
|
|
18387
|
-
self.logger.
|
|
18535
|
+
self.logger.debug(
|
|
18388
18536
|
"Current step -> '%s' is %s on proceeding step.",
|
|
18389
18537
|
automation_step_type,
|
|
18390
18538
|
"dependent" if dependent else "not dependent",
|
|
@@ -18422,6 +18570,7 @@ class Payload:
|
|
|
18422
18570
|
"Cannot log into -> %s. Skipping to next automation step...",
|
|
18423
18571
|
base_url + page,
|
|
18424
18572
|
)
|
|
18573
|
+
automation_step["result"] = "failure"
|
|
18425
18574
|
success = False
|
|
18426
18575
|
continue
|
|
18427
18576
|
self.logger.info(
|
|
@@ -18436,6 +18585,7 @@ class Payload:
|
|
|
18436
18585
|
"Automation step type -> '%s' requires 'page' parameter. Stopping automation.",
|
|
18437
18586
|
automation_step_type,
|
|
18438
18587
|
)
|
|
18588
|
+
automation_step["result"] = "failure"
|
|
18439
18589
|
success = False
|
|
18440
18590
|
break
|
|
18441
18591
|
volume = automation_step.get("volume", OTCS.VOLUME_TYPE_ENTERPRISE_WORKSPACE)
|
|
@@ -18454,6 +18604,7 @@ class Payload:
|
|
|
18454
18604
|
automation_type,
|
|
18455
18605
|
name,
|
|
18456
18606
|
)
|
|
18607
|
+
automation_step["result"] = "failure"
|
|
18457
18608
|
success = False
|
|
18458
18609
|
continue
|
|
18459
18610
|
self.logger.info(
|
|
@@ -18479,6 +18630,7 @@ class Payload:
|
|
|
18479
18630
|
"Cannot load page -> %s. Skipping this step...",
|
|
18480
18631
|
page,
|
|
18481
18632
|
)
|
|
18633
|
+
automation_step["result"] = "failure"
|
|
18482
18634
|
success = False
|
|
18483
18635
|
continue
|
|
18484
18636
|
self.logger.info(
|
|
@@ -18494,17 +18646,22 @@ class Payload:
|
|
|
18494
18646
|
"Automation step type -> '%s' requires 'selector' parameter. Stopping automation.",
|
|
18495
18647
|
automation_step_type,
|
|
18496
18648
|
)
|
|
18649
|
+
automation_step["result"] = "failure"
|
|
18497
18650
|
success = False
|
|
18498
18651
|
break
|
|
18499
18652
|
# We keep the deprecated "find" syntax supported (for now)
|
|
18500
18653
|
selector_type = automation_step.get("selector_type", automation_step.get("find", "id"))
|
|
18501
18654
|
show_error = automation_step.get("show_error", True)
|
|
18655
|
+
# Do we navigate away from the current page with the click?
|
|
18502
18656
|
navigation = automation_step.get("navigation", False)
|
|
18657
|
+
# Do we open a new browser (popup) window with the click?
|
|
18658
|
+
popup_window = automation_step.get("popup_window", False)
|
|
18659
|
+
# De we close the current (popup) window with the click?
|
|
18660
|
+
close_window = automation_step.get("close_window", False)
|
|
18661
|
+
# Do we have a 'desired' state for clicking a checkbox?
|
|
18503
18662
|
checkbox_state = automation_step.get("checkbox_state", None)
|
|
18504
|
-
# Do we have a step-specific wait mechanism? If not, we pass None
|
|
18505
|
-
# then the browser automation will take the default configured for
|
|
18506
|
-
# the whole browser automation (see BrowserAutomation() constructor called above):
|
|
18507
18663
|
wait_until = automation_step.get("wait_until", None)
|
|
18664
|
+
wait_time = automation_step.get("wait_time", 0.0)
|
|
18508
18665
|
role_type = automation_step.get("role_type", None)
|
|
18509
18666
|
result = browser_automation_object.find_elem_and_click(
|
|
18510
18667
|
selector=selector,
|
|
@@ -18512,7 +18669,10 @@ class Payload:
|
|
|
18512
18669
|
role_type=role_type,
|
|
18513
18670
|
desired_checkbox_state=checkbox_state,
|
|
18514
18671
|
is_navigation_trigger=navigation,
|
|
18672
|
+
is_popup_trigger=popup_window,
|
|
18673
|
+
is_page_close_trigger=close_window,
|
|
18515
18674
|
wait_until=wait_until,
|
|
18675
|
+
wait_time=wait_time,
|
|
18516
18676
|
show_error=show_error,
|
|
18517
18677
|
)
|
|
18518
18678
|
if not result:
|
|
@@ -18521,15 +18681,17 @@ class Payload:
|
|
|
18521
18681
|
)
|
|
18522
18682
|
if show_error:
|
|
18523
18683
|
self.logger.error(message)
|
|
18684
|
+
automation_step["result"] = "failure"
|
|
18524
18685
|
success = False
|
|
18525
18686
|
else:
|
|
18526
18687
|
self.logger.warning(message)
|
|
18527
18688
|
continue
|
|
18528
18689
|
self.logger.info(
|
|
18529
|
-
"Successfully clicked %s element selected by -> '%s' (%s)",
|
|
18690
|
+
"Successfully clicked %s element selected by -> '%s' (%s%s)",
|
|
18530
18691
|
"navigational" if navigation else "non-navigational",
|
|
18531
18692
|
selector,
|
|
18532
|
-
selector_type,
|
|
18693
|
+
"selector type -> '{}'".format(selector_type),
|
|
18694
|
+
", role type -> '{}'".format(role_type) if role_type else "",
|
|
18533
18695
|
)
|
|
18534
18696
|
case "set_elem":
|
|
18535
18697
|
# We keep the deprecated "elem" syntax supported (for now)
|
|
@@ -18539,6 +18701,7 @@ class Payload:
|
|
|
18539
18701
|
"Automation step type -> '%s' requires 'selector' parameter. Stopping automation.",
|
|
18540
18702
|
automation_step_type,
|
|
18541
18703
|
)
|
|
18704
|
+
automation_step["result"] = "failure"
|
|
18542
18705
|
success = False
|
|
18543
18706
|
break
|
|
18544
18707
|
# We keep the deprecated "find" syntax supported (for now)
|
|
@@ -18552,11 +18715,13 @@ class Payload:
|
|
|
18552
18715
|
selector,
|
|
18553
18716
|
selector_type,
|
|
18554
18717
|
)
|
|
18718
|
+
automation_step["result"] = "failure"
|
|
18555
18719
|
success = False
|
|
18556
18720
|
break
|
|
18557
18721
|
# we also support replacing placeholders that are
|
|
18558
18722
|
# enclosed in double % characters like %%OTCS_RESOURCE_ID%%:
|
|
18559
|
-
|
|
18723
|
+
if isinstance(value, str):
|
|
18724
|
+
value = self.replace_placeholders(value)
|
|
18560
18725
|
show_error = automation_step.get("show_error", True)
|
|
18561
18726
|
result = browser_automation_object.find_elem_and_set(
|
|
18562
18727
|
selector=selector,
|
|
@@ -18566,19 +18731,24 @@ class Payload:
|
|
|
18566
18731
|
show_error=show_error,
|
|
18567
18732
|
)
|
|
18568
18733
|
if not result:
|
|
18569
|
-
message = "Cannot set element selected by -> '{}' ({}) to value -> '{}'. Skipping this step...".format(
|
|
18570
|
-
selector,
|
|
18734
|
+
message = "Cannot set element selected by -> '{}' ({}{}) to value -> '{}'. Skipping this step...".format(
|
|
18735
|
+
selector,
|
|
18736
|
+
"selector type -> '{}'".format(selector_type),
|
|
18737
|
+
", role type -> '{}'".format(role_type) if role_type else "",
|
|
18738
|
+
value,
|
|
18571
18739
|
)
|
|
18572
18740
|
if show_error:
|
|
18573
18741
|
self.logger.error(message)
|
|
18742
|
+
automation_step["result"] = "failure"
|
|
18574
18743
|
success = False
|
|
18575
18744
|
else:
|
|
18576
18745
|
self.logger.warning(message)
|
|
18577
18746
|
continue
|
|
18578
18747
|
self.logger.info(
|
|
18579
|
-
"Successfully set element selected by -> '%s' (%s) to value -> '%s'.",
|
|
18748
|
+
"Successfully set element selected by -> '%s' (%s%s) to value -> '%s'.",
|
|
18580
18749
|
selector,
|
|
18581
|
-
selector_type,
|
|
18750
|
+
"selector type -> '{}'".format(selector_type),
|
|
18751
|
+
", role type -> '{}'".format(role_type) if role_type else "",
|
|
18582
18752
|
value,
|
|
18583
18753
|
)
|
|
18584
18754
|
case "check_elem":
|
|
@@ -18589,6 +18759,7 @@ class Payload:
|
|
|
18589
18759
|
"Automation step type -> '%s' requires 'selector' parameter. Stopping automation.",
|
|
18590
18760
|
automation_step_type,
|
|
18591
18761
|
)
|
|
18762
|
+
automation_step["result"] = "failure"
|
|
18592
18763
|
success = False
|
|
18593
18764
|
break
|
|
18594
18765
|
# We keep the deprecated "find" syntax supported (for now)
|
|
@@ -18613,20 +18784,33 @@ class Payload:
|
|
|
18613
18784
|
substring=substring,
|
|
18614
18785
|
min_count=min_count,
|
|
18615
18786
|
wait_time=wait_time, # time to wait before the check is actually done
|
|
18787
|
+
show_error=not want_exist, # if element is not found that we do not want to find it is not an error
|
|
18616
18788
|
)
|
|
18617
18789
|
# Check if we didn't get what we want:
|
|
18618
18790
|
if (not result and want_exist) or (result and not want_exist):
|
|
18619
18791
|
self.logger.error(
|
|
18620
18792
|
"%s %s%s%s on current page. Test failed.%s",
|
|
18621
|
-
"Cannot find" if not result else "Found",
|
|
18622
|
-
"{} elements with selector -> '{}' ({})".format(
|
|
18623
|
-
|
|
18624
|
-
|
|
18793
|
+
"Cannot find" if not result and want_exist else "Found",
|
|
18794
|
+
"{} elements with selector -> '{}' ({}{})".format(
|
|
18795
|
+
min_count if want_exist else count,
|
|
18796
|
+
selector,
|
|
18797
|
+
"selector type -> '{}'".format(selector_type),
|
|
18798
|
+
", role type -> '{}'".format(role_type) if role_type else "",
|
|
18799
|
+
)
|
|
18800
|
+
if (min_count > 1 and want_exist) or (count > 1 and not want_exist)
|
|
18801
|
+
else "an element with selector -> '{}' ({}{})".format(
|
|
18802
|
+
selector,
|
|
18803
|
+
"selector type -> '{}'".format(selector_type),
|
|
18804
|
+
", role type -> '{}'".format(role_type) if role_type else "",
|
|
18805
|
+
),
|
|
18625
18806
|
" with {}value -> '{}'".format("substring-" if substring else "", value)
|
|
18626
18807
|
if value
|
|
18627
18808
|
else "",
|
|
18628
18809
|
" in attribute -> '{}'".format(attribute) if attribute else "",
|
|
18629
|
-
" Found {}{} occurences.".format(
|
|
18810
|
+
" Found {}{} occurences.".format(
|
|
18811
|
+
count,
|
|
18812
|
+
" undesirable" if not want_exist else " from a minimum of {}".format(min_count),
|
|
18813
|
+
),
|
|
18630
18814
|
)
|
|
18631
18815
|
success = False
|
|
18632
18816
|
continue
|
|
@@ -18646,12 +18830,20 @@ class Payload:
|
|
|
18646
18830
|
automation_step_type,
|
|
18647
18831
|
automation_type.lower(),
|
|
18648
18832
|
)
|
|
18833
|
+
automation_step["result"] = "failure"
|
|
18649
18834
|
success = False
|
|
18650
18835
|
break
|
|
18651
18836
|
# end match automation_step_type:
|
|
18652
18837
|
first_step = False
|
|
18653
18838
|
# end for automation_step in automation_steps:
|
|
18839
|
+
|
|
18840
|
+
# Cleanup session and and remove reference to the object:
|
|
18654
18841
|
browser_automation_object.end_session()
|
|
18842
|
+
browser_automation_object = None
|
|
18843
|
+
|
|
18844
|
+
browser_automation["result"] = (
|
|
18845
|
+
"failure" if any(step.get("result", "success") == "failure" for step in automation_steps) else "success"
|
|
18846
|
+
)
|
|
18655
18847
|
# end for browser_automation in browser_automations:
|
|
18656
18848
|
|
|
18657
18849
|
if check_status:
|
|
@@ -22053,7 +22245,7 @@ class Payload:
|
|
|
22053
22245
|
if response["results"]:
|
|
22054
22246
|
# We add the suffix with the key which should be unique:
|
|
22055
22247
|
self.logger.warning(
|
|
22056
|
-
"Workspace
|
|
22248
|
+
"Workspace -> '%s' does already exist in folder with ID -> %s and we need to handle the name clash by using name -> '%s'",
|
|
22057
22249
|
workspace_name,
|
|
22058
22250
|
parent_id,
|
|
22059
22251
|
workspace_name + " (" + key + ")",
|
|
@@ -25151,7 +25343,7 @@ class Payload:
|
|
|
25151
25343
|
|
|
25152
25344
|
# Create Business Relationship between workspace and sub-workspace:
|
|
25153
25345
|
if workspace_id and sub_workspace_id:
|
|
25154
|
-
# Check if workspace relationship does already exist in
|
|
25346
|
+
# Check if workspace relationship does already exist in OTCS
|
|
25155
25347
|
# (this is an additional safety measure to avoid errors):
|
|
25156
25348
|
response = self._otcs_frontend.get_workspace_relationships(
|
|
25157
25349
|
workspace_id=workspace_id,
|
|
@@ -25870,7 +26062,7 @@ class Payload:
|
|
|
25870
26062
|
else:
|
|
25871
26063
|
# Case 4: no key given + name not found = item does not exist
|
|
25872
26064
|
self.logger.info(
|
|
25873
|
-
"
|
|
26065
|
+
"Cannot find document -> '%s' in parent with ID -> %s",
|
|
25874
26066
|
document_name,
|
|
25875
26067
|
parent_id,
|
|
25876
26068
|
)
|
|
@@ -25937,7 +26129,7 @@ class Payload:
|
|
|
25937
26129
|
|
|
25938
26130
|
# We add the suffix with the key which should be unique:
|
|
25939
26131
|
self.logger.warning(
|
|
25940
|
-
"Document
|
|
26132
|
+
"Document -> '%s' does already exist in workspace folder with ID -> %s and we need to handle the name clash and use name -> '%s'",
|
|
25941
26133
|
document_name,
|
|
25942
26134
|
parent_id,
|
|
25943
26135
|
document_name + " (" + key + ")",
|
|
@@ -27113,7 +27305,8 @@ class Payload:
|
|
|
27113
27305
|
)
|
|
27114
27306
|
success = False
|
|
27115
27307
|
continue
|
|
27116
|
-
|
|
27308
|
+
# end if key_attribute:
|
|
27309
|
+
# end if key:
|
|
27117
27310
|
else:
|
|
27118
27311
|
# If we haven't a key we try by parent + name
|
|
27119
27312
|
response = self._otcs_frontend.get_node_by_parent_and_name(
|
|
@@ -27140,7 +27333,7 @@ class Payload:
|
|
|
27140
27333
|
else:
|
|
27141
27334
|
# Case 4: no key given + name not found = item does not exist
|
|
27142
27335
|
self.logger.info(
|
|
27143
|
-
"No existing item
|
|
27336
|
+
"No existing item -> '%s' in parent with ID -> %s",
|
|
27144
27337
|
item_name,
|
|
27145
27338
|
parent_id,
|
|
27146
27339
|
)
|
|
@@ -27207,7 +27400,7 @@ class Payload:
|
|
|
27207
27400
|
|
|
27208
27401
|
# We add the suffix with the key which should be unique:
|
|
27209
27402
|
self.logger.warning(
|
|
27210
|
-
"Item
|
|
27403
|
+
"Item -> '%s' does already exist in workspace folder with ID -> %s and we need to handle the name clash and use name -> '%s'",
|
|
27211
27404
|
item_name,
|
|
27212
27405
|
parent_id,
|
|
27213
27406
|
item_name + " (" + key + ")",
|
|
@@ -27499,111 +27692,116 @@ class Payload:
|
|
|
27499
27692
|
|
|
27500
27693
|
success: bool = True
|
|
27501
27694
|
|
|
27502
|
-
self._avts.authenticate()
|
|
27503
|
-
|
|
27504
|
-
|
|
27505
|
-
|
|
27506
|
-
|
|
27507
|
-
|
|
27508
|
-
|
|
27509
|
-
|
|
27510
|
-
if repository is None:
|
|
27511
|
-
self.logger.info(
|
|
27512
|
-
"Repository -> '%s' does not exist, creating it...",
|
|
27513
|
-
payload_repo["name"],
|
|
27514
|
-
)
|
|
27515
|
-
|
|
27516
|
-
if payload_repo.get("type", "Extended ECM") == "Extended ECM":
|
|
27517
|
-
repository = self._avts.create_extended_ecm_repo(
|
|
27518
|
-
name=payload_repo["name"],
|
|
27519
|
-
username=payload_repo["username"],
|
|
27520
|
-
password=payload_repo["password"],
|
|
27521
|
-
otcs_url=payload_repo["otcs_url"],
|
|
27522
|
-
otcs_api_url=payload_repo["otcs_api_url"],
|
|
27523
|
-
node_id=int(payload_repo["node_id"]),
|
|
27524
|
-
)
|
|
27695
|
+
token = self._avts.authenticate()
|
|
27696
|
+
if not token:
|
|
27697
|
+
self.logger.error("Cannot authenticate at Aviator Search!")
|
|
27698
|
+
success = False
|
|
27699
|
+
else:
|
|
27700
|
+
for payload_repo in self._avts_repositories:
|
|
27701
|
+
if not payload_repo.get("enabled", True):
|
|
27702
|
+
continue
|
|
27525
27703
|
|
|
27526
|
-
|
|
27527
|
-
self.logger.warning("Not yet implemented")
|
|
27528
|
-
elif payload_repo["type"] == "MSTeams":
|
|
27529
|
-
repository = self._avts.create_msteams_repo(
|
|
27530
|
-
name=payload_repo["name"],
|
|
27531
|
-
client_id=payload_repo["client_id"],
|
|
27532
|
-
tenant_id=payload_repo["tenant_id"],
|
|
27533
|
-
certificate_file=payload_repo["certificate_file"],
|
|
27534
|
-
certificate_password=payload_repo["certificate_password"],
|
|
27535
|
-
index_attachments=payload_repo.get("index_attachments", True),
|
|
27536
|
-
index_call_recordings=payload_repo.get(
|
|
27537
|
-
"index_call_recordings",
|
|
27538
|
-
True,
|
|
27539
|
-
),
|
|
27540
|
-
index_message_replies=payload_repo.get(
|
|
27541
|
-
"index_message_replies",
|
|
27542
|
-
True,
|
|
27543
|
-
),
|
|
27544
|
-
index_user_chats=payload_repo.get("index_user_chats", True),
|
|
27545
|
-
)
|
|
27546
|
-
elif payload_repo["type"] == "SharePoint":
|
|
27547
|
-
repository = self._avts.create_sharepoint_repo(
|
|
27548
|
-
name=payload_repo["name"],
|
|
27549
|
-
client_id=payload_repo["client_id"],
|
|
27550
|
-
tenant_id=payload_repo["tenant_id"],
|
|
27551
|
-
certificate_file=payload_repo["certificate_file"],
|
|
27552
|
-
certificate_password=payload_repo["certificate_password"],
|
|
27553
|
-
sharepoint_url=payload_repo["sharepoint_url"],
|
|
27554
|
-
sharepoint_url_type=payload_repo["sharepoint_url_type"],
|
|
27555
|
-
sharepoint_mysite_url=payload_repo["sharepoint_mysite_url"],
|
|
27556
|
-
sharepoint_admin_url=payload_repo["sharepoint_admin_url"],
|
|
27557
|
-
index_user_profiles=payload_repo.get(
|
|
27558
|
-
"index_message_replies",
|
|
27559
|
-
False,
|
|
27560
|
-
),
|
|
27561
|
-
)
|
|
27562
|
-
else:
|
|
27563
|
-
self.logger.error(
|
|
27564
|
-
"Invalid repository type -> '%s' specified. Valid values are: Extended ECM, Documentum, MSTeams, SharePoint",
|
|
27565
|
-
payload_repo["type"],
|
|
27566
|
-
)
|
|
27567
|
-
success = False
|
|
27568
|
-
break
|
|
27704
|
+
repository = self._avts.get_repo_by_name(name=payload_repo["name"])
|
|
27569
27705
|
|
|
27570
27706
|
if repository is None:
|
|
27571
|
-
self.logger.error(
|
|
27572
|
-
"Creation of Aviator Search repository -> '%s' failed!",
|
|
27573
|
-
payload_repo["name"],
|
|
27574
|
-
)
|
|
27575
|
-
success = False
|
|
27576
|
-
else:
|
|
27577
27707
|
self.logger.info(
|
|
27578
|
-
"
|
|
27708
|
+
"Repository -> '%s' does not exist, creating it...",
|
|
27579
27709
|
payload_repo["name"],
|
|
27580
27710
|
)
|
|
27581
|
-
self.logger.debug("%s", repository)
|
|
27582
27711
|
|
|
27583
|
-
|
|
27584
|
-
|
|
27585
|
-
|
|
27586
|
-
|
|
27587
|
-
|
|
27712
|
+
if payload_repo.get("type", "Extended ECM") == "Extended ECM":
|
|
27713
|
+
repository = self._avts.create_extended_ecm_repo(
|
|
27714
|
+
name=payload_repo["name"],
|
|
27715
|
+
username=payload_repo["username"],
|
|
27716
|
+
password=payload_repo["password"],
|
|
27717
|
+
otcs_url=payload_repo["otcs_url"],
|
|
27718
|
+
otcs_api_url=payload_repo["otcs_api_url"],
|
|
27719
|
+
node_id=int(payload_repo["node_id"]),
|
|
27720
|
+
)
|
|
27588
27721
|
|
|
27589
|
-
|
|
27590
|
-
|
|
27722
|
+
elif payload_repo["type"] == "Documentum":
|
|
27723
|
+
self.logger.warning("Not yet implemented")
|
|
27724
|
+
elif payload_repo["type"] == "MSTeams":
|
|
27725
|
+
repository = self._avts.create_msteams_repo(
|
|
27726
|
+
name=payload_repo["name"],
|
|
27727
|
+
client_id=payload_repo["client_id"],
|
|
27728
|
+
tenant_id=payload_repo["tenant_id"],
|
|
27729
|
+
certificate_file=payload_repo["certificate_file"],
|
|
27730
|
+
certificate_password=payload_repo["certificate_password"],
|
|
27731
|
+
index_attachments=payload_repo.get("index_attachments", True),
|
|
27732
|
+
index_call_recordings=payload_repo.get(
|
|
27733
|
+
"index_call_recordings",
|
|
27734
|
+
True,
|
|
27735
|
+
),
|
|
27736
|
+
index_message_replies=payload_repo.get(
|
|
27737
|
+
"index_message_replies",
|
|
27738
|
+
True,
|
|
27739
|
+
),
|
|
27740
|
+
index_user_chats=payload_repo.get("index_user_chats", True),
|
|
27741
|
+
)
|
|
27742
|
+
elif payload_repo["type"] == "SharePoint":
|
|
27743
|
+
repository = self._avts.create_sharepoint_repo(
|
|
27744
|
+
name=payload_repo["name"],
|
|
27745
|
+
client_id=payload_repo["client_id"],
|
|
27746
|
+
tenant_id=payload_repo["tenant_id"],
|
|
27747
|
+
certificate_file=payload_repo["certificate_file"],
|
|
27748
|
+
certificate_password=payload_repo["certificate_password"],
|
|
27749
|
+
sharepoint_url=payload_repo["sharepoint_url"],
|
|
27750
|
+
sharepoint_url_type=payload_repo["sharepoint_url_type"],
|
|
27751
|
+
sharepoint_mysite_url=payload_repo["sharepoint_mysite_url"],
|
|
27752
|
+
sharepoint_admin_url=payload_repo["sharepoint_admin_url"],
|
|
27753
|
+
index_user_profiles=payload_repo.get(
|
|
27754
|
+
"index_message_replies",
|
|
27755
|
+
False,
|
|
27756
|
+
),
|
|
27757
|
+
)
|
|
27758
|
+
else:
|
|
27759
|
+
self.logger.error(
|
|
27760
|
+
"Invalid repository type -> '%s' specified. Valid values are: Extended ECM, Documentum, MSTeams, SharePoint",
|
|
27761
|
+
payload_repo["type"],
|
|
27762
|
+
)
|
|
27763
|
+
success = False
|
|
27764
|
+
break
|
|
27591
27765
|
|
|
27592
|
-
|
|
27593
|
-
|
|
27766
|
+
if repository is None:
|
|
27767
|
+
self.logger.error(
|
|
27768
|
+
"Creation of Aviator Search repository -> '%s' failed!",
|
|
27769
|
+
payload_repo["name"],
|
|
27770
|
+
)
|
|
27771
|
+
success = False
|
|
27772
|
+
else:
|
|
27773
|
+
self.logger.info(
|
|
27774
|
+
"Successfully created Aviator Search repository -> '%s'",
|
|
27775
|
+
payload_repo["name"],
|
|
27776
|
+
)
|
|
27777
|
+
self.logger.debug("%s", repository)
|
|
27594
27778
|
|
|
27595
|
-
if response is None:
|
|
27596
|
-
self.logger.error(
|
|
27597
|
-
"Aviator Search start crawling on repository failed -> '%s'",
|
|
27598
|
-
payload_repo["name"],
|
|
27599
|
-
)
|
|
27600
|
-
success = False
|
|
27601
27779
|
else:
|
|
27602
27780
|
self.logger.info(
|
|
27603
|
-
"Aviator Search
|
|
27781
|
+
"Aviator Search repository -> '%s' already exists.",
|
|
27604
27782
|
payload_repo["name"],
|
|
27605
27783
|
)
|
|
27606
|
-
|
|
27784
|
+
|
|
27785
|
+
# Start Crawling
|
|
27786
|
+
start_crawling = payload_repo.get("start", False)
|
|
27787
|
+
|
|
27788
|
+
if repository is not None and start_crawling:
|
|
27789
|
+
response = self._avts.start_crawling(repo_name=payload_repo["name"])
|
|
27790
|
+
|
|
27791
|
+
if response is None:
|
|
27792
|
+
self.logger.error(
|
|
27793
|
+
"Aviator Search start crawling on repository failed -> '%s'",
|
|
27794
|
+
payload_repo["name"],
|
|
27795
|
+
)
|
|
27796
|
+
success = False
|
|
27797
|
+
else:
|
|
27798
|
+
self.logger.info(
|
|
27799
|
+
"Aviator Search crawling started on repository -> '%s'",
|
|
27800
|
+
payload_repo["name"],
|
|
27801
|
+
)
|
|
27802
|
+
self.logger.debug("%s", response)
|
|
27803
|
+
# end for payload_repo in self._avts_repositories:
|
|
27804
|
+
# end else:
|
|
27607
27805
|
|
|
27608
27806
|
self.write_status_file(
|
|
27609
27807
|
success=success,
|
|
@@ -27646,26 +27844,28 @@ class Payload:
|
|
|
27646
27844
|
|
|
27647
27845
|
success: bool = True
|
|
27648
27846
|
|
|
27649
|
-
self._avts.authenticate()
|
|
27650
|
-
|
|
27651
27847
|
if not self._avts_questions.get("enabled", True):
|
|
27652
27848
|
self.logger.info(
|
|
27653
27849
|
"Payload section -> '%s' is not enabled. Skipping...",
|
|
27654
27850
|
section_name,
|
|
27655
27851
|
)
|
|
27656
27852
|
return True
|
|
27657
|
-
|
|
27658
27853
|
questions = self._avts_questions.get("questions", [])
|
|
27659
27854
|
self.logger.info("Sample questions -> %s", questions)
|
|
27660
27855
|
|
|
27661
|
-
|
|
27662
|
-
|
|
27663
|
-
|
|
27664
|
-
self.logger.error("Aviator Search setting questions failed")
|
|
27856
|
+
token = self._avts.authenticate()
|
|
27857
|
+
if not token:
|
|
27858
|
+
self.logger.error("Cannot authenticate at Aviator Search!")
|
|
27665
27859
|
success = False
|
|
27666
27860
|
else:
|
|
27667
|
-
self.
|
|
27668
|
-
|
|
27861
|
+
response = self._avts.set_questions(questions=questions)
|
|
27862
|
+
|
|
27863
|
+
if response is None:
|
|
27864
|
+
self.logger.error("Aviator Search setting questions failed")
|
|
27865
|
+
success = False
|
|
27866
|
+
else:
|
|
27867
|
+
self.logger.info("Aviator Search questions set succesfully")
|
|
27868
|
+
self.logger.debug("%s", response)
|
|
27669
27869
|
|
|
27670
27870
|
self.write_status_file(
|
|
27671
27871
|
success=success,
|
|
@@ -29126,7 +29326,7 @@ class Payload:
|
|
|
29126
29326
|
if response and response["results"]:
|
|
29127
29327
|
# We add the suffix with the key which should be unique:
|
|
29128
29328
|
self.logger.warning(
|
|
29129
|
-
"Classification
|
|
29329
|
+
"Classification -> '%s' does already exist in folder with ID -> %s and we need to handle the name clash by using name -> '%s'",
|
|
29130
29330
|
classification_name,
|
|
29131
29331
|
parent_id,
|
|
29132
29332
|
classification_name + " (" + key + ")",
|
|
@@ -29457,3 +29657,160 @@ class Payload:
|
|
|
29457
29657
|
return bool(response)
|
|
29458
29658
|
|
|
29459
29659
|
# end method definition
|
|
29660
|
+
|
|
29661
|
+
def process_nifi_flows(self, section_name: str = "nifi") -> bool:
|
|
29662
|
+
"""Process Knowledge Discovery Nifi flows in payload and create them in Nifi.
|
|
29663
|
+
|
|
29664
|
+
Args:
|
|
29665
|
+
section_name (str, optional):
|
|
29666
|
+
The name of the payload section. It can be overridden
|
|
29667
|
+
for cases where multiple sections of same type
|
|
29668
|
+
are used (e.g. the "Post" sections).
|
|
29669
|
+
This name is also used for the "success" status
|
|
29670
|
+
files written to the Admin Personal Workspace.
|
|
29671
|
+
|
|
29672
|
+
Returns:
|
|
29673
|
+
bool:
|
|
29674
|
+
True, if payload has been processed without errors, False otherwise
|
|
29675
|
+
|
|
29676
|
+
"""
|
|
29677
|
+
|
|
29678
|
+
if not self._nifi_flows:
|
|
29679
|
+
self.logger.info(
|
|
29680
|
+
"Payload section -> '%s' is empty. Skipping...",
|
|
29681
|
+
section_name,
|
|
29682
|
+
)
|
|
29683
|
+
return True
|
|
29684
|
+
|
|
29685
|
+
# If this payload section has been processed successfully before we
|
|
29686
|
+
# can return True and skip processing it once more:
|
|
29687
|
+
if self.check_status_file(payload_section_name=section_name):
|
|
29688
|
+
return True
|
|
29689
|
+
|
|
29690
|
+
success: bool = True
|
|
29691
|
+
|
|
29692
|
+
for nifi_flow in self._nifi_flows:
|
|
29693
|
+
if "name" not in nifi_flow:
|
|
29694
|
+
self.logger.error(
|
|
29695
|
+
"Knowledge Discovery Nifi flow needs a name! Skipping to next Nifi flow...",
|
|
29696
|
+
)
|
|
29697
|
+
success = False
|
|
29698
|
+
continue
|
|
29699
|
+
name = nifi_flow["name"]
|
|
29700
|
+
|
|
29701
|
+
# Check if element has been disabled in payload (enabled = false).
|
|
29702
|
+
# In this case we skip the element:
|
|
29703
|
+
if not nifi_flow.get("enabled", True):
|
|
29704
|
+
self.logger.info(
|
|
29705
|
+
"Payload for Knowledge Discovery Nifi flow -> '%s' is disabled. Skipping...",
|
|
29706
|
+
name,
|
|
29707
|
+
)
|
|
29708
|
+
continue
|
|
29709
|
+
|
|
29710
|
+
if "file" not in nifi_flow:
|
|
29711
|
+
self.logger.error(
|
|
29712
|
+
"Knowledge Discovery Nifi flow -> '%s' needs a file! Skipping to next Nifi flow...", name
|
|
29713
|
+
)
|
|
29714
|
+
success = False
|
|
29715
|
+
continue
|
|
29716
|
+
filename = nifi_flow["file"]
|
|
29717
|
+
|
|
29718
|
+
parameters = nifi_flow.get("parameters", [])
|
|
29719
|
+
|
|
29720
|
+
if not self._otkd:
|
|
29721
|
+
self.logger.error("Knowledge Discovery is not initialized. Stop processing Nifi flows.")
|
|
29722
|
+
success = False
|
|
29723
|
+
break
|
|
29724
|
+
|
|
29725
|
+
# Optional layout positions of the flow:
|
|
29726
|
+
position_x = nifi_flow.get("position_x", 0.0)
|
|
29727
|
+
position_y = nifi_flow.get("position_y", 0.0)
|
|
29728
|
+
start = nifi_flow.get("start", False)
|
|
29729
|
+
|
|
29730
|
+
self.logger.info("Processing Knowledge Discovery Nifi flow -> '%s'...", name)
|
|
29731
|
+
|
|
29732
|
+
existing = self._otkd.get_process_group_by_name(name=name)
|
|
29733
|
+
if existing:
|
|
29734
|
+
self.logger.warning("Nifi flow -> '%s' does already exist. Updating parameters only...", name)
|
|
29735
|
+
# We better don't start existing flows!? Otherwise this may produce errors.
|
|
29736
|
+
start = False
|
|
29737
|
+
else:
|
|
29738
|
+
response = self._otkd.upload_process_group(
|
|
29739
|
+
file_path=filename, name=name, position_x=position_x, position_y=position_y
|
|
29740
|
+
)
|
|
29741
|
+
if not response:
|
|
29742
|
+
self.logger.error("Failed to upload new Nifi flow -> '%s' for Knowledge Discovery!", name)
|
|
29743
|
+
success = False
|
|
29744
|
+
continue
|
|
29745
|
+
self.logger.info("Sucessfully uploaded new Nifi flow -> '%s' for Knowledge Discovery!", name)
|
|
29746
|
+
|
|
29747
|
+
for parameter in parameters:
|
|
29748
|
+
component = parameter.get("component", None)
|
|
29749
|
+
if not component:
|
|
29750
|
+
self.logger.error("Missing component in parameter of Nifi flow -> '%s'!", name)
|
|
29751
|
+
success = False
|
|
29752
|
+
continue
|
|
29753
|
+
parameter_name = parameter.get("name", None)
|
|
29754
|
+
if not parameter_name:
|
|
29755
|
+
self.logger.error(
|
|
29756
|
+
"Missing name in parameter of Nifi flow -> '%s', component -> '%s'!", name, component
|
|
29757
|
+
)
|
|
29758
|
+
success = False
|
|
29759
|
+
continue
|
|
29760
|
+
parameter_description = parameter.get("description", "")
|
|
29761
|
+
parameter_value = parameter.get("value", None)
|
|
29762
|
+
if not parameter_value:
|
|
29763
|
+
self.logger.error(
|
|
29764
|
+
"Missing value in parameter of Nifi flow -> '%s', component -> '%s'", name, component
|
|
29765
|
+
)
|
|
29766
|
+
success = False
|
|
29767
|
+
continue
|
|
29768
|
+
parameter_sensitive = parameter.get("sensitive", False)
|
|
29769
|
+
|
|
29770
|
+
response = self._otkd.update_parameter(
|
|
29771
|
+
component=component,
|
|
29772
|
+
parameter=parameter_name,
|
|
29773
|
+
value=parameter_value,
|
|
29774
|
+
sensitive=parameter_sensitive,
|
|
29775
|
+
description=parameter_description,
|
|
29776
|
+
)
|
|
29777
|
+
if not response:
|
|
29778
|
+
self.logger.error("Failed to update parameter -> '%s' of Nifi flow -> '%s'!", parameter_name, name)
|
|
29779
|
+
success = False
|
|
29780
|
+
continue
|
|
29781
|
+
self.logger.info(
|
|
29782
|
+
"Successfully updated parameter -> '%s' of component -> '%s' in Nifi flow -> '%s' to value -> '%s'.",
|
|
29783
|
+
parameter_name,
|
|
29784
|
+
component,
|
|
29785
|
+
name,
|
|
29786
|
+
parameter_value if not parameter_sensitive else "<sensitive>",
|
|
29787
|
+
)
|
|
29788
|
+
# end for parameter in parameters:
|
|
29789
|
+
if start:
|
|
29790
|
+
response = self._otkd.start_all_processors(name=name)
|
|
29791
|
+
if response:
|
|
29792
|
+
self.logger.info("Successfully started Nifi flow -> '%s'.", name)
|
|
29793
|
+
else:
|
|
29794
|
+
self.logger.error("Failed to start Nifi flow -> '%s'!", name)
|
|
29795
|
+
success = False
|
|
29796
|
+
|
|
29797
|
+
response = self._otkd.set_controller_services_state(name=name, state="ENABLED")
|
|
29798
|
+
if response:
|
|
29799
|
+
self.logger.info("Successfully enabled Nifi Controller Services for Nifi flow -> '%s'.", name)
|
|
29800
|
+
else:
|
|
29801
|
+
self.logger.error("Failed to enable Nifi Controller Services for Nifi flow -> '%s'!", name)
|
|
29802
|
+
success = False
|
|
29803
|
+
|
|
29804
|
+
else:
|
|
29805
|
+
self.logger.info("Don't (re)start Nifi flow -> '%s'.", name)
|
|
29806
|
+
# end for nifi_flow in self._nifi_flows:
|
|
29807
|
+
|
|
29808
|
+
self.write_status_file(
|
|
29809
|
+
success=success,
|
|
29810
|
+
payload_section_name=section_name,
|
|
29811
|
+
payload_section=self._nifi_flows,
|
|
29812
|
+
)
|
|
29813
|
+
|
|
29814
|
+
return success
|
|
29815
|
+
|
|
29816
|
+
# end method definition
|