def create_label(state_machine_name, label_name): """ Create a label with a given metadata, and start it in the state machine. Returns: - 201 Created: if the label is successfully created and started. - 409 Conflict: if the label already exists in the state machine. - 404 Not Found: if the state machine does not exist. - 400 Bad Request: if the request body is not a valid metadata. Successful return codes return the full created metadata for the label. """ app = server.config.app label = LabelRef(label_name, state_machine_name) data = request.get_json() try: initial_metadata = data['metadata'] except KeyError: abort(400, "No metadata given") try: initial_state_name = \ app.config.state_machines[state_machine_name].states[0].name metadata = state_machine.create_label(app, label, initial_metadata) return jsonify(metadata=metadata, state=initial_state_name), 201 except LookupError: msg = f"State machine '{state_machine_name}' does not exist" abort(404, msg) except LabelAlreadyExists: msg = f"Label {label_name} already exists in '{state_machine_name}'" abort(409, msg)
def test_continues_after_time_since_entering_gate(app, current_state): label = LabelRef('foo', 'test_machine_timing') gate = app.config.state_machines['test_machine_timing'].states[0] with freeze_time('2018-01-24 12:00:00'), app.new_session(): state_machine.create_label( app, label, {}, ) # 1 day later, not enough to progress with freeze_time('2018-01-25 12:00:00'), app.new_session(): process_gate( app=app, state=gate, state_machine=state_machine, label=label, ) assert current_state(label) == 'start' # 2 days later with freeze_time('2018-01-26 12:00:00'), app.new_session(): process_gate( app=app, state=gate, state_machine=state_machine, label=label, ) assert current_state(label) == 'end'
def get_label(state_machine_name, label_name): """ Get a label within a given state machine. Returns: - 200 Ok: if the label is exists. - 404 Not Found: if the state machine or label does not exist. - 410 Gone: if the label once existed but has since been deleted. Successful return codes return the full metadata for the label. """ app = server.config.app label = LabelRef(label_name, state_machine_name) try: metadata = state_machine.get_label_metadata(app, label) except UnknownLabel as e: abort( 410 if e.deleted else 404, f"Label {label.name} in state machine '{label.state_machine}' " f"does not exist.", ) except UnknownStateMachine: abort(404, f"State machine '{label.state_machine}' does not exist.") state = state_machine.get_label_state(app, label) return jsonify(metadata=metadata, state=state.name)
def test_maintains_updated_field_on_label(app, mock_test_feed): label = LabelRef('foo', 'test_machine') with mock_test_feed(), app.new_session(): state_machine.create_label( app, label, {}, ) first_updated = app.session.query(Label.updated, ).filter_by( name=label.name, state_machine=label.state_machine, ).scalar() with mock_test_feed(), app.new_session(): state_machine.update_metadata_for_label( app, label, {'foo': 'bar'}, ) second_updated = app.session.query(Label.updated, ).filter_by( name=label.name, state_machine=label.state_machine, ).scalar() assert second_updated > first_updated
def test_stays_in_gate_if_gate_processing_fails(app, mock_test_feed, current_state): label = LabelRef('foo', 'test_machine') with mock_test_feed(), app.new_session(): state_machine.create_label( app, label, {}, ) assert current_state(label) == 'start' with mock_test_feed(), mock.patch( 'routemaster.context.Context._pre_warm_feeds', side_effect=RequestException, ), app.new_session(): state_machine.update_metadata_for_label( app, label, {'should_progress': True}, ) assert metadata_triggers_processed(app, label) is False assert current_state(label) == 'start'
def test_label_get_state(app, mock_test_feed): label = LabelRef('foo', 'test_machine') with mock_test_feed(), app.new_session(): state_machine.create_label( app, label, {'foo': 'bar'}, ) with app.new_session(): assert state_machine.get_label_state(app, label).name == 'start'
def test_delete_label_only_deletes_target_label(app, assert_history, mock_test_feed): label_foo = LabelRef('foo', 'test_machine') label_bar = LabelRef('bar', 'test_machine') with mock_test_feed(), app.new_session(): state_machine.create_label(app, label_foo, {}) state_machine.create_label(app, label_bar, {}) state_machine.delete_label(app, label_foo) with app.new_session(): with pytest.raises(DeletedLabel): state_machine.get_label_metadata( app, label_foo, ) state_machine.get_label_metadata( app, label_bar, )
def test_state_machine_progresses_automatically(app, mock_webhook, mock_test_feed, current_state): label = LabelRef('foo', 'test_machine') with mock_webhook() as webhook, mock_test_feed(), app.new_session(): state_machine.create_label( app, label, {'should_progress': True}, ) webhook.assert_called_once() assert current_state(label) == 'end'
def test_state_machine_simple(app, mock_test_feed): label = LabelRef('foo', 'test_machine') with mock_test_feed(), app.new_session(): state_machine.create_label( app, label, {}, ) state_machine.update_metadata_for_label( app, label, {'foo': 'bar'}, ) with app.new_session(): assert state_machine.get_label_metadata(app, label) == {'foo': 'bar'}
def test_delete_label(app, assert_history, mock_test_feed): label_foo = LabelRef('foo', 'test_machine') with mock_test_feed(), app.new_session(): state_machine.create_label(app, label_foo, {}) state_machine.delete_label(app, label_foo) with app.new_session(): with pytest.raises(DeletedLabel): state_machine.get_label_metadata( app, label_foo, ) assert_history([ (None, 'start'), ('start', None), ])
def delete_label(state_machine_name, label_name): """ Delete a label in a state machine. Marks a label as deleted, but does not remove it from the database. Deleted labels cannot be updated and will not move state. Returns: - 204 No content: if the label is successfully deleted (or did not exist). - 404 Not Found: if the state machine does not exist. """ app = server.config.app label = LabelRef(label_name, state_machine_name) try: state_machine.delete_label(app, label) except UnknownStateMachine: msg = f"State machine '{state_machine_name}' does not exist" abort(404, msg) return '', 204
def test_state_machine_does_not_progress_when_not_eligible( app, mock_test_feed, current_state): label = LabelRef('foo', 'test_machine') with mock_test_feed(), app.new_session(): state_machine.create_label( app, label, {}, ) assert current_state(label) == 'start' with mock_test_feed(), app.new_session(): state_machine.update_metadata_for_label( app, label, {'should_progress': False}, ) assert current_state(label) == 'start'
def update_label(state_machine_name, label_name): """ Update a label in a state machine. Triggering progression if necessary according to the state machine configuration. Updates are _merged_ with existing metadata. Returns: - 200 Ok: if the label is successfully updated. - 400 Bad Request: if the request body is not a valid metadata. - 404 Not Found: if the state machine or label does not exist. - 410 Gone: if the label once existed but has since been deleted. Successful return codes return the full new metadata for a label. """ app = server.config.app label = LabelRef(label_name, state_machine_name) try: patch_metadata = request.get_json()['metadata'] except KeyError: abort(400, "No new metadata") try: new_metadata = state_machine.update_metadata_for_label( app, label, patch_metadata, ) state = state_machine.get_label_state(app, label) return jsonify(metadata=new_metadata, state=state.name) except UnknownStateMachine: msg = f"State machine '{state_machine_name}' does not exist" abort(404, msg) except UnknownLabel as e: abort( 410 if e.deleted else 404, f"Label {label_name} does not exist in state machine " f"'{state_machine_name}'.", )
def test_state_machine_progresses_on_update(app, mock_webhook, mock_test_feed, current_state): label = LabelRef('foo', 'test_machine') with mock_test_feed(), app.new_session(): state_machine.create_label( app, label, {}, ) assert current_state(label) == 'start' with mock_webhook() as webhook, mock_test_feed(), app.new_session(): state_machine.update_metadata_for_label( app, label, {'should_progress': True}, ) webhook.assert_called_once() assert metadata_triggers_processed(app, label) is True assert current_state(label) == 'end'
def test_update_metadata_for_label_raises_for_unknown_state_machine(app): label = LabelRef('foo', 'nonexistent_machine') with pytest.raises(UnknownStateMachine), app.new_session(): state_machine.update_metadata_for_label(app, label, {})
def test_label_get_state_raises_for_unknown_state_machine(app): label = LabelRef('foo', 'unknown_machine') with pytest.raises(UnknownStateMachine), app.new_session(): assert state_machine.get_label_state(app, label)