/** * Tests auto saving after an action execution. */ public function testActionAutoSave() { $rule = $this->rulesExpressionManager->createRule(); // Just leverage the entity save action, which by default uses auto-saving. $rule->addAction('rules_entity_save', ContextConfig::create()->map('entity', 'entity')); $entity = $this->prophesizeEntity(EntityInterface::class); $entity->save()->shouldBeCalledTimes(1); RulesComponent::create($rule)->addContextDefinition('entity', ContextDefinition::create('entity'))->setContextValue('entity', $entity->reveal())->execute(); }
/** * Tests passing a string context to a condition. */ public function testContextPassing() { $rule = $this->expressionManager->createRule(); $rule->addCondition('rules_test_string_condition', ContextConfig::create()->map('text', 'test')); $rule->addAction('rules_test_log'); RulesComponent::create($rule)->addContextDefinition('test', ContextDefinition::create('string'))->setContextValue('test', 'test value')->execute(); // Test that the action logged something. $this->assertRulesLogEntryExists('action called'); }
/** * @cover ::getState() */ public function testGetState() { $rule = $this->rulesExpressionManager->createRule(); $component = RulesComponent::create($rule); $this->assertInstanceOf(ExecutionStateInterface::class, $component->getState()); // Test that set context values are available in the state. $component->addContextDefinition('foo', ContextDefinition::create('string'))->setContextValue('foo', 'bar'); $this->assertEquals($component->getState()->getVariableValue('foo'), 'bar'); }
/** * {@inheritdoc} */ public function __construct(array $values) { // Filter out any @Translation annotation objects. foreach ($values as $key => $value) { if ($value instanceof Translation) { $values[$key] = $value->get(); } } $this->definition = RulesContextDefinition::createFromArray($values); }
/** * Tests that NULL values for contexts are allowed if specified. */ public function testAllowNullValue() { // Configure a simple rule with the data set action which allows NULL // values. $action = $this->expressionManager->createInstance('rules_action', ContextConfig::create()->setConfigKey('action_id', 'rules_data_set')->map('data', 'null_variable')->map('value', 'new_value')->toArray()); $rule = $this->expressionManager->createRule()->addExpressionObject($action); $component = RulesComponent::create($rule)->addContextDefinition('null_variable', ContextDefinition::create('string'))->addContextDefinition('new_value', ContextDefinition::create('string'))->setContextValue('null_variable', NULL)->setContextValue('new_value', 'new value'); $component->execute(); $this->assertEquals('new value', $component->getState()->getVariableValue('null_variable')); }
/** * {@inheritdoc} */ public function processDefinition(&$definition, $plugin_id) { parent::processDefinition($definition, $plugin_id); if (!isset($definition['context'])) { $definition['context'] = []; } // Convert the flat context arrays into ContextDefinition objects. foreach ($definition['context'] as $context_name => $values) { $definition['context'][$context_name] = ContextDefinition::createFromArray($values); } }
/** * Tests that the loop list item is removed after the loop. */ public function testPrepareAfterLoop() { $rule = $this->rulesExpressionManager->createRule(); $loop = $this->rulesExpressionManager->createInstance('rules_loop', ['list' => 'string_list']); $action = $this->rulesExpressionManager->createAction('rules_test_string')->setConfiguration(ContextConfig::create()->setValue('text', 'x')->toArray()); $loop->addExpressionObject($action); $rule->addExpressionObject($loop); $state = RulesComponent::create($rule)->addContextDefinition('string_list', ContextDefinition::create('string')->setMultiple())->getMetadataState(); $found = $rule->prepareExecutionMetadataState($state); $this->assertFalse($state->hasDataDefinition('list_item')); $this->assertNull($found); }
/** * Make sure that expressions using context definitions can be exported. */ public function testContextDefinitionExport() { $rule = $this->expressionManager->createRule(['context_definitions' => ['test' => ContextDefinition::create('string')->setLabel('Test string')->toArray()]]); $config_entity = $this->storage->create(['id' => 'test_rule'])->setExpression($rule); $config_entity->save(); $loaded_entity = $this->storage->load('test_rule'); // Create the Rules expression object from the configuration. $expression = $loaded_entity->getExpression(); $context_definitions = $expression->getContextDefinitions(); $this->assertEqual($context_definitions['test']->getDataType(), 'string', 'Data type of context definition is correct.'); $this->assertEqual($context_definitions['test']->getLabel(), 'Test string', 'Label of context definition is correct.'); }
/** * Tests that the numeric offset plugin works. */ public function testNumericOffset() { // Configure a simple rule with one action. $action = $this->expressionManager->createInstance('rules_action', ContextConfig::create()->map('message', 'message')->map('type', 'type')->process('message', 'rules_numeric_offset', ['offset' => 1])->setConfigKey('action_id', 'rules_system_message')->toArray()); $component = RulesComponent::create($this->expressionManager->createRule())->addContextDefinition('message', ContextDefinition::create('string'))->addContextDefinition('type', ContextDefinition::create('string'))->setContextValue('message', 1)->setContextValue('type', 'status'); $component->getExpression()->addExpressionObject($action); $component->execute(); $messages = drupal_set_message(); // The original value was 1 and the processor adds 1, so the result should // be 2. $this->assertEquals((string) $messages['status'][0], '2'); }
/** * Make sure that expressions using context definitions can be exported. */ public function testContextDefinitionExport() { $component = RulesComponent::create($this->expressionManager->createRule())->addContextDefinition('test', ContextDefinition::create('string')->setLabel('Test string')); $config_entity = $this->storage->create(['id' => 'test_rule'])->updateFromComponent($component); $config_entity->save(); $loaded_entity = $this->storage->load('test_rule'); // Create the Rules expression object from the configuration. $expression = $loaded_entity->getExpression(); $this->assertInstanceOf(Rule::class, $expression); $context_definitions = $loaded_entity->getContextDefinitions(); $this->assertEquals($context_definitions['test']->getDataType(), 'string', 'Data type of context definition is correct.'); $this->assertEquals($context_definitions['test']->getLabel(), 'Test string', 'Label of context definition is correct.'); }
/** * Tests that date tokens are formatted correctly. */ public function testSystemDateToken() { // Configure a simple rule with one action. and token replacements enabled. $action = $this->expressionManager->createInstance('rules_action', ContextConfig::create()->setValue('message', "The date is {{ date | format_date('custom', 'Y-m') }}!")->setValue('type', 'status')->process('message', 'rules_tokens')->setConfigKey('action_id', 'rules_system_message')->toArray()); $rule = $this->expressionManager->createRule(); $rule->addExpressionObject($action); RulesComponent::create($rule)->addContextDefinition('date', ContextDefinition::create('timestamp'))->setContextValue('date', REQUEST_TIME)->execute(); $messages = drupal_set_message(); /** @var \Drupal\Core\Datetime\DateFormatterInterface $date_formatter */ $date_formatter = $this->container->get('date.formatter'); $date = $date_formatter->format(REQUEST_TIME, 'custom', 'Y-m'); $this->assertEquals("The date is {$date}!", (string) $messages['status'][0]); }
/** * Tests that date tokens are formatted correctly. */ public function testSystemDateToken() { // Configure a simple rule with one action. and token replacements enabled. $action = $this->expressionManager->createInstance('rules_action', ContextConfig::create()->map('message', 'message')->map('type', 'type')->process('message', 'rules_tokens')->setConfigKey('action_id', 'rules_system_message')->toArray()); $rule = $this->expressionManager->createRule(['context_definitions' => ['message' => ContextDefinition::create('string')->toArray(), 'type' => ContextDefinition::create('string')->toArray()]]); $rule->setContextValue('message', 'The date is [date:custom:Y-m]!'); $rule->setContextValue('type', 'status'); $rule->addExpressionObject($action); $rule->execute(); $messages = drupal_set_message(); $date = format_date(time(), 'custom', 'Y-m'); $this->assertEqual((string) $messages['status'][0], "The date is {$date}!"); }
/** * Creates a component based on the given configuration array. * * @param array $configuration * The component configuration, as returned from ::getConfiguration(). * * @return static */ public static function createFromConfiguration(array $configuration) { $configuration += ['context_definitions' => [], 'provided_context_definitions' => []]; // @todo: Can we improve this use dependency injection somehow? $expression_manager = \Drupal::service('plugin.manager.rules_expression'); $expression = $expression_manager->createInstance($configuration['expression']['id'], $configuration['expression']); $component = static::create($expression); foreach ($configuration['context_definitions'] as $name => $definition) { $component->addContextDefinition($name, ContextDefinition::createFromArray($definition)); } foreach ($configuration['provided_context_definitions'] as $name => $definition) { $component->provideContext($name); } return $component; }
/** * Tests that the data set action works on entities. */ public function testDataSetEntities() { $entity_type_manager = $this->container->get('entity_type.manager'); $entity_type_manager->getStorage('node_type')->create(['type' => 'page'])->save(); $node = $entity_type_manager->getStorage('node')->create(['title' => 'test', 'type' => 'page']); // Configure a simple rule with one action. $action = $this->expressionManager->createInstance('rules_action', ContextConfig::create()->setConfigKey('action_id', 'rules_data_set')->map('data', 'node.title')->map('value', 'new_title')->toArray()); $rule = $this->expressionManager->createRule()->addExpressionObject($action); RulesComponent::create($rule)->addContextDefinition('node', ContextDefinition::create('entity:node'))->addContextDefinition('new_title', ContextDefinition::create('string'))->setContextValue('node', $node)->setContextValue('new_title', 'new title')->execute(); $this->assertEquals('new title', $node->getTitle()); $this->assertNotNull($node->id(), 'Node ID is set, which means that the node has been auto-saved.'); }
/** * Tests that provided context definitons are created from configuration. */ public function testProvidedDefinitionFromConfig() { $rule = new Rule(['provided_definitions' => ['node' => ContextDefinition::create('entity:node')->setLabel('node')->toArray()]], 'rules_rule', [], $this->expressionManager->reveal()); $provided_definition = $rule->getProvidedContextDefinition('node'); $this->assertSame($provided_definition->getDataType(), 'entity:node'); }
/** * Tests that a complex data context is assigned something that matches. */ public function testComplexTypeViolation() { $rule = $this->rulesExpressionManager->createRule(); // The condition expects a node context but gets a list instead which cause // the violation. $rule->addCondition('rules_node_is_of_type', ContextConfig::create()->map('node', 'list_variable')->map('types', 'list_variable')); $violation_list = RulesComponent::create($rule)->addContextDefinition('list_variable', ContextDefinition::create('list'))->checkIntegrity(); $this->assertEquals(1, iterator_count($violation_list)); $this->assertEquals('Expected a complex data type for context <em class="placeholder">Node</em> but got a list data type instead.', (string) $violation_list[0]->getMessage()); }
/** * Helper to mock a context definition with a mocked data definition. * * @param string $data_type * The data type, example "entity:node". * @param \Prophecy\Prophecy\ProphecyInterface $data_definition * A prophecy that represents a data definition object. * * @return \Drupal\rules\Context\ContextDefinition * The context definition with the data definition prophecy in it. */ protected function getContextDefinitionFor($data_type, ProphecyInterface $data_definition) { // Mock all the setter calls on the data definition that can be ignored. $data_definition->setLabel(Argument::any())->willReturn($data_definition->reveal()); $data_definition->setDescription(Argument::any())->willReturn($data_definition->reveal()); $data_definition->setRequired(Argument::any())->willReturn($data_definition->reveal()); $data_definition->setLabel(Argument::any())->willReturn($data_definition->reveal()); $data_definition->setConstraints(Argument::any())->willReturn($data_definition->reveal()); $data_definition->getConstraints()->willReturn([]); $data_definition->getDataType()->willReturn($data_type); $original_definition = $this->typedDataManager->getDefinition($data_type); $data_definition->getClass()->willReturn($original_definition['class']); $context_definition = ContextDefinition::create($data_type); // Inject a fake typed data manger that will return our data definition // prophecy if asked for it in the ContextDefinition class. $typed_data_manager = $this->prophesize(TypedDataManagerInterface::class); $typed_data_manager->createDataDefinition($data_type)->willReturn($data_definition->reveal()); $context_definition->setTypedDataManager($typed_data_manager->reveal()); return $context_definition; }
/** * Tests asserted metadata of negated conditions is ignored. */ public function testAssertingOfNegatedConditions() { // Negate the condition only and make sure it is ignored. $rule = $this->expressionManager->createRule(); $rule->addCondition('rules_entity_is_of_bundle', ContextConfig::create()->map('entity', 'node')->setValue('type', 'node')->setValue('bundle', 'page'))->negate(TRUE); $rule->addAction('rules_system_message', ContextConfig::create()->map('message', 'node.field_text.value')->setValue('type', 'status')); $violation_list = RulesComponent::create($rule)->addContextDefinition('node', ContextDefinition::create('entity:node'))->checkIntegrity(); $this->assertEquals(1, iterator_count($violation_list)); // Add an negated AND and make sure it is ignored. $rule = $this->expressionManager->createRule(); $and = $this->expressionManager->createAnd(); $and->addCondition('rules_entity_is_of_bundle', ContextConfig::create()->map('entity', 'node')->setValue('type', 'node')->setValue('bundle', 'page')); $and->negate(TRUE); $rule->addExpressionObject($and); $rule->addAction('rules_system_message', ContextConfig::create()->map('message', 'node.field_text.value')->setValue('type', 'status')); $violation_list = RulesComponent::create($rule)->addContextDefinition('node', ContextDefinition::create('entity:node'))->checkIntegrity(); $this->assertEquals(1, iterator_count($violation_list)); }
/** * Gets the definitions of the used context. * * @return \Drupal\rules\Context\ContextDefinitionInterface[] * The array of context definition, keyed by context name. */ public function getContextDefinitions() { $definitions = []; foreach ($this->context_definitions as $name => $definition) { $definitions[$name] = ContextDefinition::createFromArray($definition); } return $definitions; }
/** * Tests that auto saving is only triggered once with nested components. */ public function testAutosaveOnlyOnce() { $entity = $this->prophesizeEntity(EntityInterface::class); $nested_rule = $this->rulesExpressionManager->createRule(); $nested_rule->addAction('rules_entity_save', ContextConfig::create()->map('entity', 'entity')); $rules_config = new RulesComponentConfig(['id' => 'test_rule', 'label' => 'Test rule'], 'rules_component'); $rules_config->setExpression($nested_rule); $rules_config->setContextDefinitions(['entity' => ContextDefinition::create('entity')]); $this->prophesizeStorage([$rules_config]); // Create a rule with a nested rule. Overall there are 2 actions to set the // entity then. $rule = $this->rulesExpressionManager->createRule(); $rule->addAction('rules_component:test_rule', ContextConfig::create()->map('entity', 'entity')); $rule->addAction('rules_entity_save', ContextConfig::create()->map('entity', 'entity')); // Auto-saving should only be triggered once on the entity. $entity->save()->shouldBeCalledTimes(1); RulesComponent::create($rule)->addContextDefinition('entity', ContextDefinition::create('entity'))->setContextValue('entity', $entity->reveal())->execute(); }
/** * {@inheritdoc} */ public function __construct(array $values) { $this->definition = RulesContextDefinition::createFromArray($values); }
/** * Converts a context definition configuration array into objects. * * @param array $configuration * The configuration properties for populating the context definition * object. * * @return \Drupal\Core\Plugin\Context\ContextDefinitionInterface[] * A list of context definitions with the same keys. */ protected function createContextDefinitions(array $configuration) { return array_map(function ($definition_array) { return ContextDefinition::createFromArray($definition_array); }, $configuration); }
/** * Tests context can be refined based upon mapped context. */ public function testRefiningContextBasedonMappedContext() { // DataComparision condition refines context based on selected data. Thus // it for the test and ensure checking integrity passes when the comparison // value is of a compatible type and fails else. $rule = $this->rulesExpressionManager->createRule(); $rule->addCondition('rules_data_comparison', ContextConfig::create()->map('data', 'text')->map('value', 'text2')); $violation_list = RulesComponent::create($rule)->addContextDefinition('text', ContextDefinition::create('string'))->addContextDefinition('text2', ContextDefinition::create('string'))->checkIntegrity(); $this->assertEquals(0, iterator_count($violation_list)); $violation_list = RulesComponent::create($rule)->addContextDefinition('text', ContextDefinition::create('string'))->addContextDefinition('text2', ContextDefinition::create('integer'))->checkIntegrity(); $this->assertEquals(1, iterator_count($violation_list)); }
/** * Gets the definitions of the provided context. * * @return \Drupal\rules\Context\ContextDefinitionInterface[] * The array of context definition, keyed by context name. */ public function getProvidedContextDefinitions() { $definitions = []; if (!empty($this->component['provided_context_definitions'])) { foreach ($this->component['provided_context_definitions'] as $name => $definition) { $definitions[$name] = ContextDefinition::createFromArray($definition); } } return $definitions; }
/** * {@inheritdoc} */ public function getDerivativeDefinitions($base_plugin_definition) { foreach ($this->entityTypeManager->getDefinitions() as $entity_type_id => $entity_type) { // Only allow content entities and ignore configuration entities. if (!$entity_type instanceof ContentEntityTypeInterface) { continue; } $this->derivatives[$entity_type_id] = ['label' => $this->t('Create a new @entity_type', ['@entity_type' => $entity_type->getLowercaseLabel()]), 'category' => $entity_type->getLabel(), 'entity_type_id' => $entity_type_id, 'context' => [], 'provides' => ['entity' => ContextDefinition::create("entity:{$entity_type_id}")->setLabel($entity_type->getLabel())->setRequired(TRUE)]] + $base_plugin_definition; // Add a required context for the bundle key, and optional contexts for // other required base fields. This matches the storage create() behavior, // where only the bundle requirement is enforced. $bundle_key = $entity_type->getKey('bundle'); $this->derivatives[$entity_type_id]['bundle_key'] = $bundle_key; $base_field_definitions = $this->entityFieldManager->getBaseFieldDefinitions($entity_type_id); foreach ($base_field_definitions as $field_name => $definition) { if ($field_name != $bundle_key && !$definition->isRequired()) { continue; } $item_definition = $definition->getItemDefinition(); $type_definition = $item_definition->getPropertyDefinition($item_definition->getMainPropertyName()); // If this is an entity reference then we expect the target type as // context. if ($type_definition instanceof DataReferenceDefinitionInterface) { $type_definition->getTargetDefinition(); } $type = $type_definition->getDataType(); $is_bundle = $field_name == $bundle_key; $multiple = $definition->getCardinality() === 1 ? FALSE : TRUE; $context_definition = ContextDefinition::create($type)->setLabel($definition->getLabel())->setRequired($is_bundle)->setMultiple($multiple)->setDescription($definition->getDescription()); if ($is_bundle) { $context_definition->setAssignmentRestriction(ContextDefinition::ASSIGNMENT_RESTRICTION_INPUT); } $this->derivatives[$entity_type_id]['context'][$field_name] = $context_definition; } } return $this->derivatives; }
/** * Tests that the loop list item variable is not available after the loop. * * @expectedException \Drupal\rules\Exception\RulesEvaluationException * * @expectedExceptionMessage Unable to get variable list_item, it is not defined. */ public function testOutOfScopeVariableExecution() { $rule = $this->rulesExpressionManager->createRule(); $loop = $this->rulesExpressionManager->createInstance('rules_loop', ['list' => 'string_list']); $rule->addExpressionObject($loop); $rule->addAction('rules_test_string', ContextConfig::create()->map('text', 'list_item')); RulesComponent::create($rule)->addContextDefinition('string_list', ContextDefinition::create('string')->setMultiple())->setContextValue('string_list', ['one', 'two'])->execute(); }