def test_change_parameters_on_soft_deleted_wf_module(self):
        workflow = Workflow.create_and_init()

        ModuleVersion.create_or_replace_from_spec({
            'id_name': 'loadurl',
            'name': 'loadurl',
            'category': 'Clean',
            'parameters': [
                {'id_name': 'url', 'type': 'string'},
            ]
        })

        wf_module = workflow.tabs.first().wf_modules.create(
            order=0,
            module_id_name='loadurl',
            last_relevant_delta_id=workflow.last_delta_id,
            is_deleted=True,
            params={'url': ''}
        )

        cmd = self.run_with_async_db(ChangeParametersCommand.create(
            workflow=workflow,
            wf_module=wf_module,
            new_values={'url': 'https://example.com'}
        ))
        self.assertIsNone(cmd)
    def test_change_parameters_on_soft_deleted_wf_module(self):
        workflow = Workflow.create_and_init()

        ModuleVersion.create_or_replace_from_spec({
            "id_name":
            "loadurl",
            "name":
            "loadurl",
            "category":
            "Clean",
            "parameters": [{
                "id_name": "url",
                "type": "string"
            }],
        })

        wf_module = workflow.tabs.first().wf_modules.create(
            order=0,
            slug="step-1",
            module_id_name="loadurl",
            last_relevant_delta_id=workflow.last_delta_id,
            is_deleted=True,
            params={"url": ""},
        )

        cmd = self.run_with_async_db(
            ChangeParametersCommand.create(
                workflow=workflow,
                wf_module=wf_module,
                new_values={"url": "https://example.com"},
            ))
        self.assertIsNone(cmd)
    def test_change_parameters_deny_invalid_params(self, load_module):
        workflow = Workflow.create_and_init()
        wf_module = workflow.tabs.first().wf_modules.create(
            order=0,
            module_id_name='x',
            last_relevant_delta_id=workflow.last_delta_id,
            params={'x': 1}
        )

        ModuleVersion.create_or_replace_from_spec({
            'id_name': 'x', 'name': 'x', 'category': 'Clean',
            'parameters': [
                {'id_name': 'x', 'type': 'integer'},
            ]
        })
        load_module.return_value = LoadedModule('x', '1', ParamDType.Dict({
            'x': ParamDType.Integer(),
        }), migrate_params_impl=lambda x: x)

        with self.assertRaises(ValueError):
            # Now the user requests to change params, giving an invalid param.
            self.run_with_async_db(ChangeParametersCommand.create(
                workflow=workflow,
                wf_module=wf_module,
                new_values={'x': 'Threeve'}
            ))
    def test_change_parameters_deny_invalid_params(self, load_module):
        workflow = Workflow.create_and_init()
        wf_module = workflow.tabs.first().wf_modules.create(
            order=0,
            slug="step-1",
            module_id_name="x",
            last_relevant_delta_id=workflow.last_delta_id,
            params={"x": 1},
        )

        ModuleVersion.create_or_replace_from_spec({
            "id_name":
            "x",
            "name":
            "x",
            "category":
            "Clean",
            "parameters": [{
                "id_name": "x",
                "type": "integer"
            }],
        })
        load_module.return_value = LoadedModule(
            "x",
            "1",
            ParamDType.Dict({"x": ParamDType.Integer()}),
            migrate_params_impl=lambda x: x,
        )

        with self.assertRaises(ValueError):
            # Now the user requests to change params, giving an invalid param.
            self.run_with_async_db(
                ChangeParametersCommand.create(workflow=workflow,
                                               wf_module=wf_module,
                                               new_values={"x": "Threeve"}))
    def test_change_parameters(self):
        # Setup: workflow with loadurl module
        #
        # loadurl is a good choice because it has three parameters, two of
        # which are useful.
        workflow = Workflow.create_and_init()

        ModuleVersion.create_or_replace_from_spec({
            'id_name': 'loadurl',
            'name': 'loadurl',
            'category': 'Clean',
            'parameters': [
                {'id_name': 'url', 'type': 'string'},
                {'id_name': 'has_header', 'type': 'checkbox', 'name': 'HH'},
                {'id_name': 'version_select', 'type': 'custom'},
            ]
        })

        params1 = {
            'url': 'http://example.org',
            'has_header': True,
            'version_select': '',
        }

        wf_module = workflow.tabs.first().wf_modules.create(
            module_id_name='loadurl',
            order=0,
            last_relevant_delta_id=workflow.last_delta_id,
            params=params1
        )

        # Create and apply delta. It should change params.
        cmd = self.run_with_async_db(ChangeParametersCommand.create(
            workflow=workflow,
            wf_module=wf_module,
            new_values={
                'url': 'http://example.com/foo',
                'has_header': False,
            }
        ))
        wf_module.refresh_from_db()

        params2 = {
            'url': 'http://example.com/foo',
            'has_header': False,
            'version_select': '',
        }
        self.assertEqual(wf_module.params, params2)

        # undo
        self.run_with_async_db(cmd.backward())
        wf_module.refresh_from_db()
        self.assertEqual(wf_module.params, params1)

        # redo
        self.run_with_async_db(cmd.forward())
        wf_module.refresh_from_db()
        self.assertEqual(wf_module.params, params2)
    def test_change_parameters_across_module_versions(self, load_module):
        workflow = Workflow.create_and_init()

        # Initialize a WfModule that used module 'x' version '1' (which we
        # don't need to write in code -- after all, that version might be long
        # gone when ChangeParametersCommand is called.
        wf_module = workflow.tabs.first().wf_modules.create(
            order=0,
            module_id_name='x',
            last_relevant_delta_id=workflow.last_delta_id,
            params={'version': 'v1', 'x': 1}  # version-'1' params
        )

        # Now install version '2' of module 'x'.
        #
        # Version '2''s migrate_params() could do anything; in this test, it
        # simply changes 'version' from 'v1' to 'v2'
        ModuleVersion.create_or_replace_from_spec({
            'id_name': 'x', 'name': 'x', 'category': 'Clean',
            'parameters': [
                {'id_name': 'version', 'type': 'string'},
                {'id_name': 'x', 'type': 'integer'},
            ]
        }, source_version_hash='2')
        load_module.return_value = LoadedModule(
            'x',
            '2',
            ParamDType.Dict({
                'version': ParamDType.String(),
                'x': ParamDType.Integer(),
            }),
            migrate_params_impl=lambda params: {**params, 'version': 'v2'}
        )

        # Now the user requests to change params.
        #
        # The user was _viewing_ version '2' of module 'x', though
        # `wf_module.params` was at version 1. (Workbench ran
        # `migrate_params()` without saving the result when it
        # presented `params` to the user.) So the changes should apply atop
        # _migrated_ params.
        cmd = self.run_with_async_db(ChangeParametersCommand.create(
            workflow=workflow,
            wf_module=wf_module,
            new_values={'x': 2}
        ))
        self.assertEqual(wf_module.params, {
            'version': 'v2',  # migrate_params() ran
            'x': 2,  # and we applied changes on top of its output
        })

        self.run_with_async_db(cmd.backward())
        self.assertEqual(wf_module.params, {
            'version': 'v1',  # exactly what we had before
            'x': 1,
        })
    def test_change_parameters_update_tab_delta_ids(self, load_module):
        workflow = Workflow.create_and_init()
        # tab1's wfm1 depends on tab2's wfm2
        wfm1 = workflow.tabs.first().wf_modules.create(
            order=0,
            module_id_name='tabby',
            last_relevant_delta_id=workflow.last_delta_id,
            params={'tab': 'tab-2'})
        tab2 = workflow.tabs.create(position=1, slug='tab-2')
        wfm2 = tab2.wf_modules.create(
            order=0,
            module_id_name='x',
            last_relevant_delta_id=workflow.last_delta_id,
            params={'x': 1})

        # Build the modules
        ModuleVersion.create_or_replace_from_spec({
            'id_name':
            'x',
            'name':
            'x',
            'category':
            'Clean',
            'parameters': [
                {
                    'id_name': 'x',
                    'type': 'integer'
                },
            ]
        })
        ModuleVersion.create_or_replace_from_spec({
            'id_name':
            'tabby',
            'name':
            'tabby',
            'category':
            'Clean',
            'parameters': [
                {
                    'id_name': 'tab',
                    'type': 'tab'
                },
            ]
        })
        load_module.return_value = LoadedModule('x', '1')

        cmd = self.run_with_async_db(
            ChangeParametersCommand.create(workflow=workflow,
                                           wf_module=wfm2,
                                           new_values={'x': 2}))

        wfm1.refresh_from_db()
        wfm2.refresh_from_db()
        self.assertEqual(wfm1.last_relevant_delta_id, cmd.id)
        self.assertEqual(wfm2.last_relevant_delta_id, cmd.id)
    def test_change_parameters_update_tab_delta_ids(self):
        workflow = Workflow.create_and_init()
        # tab1's wfm1 depends on tab2's wfm2
        wfm1 = workflow.tabs.first().wf_modules.create(
            order=0,
            slug="step-1",
            module_id_name="tabby",
            last_relevant_delta_id=workflow.last_delta_id,
            params={"tab": "tab-2"},
        )
        tab2 = workflow.tabs.create(position=1, slug="tab-2")
        wfm2 = tab2.wf_modules.create(
            order=0,
            slug="step-1",
            module_id_name="x",
            last_relevant_delta_id=workflow.last_delta_id,
            params={"x": 1},
        )

        # Build the modules
        ModuleVersion.create_or_replace_from_spec({
            "id_name":
            "x",
            "name":
            "x",
            "category":
            "Clean",
            "parameters": [{
                "id_name": "x",
                "type": "integer"
            }],
        })
        ModuleVersion.create_or_replace_from_spec({
            "id_name":
            "tabby",
            "name":
            "tabby",
            "category":
            "Clean",
            "parameters": [{
                "id_name": "tab",
                "type": "tab"
            }],
        })

        cmd = self.run_with_async_db(
            ChangeParametersCommand.create(workflow=workflow,
                                           wf_module=wfm2,
                                           new_values={"x": 2}))

        wfm1.refresh_from_db()
        wfm2.refresh_from_db()
        self.assertEqual(wfm1.last_relevant_delta_id, cmd.id)
        self.assertEqual(wfm2.last_relevant_delta_id, cmd.id)
Exemplo n.º 9
0
    def test_undo_redo(self):
        ModuleVersion.create_or_replace_from_spec({
            'id_name':
            'pastecsv',
            'name':
            'pastecsv',
            'category':
            'Clean',
            'parameters': [
                {
                    'id_name': 'csv',
                    'type': 'string'
                },
            ]
        })

        workflow = Workflow.create_and_init()
        tab = workflow.tabs.first()

        all_modules = tab.live_wf_modules  # beginning state: nothing

        v0 = workflow.last_delta_id

        # Test undoing nothing at all. Should NOP
        self.run_with_async_db(WorkflowUndo(workflow))
        workflow.refresh_from_db()
        self.assertEqual(workflow.last_delta_id, v0)
        self.assertEqual(all_modules.count(), 0)
        self.assertEqual(workflow.last_delta_id, v0)

        # Add a module
        cmd1 = self.run_with_async_db(
            AddModuleCommand.create(workflow=workflow,
                                    tab=tab,
                                    module_id_name='pastecsv',
                                    position=0,
                                    param_values={}))
        v1 = cmd1.id
        workflow.refresh_from_db()
        self.assertEqual(all_modules.count(), 1)
        self.assertGreater(v1, v0)
        self.assertEqual(workflow.last_delta_id, v1)
        self.assertWfModuleVersions(tab, [v1])

        # Undo, ensure we are back at start
        self.run_with_async_db(WorkflowUndo(workflow))
        workflow.refresh_from_db()
        self.assertEqual(all_modules.count(), 0)
        self.assertEqual(workflow.last_delta_id, v0)
        self.assertWfModuleVersions(tab, [])

        # Redo, ensure we are back at v1
        self.run_with_async_db(WorkflowRedo(workflow))
        workflow.refresh_from_db()
        self.assertEqual(all_modules.count(), 1)
        self.assertEqual(workflow.last_delta_id, v1)
        self.assertWfModuleVersions(tab, [v1])

        # Change a parameter
        cmd2 = self.run_with_async_db(
            ChangeParametersCommand.create(
                workflow=workflow,
                wf_module=tab.live_wf_modules.first(),
                new_values={'csv': 'some value'}))
        v2 = cmd2.id
        workflow.refresh_from_db()
        self.assertEqual(tab.live_wf_modules.first().params['csv'],
                         'some value')
        self.assertEqual(workflow.last_delta_id, v2)
        self.assertGreater(v2, v1)
        self.assertWfModuleVersions(tab, [v2])

        # Undo parameter change
        self.run_with_async_db(WorkflowUndo(workflow))
        workflow.refresh_from_db()
        self.assertEqual(workflow.last_delta_id, v1)
        self.assertEqual(tab.live_wf_modules.first().params['csv'], '')
        self.assertWfModuleVersions(tab, [v1])

        # Redo
        self.run_with_async_db(WorkflowRedo(workflow))
        workflow.refresh_from_db()
        self.assertEqual(workflow.last_delta_id, v2)
        self.assertEqual(tab.live_wf_modules.first().params['csv'],
                         'some value')
        self.assertWfModuleVersions(tab, [v2])

        # Redo again should do nothing
        self.run_with_async_db(WorkflowRedo(workflow))
        workflow.refresh_from_db()
        self.assertEqual(workflow.last_delta_id, v2)
        self.assertEqual(tab.live_wf_modules.first().params['csv'],
                         'some value')
        self.assertWfModuleVersions(tab, [v2])

        # Add one more command so the stack is 3 deep
        cmd3 = self.run_with_async_db(
            ChangeWorkflowTitleCommand.create(workflow=workflow,
                                              new_value='New Title'))
        v3 = cmd3.id
        self.assertGreater(v3, v2)
        self.assertWfModuleVersions(tab, [v2])

        # Undo twice
        self.run_with_async_db(WorkflowUndo(workflow))
        workflow.refresh_from_db()
        self.assertEqual(workflow.last_delta, cmd2)
        self.assertWfModuleVersions(tab, [v2])
        self.run_with_async_db(WorkflowUndo(workflow))
        workflow.refresh_from_db()
        self.assertEqual(workflow.last_delta, cmd1)
        self.assertWfModuleVersions(tab, [v1])

        # Redo twice
        self.run_with_async_db(WorkflowRedo(workflow))
        workflow.refresh_from_db()
        self.assertEqual(workflow.last_delta, cmd2)
        self.assertWfModuleVersions(tab, [v2])
        self.run_with_async_db(WorkflowRedo(workflow))
        workflow.refresh_from_db()
        self.assertEqual(workflow.last_delta, cmd3)
        self.assertWfModuleVersions(tab, [v2])

        # Undo again to get to a place where we have two commands to redo
        self.run_with_async_db(WorkflowUndo(workflow))
        self.run_with_async_db(WorkflowUndo(workflow))
        workflow.refresh_from_db()
        self.assertEqual(workflow.last_delta, cmd1)

        # Now add a new command. It should remove cmd2, cmd3 from the redo
        # stack and delete them from the db
        wfm = all_modules.first()
        cmd4 = self.run_with_async_db(
            ChangeWfModuleNotesCommand.create(workflow=workflow,
                                              wf_module=wfm,
                                              new_value='Note of no note'))
        v4 = cmd4.id
        workflow.refresh_from_db()
        self.assertEqual(workflow.last_delta_id, v4)
        self.assertEqual(set(Delta.objects.values_list('id', flat=True)),
                         {v0, v1, v4})  # v2, v3 deleted

        # Undo back to start, then add a command, ensure it deletes dangling
        # commands (tests an edge case in Delta.save)
        self.run_with_async_db(WorkflowUndo(workflow))
        workflow.refresh_from_db()
        self.assertEqual(workflow.last_delta_id, v1)
        cmd5 = self.run_with_async_db(
            ChangeWfModuleNotesCommand.create(workflow=workflow,
                                              wf_module=cmd1.wf_module,
                                              new_value='Note of some note'))
        v5 = cmd5.id
        workflow.refresh_from_db()
        self.assertEqual(workflow.last_delta_id, v5)
        self.assertEqual(set(Delta.objects.values_list('id', flat=True)),
                         {v0, v1, v5})  # v1, v4 deleted
        self.assertWfModuleVersions(tab, [v1])
    def test_change_parameters(self):
        # Setup: workflow with loadurl module
        #
        # loadurl is a good choice because it has three parameters, two of
        # which are useful.
        workflow = Workflow.create_and_init()

        ModuleVersion.create_or_replace_from_spec({
            "id_name":
            "loadurl",
            "name":
            "loadurl",
            "category":
            "Clean",
            "parameters": [
                {
                    "id_name": "url",
                    "type": "string"
                },
                {
                    "id_name": "has_header",
                    "type": "checkbox",
                    "name": "HH"
                },
                {
                    "id_name": "version_select",
                    "type": "custom"
                },
            ],
        })

        params1 = {
            "url": "http://example.org",
            "has_header": True,
            "version_select": "",
        }

        wf_module = workflow.tabs.first().wf_modules.create(
            module_id_name="loadurl",
            order=0,
            slug="step-1",
            last_relevant_delta_id=workflow.last_delta_id,
            params=params1,
        )

        # Create and apply delta. It should change params.
        cmd = self.run_with_async_db(
            ChangeParametersCommand.create(
                workflow=workflow,
                wf_module=wf_module,
                new_values={
                    "url": "http://example.com/foo",
                    "has_header": False
                },
            ))
        wf_module.refresh_from_db()

        params2 = {
            "url": "http://example.com/foo",
            "has_header": False,
            "version_select": "",
        }
        self.assertEqual(wf_module.params, params2)

        # undo
        self.run_with_async_db(cmd.backward())
        wf_module.refresh_from_db()
        self.assertEqual(wf_module.params, params1)

        # redo
        self.run_with_async_db(cmd.forward())
        wf_module.refresh_from_db()
        self.assertEqual(wf_module.params, params2)