launchpy 0.4.4.post3__tar.gz → 0.4.5.post2__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.4.post3/launchpy.egg-info → launchpy-0.4.5.post2}/PKG-INFO +2 -2
- {launchpy-0.4.4.post3 → launchpy-0.4.5.post2}/docs/releases.md +11 -0
- {launchpy-0.4.4.post3 → launchpy-0.4.5.post2}/docs/synchronizer.md +53 -21
- launchpy-0.4.5.post2/launchpy/__version__.py +1 -0
- {launchpy-0.4.4.post3 → launchpy-0.4.5.post2}/launchpy/configs.py +6 -4
- {launchpy-0.4.4.post3 → launchpy-0.4.5.post2}/launchpy/property.py +98 -28
- {launchpy-0.4.4.post3 → launchpy-0.4.5.post2}/launchpy/synchronizer.py +187 -113
- {launchpy-0.4.4.post3 → launchpy-0.4.5.post2/launchpy.egg-info}/PKG-INFO +2 -2
- {launchpy-0.4.4.post3 → launchpy-0.4.5.post2}/requirements.txt +3 -1
- {launchpy-0.4.4.post3 → launchpy-0.4.5.post2}/setup.py +2 -2
- launchpy-0.4.4.post3/launchpy/__version__.py +0 -1
- {launchpy-0.4.4.post3 → launchpy-0.4.5.post2}/LICENSE +0 -0
- {launchpy-0.4.4.post3 → launchpy-0.4.5.post2}/README.md +0 -0
- {launchpy-0.4.4.post3 → launchpy-0.4.5.post2}/docs/admin.md +0 -0
- {launchpy-0.4.4.post3 → launchpy-0.4.5.post2}/docs/getstarted.md +0 -0
- {launchpy-0.4.4.post3 → launchpy-0.4.5.post2}/docs/library.md +0 -0
- {launchpy-0.4.4.post3 → launchpy-0.4.5.post2}/docs/main.md +0 -0
- {launchpy-0.4.4.post3 → launchpy-0.4.5.post2}/docs/property.md +0 -0
- {launchpy-0.4.4.post3 → launchpy-0.4.5.post2}/docs/translator.md +0 -0
- {launchpy-0.4.4.post3 → launchpy-0.4.5.post2}/launchpy/__init__.py +0 -0
- {launchpy-0.4.4.post3 → launchpy-0.4.5.post2}/launchpy/admin.py +0 -0
- {launchpy-0.4.4.post3 → launchpy-0.4.5.post2}/launchpy/config.py +0 -0
- {launchpy-0.4.4.post3 → launchpy-0.4.5.post2}/launchpy/connector.py +0 -0
- {launchpy-0.4.4.post3 → launchpy-0.4.5.post2}/launchpy/launchpy.py +0 -0
- {launchpy-0.4.4.post3 → launchpy-0.4.5.post2}/launchpy/library.py +0 -0
- {launchpy-0.4.4.post3 → launchpy-0.4.5.post2}/launchpy.egg-info/SOURCES.txt +0 -0
- {launchpy-0.4.4.post3 → launchpy-0.4.5.post2}/launchpy.egg-info/dependency_links.txt +0 -0
- {launchpy-0.4.4.post3 → launchpy-0.4.5.post2}/launchpy.egg-info/requires.txt +0 -0
- {launchpy-0.4.4.post3 → launchpy-0.4.5.post2}/launchpy.egg-info/top_level.txt +0 -0
- {launchpy-0.4.4.post3 → launchpy-0.4.5.post2}/pyproject.toml +0 -0
- {launchpy-0.4.4.post3 → launchpy-0.4.5.post2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: launchpy
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.5.post2
|
|
4
4
|
Summary: Adobe Launch API wrapper
|
|
5
5
|
Home-page: https://github.com/pitchmuc/launchpy
|
|
6
6
|
Author: Julien Piccini
|
|
@@ -19,7 +19,7 @@ Classifier: Programming Language :: Python :: 3.7
|
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.8
|
|
20
20
|
Classifier: Programming Language :: Python :: 3.9
|
|
21
21
|
Classifier: Development Status :: 4 - Beta
|
|
22
|
-
Requires-Python: >=3.
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
23
|
Description-Content-Type: text/markdown
|
|
24
24
|
License-File: LICENSE
|
|
25
25
|
Requires-Dist: pandas
|
|
@@ -3,6 +3,17 @@
|
|
|
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.5
|
|
7
|
+
* adding `getExtension` in `Property` class
|
|
8
|
+
* improve `upgradeExtension` in `Property` class
|
|
9
|
+
* improving `checkComponentSync` in `Synchronizer` class
|
|
10
|
+
* improving `getLatestPublishedVersion` with error message defining which property is failing\
|
|
11
|
+
Patch:
|
|
12
|
+
* Better support of `publishedVersion` in checkComponentSync
|
|
13
|
+
* Adding more check in the `checkComponentSync` report
|
|
14
|
+
* Documentation improved in synchronizer
|
|
15
|
+
* Support of extension added in `createTargetsLibrary`
|
|
16
|
+
|
|
6
17
|
## 0.4.4
|
|
7
18
|
* adding a `getBuilds` method on the `Library` class instance
|
|
8
19
|
* extend support for pagination on `getEnvironments`
|
|
@@ -104,36 +104,68 @@ Arguments:
|
|
|
104
104
|
|
|
105
105
|
## checkComponentSync
|
|
106
106
|
|
|
107
|
-
|
|
108
|
-
It
|
|
109
|
-
It
|
|
107
|
+
Check if the component,from the base property, is synced to the different target properties.
|
|
108
|
+
It can also check for the Extensions.\
|
|
109
|
+
It will return a dictionary with the key being the target property and the value being the result of the evaluation, such as {targetProperty: 'similar'} when component have same settings or {targetPropery : '<difference>'} when component do not have same settings.\
|
|
110
|
+
See below for differences.\
|
|
110
111
|
Arguments:
|
|
111
|
-
* componentName : REQUIRED :
|
|
112
|
-
*
|
|
113
|
-
* publishedVersion : OPTIONAL : if you want to compare to the version that has been published in your base
|
|
112
|
+
* componentName : REQUIRED : the name of the component to compare
|
|
113
|
+
* componentID : REQUIRED : the id of the component to compare
|
|
114
|
+
* publishedVersion : OPTIONAL : if you want to compare to the version that has been published in your base vs the published version of your target.
|
|
115
|
+
possible kwargs:
|
|
116
|
+
* action_setting_path : [str,list] : The dot notation of the paths you want to verify for the settings object. ex: ["code","customAttributes"]. If not provided, the complete settings are compared.
|
|
117
|
+
* condition_setting_path : [str,list] : The dot notation of the paths you want to verify for the setting object . ex: ["id",""]. If not provided, the complete settings are compared
|
|
118
|
+
* event_setting_path : [str,list] : The dot notation of the paths you want to verify for the setting object . ex: ["id",""]. If not provided, the complete settings are compared
|
|
114
119
|
|
|
115
120
|
### Check realised for component
|
|
116
121
|
|
|
117
122
|
The script will provide these evaluations:
|
|
118
123
|
For Extensions:
|
|
119
|
-
* It will check if the
|
|
120
|
-
* It will check if the same
|
|
124
|
+
* It will check if the extension is also present in that property. (returns the following sentence `f'Extension "{extension_name}" is not present'` if not the case )
|
|
125
|
+
* It will check if the same version of the extension is used (return the following sentence `f'Extension version is different: {version_base} vs {version_target}` in case different versions)
|
|
126
|
+
* It will check if the same settings has been applied (return `'Extension settings are different'` if not the same)
|
|
121
127
|
|
|
122
128
|
For Rules:
|
|
123
|
-
* It will check if the
|
|
124
|
-
* It will check
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
129
|
+
* It will check if the rule exist in the target property (if not, returning `"Rule does not exist in Target"`)
|
|
130
|
+
* It will check if the rules contain the same number of ruleComponents (return `"The rule does not have the same number of components"` in case different numbers)
|
|
131
|
+
* It will check if the enabled state is the same than your base (if not, returning `"Rule enabled status is different"`)
|
|
132
|
+
* It will check if the published state is the same than your base (if not, returning `"Rule published status is different"`)
|
|
133
|
+
* It will check each ruleComponent if it can be found (based on name) and generate a list of differences based on each component check:
|
|
134
|
+
* If the ruleComponent name cannot be found in the target property : return `f'component {rule-component-name} does not exist in Target'`
|
|
135
|
+
* If the ruleComponent is an event it will check if the order of the event is different. If different, it will return: `rule_order is different`
|
|
136
|
+
* If the ruleComponent is an condition or an action it will check if the tiemout is different. If different, it will return: `f"condition|action {rule-component-name} timeout is different"`
|
|
137
|
+
* If the ruleComponent cannot be found in the target rule, it will add the following elements: `'component {rule-component-name} does not exist in Target'`
|
|
138
|
+
* If the ruleComponent name is found and the settings are different : return `f'<componentType> {rule-component-name} has different settings'`.\
|
|
139
|
+
ComponentType can be: `event, condition, action`\
|
|
140
|
+
The settings can be check globally or you can specify a specific part of the setting to be checked (see `possible kwargs`)
|
|
141
|
+
* If the ruleComponent are all similar, then the result will be `Similar`
|
|
142
|
+
|
|
133
143
|
|
|
134
144
|
For Data Elements:
|
|
135
|
-
* It will check if the Data Element name can be found : return `
|
|
136
|
-
* If
|
|
145
|
+
* It will check if the Data Element name can be found : return `f'Data Element "{data-element-name}" does not exist in Target'` if not found
|
|
146
|
+
* If Data Element (based on name) is found and settings are the same : return `"Similar"`, else return `"Data Element settings are different"`
|
|
147
|
+
|
|
148
|
+
#### Additional elements
|
|
149
|
+
On top of the different properties check, additional information will be provided for the base component:
|
|
150
|
+
* `base-publihsed`: If the current component check is published.\
|
|
151
|
+
**IMPORTANT NOTE**: When you using the `publishedVersion` parameter set to True, most of the element will be set to Published. In reverse, it is not because the component current version is not published that it has never been published. Use the `publishedVersion` to ensure this.
|
|
152
|
+
* `base-enabled`: If the current component check is enabled.
|
|
153
|
+
* `base-state`:
|
|
154
|
+
* `Latest`: If the component retrieved is the latest latest version
|
|
155
|
+
* `Unknown`: If the component retrieved cannot be evaluated to know if it is the latest.
|
|
156
|
+
* `Draft` : If the component retrieved is not the latest and it has not yet been published
|
|
157
|
+
* `Edited`: If the component retrieved is not the latest and has been published before.
|
|
158
|
+
|
|
159
|
+
#### Similar vs Same
|
|
160
|
+
The idea of checking the similarity of 2 components is partially done as not everything can be tested during the check.\
|
|
161
|
+
For that reason, the wording `Similar` is used and not `Same` as they are not technically the same. (different ids, different things.)
|
|
162
|
+
|
|
163
|
+
#### publishedVersion for checking
|
|
164
|
+
In the case that you want to compare the published versions, 2 similar problems could occure:
|
|
165
|
+
- **The base component is not published**. The result of that would be the **failing of the comparison** (an exception will be thrown) because you need to have your base clean to start a comparison with a published version.
|
|
166
|
+
- **One or all the Targets properties do not have the component published**. In that case, I tried to handle that nicely and then try to default to the latest version for comparison. In the result, the following parenthesis would be added `(draft)`
|
|
167
|
+
|
|
168
|
+
Therefore, the `(draft)` is always referring to the target property.
|
|
137
169
|
|
|
138
170
|
## Dynamic Component filter
|
|
139
171
|
|
|
@@ -172,7 +204,7 @@ An example on how the data element looks like:
|
|
|
172
204
|
|
|
173
205
|
#### Data Elements
|
|
174
206
|
|
|
175
|
-
You can pass a Data Element name during the instanciation of the `Synchronizer`. This data element needs to resides in the base property and it will contain a valid JSON that will describe the different rules that will apply during the synchronization.\
|
|
207
|
+
You can pass a Data Element name during the instanciation of the `Synchronizer`. This data element needs to resides in the base property and it will contain a **valid** JSON that will describe the different rules that will apply during the synchronization.\
|
|
176
208
|
The format of the JSON representation for the data element is [explained above](#dynamic-component-filter)\
|
|
177
209
|
The data element name needs to be passed in the `dynamicRuleComponent` parameter.
|
|
178
210
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.4.5-2"
|
|
@@ -22,7 +22,7 @@ def find_path(path: str) -> Optional[Path]:
|
|
|
22
22
|
return None
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
def createConfigFile(scope: str = "https://ims-na1.adobelogin.com/s/ent_reactor_admin_sdk",
|
|
25
|
+
def createConfigFile(filename:str='config_launch',auth_type: str = "oauthV2", scope: str = "https://ims-na1.adobelogin.com/s/ent_reactor_admin_sdk", verbose: object = False)->None:
|
|
26
26
|
"""
|
|
27
27
|
This function will create a 'config_launch_admin.json' file where you can store your access data.
|
|
28
28
|
Arguments:
|
|
@@ -36,18 +36,20 @@ def createConfigFile(scope: str = "https://ims-na1.adobelogin.com/s/ent_reactor_
|
|
|
36
36
|
'org_id': '<orgID>',
|
|
37
37
|
'client_id': "<client_id>",
|
|
38
38
|
'secret': "<YourSecret>",
|
|
39
|
-
'scope': scope
|
|
40
39
|
}
|
|
41
40
|
if auth_type == 'oauthV2':
|
|
42
41
|
json_data['scopes'] = "<scopes>"
|
|
43
42
|
elif auth_type == 'jwt':
|
|
44
43
|
json_data["tech_id"] = "<something>@techacct.adobe.com"
|
|
45
44
|
json_data["pathToKey"] = "<path/to/your/privatekey.key>"
|
|
46
|
-
|
|
45
|
+
json_data['scope'] = scope
|
|
46
|
+
if '.json' not in filename:
|
|
47
|
+
filename = f"{filename}.json"
|
|
48
|
+
with open(filename, 'w') as cf:
|
|
47
49
|
cf.write(json.dumps(json_data, indent=4))
|
|
48
50
|
if verbose:
|
|
49
51
|
print(
|
|
50
|
-
f" file created at this location : {os.getcwd()}{os.sep}
|
|
52
|
+
f" file created at this location : {os.getcwd()}{os.sep}{filename}")
|
|
51
53
|
|
|
52
54
|
def importConfigFile(path: str = None,auth_type:str=None) -> None:
|
|
53
55
|
"""Reads the file denoted by the supplied `path` and retrieves the configuration information
|
|
@@ -63,7 +63,7 @@ class Property:
|
|
|
63
63
|
return str(json.dumps(self.definition, indent=4))
|
|
64
64
|
|
|
65
65
|
|
|
66
|
-
def
|
|
66
|
+
def _getExtensionPackageIdByName(self, ext_name: str, platform: str = "web", verbose: bool = False)->dict:
|
|
67
67
|
"""
|
|
68
68
|
Retrieve extension id of the catalog from an extension name.
|
|
69
69
|
It will be used later on to check for available updates.
|
|
@@ -82,6 +82,23 @@ class Property:
|
|
|
82
82
|
print('extension name : ' + str(data['attributes']['name']))
|
|
83
83
|
print('extension id : ' + str(data['id']))
|
|
84
84
|
return extension_id
|
|
85
|
+
|
|
86
|
+
def getExtensionPackage(self, extension_id: str = None, verbose: bool = False)->dict:
|
|
87
|
+
"""
|
|
88
|
+
Retrieve the extension package information based on the extension id.
|
|
89
|
+
Arguments:
|
|
90
|
+
extension_id : REQUIRED : ID of the extension package to retrieve
|
|
91
|
+
verbose : OPTIONAL : if set to True, will print the extension name and id.
|
|
92
|
+
"""
|
|
93
|
+
if extension_id is None:
|
|
94
|
+
raise ValueError("You must provide an extension id")
|
|
95
|
+
path = f'/extension_packages/{extension_id}'
|
|
96
|
+
res_ext = self.connector.getData(self.endpoint+path)
|
|
97
|
+
data = res_ext['data']
|
|
98
|
+
if verbose:
|
|
99
|
+
print('extension name : ' + str(data['attributes']['name']))
|
|
100
|
+
print('extension id : ' + str(data['id']))
|
|
101
|
+
return data
|
|
85
102
|
|
|
86
103
|
def getRessource(self,res_url: str = None, params: dict = None):
|
|
87
104
|
"""
|
|
@@ -145,6 +162,16 @@ class Property:
|
|
|
145
162
|
except:
|
|
146
163
|
data = extensions
|
|
147
164
|
return data
|
|
165
|
+
|
|
166
|
+
def getExtension(self,extensionId:str=None)-> object:
|
|
167
|
+
"""
|
|
168
|
+
Retrieve the definiton of the extension based on its ID.
|
|
169
|
+
Arguments:
|
|
170
|
+
extensionId : REQUIRED : ID of the extension to retrieve
|
|
171
|
+
"""
|
|
172
|
+
path = f"/extensions/{extensionId}"
|
|
173
|
+
extensions = self.connector.getData(self.endpoint+path)
|
|
174
|
+
return extensions['data']
|
|
148
175
|
|
|
149
176
|
def checkExtensionUpdate(self, name:str=None, platform: str = "web", verbose: bool = False):
|
|
150
177
|
"""
|
|
@@ -174,7 +201,7 @@ class Property:
|
|
|
174
201
|
'internal_id': ext['id']
|
|
175
202
|
} for ext in extensions if ext['attributes']['name'] == name}
|
|
176
203
|
for name in dict_extensions:
|
|
177
|
-
new_id = self.
|
|
204
|
+
new_id = self._getExtensionPackageIdByName(name, platform=platform, verbose=verbose)
|
|
178
205
|
if new_id != dict_extensions[name]['package_id']:
|
|
179
206
|
dict_extensions[name]['package_id'] = new_id
|
|
180
207
|
dict_extensions[name]['update'] = True
|
|
@@ -188,28 +215,63 @@ class Property:
|
|
|
188
215
|
extension_id : REQUIRED : Your internal ID for this extension in your property (EX....)
|
|
189
216
|
package_id : REQUIRED : new extension id for the extension (EP...)
|
|
190
217
|
"""
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
218
|
+
if extension_id is None:
|
|
219
|
+
raise ValueError("You must provide an extension id")
|
|
220
|
+
if package_id is None:
|
|
221
|
+
raise ValueError("You must provide a package id")
|
|
222
|
+
myExtension = self.getExtension(extension_id)
|
|
223
|
+
latest_package_id = myExtension.get('meta').get('upgrade_extension_package_id',None)
|
|
224
|
+
current_package_id = myExtension['relationships']['extension_package']['data']['id']
|
|
225
|
+
if latest_package_id is None:
|
|
226
|
+
settings = myExtension.get('attributes',{}).get('settings','{}')
|
|
227
|
+
attributes = {'delegate_descriptor_id': f'{myExtension.get("attributes",{}).get("name")}::extensionConfiguration::config','settings':settings}
|
|
228
|
+
data = {'data': {
|
|
229
|
+
'id': extension_id,
|
|
230
|
+
"type": "extensions",
|
|
231
|
+
"relationships": {"extension_package": {
|
|
232
|
+
"links": {
|
|
233
|
+
"related": "https://reactor.adobe.io/extensions/"+extension_id+"/extension_package"
|
|
234
|
+
},
|
|
235
|
+
"data": {
|
|
236
|
+
"id": package_id,
|
|
237
|
+
"type": "extension_packages"
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
},
|
|
241
|
+
'meta': {'upgrade_extension_package_id': package_id}
|
|
242
|
+
}
|
|
201
243
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
'
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
244
|
+
else:
|
|
245
|
+
if latest_package_id != current_package_id: ##using a different package that the one provided
|
|
246
|
+
settings = myExtension.get('attributes',{}).get('settings','{}')
|
|
247
|
+
attributes = {'delegate_descriptor_id': f'{myExtension.get("attributes",{}).get("name")}::extensionConfiguration::config','settings':settings}
|
|
248
|
+
data = {'data': {
|
|
249
|
+
"attributes": attributes,
|
|
250
|
+
'id': extension_id,
|
|
251
|
+
"type": "extensions",
|
|
252
|
+
"relationships": {"extension_package": {
|
|
253
|
+
"links": {
|
|
254
|
+
"related": "https://reactor.adobe.io/extensions/"+extension_id+"/extension_package"
|
|
255
|
+
},
|
|
256
|
+
"data": {
|
|
257
|
+
"id": latest_package_id,
|
|
258
|
+
"type": "extension_packages"
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
else:
|
|
265
|
+
raise ValueError("Extension already up to date")
|
|
266
|
+
|
|
209
267
|
path = f"/extensions/{extension_id}"
|
|
210
268
|
res = self.connector.patchData(
|
|
211
269
|
self.endpoint+path, data=data)
|
|
212
|
-
|
|
270
|
+
try:
|
|
271
|
+
data = res['data'] # skip meta for now
|
|
272
|
+
except:
|
|
273
|
+
data = res
|
|
274
|
+
return data
|
|
213
275
|
|
|
214
276
|
def getProfile(self)->dict:
|
|
215
277
|
"""
|
|
@@ -926,20 +988,27 @@ class Property:
|
|
|
926
988
|
lastPage=True
|
|
927
989
|
return data
|
|
928
990
|
|
|
929
|
-
def getLatestPublishedVersion(self,revisions:list=None)->dict:
|
|
991
|
+
def getLatestPublishedVersion(self,revisions:Union[list,dict]=None)->dict:
|
|
930
992
|
"""
|
|
931
993
|
Find the latest published version of a component based on the list of revisions retrieved via getRevisions methods.
|
|
932
994
|
Arguments:
|
|
933
|
-
revisions : REQUIRED : list of revisions
|
|
995
|
+
revisions : REQUIRED : list of revisions or dictionary of the component.
|
|
934
996
|
"""
|
|
935
997
|
if revisions is None:
|
|
936
|
-
raise ValueError('Require a list of revisions')
|
|
998
|
+
raise ValueError('Require a list of revisions at least')
|
|
999
|
+
if type(revisions) == dict:
|
|
1000
|
+
revisions = self.getRevisions(element=revisions)
|
|
937
1001
|
publishedIndexVersions:dict = {index:rev['attributes']['revision_number'] for index, rev in enumerate(revisions) if rev['attributes']['published'] == True}
|
|
1002
|
+
maxVersion = max(e['attributes']['revision_number'] for e in revisions)
|
|
938
1003
|
if len(publishedIndexVersions) == 0:
|
|
939
|
-
raise IndexError("You want to retrieve a published version of the component.\nBut no published version can be found. Please check if your component has been published")
|
|
1004
|
+
raise IndexError(f"You want to retrieve a published version of the component.\nBut no published version can be found in {self.name}. Please check if your component has been published")
|
|
940
1005
|
maxRevisionIndex = [index for index,value in publishedIndexVersions.items() if value == max(list(publishedIndexVersions.values()))][0]
|
|
941
|
-
|
|
942
|
-
|
|
1006
|
+
publishedVersion = revisions[maxRevisionIndex]
|
|
1007
|
+
if maxVersion == publishedVersion['attributes']['revision_number']:
|
|
1008
|
+
publishedVersion['attributes']['latest'] = True
|
|
1009
|
+
else:
|
|
1010
|
+
publishedVersion['attributes']['latest'] = False
|
|
1011
|
+
return publishedVersion
|
|
943
1012
|
|
|
944
1013
|
def reviseDataElement(self, dataElement_id: str)->dict:
|
|
945
1014
|
"""
|
|
@@ -1127,6 +1196,8 @@ class Property:
|
|
|
1127
1196
|
argument:
|
|
1128
1197
|
extension_id : REQUIRED : the extension id
|
|
1129
1198
|
attr_dict : REQUIRED : dictionary that will be passed to Launch for update
|
|
1199
|
+
possible kwargs:
|
|
1200
|
+
upgradeLatest : OPTIONAL : Boolean if you want to update the relationships of the extension to the new version
|
|
1130
1201
|
"""
|
|
1131
1202
|
obj = {
|
|
1132
1203
|
"data": {
|
|
@@ -1135,8 +1206,7 @@ class Property:
|
|
|
1135
1206
|
"type": "extensions"
|
|
1136
1207
|
}
|
|
1137
1208
|
}
|
|
1138
|
-
extensions = self.connector.patchData(
|
|
1139
|
-
config.endpoints['global']+'/extensions/'+extension_id, data=obj)
|
|
1209
|
+
extensions = self.connector.patchData(self.endpoint+'/extensions/'+extension_id, data=obj)
|
|
1140
1210
|
try:
|
|
1141
1211
|
data = extensions['data']
|
|
1142
1212
|
except:
|
|
@@ -6,6 +6,7 @@ from .library import Library
|
|
|
6
6
|
from .launchpy import Translator, copySettings
|
|
7
7
|
from collections import defaultdict
|
|
8
8
|
from copy import deepcopy
|
|
9
|
+
from aepp import som
|
|
9
10
|
|
|
10
11
|
class Synchronizer:
|
|
11
12
|
"""
|
|
@@ -54,7 +55,7 @@ class Synchronizer:
|
|
|
54
55
|
self.targets[target]['rules'] = self.targets[target]['api'].getRules()
|
|
55
56
|
self.targets[target]['extensions'] = self.targets[target]['api'].getExtensions()
|
|
56
57
|
self.targets[target]['dataElements'] = self.targets[target]['api'].getDataElements()
|
|
57
|
-
self.targets[target]['libraryStack'] = {'dataElements':[],'rules':[]}
|
|
58
|
+
self.targets[target]['libraryStack'] = {'dataElements':[],'rules':[],"extensions":[]}
|
|
58
59
|
self.translator.extendExtensions(self.targets[target]['extensions'],self.targets[target]['name'])
|
|
59
60
|
if len(self.targets[target]['rules']) > 0:
|
|
60
61
|
self.translator.extendRules(self.targets[target]['rules'],self.targets[target]['name'])
|
|
@@ -114,23 +115,14 @@ class Synchronizer:
|
|
|
114
115
|
'inclComponents': deepcopy(self.dict_config[rule]['inclComponents'])
|
|
115
116
|
}
|
|
116
117
|
|
|
117
|
-
|
|
118
|
-
def syncComponent(self,componentName:str=None,componentId:str=None,publishedVersion:bool=False,**kwargs)->None:
|
|
118
|
+
def __prepareBaseComponent__(self,componentName:str=None,componentId:str=None,publishedVersion:bool=False)->dict:
|
|
119
119
|
"""
|
|
120
|
-
|
|
121
|
-
It will detect the component with the same name in target properties.
|
|
122
|
-
It will delete the components related objects (ruleComponents, data element code) in the target property and push the template version.
|
|
123
|
-
If you uploaded a dynamicRuleComponent data element config during the init method, any element that are not sync because of the exclComponentList will return a dict: {componentName:False}
|
|
120
|
+
Prepare the base component to be used in the syncComponent method.
|
|
124
121
|
Arguments:
|
|
125
122
|
componentName : REQUIRED : the name of the component to sync
|
|
126
123
|
componentID : REQUIRED : the id of the component to sync
|
|
127
124
|
publishedVersion : OPTIONAL : if you want to take the version that has been published
|
|
128
|
-
possible kwargs:
|
|
129
|
-
timeout : OPTIONAL : The timeout to be used for the rule component. If not provided, the existing timeout will be used.
|
|
130
125
|
"""
|
|
131
|
-
timeout = kwargs.get('timeout',None)
|
|
132
|
-
if componentName is None and componentId is None:
|
|
133
|
-
raise ValueError('Require a component Name of a component ID')
|
|
134
126
|
cmp_base=None
|
|
135
127
|
if componentId is not None:
|
|
136
128
|
if componentId.startswith('DE'): ## if data element
|
|
@@ -143,6 +135,11 @@ class Synchronizer:
|
|
|
143
135
|
if len(componentBase)==0:
|
|
144
136
|
raise KeyError("Component ID cannot be found")
|
|
145
137
|
cmp_base = componentBase[0]
|
|
138
|
+
elif componentId.startswith('EX'): ## if an extension
|
|
139
|
+
componentBase = [ext for ext in self.base['extensions'] if ext['id'] == componentId]
|
|
140
|
+
if len(componentBase)==0:
|
|
141
|
+
raise KeyError("Component ID cannot be found")
|
|
142
|
+
cmp_base = componentBase[0]
|
|
146
143
|
if componentId is None and componentName is not None: ## If only componentName
|
|
147
144
|
for rule in self.base['rules']:
|
|
148
145
|
if componentName == rule['attributes']['name']:
|
|
@@ -151,12 +148,18 @@ class Synchronizer:
|
|
|
151
148
|
for de in self.base['dataElements']:
|
|
152
149
|
if componentName == de['attributes']['name']:
|
|
153
150
|
cmp_base = de
|
|
151
|
+
if cmp_base is None:
|
|
152
|
+
for ext in self.base['extensions']:
|
|
153
|
+
if componentName == ext['attributes']['name']:
|
|
154
|
+
cmp_base = ext
|
|
154
155
|
## In case we do not find any match
|
|
155
156
|
if cmp_base is None:
|
|
156
157
|
raise KeyError("The component ID or component Name cannot be matched in your template property")
|
|
157
158
|
## Here creating a dictionary that provide all information related to your component
|
|
158
159
|
cmp_baseDict = {'id':cmp_base['id'],'name':cmp_base['attributes']['name'],'component':cmp_base,'copy':copySettings(cmp_base)}
|
|
159
|
-
if
|
|
160
|
+
if cmp_baseDict['component']['type'] == 'extensions':
|
|
161
|
+
cmp_baseDict['version'] = cmp_base['attributes'].get('version','1.0.0')
|
|
162
|
+
if publishedVersion and cmp_baseDict['component']['type'] not in ['extensions']:
|
|
160
163
|
data = self.base['api'].getRevisions(cmp_baseDict['component'])
|
|
161
164
|
publishedVersion = self.base['api'].getLatestPublishedVersion(data)
|
|
162
165
|
if publishedVersion['attributes']['name'] != cmp_baseDict['name']:
|
|
@@ -173,6 +176,25 @@ class Synchronizer:
|
|
|
173
176
|
cmp_baseDict['name'] = publishedVersion['attributes']['name']
|
|
174
177
|
cmp_baseDict['component'] = publishedVersion
|
|
175
178
|
cmp_baseDict['copy'] = copySettings(publishedVersion)
|
|
179
|
+
return cmp_baseDict
|
|
180
|
+
|
|
181
|
+
def syncComponent(self,componentName:str=None,componentId:str=None,publishedVersion:bool=False,**kwargs)->None:
|
|
182
|
+
"""
|
|
183
|
+
Synchronize a component from the base property to the different target properties.
|
|
184
|
+
It will detect the component with the same name in target properties.
|
|
185
|
+
It will delete the components related objects (ruleComponents, data element code) in the target property and push the template version.
|
|
186
|
+
If you uploaded a dynamicRuleComponent data element config during the init method, any element that are not sync because of the exclComponentList will return a dict: {componentName:False}
|
|
187
|
+
Arguments:
|
|
188
|
+
componentName : REQUIRED : the name of the component to sync
|
|
189
|
+
componentID : REQUIRED : the id of the component to sync
|
|
190
|
+
publishedVersion : OPTIONAL : if you want to take the version that has been published
|
|
191
|
+
possible kwargs:
|
|
192
|
+
timeout : OPTIONAL : The timeout to be used for the rule component. If not provided, the existing timeout will be used.
|
|
193
|
+
"""
|
|
194
|
+
timeout = kwargs.get('timeout',None)
|
|
195
|
+
if componentName is None and componentId is None:
|
|
196
|
+
raise ValueError('Require a component Name of a component ID')
|
|
197
|
+
cmp_baseDict = self.__prepareBaseComponent__(componentName=componentName,componentId=componentId,publishedVersion=publishedVersion)
|
|
176
198
|
## handling the data element
|
|
177
199
|
if cmp_baseDict['component']['type'] == 'data_elements':
|
|
178
200
|
for target in list(self.targets.keys()):
|
|
@@ -206,7 +228,7 @@ class Synchronizer:
|
|
|
206
228
|
self.targets[target]['libraryStack']['dataElements'].append(comp)
|
|
207
229
|
self.targets[target]['dataElements'].append(comp)
|
|
208
230
|
else:
|
|
209
|
-
index,old_component = [(index,de) for index,de in enumerate(self.targets[target]['dataElements']) if de
|
|
231
|
+
index,old_component = [(index,de) for index,de in enumerate(self.targets[target]['dataElements']) if de.get('attributes',{}).get('name') == cmp_baseDict['name']][0]
|
|
210
232
|
attributes = {
|
|
211
233
|
"name" : translatedComponent['name'],
|
|
212
234
|
"enabled" : translatedComponent["enabled"],
|
|
@@ -359,6 +381,13 @@ class Synchronizer:
|
|
|
359
381
|
self.targets[target]['library'].updateDataElements(existingDataElements)
|
|
360
382
|
if len(newDataElements)>0:
|
|
361
383
|
self.targets[target]['library'].addDataElements(newDataElements)
|
|
384
|
+
## taking care of the extensions
|
|
385
|
+
existingExtensions = [ext['id'] for ext in self.targets[target]['library'].relationships['extensions'] if ext['id'] in [e['id'] for e in self.targets[target]['libraryStack']['extensions']]]
|
|
386
|
+
newExtensions = [ext['id'] for ext in self.targets[target]['libraryStack']['extensions'] if ext['id'] not in existingExtensions]
|
|
387
|
+
if len(existingExtensions) > 0:
|
|
388
|
+
self.targets[target]['library'].updateExtensions(existingExtensions)
|
|
389
|
+
if len(newExtensions)>0:
|
|
390
|
+
self.targets[target]['library'].addExtensions(newExtensions)
|
|
362
391
|
|
|
363
392
|
def upgradeTargetExtension(self,extensionName:str=None,platform:str="web")->dict:
|
|
364
393
|
"""
|
|
@@ -370,14 +399,20 @@ class Synchronizer:
|
|
|
370
399
|
"""
|
|
371
400
|
if extensionName is None:
|
|
372
401
|
raise ValueError("Require an extension name")
|
|
373
|
-
for prop, target in self.targets:
|
|
402
|
+
for prop, target in self.targets.items():
|
|
374
403
|
try:
|
|
375
404
|
extensionUpdate = target['api'].checkExtensionUpdate(extensionName)
|
|
405
|
+
for extName, extUpdateDict in extensionUpdate.items():
|
|
406
|
+
if extUpdateDict["update"]:
|
|
407
|
+
res = target['api'].upgradeExtension(extUpdateDict['internal_id'],extUpdateDict["package_id"])
|
|
408
|
+
target['libraryStack']['extensions'].append(res)
|
|
409
|
+
if len([index for index, ext in enumerate(target['extensions']) if ext['attributes']['name'] == extName])>0:
|
|
410
|
+
index = [index for index, ext in enumerate(target['extensions']) if ext['attributes']['name'] == extName][0]
|
|
411
|
+
del target['extensions'][index]
|
|
412
|
+
target['extensions'].append(res)
|
|
413
|
+
|
|
376
414
|
except:
|
|
377
415
|
raise ValueError(f"Could not find an extension name: {extensionName}")
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
416
|
|
|
382
417
|
|
|
383
418
|
def renameComponent(self,old_name:str=None,new_name:str=None)->None:
|
|
@@ -419,83 +454,62 @@ class Synchronizer:
|
|
|
419
454
|
self.targets[target]['libraryStack']['rules'].append(comp)
|
|
420
455
|
|
|
421
456
|
|
|
422
|
-
def checkComponentSync(self,componentName:str=None,componentId:str=None,publishedVersion:bool=False,**kwargs)->bool:
|
|
457
|
+
def checkComponentSync(self,componentName:str=None,componentId:str=None,publishedVersion:bool=False,excludeSimilar:bool=False,**kwargs)->bool:
|
|
423
458
|
"""
|
|
424
459
|
Check if the component,from the base property, is synced to the different target properties.
|
|
425
460
|
It can also check for the Extensions.
|
|
426
|
-
It will
|
|
461
|
+
It will return a dictionary with the key being the target property and the value being the result of the evaluation, such as {targetProperty: 'similar'} when component have same settings or {targetPropery : False} when component do not have same settings.
|
|
427
462
|
Arguments:
|
|
428
463
|
componentName : REQUIRED : the name of the component to compare
|
|
429
464
|
componentID : REQUIRED : the id of the component to compare
|
|
430
|
-
publishedVersion : OPTIONAL : if you want to compare to the version that has been published in your base
|
|
465
|
+
publishedVersion : OPTIONAL : if you want to compare to the version that has been published in your base vs the published version of your target.
|
|
466
|
+
excludeSimilar : OPTIONAL : If you do not want to see the result if the comparison provide a "similar" result. Similar means that the elements are about the same. Default: False.
|
|
467
|
+
possible kwargs:
|
|
468
|
+
action_setting_path : [str,list] : The dot notation of the paths you want to verify for the settings object. ex: ["code","customAttributes"]. If not provided, the complete settings are compared.
|
|
469
|
+
condition_setting_path : [str,list] : The dot notation of the paths you want to verify for the setting object . ex: ["id",""]. If not provided, the complete settings are compared
|
|
470
|
+
event_setting_path : [str,list] : The dot notation of the paths you want to verify for the setting object . ex: ["id",""]. If not provided, the complete settings are compared
|
|
431
471
|
"""
|
|
432
472
|
if componentName is None and componentId is None:
|
|
433
473
|
raise ValueError('Require a component Name of a component ID')
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
elif
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
cmp_base = componentBase[0]
|
|
451
|
-
if componentId is None and componentName is not None: ## If only componentName
|
|
452
|
-
for rule in self.base['rules']:
|
|
453
|
-
if componentName == rule['attributes']['name']:
|
|
454
|
-
cmp_base = rule
|
|
455
|
-
if cmp_base is None: ## data elements
|
|
456
|
-
for de in self.base['dataElements']:
|
|
457
|
-
if componentName == de['attributes']['name']:
|
|
458
|
-
cmp_base = de
|
|
459
|
-
if cmp_base is None: ## extensions
|
|
460
|
-
for ext in self.base['extensions']:
|
|
461
|
-
if componentName == ext['attributes']['name']:
|
|
462
|
-
cmp_base = ext
|
|
463
|
-
## In case we do not find any match
|
|
464
|
-
if cmp_base is None:
|
|
465
|
-
raise KeyError("The component ID or component Name cannot be matched in your template property")
|
|
466
|
-
## Here creating a dictionary that provide all information related to your component
|
|
467
|
-
cmp_baseDict = {'id':cmp_base['id'],'name':cmp_base['attributes']['name'],'component':cmp_base,'copy':copySettings(cmp_base)}
|
|
468
|
-
if publishedVersion:
|
|
469
|
-
data = self.base['api'].getRevisions(cmp_baseDict['component'])
|
|
470
|
-
publishedVersion = self.base['api'].getLatestPublishedVersion(data)
|
|
471
|
-
if publishedVersion['attributes']['name'] != cmp_baseDict['name']:
|
|
472
|
-
## Updating mapping table with old name when published version name diff than last version name.
|
|
473
|
-
if cmp_baseDict['component']['type'] == 'rules':
|
|
474
|
-
self.translator.extendBaseRules(
|
|
475
|
-
ruleName=publishedVersion['attributes']['name'],
|
|
476
|
-
ruleId=publishedVersion['id'],
|
|
477
|
-
property_name=self.base["name"])
|
|
478
|
-
self.base['rules'].append(publishedVersion)
|
|
479
|
-
if cmp_baseDict['component']['type'] == 'data_elements':
|
|
480
|
-
self.base['dataElements'].append(publishedVersion)
|
|
481
|
-
if cmp_baseDict['component']['type'] == 'extensions':
|
|
482
|
-
self.base['extensions'].append(publishedVersion)
|
|
483
|
-
cmp_baseDict['id'] = publishedVersion['id']
|
|
484
|
-
cmp_baseDict['name'] = publishedVersion['attributes']['name']
|
|
485
|
-
cmp_baseDict['component'] = publishedVersion
|
|
486
|
-
cmp_baseDict['copy'] = copySettings(publishedVersion)
|
|
487
|
-
dict_result = {tar:False for tar in self.targets.keys()}
|
|
474
|
+
cmp_baseDict = self.__prepareBaseComponent__(componentName=componentName,componentId=componentId,publishedVersion=publishedVersion)
|
|
475
|
+
dict_result = {tar:"" for tar in self.targets.keys()}
|
|
476
|
+
dict_result['base-enabled'] = cmp_baseDict['component']['attributes'].get('enabled',False)
|
|
477
|
+
dict_result['base-published'] = cmp_baseDict['component']['attributes'].get('published',False)
|
|
478
|
+
if cmp_baseDict['component']['attributes'].get('latest',False):
|
|
479
|
+
dict_result['base-state'] = "Latest"
|
|
480
|
+
else:
|
|
481
|
+
if cmp_baseDict['component']['attributes'].get('latest','unknown') == 'unknown':
|
|
482
|
+
if dict_result['base-enabled']:
|
|
483
|
+
dict_result['base-state'] = "Draft"
|
|
484
|
+
else:
|
|
485
|
+
dict_result['base-state'] = "Unknown"
|
|
486
|
+
elif dict_result['base-published'] == False:
|
|
487
|
+
dict_result['base-state'] = "Draft"
|
|
488
|
+
else:
|
|
489
|
+
dict_result['base-state'] = "Edited"
|
|
488
490
|
if cmp_baseDict['component']['type'] == 'data_elements':
|
|
489
491
|
for target in list(self.targets.keys()):
|
|
490
492
|
## if it does not exist
|
|
491
|
-
|
|
492
|
-
|
|
493
|
+
if cmp_baseDict['name'] not in [de.get('attributes',{}).get('name') for de in self.targets[target]['dataElements']]:
|
|
494
|
+
dict_result[target] = f'Data Element "{cmp_baseDict["name"]}" does not exist in Target'
|
|
495
|
+
else:
|
|
496
|
+
index,target_de = [(index,de) for index,de in enumerate(self.targets[target]['dataElements']) if de['attributes']['name'] == cmp_baseDict['name']][0]
|
|
497
|
+
issue_pub = ""
|
|
498
|
+
if publishedVersion:
|
|
499
|
+
try:
|
|
500
|
+
revisions_dataElement = self.targets[target]["api"].getRevisions(target_de)
|
|
501
|
+
target_de = self.targets[target]["api"].getLatestPublishedVersion(revisions_dataElement)
|
|
502
|
+
except:
|
|
503
|
+
issue_pub = " (draft)"
|
|
504
|
+
if target_de['attributes']['enabled'] != cmp_baseDict['component']['attributes']['enabled']:
|
|
505
|
+
dict_result[target] = "Data Element enabled status is different"+issue_pub
|
|
506
|
+
if target_de['attributes']['published'] != cmp_baseDict['component']['attributes']['published']:
|
|
507
|
+
dict_result[target] = "Data Element published status is different"+issue_pub
|
|
508
|
+
if target_de['attributes']['settings'] == cmp_baseDict['component']['attributes']['settings']:
|
|
509
|
+
if not excludeSimilar:
|
|
510
|
+
dict_result[target] = "Similar"+issue_pub
|
|
493
511
|
else:
|
|
494
|
-
|
|
495
|
-
if old_component['attributes']['settings'] == cmp_baseDict['component']['attributes']['settings']:
|
|
496
|
-
dict_result[target] = True
|
|
497
|
-
else:
|
|
498
|
-
dict_result[target] = False
|
|
512
|
+
dict_result[target] = "Data Element settings are different"+issue_pub
|
|
499
513
|
if cmp_baseDict['component']['type'] == 'rules':
|
|
500
514
|
rcsLink = cmp_baseDict['component'].get('relationships',{}).get('rule_components',{}).get('links',{}).get('related')
|
|
501
515
|
resResource = self.base['api'].getRessource(rcsLink)
|
|
@@ -506,53 +520,113 @@ class Synchronizer:
|
|
|
506
520
|
for target in list(self.targets.keys()):
|
|
507
521
|
## if rule does not exist
|
|
508
522
|
if cmp_baseDict['name'] not in [rule['attributes']['name'] for rule in self.targets[target]['rules']]:
|
|
509
|
-
dict_result[target] =
|
|
523
|
+
dict_result[target] = "Rule does not exist in Target"
|
|
510
524
|
else:
|
|
511
525
|
index, targetRule = [(index,rule) for index, rule in enumerate(self.targets[target]['rules']) if rule['attributes']['name'] == cmp_baseDict['name']][0]
|
|
512
|
-
|
|
526
|
+
issue_pub = ""
|
|
527
|
+
componentsDifferences = [] ## list of differences for rule components for rule to check difference
|
|
528
|
+
if publishedVersion:
|
|
529
|
+
try:
|
|
530
|
+
revisions_targetRule = self.targets[target]["api"].getRevisions(targetRule)
|
|
531
|
+
targetRule = self.targets[target]["api"].getLatestPublishedVersion(revisions_targetRule)
|
|
532
|
+
except:
|
|
533
|
+
issue_pub = " (not published)"
|
|
534
|
+
if targetRule['attributes']['enabled'] != cmp_baseDict['component']['attributes']['enabled']:
|
|
535
|
+
componentsDifferences.append("Rule enabled status is different")
|
|
536
|
+
if targetRule['attributes']['published'] != cmp_baseDict['component']['attributes']['published']:
|
|
537
|
+
componentsDifferences.append("Rule published status is different")
|
|
513
538
|
rcsLinkTarget = targetRule.get('relationships',{}).get('rule_components',{}).get('links',{}).get('related')
|
|
514
539
|
resResource = self.targets[target]['api'].getRessource(rcsLinkTarget)
|
|
515
|
-
|
|
540
|
+
target_rule_components:list = resResource['data']
|
|
516
541
|
#if not same amunt of rule component
|
|
517
|
-
if len(template_ruleComponents) != len(
|
|
518
|
-
dict_result[target] =
|
|
519
|
-
dict_result[target] = False ## bydefault, there is a difference
|
|
520
|
-
componentsDifferences = [] ## array of compontent for rule to check difference
|
|
542
|
+
if len(template_ruleComponents) != len(target_rule_components):
|
|
543
|
+
dict_result[target] = "The rule does not have the same number of components"
|
|
521
544
|
for base_comp in template_ruleComponents:
|
|
522
545
|
checkExist = False
|
|
523
|
-
for
|
|
524
|
-
if
|
|
546
|
+
for comp in target_rule_components:
|
|
547
|
+
if comp['attributes']['name'] == base_comp['attributes']['name']:
|
|
525
548
|
checkExist = True
|
|
526
|
-
if
|
|
527
|
-
if '
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
549
|
+
if '::events::' in base_comp['attributes']['delegate_descriptor_id']:
|
|
550
|
+
if comp['attributes']['rule_order'] != base_comp['attributes']['rule_order']:
|
|
551
|
+
componentsDifferences.append("rule_order is different")
|
|
552
|
+
if kwargs.get('event_setting_path',None) is not None:
|
|
553
|
+
list_event_path = kwargs.get('event_setting_path',None)
|
|
554
|
+
if type(list_event_path) == str:
|
|
555
|
+
list_event_path = list(list_event_path)
|
|
556
|
+
mySettings_base = som.Som(json.loads(base_comp['attributes']['settings']))
|
|
557
|
+
mySettings_target = som.Som(json.loads(comp['attributes']['settings']))
|
|
558
|
+
for path in list_event_path:
|
|
559
|
+
tmp_base = mySettings_base.get(path,'')
|
|
560
|
+
tmp_target = mySettings_target.get(path,'')
|
|
561
|
+
if tmp_base != tmp_target:
|
|
562
|
+
componentsDifferences.append(f'event "{comp['attributes']['name']}" has different settings')
|
|
563
|
+
else:
|
|
564
|
+
if base_comp['attributes']['settings'] != comp['attributes']['settings']:
|
|
565
|
+
componentsDifferences.append(f'event "{comp['attributes']['name']}" has different settings')
|
|
566
|
+
elif '::conditions::' in base_comp['attributes']['delegate_descriptor_id']:
|
|
567
|
+
if comp['attributes']['timeout'] != base_comp['attributes']['timeout']:
|
|
568
|
+
componentsDifferences.append(f'condition "{comp['attributes']['name']}" timeout is different')
|
|
569
|
+
if kwargs.get('condition_setting_path',None) is not None:
|
|
570
|
+
list_event_path = kwargs.get('condition_setting_path',None)
|
|
571
|
+
if type(list_event_path) == str:
|
|
572
|
+
list_event_path = list(list_event_path)
|
|
573
|
+
mySettings_base = som.Som(json.loads(base_comp['attributes']['settings']))
|
|
574
|
+
mySettings_target = som.Som(json.loads(comp['attributes']['settings']))
|
|
575
|
+
for path in list_event_path:
|
|
576
|
+
tmp_base = mySettings_base.get(path,'')
|
|
577
|
+
tmp_target = mySettings_target.get(path,'')
|
|
578
|
+
if tmp_base != tmp_target:
|
|
579
|
+
componentsDifferences.append(f'condition "{comp['attributes']['name']}" has different settings')
|
|
580
|
+
else:
|
|
581
|
+
if base_comp['attributes']['settings'] != comp['attributes']['settings']:
|
|
582
|
+
componentsDifferences.append(f'condition "{comp['attributes']['name']}" has different settings')
|
|
583
|
+
elif '::actions::' in base_comp['attributes']['delegate_descriptor_id']:
|
|
584
|
+
if comp['attributes']['timeout'] != base_comp['attributes']['timeout']:
|
|
585
|
+
componentsDifferences.append(f'action "{comp['attributes']['name']}" timeout is different')
|
|
586
|
+
if kwargs.get('action_setting_path',None) is not None:
|
|
587
|
+
list_actions_path = kwargs.get('action_setting_path',None)
|
|
588
|
+
if type(list_actions_path) == str:
|
|
589
|
+
list_actions_path = list(list_actions_path)
|
|
590
|
+
mySettings_base = som.Som(json.loads(base_comp['attributes']['settings']))
|
|
591
|
+
mySettings_target = som.Som(json.loads(comp['attributes']['settings']))
|
|
592
|
+
for path in list_actions_path:
|
|
593
|
+
tmp_base = mySettings_base.get(path,'')
|
|
594
|
+
tmp_target = mySettings_target.get(path,'')
|
|
595
|
+
if tmp_base != tmp_target:
|
|
596
|
+
componentsDifferences.append(f'action "{comp['attributes']['name']}" has different settings')
|
|
597
|
+
else:
|
|
598
|
+
if base_comp['attributes']['settings'] != comp['attributes']['settings']:
|
|
599
|
+
componentsDifferences.append(f'action "{comp['attributes']['name']}" has different settings')
|
|
541
600
|
if checkExist == False: ## does not exist
|
|
542
|
-
componentsDifferences.append(
|
|
543
|
-
if
|
|
544
|
-
dict_result[target] =
|
|
601
|
+
componentsDifferences.append(f'component "{base_comp['attributes']['name']}" does not exist in Target')
|
|
602
|
+
if len(componentsDifferences)>0:
|
|
603
|
+
dict_result[target] = ','.join(componentsDifferences) + issue_pub
|
|
604
|
+
else:
|
|
605
|
+
if not excludeSimilar:
|
|
606
|
+
dict_result[target] = 'Similar' + issue_pub
|
|
545
607
|
if cmp_baseDict['component']['type'] == 'extensions':
|
|
546
608
|
for target in list(self.targets.keys()):
|
|
547
609
|
if cmp_baseDict['name'] not in [ext['attributes']['name'] for ext in self.targets[target]['extensions']]:
|
|
548
|
-
dict_result[target] =
|
|
610
|
+
dict_result[target] = f'Extension "{cmp_baseDict['name']}" is not present'
|
|
549
611
|
else:
|
|
550
612
|
index, extensionTarget = [(index,ext) for index, ext in enumerate(self.targets[target]['extensions']) if ext['attributes']['name'] == cmp_baseDict['name']][0]
|
|
613
|
+
issue_pub = ""
|
|
614
|
+
if publishedVersion:
|
|
615
|
+
try:
|
|
616
|
+
revisions_targetExt = self.targets[target]["api"].getRevisions(extensionTarget)
|
|
617
|
+
extensionTarget = self.targets[target]["api"].getLatestPublishedVersion(revisions_targetExt)
|
|
618
|
+
except:
|
|
619
|
+
issue_pub = " (not published)"
|
|
620
|
+
if extensionTarget['attributes']['enabled'] != cmp_baseDict['component']['attributes']['enabled']:
|
|
621
|
+
dict_result[target] = f'Extension enabled status is different: {cmp_baseDict["component"]["attributes"]["enabled"]} vs {extensionTarget["attributes"]["enabled"]}'+issue_pub
|
|
622
|
+
if extensionTarget['attributes']['published'] != cmp_baseDict['component']['attributes']['published']:
|
|
623
|
+
dict_result[target] = f'Extension published status is different: {cmp_baseDict["component"]["attributes"]["published"]} vs {extensionTarget["attributes"]["published"]}'+issue_pub
|
|
551
624
|
if extensionTarget['attributes']['version'] != cmp_baseDict['component']['attributes']['version']:
|
|
552
|
-
dict_result[target] =
|
|
625
|
+
dict_result[target] = f'Extension version is different: {cmp_baseDict['component']['attributes']['version']} vs {extensionTarget['attributes']['version']}'+issue_pub
|
|
553
626
|
else:
|
|
554
627
|
if extensionTarget['attributes']['settings'] != cmp_baseDict['component']['attributes']['settings']:
|
|
555
|
-
dict_result[target] =
|
|
628
|
+
dict_result[target] = 'Extension settings are different'+issue_pub
|
|
556
629
|
else:
|
|
557
|
-
|
|
630
|
+
if not excludeSimilar:
|
|
631
|
+
dict_result[target] = 'Similar'+issue_pub
|
|
558
632
|
return dict_result
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: launchpy
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.5.post2
|
|
4
4
|
Summary: Adobe Launch API wrapper
|
|
5
5
|
Home-page: https://github.com/pitchmuc/launchpy
|
|
6
6
|
Author: Julien Piccini
|
|
@@ -19,7 +19,7 @@ Classifier: Programming Language :: Python :: 3.7
|
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.8
|
|
20
20
|
Classifier: Programming Language :: Python :: 3.9
|
|
21
21
|
Classifier: Development Status :: 4 - Beta
|
|
22
|
-
Requires-Python: >=3.
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
23
|
Description-Content-Type: text/markdown
|
|
24
24
|
License-File: LICENSE
|
|
25
25
|
Requires-Dist: pandas
|
|
@@ -42,7 +42,7 @@ setup(
|
|
|
42
42
|
"Topic :: Software Development :: Libraries",
|
|
43
43
|
"Development Status :: 4 - Beta"
|
|
44
44
|
],
|
|
45
|
-
python_requires='>=3.
|
|
45
|
+
python_requires='>=3.10',
|
|
46
46
|
install_requires=["requests",
|
|
47
|
-
"PyJWT", "pathlib2", "pathlib", "PyJWT[crypto]"],
|
|
47
|
+
"PyJWT", "pathlib2", "pathlib", "PyJWT[crypto]","aepp","pandas"],
|
|
48
48
|
)
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.4.4-3"
|
|
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
|