launchpy 0.4.5.post6__tar.gz → 0.4.6__tar.gz
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.
- {launchpy-0.4.5.post6/launchpy.egg-info → launchpy-0.4.6}/PKG-INFO +1 -1
- {launchpy-0.4.5.post6 → launchpy-0.4.6}/docs/releases.md +3 -0
- {launchpy-0.4.5.post6 → launchpy-0.4.6}/docs/synchronizer.md +52 -3
- launchpy-0.4.6/launchpy/__version__.py +1 -0
- {launchpy-0.4.5.post6 → launchpy-0.4.6}/launchpy/connector.py +6 -6
- {launchpy-0.4.5.post6 → launchpy-0.4.6}/launchpy/launchpy.py +20 -4
- {launchpy-0.4.5.post6 → launchpy-0.4.6}/launchpy/synchronizer.py +23 -12
- {launchpy-0.4.5.post6 → launchpy-0.4.6/launchpy.egg-info}/PKG-INFO +1 -1
- launchpy-0.4.5.post6/launchpy/__version__.py +0 -1
- {launchpy-0.4.5.post6 → launchpy-0.4.6}/LICENSE +0 -0
- {launchpy-0.4.5.post6 → launchpy-0.4.6}/README.md +0 -0
- {launchpy-0.4.5.post6 → launchpy-0.4.6}/docs/admin.md +0 -0
- {launchpy-0.4.5.post6 → launchpy-0.4.6}/docs/getstarted.md +0 -0
- {launchpy-0.4.5.post6 → launchpy-0.4.6}/docs/library.md +0 -0
- {launchpy-0.4.5.post6 → launchpy-0.4.6}/docs/main.md +0 -0
- {launchpy-0.4.5.post6 → launchpy-0.4.6}/docs/property.md +0 -0
- {launchpy-0.4.5.post6 → launchpy-0.4.6}/docs/translator.md +0 -0
- {launchpy-0.4.5.post6 → launchpy-0.4.6}/launchpy/__init__.py +0 -0
- {launchpy-0.4.5.post6 → launchpy-0.4.6}/launchpy/admin.py +0 -0
- {launchpy-0.4.5.post6 → launchpy-0.4.6}/launchpy/config.py +0 -0
- {launchpy-0.4.5.post6 → launchpy-0.4.6}/launchpy/configs.py +0 -0
- {launchpy-0.4.5.post6 → launchpy-0.4.6}/launchpy/library.py +0 -0
- {launchpy-0.4.5.post6 → launchpy-0.4.6}/launchpy/property.py +0 -0
- {launchpy-0.4.5.post6 → launchpy-0.4.6}/launchpy.egg-info/SOURCES.txt +0 -0
- {launchpy-0.4.5.post6 → launchpy-0.4.6}/launchpy.egg-info/dependency_links.txt +0 -0
- {launchpy-0.4.5.post6 → launchpy-0.4.6}/launchpy.egg-info/requires.txt +0 -0
- {launchpy-0.4.5.post6 → launchpy-0.4.6}/launchpy.egg-info/top_level.txt +0 -0
- {launchpy-0.4.5.post6 → launchpy-0.4.6}/pyproject.toml +0 -0
- {launchpy-0.4.5.post6 → launchpy-0.4.6}/requirements.txt +0 -0
- {launchpy-0.4.5.post6 → launchpy-0.4.6}/setup.cfg +0 -0
- {launchpy-0.4.5.post6 → launchpy-0.4.6}/setup.py +0 -0
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
This page gathered the changes made between version of the launchpy module.\
|
|
4
4
|
This has been started after the 0.3.0 release.\
|
|
5
5
|
|
|
6
|
+
## 0.4.6
|
|
7
|
+
* Allow Synchronizer to be used in multi IMS organization setup.
|
|
8
|
+
|
|
6
9
|
## 0.4.5
|
|
7
10
|
* adding `getExtension` in `Property` class
|
|
8
11
|
* improve `upgradeExtension` in `Property` class
|
|
@@ -13,7 +13,6 @@ The Synchronizer will allow you to specify a template property and target proper
|
|
|
13
13
|
This class is currently requiring the following elements on your side:
|
|
14
14
|
|
|
15
15
|
* You have imported the config file (via `importConfigFile`) or configure your module via `configure` method.
|
|
16
|
-
* Your template property and target properties are located in the same organization
|
|
17
16
|
* You have unique name for your rule and data elements
|
|
18
17
|
* You have the same extension installed in both the template and target properties.\
|
|
19
18
|
The Synchronizer will not sync the extensions configuration.
|
|
@@ -24,8 +23,8 @@ If a rule and a data element have the same name, by passing that name in the `sy
|
|
|
24
23
|
|
|
25
24
|
In order to instantiate the `Synchronizer` class, you will need to provide 2 elements:
|
|
26
25
|
|
|
27
|
-
* The name of the base property
|
|
28
|
-
* The names of the target properties (as a list of strings)
|
|
26
|
+
* The name of the base property, as `string` or `launchpy.Property` instance
|
|
27
|
+
* The names of the target properties (as a list of `strings`, or `launchpy.Property` instances)
|
|
29
28
|
|
|
30
29
|
Example:
|
|
31
30
|
|
|
@@ -38,6 +37,56 @@ synchronizor = lp.Synchronizer(base='Prop1',targets=['Prop2'])
|
|
|
38
37
|
|
|
39
38
|
```
|
|
40
39
|
|
|
40
|
+
### Multi Organization environments
|
|
41
|
+
|
|
42
|
+
For some clients, they may want to synchronize the rules and data elements across organization setup.\
|
|
43
|
+
That is possible, but the requirements are still needed regarding the following elements:
|
|
44
|
+
* You have unique name for your rule and data elements
|
|
45
|
+
* You have the same extension installed in both the template and target properties.
|
|
46
|
+
|
|
47
|
+
The synchronizer will use the environment credential lastly loaded in the system.\
|
|
48
|
+
We would recommend to load first the target organization that you want to sync to, and then the base organization.\
|
|
49
|
+
You will be able to use the Property instance of the target organization in order to sync the element afterwards.
|
|
50
|
+
|
|
51
|
+
Example of setup
|
|
52
|
+
|
|
53
|
+
```py
|
|
54
|
+
import launchpy as lp
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
### targeted org
|
|
58
|
+
lp.importConfigFile('config_target.json')
|
|
59
|
+
admin_target = lp.Admin()
|
|
60
|
+
targetId = admin_target.getCompanyId()
|
|
61
|
+
target_props = admin_target.getProperties(targetId)
|
|
62
|
+
|
|
63
|
+
target_prop = lp.Property(target_props[1]) ## selecting one of the property
|
|
64
|
+
|
|
65
|
+
### Source org for base
|
|
66
|
+
lp.importConfigFile('config_source.json')
|
|
67
|
+
admin = lp.Admin()
|
|
68
|
+
sourceId = admin_target.getCompanyId()
|
|
69
|
+
source_props = admin.getProperties(sourceId)
|
|
70
|
+
|
|
71
|
+
base = lp.Property(source_props[0]) ### selecting the first one as base - it can be any
|
|
72
|
+
|
|
73
|
+
synchronizor = lp.Synchronizer(base=base,targets=['another Property Name to sync',target_prop])
|
|
74
|
+
|
|
75
|
+
### rest is as usual
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
If you have private extension that are not released between organization, you can pass a mapping to the synchronizer to tell that these extensions are to be treated as one.\
|
|
79
|
+
You would need to pass the target extension name and the base extension name so that it is mapped as a single one.
|
|
80
|
+
|
|
81
|
+
Example:
|
|
82
|
+
```py
|
|
83
|
+
|
|
84
|
+
synchronizor = lp.Synchronizer(base=base,targets=['another Property Name to sync',target_prop],mapping_extensions={'extension_name_target':'extension_name_base'})
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
### Additional options
|
|
41
90
|
There is a possibility to setup some rules so you can filter when migrating from one property to another. See [dynamic Filtering](#dynamic-component-filter)
|
|
42
91
|
|
|
43
92
|
## SyncComponent Method
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.4.6"
|
|
@@ -31,7 +31,7 @@ class AdobeRequest:
|
|
|
31
31
|
raise Exception(
|
|
32
32
|
'You have to upload the configuration file with importConfigFile method.')
|
|
33
33
|
self.config = deepcopy(config_object)
|
|
34
|
-
self.header = header
|
|
34
|
+
self.header = deepcopy(header)
|
|
35
35
|
self.retry = retry
|
|
36
36
|
if self.config['token'] == '' or time.time() > self.config['date_limit']:
|
|
37
37
|
if 'scopes' in self.config.keys() and self.config.get('scopes',None) is not None:
|
|
@@ -42,9 +42,9 @@ class AdobeRequest:
|
|
|
42
42
|
token_and_expiry = self.get_jwt_token_and_expiry_for_config(config=self.config, verbose=verbose)
|
|
43
43
|
token = token_and_expiry['token']
|
|
44
44
|
expiry = token_and_expiry['expiry']
|
|
45
|
-
self.token = token
|
|
46
|
-
self.config['token'] = token
|
|
47
|
-
self.config['date_limit'] = time.time() + expiry - 500
|
|
45
|
+
self.token = deepcopy(token)
|
|
46
|
+
self.config['token'] = deepcopy(token)
|
|
47
|
+
self.config['date_limit'] = deepcopy(time.time() + expiry - 500)
|
|
48
48
|
self.header.update({'Authorization': f'Bearer {token}'})
|
|
49
49
|
|
|
50
50
|
def get_jwt_token_and_expiry_for_config(self,config: dict, verbose: bool = False, save: bool = False, *args, **kwargs) -> Dict[str, str]:
|
|
@@ -166,8 +166,8 @@ class AdobeRequest:
|
|
|
166
166
|
elif self.connectionType == 'jwt':
|
|
167
167
|
token_and_expiry = self.get_jwt_token_and_expiry_for_config(config=self.config)
|
|
168
168
|
token = token_and_expiry['token']
|
|
169
|
-
self.config['token'] = token
|
|
170
|
-
self.config['date_limit'] = time.time() + token_and_expiry['expiry'] - 500
|
|
169
|
+
self.config['token'] = deepcopy(token)
|
|
170
|
+
self.config['date_limit'] = deepcopy(time.time() + token_and_expiry['expiry'] - 500)
|
|
171
171
|
self.header.update({'Authorization': f'Bearer {token}'})
|
|
172
172
|
|
|
173
173
|
def getData(self, endpoint: str, params: dict = None, data: dict = None, headers: dict = None, *args, **kwargs):
|
|
@@ -405,14 +405,23 @@ class Translator:
|
|
|
405
405
|
--> You can use the translate method to translate Data Element settings
|
|
406
406
|
3. setBaseRules
|
|
407
407
|
4. extendRules
|
|
408
|
-
--> You can use the translate method to translate Rule Components settings
|
|
408
|
+
--> You can use the translate method to translate Rule Components settings
|
|
409
|
+
|
|
410
|
+
Possible arguments:
|
|
411
|
+
mapping_extensions : dictionary of extension names that should be replaced.
|
|
412
|
+
Such as: {'old_name':'new_name'}
|
|
409
413
|
"""
|
|
410
414
|
|
|
411
|
-
def __init__(self):
|
|
415
|
+
def __init__(self,**kwargs):
|
|
412
416
|
self.rules = {}
|
|
413
417
|
self.baseRuleIdName = {}
|
|
414
418
|
self.extensions = {}
|
|
415
419
|
self.baseExtensionIdName = {}
|
|
420
|
+
self.mapping_extension = {}
|
|
421
|
+
if kwargs.get('mapping_extensions') is not None:
|
|
422
|
+
if type(kwargs.get('mapping_extensions')) == dict:
|
|
423
|
+
for key, value in kwargs.get('mapping_extensions').items():
|
|
424
|
+
self.mapping_extension[key] = value
|
|
416
425
|
|
|
417
426
|
def setBaseExtensions(self, base_property_extensions: list, property_name: str):
|
|
418
427
|
"""
|
|
@@ -422,8 +431,12 @@ class Translator:
|
|
|
422
431
|
property_name : REQUIRED : name of your base property.
|
|
423
432
|
"""
|
|
424
433
|
self.baseExtensionIdName = {ext['id'] : ext['attributes']['name'] for ext in base_property_extensions}
|
|
434
|
+
for extId, extName in self.baseExtensionIdName.items():
|
|
435
|
+
if extName in self.mapping_extension.keys():
|
|
436
|
+
self.baseExtensionIdName[extId] = self.mapping_extension[extName]
|
|
425
437
|
self.extensions = {ext['attributes']['name']:{property_name:ext['id']} for ext in base_property_extensions}
|
|
426
438
|
|
|
439
|
+
|
|
427
440
|
def extendExtensions(self, new_property_extensions: list, new_prop_name: str)-> None:
|
|
428
441
|
"""
|
|
429
442
|
Add the extensions id from a target property.
|
|
@@ -432,8 +445,11 @@ class Translator:
|
|
|
432
445
|
new_prop_name : REQUIRED : target property name.
|
|
433
446
|
"""
|
|
434
447
|
for ext in new_property_extensions:
|
|
435
|
-
|
|
436
|
-
|
|
448
|
+
extName = ext['attributes']['name']
|
|
449
|
+
if extName in self.mapping_extension.keys():
|
|
450
|
+
extName = self.mapping_extension[extName]
|
|
451
|
+
if extName in list(self.extensions.keys()):
|
|
452
|
+
self.extensions[extName][new_prop_name] = ext['id']
|
|
437
453
|
return self.extensions
|
|
438
454
|
|
|
439
455
|
def setBaseRules(self, base_property_rules: list, property_name: str):
|
|
@@ -21,37 +21,48 @@ class Synchronizer:
|
|
|
21
21
|
It requires that you have imported a configuration file.
|
|
22
22
|
"""
|
|
23
23
|
|
|
24
|
-
def __init__(self,base:str=None,targets:list=None,**kwargs)->None:
|
|
24
|
+
def __init__(self,base:str | Property = None,targets:list=None,**kwargs)->None:
|
|
25
25
|
"""
|
|
26
26
|
Instantiating the Synchronizer taking 2 parameters, base property name, target property list.
|
|
27
27
|
Arguments:
|
|
28
|
-
base : REQUIRED : The name of your base/template property
|
|
28
|
+
base : REQUIRED : The name of your base/template property or the instance of Property class
|
|
29
29
|
targets : REQUIRED : The list of property names that you want to sync with.
|
|
30
|
+
the list of target can be string of property name or Property instance for other organization.
|
|
30
31
|
possible kwargs:
|
|
31
32
|
dynamicRuleComponent: A data element name that contains rule for synchronization on the property.
|
|
33
|
+
mapping_extensions : A dictionary of target extension name and base extension name when in different IMS org and 2 private extensions are name differently for the same purpose.
|
|
32
34
|
"""
|
|
33
35
|
tmp_admin = Admin()
|
|
34
36
|
cid = tmp_admin.getCompanyId()
|
|
35
37
|
properties = tmp_admin.getProperties(cid)
|
|
36
|
-
|
|
37
|
-
if len(base_property) ==0:
|
|
38
|
-
raise KeyError("The base property name has not been found in your account")
|
|
38
|
+
mapping_extensions = kwargs.get('mapping_extensions',None)
|
|
39
39
|
self.base = {}
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
if type(base) == str:
|
|
41
|
+
base_property = [prop for prop in properties if prop['attributes']['name'] == base]
|
|
42
|
+
if len(base_property) ==0:
|
|
43
|
+
raise KeyError("The base property name has not been found in your account")
|
|
44
|
+
self.base["name"] = base
|
|
45
|
+
self.base["api"]:Property = Property(base_property[0])
|
|
46
|
+
elif type(base) == Property:
|
|
47
|
+
self.base["name"] = base.name
|
|
48
|
+
self.base["api"]:Property = base
|
|
42
49
|
self.base['rules'] = self.base['api'].getRules()
|
|
43
50
|
self.base['dataElements'] = self.base['api'].getDataElements()
|
|
44
51
|
self.base['extensions'] = self.base['api'].getExtensions()
|
|
45
|
-
self.translator = Translator()
|
|
52
|
+
self.translator = Translator(mapping_extensions=mapping_extensions)
|
|
46
53
|
self.translator.setBaseExtensions(self.base['extensions'],self.base['name'])
|
|
47
54
|
self.translator.setBaseRules(self.base['rules'],self.base['name'])
|
|
48
55
|
self.targets = {}
|
|
49
56
|
self.target_configs = {}
|
|
50
57
|
for target in targets:
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
58
|
+
if isinstance(target,str):
|
|
59
|
+
tmp_target = [prop for prop in properties if prop['attributes']['name'] == target]
|
|
60
|
+
if len(tmp_target) == 0:
|
|
61
|
+
raise KeyError(f"The target property : {target} cannot be found. Please, fix it")
|
|
62
|
+
self.targets[target] = {'api' : Property(tmp_target[0]),'name':target}
|
|
63
|
+
elif isinstance(target,Property):
|
|
64
|
+
self.targets[target.name] = {'api' : deepcopy(target),'name':target.name}
|
|
65
|
+
target = target.name
|
|
55
66
|
self.targets[target]['rules'] = self.targets[target]['api'].getRules()
|
|
56
67
|
self.targets[target]['extensions'] = self.targets[target]['api'].getExtensions()
|
|
57
68
|
self.targets[target]['dataElements'] = self.targets[target]['api'].getDataElements()
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.4.5-6"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|