-
-
Save smitchell/35702d90240ee01ffe14735980d9cdd9 to your computer and use it in GitHub Desktop.
| #!/usr/bin/env python3 | |
| from pprint import pprint | |
| from marshmallow import Schema | |
| from marshmallow import fields | |
| from marshmallow import post_load | |
| class ModifyEntryRequest: | |
| def __init__(self, dn, changes, controls=None): | |
| self.dn = dn | |
| self.changes = changes | |
| self.controls = controls | |
| class ModifyEntryRequestSchema(Schema): | |
| dn = fields.Str() | |
| changes = fields.Dict( | |
| keys=fields.Str(), | |
| values=fields.List(fields.Dict(keys=fields.Str(), values=fields.List(fields.Str()))), required=True) | |
| controls = fields.Dict(keys=fields.Str(), values=fields.Str(), allow_none=True) | |
| def convert_dict_to_tuple(self, changes): | |
| for attribute_key in changes: | |
| attribute = changes[attribute_key] | |
| temp_dict = {} | |
| result = [] | |
| for operation in attribute: | |
| for operation_key in operation.keys(): | |
| if operation_key in temp_dict: | |
| temp_dict[operation_key] += operation[operation_key] | |
| else: | |
| temp_dict[operation_key] = operation[operation_key] | |
| for key in temp_dict.keys(): | |
| result.append(tuple([key] + temp_dict[key])) | |
| changes[attribute_key] = result | |
| @post_load | |
| def create_add_entry_request(self, data, **kwargs): | |
| self.convert_dict_to_tuple(data['changes']) | |
| return ModifyEntryRequest(**data) | |
| def test_schema(): | |
| print('test_schema()') | |
| schema = ModifyEntryRequestSchema() | |
| # changes = {'mobile': [{'MODIFY_ADD': ['913-222-3344']}]} | |
| changeEntityRequestJson = {'dn': 'cn=mwatkins,cn=users,cn=employees,ou=test,o=lab', 'changes': {'mobile': [{'MODIFY_ADD': ['913-222-3344']}]}} | |
| modify_entry_request = schema.load(changeEntityRequestJson) | |
| changes = modify_entry_request.changes | |
| for attribute_key in changes: | |
| attribute = changes[attribute_key] | |
| for operation in attribute: | |
| operation_type = type(operation) | |
| if operation_type != tuple: | |
| raise Exception(f'Expected class tuple but found {type(operation)}') | |
| if modify_entry_request.dn != changeEntityRequestJson['dn']: | |
| raise Exception(f'Expected {changeEntityRequestJson["dn"]} but found {modify_entry_request.dn}') | |
| if __name__ == '__main__': | |
| test_schema() |
| #!/usr/bin/env python3 | |
| from pprint import pprint | |
| def convert_dict_to_tuple(data): | |
| for attribute_key in data: | |
| attribute = data[attribute_key] | |
| temp_dict = {} | |
| result = [] | |
| for operation in attribute: | |
| for operation_key in operation.keys(): | |
| if operation_key in temp_dict: | |
| temp_dict[operation_key] += operation[operation_key] | |
| else: | |
| temp_dict[operation_key] = operation[operation_key] | |
| for key in temp_dict.keys(): | |
| result.append(tuple([key] + temp_dict[key])) | |
| data[attribute_key] = result | |
| def test_convert_dict_to_tuple(): | |
| data = {'mobile': [{'MODIFY_ADD': ['913-222-3344']}]} | |
| print('BEFORE:') | |
| pprint(data) | |
| convert_dict_to_tuple(data) | |
| for attribute_key in data: | |
| attribute = data[attribute_key] | |
| for operation in attribute: | |
| operation_type = type(operation) | |
| if operation_type != tuple: | |
| raise Exception(f'Expected class tuple but found {type(operation)}') | |
| print('AFTER:') | |
| pprint(data) | |
| if __name__ == '__main__': | |
| test_convert_dict_to_tuple() |
| #!/usr/bin/env python3 | |
| # -*- coding: utf-8 -*- | |
| import asyncio | |
| import logging | |
| from nats.aio.client import Client as NATS, ErrNoServers | |
| from nats.aio.errors import ErrSlowConsumer | |
| DEFAULT_ITERATIONS = 10000 | |
| HASH_MODULO = 250 | |
| async def run(loop): | |
| nc = NATS() | |
| nats_host = 'nats://127.0.0.1:4222' | |
| nats_subject = 'Test Subject' | |
| async def disconnected_cb(): | |
| print('Got disconnected!') | |
| async def reconnected_cb(): | |
| print('Got reconnected to {url}'.format(url=nc.connected_url.netloc)) | |
| async def error_cb(e): | |
| if type(e) is ErrSlowConsumer: | |
| print('Slow consumer error, unsubscribing from handling further messages...') | |
| await nc.unsubscribe(e.sid) | |
| async def closed_cb(): | |
| print('Connection is closed') | |
| async def subscribe_handler(mesg): | |
| print('Got message: ', mesg.subject, mesg.reply, mesg.data) | |
| options = { | |
| 'servers': [nats_host], | |
| 'name': 'NATS test', | |
| 'loop': loop, | |
| 'disconnected_cb': disconnected_cb, | |
| 'reconnected_cb': reconnected_cb, | |
| 'error_cb': error_cb, | |
| 'closed_cb': closed_cb | |
| } | |
| try: | |
| print(f'Connecting to {nats_host}') | |
| await nc.connect(**options) | |
| except ErrNoServers as e: | |
| print(f'Connection error {e}') | |
| return | |
| if nc.is_connected: | |
| print(f'Subscribing to {nats_subject}') | |
| await nc.subscribe(nats_subject, cb=subscribe_handler) | |
| # | |
| # This was what was causing my subscriber to disconnect. Now, the code falls through to the run_forever(). | |
| # | |
| # print('Draining') | |
| # await nc.drain() | |
| # return | |
| if __name__ == '__main__': | |
| loop = asyncio.get_event_loop() | |
| try: | |
| loop.run_until_complete(run(loop)) | |
| loop.run_forever() | |
| except KeyboardInterrupt as e: | |
| logging.warning('Keyboard interrupt') | |
| finally: | |
| loop.run_until_complete(loop.shutdown_asyncgens()) | |
| loop.close() |
Since tuple doesn't exist in JSON, I tried changing the schema from tuple to duct, and in @post_load I tried converting the list of the dictionaries into a list of tuples. I never hit @post_load because I get this error.
Traceback (most recent call last):
File "main.py", line 64, in
test_schema()
File "main.py", line 53, in test_schema
{'MODIFY_ADD', ['913-222-3344']}
TypeError: unhashable type: 'list'
I think you're just passing a badly formatted dict to the schema, you've defined it correctly (although I would look at possibly using nested schemas rather than a nested and inflexible fields.Dict - Nested) I changed changes to the following:
changes = {"changes": {"mobile": [{"MODIFY_ADD": ["913-222-3344"]}]}, "dn": "blah"}
And the test case passed for me.
you just beat me to it!:
payload = {'dn': 'som-DN', 'changes': {'mobile': [{'MODIFY_ADD': ['913-222-3344']}]}}
modify_entry_request = schema.load(payload)
Sweet! Thanks, @chaseWilliams, and @ephoning. It's has been a long week. Once I switched to the entire request object, and not just changes it worked and I was able to include the code to transform the list of dictionaries to a list of tuples successfully.
The code demonstrates a problem I am having representing a tuple with Marshmallow.
Specifically, I can't get the schema to work with The LDAP3 Modify operation which
includes an array of tuples. I get this error:
marshmallow.exceptions.ValidationError: {'changes': ['Missing data for required field.'], 'mobile': ['Unknown field.']}
I am testing with this input:
{
mobile': [('MODIFY_ADD', ['913-222-3344'])]
}
Here is the spec:
{
‘attribute1’: [(operation, [val1, val2, …]), (operation2, [val1, val2, …]), …],
‘attribute2’: [(operation, [val1, val2, …])], …
}