@Transactional
  @Override
  @CacheEvict(value = "hooks", allEntries = true)
  public CommandProcessingResult updateHook(final Long hookId, final JsonCommand command) {

    try {
      this.context.authenticatedUser();

      this.fromApiJsonDeserializer.validateForUpdate(command.json());

      final Hook hook = retrieveHookBy(hookId);
      final HookTemplate template = hook.getHookTemplate();
      final Map<String, Object> changes = hook.update(command);

      if (!changes.isEmpty()) {

        if (changes.containsKey(templateIdParamName)) {
          final Long ugdTemplateId = command.longValueOfParameterNamed(templateIdParamName);
          final Template ugdTemplate = this.ugdTemplateRepository.findOne(ugdTemplateId);
          if (ugdTemplate == null) {
            changes.remove(templateIdParamName);
            throw new TemplateNotFoundException(ugdTemplateId);
          }
          hook.updateUgdTemplate(ugdTemplate);
        }

        if (changes.containsKey(eventsParamName)) {
          final Set<HookResource> events =
              assembleSetOfEvents(command.arrayOfParameterNamed(eventsParamName));
          final boolean updated = hook.updateEvents(events);
          if (!updated) {
            changes.remove(eventsParamName);
          }
        }

        if (changes.containsKey(configParamName)) {
          final String configJson = command.jsonFragment(configParamName);
          final Set<HookConfiguration> config =
              assembleConfig(command.mapValueOfParameterNamed(configJson), template);
          final boolean updated = hook.updateConfig(config);
          if (!updated) {
            changes.remove(configParamName);
          }
        }

        this.hookRepository.saveAndFlush(hook);
      }

      return new CommandProcessingResultBuilder() //
          .withCommandId(command.commandId()) //
          .withEntityId(hookId) //
          .with(changes) //
          .build();
    } catch (final DataIntegrityViolationException dve) {
      handleHookDataIntegrityIssues(command, dve);
      return null;
    }
  }
  @Transactional
  @Override
  @CacheEvict(value = "hooks", allEntries = true)
  public CommandProcessingResult createHook(final JsonCommand command) {

    try {
      this.context.authenticatedUser();

      this.fromApiJsonDeserializer.validateForCreate(command.json());

      final HookTemplate template =
          retrieveHookTemplateBy(command.stringValueOfParameterNamed(nameParamName));
      final String configJson = command.jsonFragment(configParamName);
      final Set<HookConfiguration> config =
          assembleConfig(command.mapValueOfParameterNamed(configJson), template);
      final JsonArray events = command.arrayOfParameterNamed(eventsParamName);
      final Set<HookResource> allEvents = assembleSetOfEvents(events);
      Template ugdTemplate = null;
      if (command.hasParameter(templateIdParamName)) {
        final Long ugdTemplateId = command.longValueOfParameterNamed(templateIdParamName);
        ugdTemplate = this.ugdTemplateRepository.findOne(ugdTemplateId);
        if (ugdTemplate == null) {
          throw new TemplateNotFoundException(ugdTemplateId);
        }
      }
      final Hook hook = Hook.fromJson(command, template, config, allEvents, ugdTemplate);

      validateHookRules(template, config, allEvents);

      this.hookRepository.save(hook);

      return new CommandProcessingResultBuilder()
          .withCommandId(command.commandId())
          .withEntityId(hook.getId())
          .build();
    } catch (final DataIntegrityViolationException dve) {
      handleHookDataIntegrityIssues(command, dve);
      return CommandProcessingResult.empty();
    }
  }