@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Remove HeraldConditionConfig

Summary: Ref T2769. Moves all traces of HeraldConditionConfig into Adapters.

Test Plan: Edited rules and used Test Console to exercise both affected code paths. Tried to save invalid rules to hit error pat.

Reviewers: btrahan

Reviewed By: btrahan

CC: aran

Maniphest Tasks: T2769

Differential Revision: https://secure.phabricator.com/D6679

+255 -253
-1
src/__phutil_library_map__.php
··· 600 600 'HeraldApplyTranscript' => 'applications/herald/storage/transcript/HeraldApplyTranscript.php', 601 601 'HeraldCommitAdapter' => 'applications/herald/adapter/HeraldCommitAdapter.php', 602 602 'HeraldCondition' => 'applications/herald/storage/HeraldCondition.php', 603 - 'HeraldConditionConfig' => 'applications/herald/config/HeraldConditionConfig.php', 604 603 'HeraldConditionTranscript' => 'applications/herald/storage/transcript/HeraldConditionTranscript.php', 605 604 'HeraldController' => 'applications/herald/controller/HeraldController.php', 606 605 'HeraldDAO' => 'applications/herald/storage/HeraldDAO.php',
+230 -1
src/applications/herald/adapter/HeraldAdapter.php
··· 54 54 55 55 abstract public function getPHID(); 56 56 abstract public function getHeraldName(); 57 - abstract public function getHeraldField($field_name); 57 + 58 + public function getHeraldField($field_name) { 59 + switch ($field_name) { 60 + case self::FIELD_RULE: 61 + return null; 62 + default: 63 + throw new Exception( 64 + "Unknown field '{$field_name}'!"); 65 + } 66 + } 67 + 58 68 abstract public function applyHeraldEffects(array $effects); 59 69 60 70 public function isEnabled() { ··· 173 183 default: 174 184 throw new Exception( 175 185 "This adapter does not define conditions for field '{$field}'!"); 186 + } 187 + } 188 + 189 + public function doesConditionMatch( 190 + HeraldEngine $engine, 191 + HeraldRule $rule, 192 + HeraldCondition $condition, 193 + $field_value) { 194 + 195 + $condition_type = $condition->getFieldCondition(); 196 + $condition_value = $condition->getValue(); 197 + 198 + switch ($condition_type) { 199 + case self::CONDITION_CONTAINS: 200 + // "Contains" can take an array of strings, as in "Any changed 201 + // filename" for diffs. 202 + foreach ((array)$field_value as $value) { 203 + if (stripos($value, $condition_value) !== false) { 204 + return true; 205 + } 206 + } 207 + return false; 208 + case self::CONDITION_NOT_CONTAINS: 209 + return (stripos($field_value, $condition_value) === false); 210 + case self::CONDITION_IS: 211 + return ($field_value == $condition_value); 212 + case self::CONDITION_IS_NOT: 213 + return ($field_value != $condition_value); 214 + case self::CONDITION_IS_ME: 215 + return ($field_value == $rule->getAuthorPHID()); 216 + case self::CONDITION_IS_NOT_ME: 217 + return ($field_value != $rule->getAuthorPHID()); 218 + case self::CONDITION_IS_ANY: 219 + if (!is_array($condition_value)) { 220 + throw new HeraldInvalidConditionException( 221 + "Expected condition value to be an array."); 222 + } 223 + $condition_value = array_fuse($condition_value); 224 + return isset($condition_value[$field_value]); 225 + case self::CONDITION_IS_NOT_ANY: 226 + if (!is_array($condition_value)) { 227 + throw new HeraldInvalidConditionException( 228 + "Expected condition value to be an array."); 229 + } 230 + $condition_value = array_fuse($condition_value); 231 + return !isset($condition_value[$field_value]); 232 + case self::CONDITION_INCLUDE_ALL: 233 + if (!is_array($field_value)) { 234 + throw new HeraldInvalidConditionException( 235 + "Object produced non-array value!"); 236 + } 237 + if (!is_array($condition_value)) { 238 + throw new HeraldInvalidConditionException( 239 + "Expected conditionv value to be an array."); 240 + } 241 + 242 + $have = array_select_keys(array_fuse($field_value), $condition_value); 243 + return (count($have) == count($condition_value)); 244 + case self::CONDITION_INCLUDE_ANY: 245 + return (bool)array_select_keys( 246 + array_fuse($field_value), 247 + $condition_value); 248 + case self::CONDITION_INCLUDE_NONE: 249 + return !array_select_keys( 250 + array_fuse($field_value), 251 + $condition_value); 252 + case self::CONDITION_EXISTS: 253 + return (bool)$field_value; 254 + case self::CONDITION_NOT_EXISTS: 255 + return !$field_value; 256 + case self::CONDITION_REGEXP: 257 + foreach ((array)$field_value as $value) { 258 + // We add the 'S' flag because we use the regexp multiple times. 259 + // It shouldn't cause any troubles if the flag is already there 260 + // - /.*/S is evaluated same as /.*/SS. 261 + $result = @preg_match($condition_value . 'S', $value); 262 + if ($result === false) { 263 + throw new HeraldInvalidConditionException( 264 + "Regular expression is not valid!"); 265 + } 266 + if ($result) { 267 + return true; 268 + } 269 + } 270 + return false; 271 + case self::CONDITION_REGEXP_PAIR: 272 + // Match a JSON-encoded pair of regular expressions against a 273 + // dictionary. The first regexp must match the dictionary key, and the 274 + // second regexp must match the dictionary value. If any key/value pair 275 + // in the dictionary matches both regexps, the condition is satisfied. 276 + $regexp_pair = json_decode($condition_value, true); 277 + if (!is_array($regexp_pair)) { 278 + throw new HeraldInvalidConditionException( 279 + "Regular expression pair is not valid JSON!"); 280 + } 281 + if (count($regexp_pair) != 2) { 282 + throw new HeraldInvalidConditionException( 283 + "Regular expression pair is not a pair!"); 284 + } 285 + 286 + $key_regexp = array_shift($regexp_pair); 287 + $value_regexp = array_shift($regexp_pair); 288 + 289 + foreach ((array)$field_value as $key => $value) { 290 + $key_matches = @preg_match($key_regexp, $key); 291 + if ($key_matches === false) { 292 + throw new HeraldInvalidConditionException( 293 + "First regular expression is invalid!"); 294 + } 295 + if ($key_matches) { 296 + $value_matches = @preg_match($value_regexp, $value); 297 + if ($value_matches === false) { 298 + throw new HeraldInvalidConditionException( 299 + "Second regular expression is invalid!"); 300 + } 301 + if ($value_matches) { 302 + return true; 303 + } 304 + } 305 + } 306 + return false; 307 + case self::CONDITION_RULE: 308 + case self::CONDITION_NOT_RULE: 309 + $rule = $engine->getRule($condition_value); 310 + if (!$rule) { 311 + throw new HeraldInvalidConditionException( 312 + "Condition references a rule which does not exist!"); 313 + } 314 + 315 + $is_not = ($condition_type == self::CONDITION_NOT_RULE); 316 + $result = $engine->doesRuleMatch($rule, $this); 317 + if ($is_not) { 318 + $result = !$result; 319 + } 320 + return $result; 321 + default: 322 + throw new HeraldInvalidConditionException( 323 + "Unknown condition '{$condition_type}'."); 324 + } 325 + } 326 + 327 + public function willSaveCondition(HeraldCondition $condition) { 328 + $condition_type = $condition->getFieldCondition(); 329 + $condition_value = $condition->getValue(); 330 + 331 + switch ($condition_type) { 332 + case self::CONDITION_REGEXP: 333 + $ok = @preg_match($condition_value, ''); 334 + if ($ok === false) { 335 + throw new HeraldInvalidConditionException( 336 + pht( 337 + 'The regular expression "%s" is not valid. Regular expressions '. 338 + 'must have enclosing characters (e.g. "@/path/to/file@", not '. 339 + '"/path/to/file") and be syntactically correct.', 340 + $condition_value)); 341 + } 342 + break; 343 + case self::CONDITION_REGEXP_PAIR: 344 + $json = json_decode($condition_value, true); 345 + if (!is_array($json)) { 346 + throw new HeraldInvalidConditionException( 347 + pht( 348 + 'The regular expression pair "%s" is not valid JSON. Enter a '. 349 + 'valid JSON array with two elements.', 350 + $condition_value)); 351 + } 352 + 353 + if (count($json) != 2) { 354 + throw new HeraldInvalidConditionException( 355 + pht( 356 + 'The regular expression pair "%s" must have exactly two '. 357 + 'elements.', 358 + $condition_value)); 359 + } 360 + 361 + $key_regexp = array_shift($json); 362 + $val_regexp = array_shift($json); 363 + 364 + $key_ok = @preg_match($key_regexp, ''); 365 + if ($key_ok === false) { 366 + throw new HeraldInvalidConditionException( 367 + pht( 368 + 'The first regexp in the regexp pair, "%s", is not a valid '. 369 + 'regexp.', 370 + $key_regexp)); 371 + } 372 + 373 + $val_ok = @preg_match($val_regexp, ''); 374 + if ($val_ok === false) { 375 + throw new HeraldInvalidConditionException( 376 + pht( 377 + 'The second regexp in the regexp pair, "%s", is not a valid '. 378 + 'regexp.', 379 + $val_regexp)); 380 + } 381 + break; 382 + case self::CONDITION_CONTAINS: 383 + case self::CONDITION_NOT_CONTAINS: 384 + case self::CONDITION_IS: 385 + case self::CONDITION_IS_NOT: 386 + case self::CONDITION_IS_ANY: 387 + case self::CONDITION_IS_NOT_ANY: 388 + case self::CONDITION_INCLUDE_ALL: 389 + case self::CONDITION_INCLUDE_ANY: 390 + case self::CONDITION_INCLUDE_NONE: 391 + case self::CONDITION_IS_ME: 392 + case self::CONDITION_IS_NOT_ME: 393 + case self::CONDITION_RULE: 394 + case self::CONDITION_NOT_RULE: 395 + case self::CONDITION_EXISTS: 396 + case self::CONDITION_NOT_EXISTS: 397 + // No explicit validation for these types, although there probably 398 + // should be in some cases. 399 + break; 400 + default: 401 + throw new HeraldInvalidConditionException( 402 + pht( 403 + 'Unknown condition "%s"!', 404 + $condition_type)); 176 405 } 177 406 } 178 407
+2 -2
src/applications/herald/adapter/HeraldCommitAdapter.php
··· 287 287 return array(); 288 288 } 289 289 return $revision->getCCPHIDs(); 290 - default: 291 - throw new Exception("Invalid field '{$field}'."); 292 290 } 291 + 292 + return parent::getHeraldField($field); 293 293 } 294 294 295 295 public function applyHeraldEffects(array $effects) {
+2 -2
src/applications/herald/adapter/HeraldDifferentialRevisionAdapter.php
··· 239 239 $packages = $this->loadAffectedPackages(); 240 240 return PhabricatorOwnersOwner::loadAffiliatedUserPHIDs( 241 241 mpull($packages, 'getID')); 242 - default: 243 - throw new Exception("Invalid field '{$field}'."); 244 242 } 243 + 244 + return parent::getHeraldField($field); 245 245 } 246 246 247 247 public function getActions($rule_type) {
-24
src/applications/herald/config/HeraldConditionConfig.php
··· 1 - <?php 2 - 3 - final class HeraldConditionConfig { 4 - 5 - // TODO: Remove; still used by Engine/etc. 6 - const CONDITION_CONTAINS = 'contains'; 7 - const CONDITION_NOT_CONTAINS = '!contains'; 8 - const CONDITION_IS = 'is'; 9 - const CONDITION_IS_NOT = '!is'; 10 - const CONDITION_IS_ANY = 'isany'; 11 - const CONDITION_IS_NOT_ANY = '!isany'; 12 - const CONDITION_INCLUDE_ALL = 'all'; 13 - const CONDITION_INCLUDE_ANY = 'any'; 14 - const CONDITION_INCLUDE_NONE = 'none'; 15 - const CONDITION_IS_ME = 'me'; 16 - const CONDITION_IS_NOT_ME = '!me'; 17 - const CONDITION_REGEXP = 'regexp'; 18 - const CONDITION_RULE = 'conditions'; 19 - const CONDITION_NOT_RULE = '!conditions'; 20 - const CONDITION_EXISTS = 'exists'; 21 - const CONDITION_NOT_EXISTS = '!exists'; 22 - const CONDITION_REGEXP_PAIR = 'regexp-pair'; 23 - 24 - }
+6 -42
src/applications/herald/controller/HeraldRuleController.php
··· 68 68 $e_name = true; 69 69 $errors = array(); 70 70 if ($request->isFormPost() && $request->getStr('save')) { 71 - list($e_name, $errors) = $this->saveRule($rule, $request); 71 + list($e_name, $errors) = $this->saveRule($adapter, $rule, $request); 72 72 if (!$errors) { 73 73 $uri = '/herald/view/'. 74 74 $rule->getContentType().'/'. ··· 198 198 )); 199 199 } 200 200 201 - private function saveRule($rule, $request) { 201 + private function saveRule(HeraldAdapter $adapter, $rule, $request) { 202 202 $rule->setName($request->getStr('name')); 203 203 $rule->setMustMatchAll(($request->getStr('must_match') == 'all')); 204 204 ··· 239 239 $obj->setValue($condition[2]); 240 240 } 241 241 242 - $cond_type = $obj->getFieldCondition(); 243 - 244 - if ($cond_type == HeraldConditionConfig::CONDITION_REGEXP) { 245 - if (@preg_match($obj->getValue(), '') === false) { 246 - $errors[] = 247 - pht('The regular expression "%s" is not valid. '. 248 - 'Regular expressions must have enclosing characters (e.g. '. 249 - '"@/path/to/file@", not "/path/to/file") and be syntactically '. 250 - 'correct.', $obj->getValue()); 251 - } 252 - } 253 - 254 - if ($cond_type == HeraldConditionConfig::CONDITION_REGEXP_PAIR) { 255 - $json = json_decode($obj->getValue(), true); 256 - if (!is_array($json)) { 257 - $errors[] = 258 - pht('The regular expression pair "%s" is not '. 259 - 'valid JSON. Enter a valid JSON array with two elements.', 260 - $obj->getValue()); 261 - } else { 262 - if (count($json) != 2) { 263 - $errors[] = 264 - pht('The regular expression pair "%s" must have '. 265 - 'exactly two elements.', $obj->getValue()); 266 - } else { 267 - $key_regexp = array_shift($json); 268 - $val_regexp = array_shift($json); 269 - 270 - if (@preg_match($key_regexp, '') === false) { 271 - $errors[] = 272 - pht('The first regexp, "%s" in the regexp pair '. 273 - 'is not a valid regexp.', $key_regexp); 274 - } 275 - if (@preg_match($val_regexp, '') === false) { 276 - $errors[] = 277 - pht('The second regexp, "%s" in the regexp pair '. 278 - 'is not a valid regexp.', $val_regexp); 279 - } 280 - } 281 - } 242 + try { 243 + $adapter->willSaveCondition($obj); 244 + } catch (HeraldInvalidConditionException $ex) { 245 + $errors[] = $ex->getMessage(); 282 246 } 283 247 284 248 $conditions[] = $obj;
+15 -181
src/applications/herald/engine/HeraldEngine.php
··· 10 10 protected $fieldCache = array(); 11 11 protected $object = null; 12 12 13 + public function getRule($id) { 14 + return idx($this->rules, $id); 15 + } 16 + 13 17 public static function loadAndApplyRules(HeraldAdapter $object) { 14 18 $content_type = $object->getAdapterContentType(); 15 19 $rules = HeraldRule::loadAllByContentTypeWithFullData( ··· 173 177 return $this->transcript; 174 178 } 175 179 176 - protected function doesRuleMatch( 180 + public function doesRuleMatch( 177 181 HeraldRule $rule, 178 182 HeraldAdapter $object) { 179 183 ··· 272 276 $transcript->setCondition($cond); 273 277 $transcript->setTestValue($test_value); 274 278 275 - $result = null; 276 - switch ($cond) { 277 - case HeraldConditionConfig::CONDITION_CONTAINS: 278 - // "Contains" can take an array of strings, as in "Any changed 279 - // filename" for diffs. 280 - foreach ((array)$object_value as $value) { 281 - $result = (stripos($value, $test_value) !== false); 282 - if ($result) { 283 - break; 284 - } 285 - } 286 - break; 287 - case HeraldConditionConfig::CONDITION_NOT_CONTAINS: 288 - $result = (stripos($object_value, $test_value) === false); 289 - break; 290 - case HeraldConditionConfig::CONDITION_IS: 291 - $result = ($object_value == $test_value); 292 - break; 293 - case HeraldConditionConfig::CONDITION_IS_NOT: 294 - $result = ($object_value != $test_value); 295 - break; 296 - case HeraldConditionConfig::CONDITION_IS_ME: 297 - $result = ($object_value == $rule->getAuthorPHID()); 298 - break; 299 - case HeraldConditionConfig::CONDITION_IS_NOT_ME: 300 - $result = ($object_value != $rule->getAuthorPHID()); 301 - break; 302 - case HeraldConditionConfig::CONDITION_IS_ANY: 303 - $test_value = array_flip($test_value); 304 - $result = isset($test_value[$object_value]); 305 - break; 306 - case HeraldConditionConfig::CONDITION_IS_NOT_ANY: 307 - $test_value = array_flip($test_value); 308 - $result = !isset($test_value[$object_value]); 309 - break; 310 - case HeraldConditionConfig::CONDITION_INCLUDE_ALL: 311 - if (!is_array($object_value)) { 312 - $transcript->setNote('Object produced bad value!'); 313 - $result = false; 314 - } else { 315 - $have = array_select_keys(array_flip($object_value), 316 - $test_value); 317 - $result = (count($have) == count($test_value)); 318 - } 319 - break; 320 - case HeraldConditionConfig::CONDITION_INCLUDE_ANY: 321 - $result = (bool)array_select_keys(array_flip($object_value), 322 - $test_value); 323 - break; 324 - case HeraldConditionConfig::CONDITION_INCLUDE_NONE: 325 - $result = !array_select_keys(array_flip($object_value), 326 - $test_value); 327 - break; 328 - case HeraldConditionConfig::CONDITION_EXISTS: 329 - $result = (bool)$object_value; 330 - break; 331 - case HeraldConditionConfig::CONDITION_NOT_EXISTS: 332 - $result = !$object_value; 333 - break; 334 - case HeraldConditionConfig::CONDITION_REGEXP: 335 - foreach ((array)$object_value as $value) { 336 - // We add the 'S' flag because we use the regexp multiple times. 337 - // It shouldn't cause any troubles if the flag is already there 338 - // - /.*/S is evaluated same as /.*/SS. 339 - $result = @preg_match($test_value . 'S', $value); 340 - if ($result === false) { 341 - $transcript->setNote( 342 - "Regular expression is not valid!"); 343 - break; 344 - } 345 - if ($result) { 346 - break; 347 - } 348 - } 349 - $result = (bool)$result; 350 - break; 351 - case HeraldConditionConfig::CONDITION_REGEXP_PAIR: 352 - // Match a JSON-encoded pair of regular expressions against a 353 - // dictionary. The first regexp must match the dictionary key, and the 354 - // second regexp must match the dictionary value. If any key/value pair 355 - // in the dictionary matches both regexps, the condition is satisfied. 356 - $regexp_pair = json_decode($test_value, true); 357 - if (!is_array($regexp_pair)) { 358 - $result = false; 359 - $transcript->setNote("Regular expression pair is not valid JSON!"); 360 - break; 361 - } 362 - if (count($regexp_pair) != 2) { 363 - $result = false; 364 - $transcript->setNote("Regular expression pair is not a pair!"); 365 - break; 366 - } 367 - 368 - $key_regexp = array_shift($regexp_pair); 369 - $value_regexp = array_shift($regexp_pair); 370 - 371 - foreach ((array)$object_value as $key => $value) { 372 - $key_matches = @preg_match($key_regexp, $key); 373 - if ($key_matches === false) { 374 - $result = false; 375 - $transcript->setNote("First regular expression is invalid!"); 376 - break 2; 377 - } 378 - if ($key_matches) { 379 - $value_matches = @preg_match($value_regexp, $value); 380 - if ($value_matches === false) { 381 - $result = false; 382 - $transcript->setNote("Second regular expression is invalid!"); 383 - break 2; 384 - } 385 - if ($value_matches) { 386 - $result = true; 387 - break 2; 388 - } 389 - } 390 - } 391 - $result = false; 392 - break; 393 - case HeraldConditionConfig::CONDITION_RULE: 394 - case HeraldConditionConfig::CONDITION_NOT_RULE: 395 - 396 - $rule = idx($this->rules, $test_value); 397 - if (!$rule) { 398 - $transcript->setNote( 399 - "Condition references a rule which does not exist!"); 400 - $result = false; 401 - } else { 402 - $is_not = ($cond == HeraldConditionConfig::CONDITION_NOT_RULE); 403 - $result = $this->doesRuleMatch($rule, $object); 404 - if ($is_not) { 405 - $result = !$result; 406 - } 407 - } 408 - break; 409 - default: 410 - throw new HeraldInvalidConditionException( 411 - "Unknown condition '{$cond}'."); 279 + try { 280 + $result = $object->doesConditionMatch( 281 + $this, 282 + $rule, 283 + $condition, 284 + $object_value); 285 + } catch (HeraldInvalidConditionException $ex) { 286 + $result = false; 287 + $transcript->setNote($ex->getMessage()); 412 288 } 413 289 414 290 $transcript->setResult($result); ··· 432 308 return $this->fieldCache[$field]; 433 309 } 434 310 435 - $result = null; 436 - switch ($field) { 437 - case HeraldFieldConfig::FIELD_RULE: 438 - $result = null; 439 - break; 440 - case HeraldFieldConfig::FIELD_TITLE: 441 - case HeraldFieldConfig::FIELD_BODY: 442 - case HeraldFieldConfig::FIELD_DIFF_FILE: 443 - case HeraldFieldConfig::FIELD_DIFF_CONTENT: 444 - // TODO: Type should be string. 445 - $result = $this->object->getHeraldField($field); 446 - break; 447 - case HeraldFieldConfig::FIELD_AUTHOR: 448 - case HeraldFieldConfig::FIELD_REPOSITORY: 449 - // TODO: Type should be PHID. 450 - $result = $this->object->getHeraldField($field); 451 - break; 452 - case HeraldFieldConfig::FIELD_TAGS: 453 - case HeraldFieldConfig::FIELD_REVIEWER: 454 - case HeraldFieldConfig::FIELD_REVIEWERS: 455 - case HeraldFieldConfig::FIELD_CC: 456 - case HeraldFieldConfig::FIELD_DIFFERENTIAL_REVIEWERS: 457 - case HeraldFieldConfig::FIELD_DIFFERENTIAL_CCS: 458 - // TODO: Type should be list. 459 - $result = $this->object->getHeraldField($field); 460 - break; 461 - case HeraldFieldConfig::FIELD_AFFECTED_PACKAGE: 462 - case HeraldFieldConfig::FIELD_AFFECTED_PACKAGE_OWNER: 463 - case HeraldFieldConfig::FIELD_NEED_AUDIT_FOR_PACKAGE: 464 - $result = $this->object->getHeraldField($field); 465 - if (!is_array($result)) { 466 - throw new HeraldInvalidFieldException( 467 - "Value of field type {$field} is not an array!"); 468 - } 469 - break; 470 - case HeraldFieldConfig::FIELD_DIFFERENTIAL_REVISION: 471 - // TODO: Type should be boolean I guess. 472 - $result = $this->object->getHeraldField($field); 473 - break; 474 - default: 475 - throw new HeraldInvalidConditionException( 476 - "Unknown field type '{$field}'!"); 477 - } 311 + $result = $this->object->getHeraldField($field); 478 312 479 313 $this->fieldCache[$field] = $result; 480 314 return $result;