Complex Mapping feature (LIGHTLY TESTED, NEEDS REVIEW)#31
Conversation
…feature - config support only; functionality to come.
…med. (Still not used though.)
…d - will test with hook invoke.
…complete smoke test working.
| for extension_config in extensions_config.iter_dict_configs(): | ||
| context = extension_config.get_string('context') | ||
| if context == 'per-user': | ||
| if (after_mapping_hook == None): |
There was a problem hiding this comment.
Is this test needed/correct? Looks like after_mapping_hook set to None a few lines up.
There was a problem hiding this comment.
This is testing for multiple per-user extensions; see the matching else.
There was a problem hiding this comment.
Got it. Missed the loop.
adobeDan
left a comment
There was a problem hiding this comment.
This is almost ready to be merged, and most of my suggested changes are not critical, but a few at least need to be considered carefully before we merge.
| after_mapping_hook = None | ||
| extended_attributes = None | ||
| extensions_config = self.main_config.get_list_config('extensions', True) | ||
| if (extensions_config != None): |
There was a problem hiding this comment.
In line 202 you use the correct construction if group is None but here you are not: it should be if extensions_config is not None. I suspect you are being influenced by the existing code style (see line 307), but this code is materially worse in performance because it forces a type check.
There was a problem hiding this comment.
I used the equality when in the midst of a block of code that was already using it; otherwise I did what you're calling the right way. Should be addressed globally.
| organization_name = user_sync.rules.OWNING_ORGANIZATION_NAME | ||
| if (len(group_name) == 0): | ||
| group = self.create_dashboard_group(dashboard_group) | ||
| if (group is None): |
There was a problem hiding this comment.
Ouch! Parentheses around if conditions are not only unnecessary in Python, they are actively discouraged. I assume you are doing this because the rest of the Ensemble code does this, but yuck! We really should fix all this code. Parentheses in Python should be used entirely for precedence and readability; nothing else.
There was a problem hiding this comment.
Yes, following the style of the codebase...
| if context == 'per-user': | ||
| if (after_mapping_hook == None): | ||
| after_mapping_hook_text = extension_config.get_string('after_mapping_hook') | ||
| if (after_mapping_hook_text is not None): |
There was a problem hiding this comment.
This kind of deeply nested if (the three tests including this one) where the else parts are errors make the code really hard to follow. You should do the "go" thing here: invert the test and have the if part log the warning and skip the rest of the code.
if context != 'per-user':
self.logger.warning("Unrecognized extension context '%s' ignored", context)
continue
...
Also consider breaking this loop or body of this loop out as a helper function.
There was a problem hiding this comment.
Leaving this for now, but agree with you. Will fix if I have time.
| # thoroughly tested. | ||
| # | ||
| for extended_dashboard_group in extension_config.get_list('extended_dashboard_groups'): | ||
| group = self.create_dashboard_group(extended_dashboard_group) |
There was a problem hiding this comment.
I can see from the code later on that create_dashboard_group ignores the group if it already exists as a dashboard group. This means you are guarding against people specifying groups in their extended_dashboard_groups that are already groups in the mapping, which seems like it would be a common (and forgiveable) mistake. A comment to that effect would be useful here (and also in the docs).
There was a problem hiding this comment.
Actually create_dashboard_group did not behave that way. But it does now :)
| :rtype (bool, iterable(dict)) | ||
| ''' | ||
| if (extended_attributes is not None): | ||
| self.logger.warning("CSV directory connector doesn't support extended_attributes; ignored") |
There was a problem hiding this comment.
Really? Seems like it would be really really easy for the CSV connector to support extended attributes, since you could just add them as column headers. And this seems like it would be a really, really handy way to test extended logic on just a few people...
There was a problem hiding this comment.
Doesn't really make sense for csv to have extended attrs because all csv attrs are always included in the data. No need to ask it to include more.
There was a problem hiding this comment.
Right, that's exactly my point 👍 . We are warning the user that the CSV connector doesn't support extended attributes but in fact I believe the CSV connector already supports extended attributes without our doing any coding!
There was a problem hiding this comment.
So I think the action here is to silently ignore the extended attrs list. Just like with AD, if there is no such attr, it just won't show up.
There was a problem hiding this comment.
Right, so Phil and I agree: just remove this test. The CSV directory connector does support extended attributes, as far as we know. Have you verified otherwise, Mike?
| self.after_mapping_hook_scope['target_attributes'].pop('source_attributes', None) | ||
|
|
||
| # invoke the customer's hook code | ||
| # [TODO morr 2017-02-27: Not putting any guardrails around the customer's code, which is treated as |
There was a problem hiding this comment.
You're giving them a confined scope; there's nothing really they can do except crash us or hang us. And there's no good way to guard against that :).
There was a problem hiding this comment.
What about exceptions flying out?
There was a problem hiding this comment.
Right, that's the crash case. I don't think we should guard against that, because we can't continue, and it's pointless for us to catch, log, and rethrow.
There was a problem hiding this comment.
Agreed, removing the comment. Similarly we don't catch errors compiling the hook code, and the resulting behavior is excellent.
| else: | ||
| self.logger.error('Target dashboard group %s is not known; ignored', target_group_qualified_name) | ||
|
|
||
| self.logger.info('Total directory users after filtering: %d', len(filtered_directory_user_by_user_key)) |
There was a problem hiding this comment.
Is there some corresponding log of Total directory users before filtering? If not, why not, and if so, why isn't this statement next to it?
There was a problem hiding this comment.
This was not changed by me, but fwiw this is not confusing in the actual log, modulo the fact that the term "filtering" is used in multiple ways - the --users way and the all_users_filter way. (This is talking about the former.) Anyway I'm not doing anything here.
|
|
||
|
|
||
|
|
||
| def log_after_mapping_hook_scope(self, before_call): |
There was a problem hiding this comment.
Ouch! A boolean variable named before_call, so the call has True or False in it? That's really opaque! Either use keyword variables before_call=False, after_call=False and have the caller specify one of them (and err if caller doesn't), or have a variable context which has a string value which must be before_call or after_call (and is an error otherwise).
There was a problem hiding this comment.
Agree with your complaint, though I can't say I'm crazy about either proposed fix. Leaving alone for now.
| # invoke the customer's hook code | ||
| # [TODO morr 2017-02-27: Not putting any guardrails around the customer's code, which is treated as | ||
| # friendly. Is there anything we should be doing? | ||
| self.log_after_mapping_hook_scope(True) |
There was a problem hiding this comment.
Ouch! How opaque is this! See comment at line 683.
| def log_after_mapping_hook_scope(self, before_call): | ||
| when = 'before' if before_call else 'after' | ||
| if (before_call): | ||
| self.logger.debug('.') |
There was a problem hiding this comment.
what is this about? are you trying to get a separator line in the log? I bet we could be a lot clearer than a simple period.
There was a problem hiding this comment.
It's pretty great the way it is. What would you suggest? :)
No description provided.