<commit_before>// Copyright (C) 2009 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.gerrit.sshd.commands;

import com.google.gerrit.common.data.ApprovalType;
import com.google.gerrit.common.data.ApprovalTypes;
import com.google.gerrit.common.data.ReviewResult;
import com.google.gerrit.reviewdb.ApprovalCategory;
import com.google.gerrit.reviewdb.ApprovalCategoryValue;
import com.google.gerrit.reviewdb.Change;
import com.google.gerrit.reviewdb.PatchSet;
import com.google.gerrit.reviewdb.PatchSetApproval;
import com.google.gerrit.reviewdb.RevId;
import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.changedetail.AbandonChange;
import com.google.gerrit.server.changedetail.DeleteDraftPatchSet;
import com.google.gerrit.server.changedetail.PublishDraft;
import com.google.gerrit.server.changedetail.RestoreChange;
import com.google.gerrit.server.changedetail.Submit;
import com.google.gerrit.server.mail.EmailException;
import com.google.gerrit.server.patch.PublishComments;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.InvalidChangeOperationException;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.server.workflow.FunctionState;
import com.google.gerrit.sshd.BaseCommand;
import com.google.gerrit.util.cli.CmdLineParser;
import com.google.gwtorm.client.OrmException;
import com.google.gwtorm.client.ResultSet;
import com.google.inject.Inject;

import org.apache.sshd.server.Environment;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.Option;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class ReviewCommand extends BaseCommand {
  private static final Logger log =
      LoggerFactory.getLogger(ReviewCommand.class);

  @Override
  protected final CmdLineParser newCmdLineParser() {
    final CmdLineParser parser = super.newCmdLineParser();
    for (ApproveOption c : optionList) {
      parser.addOption(c, c);
    }
    return parser;
  }

  private final Set<PatchSet.Id> patchSetIds = new HashSet<PatchSet.Id>();

  @Argument(index = 0, required = true, multiValued = true, metaVar = "{COMMIT | CHANGE,PATCHSET}", usage = "patch to review")
  void addPatchSetId(final String token) {
    try {
      patchSetIds.addAll(parsePatchSetId(token));
    } catch (UnloggedFailure e) {
      throw new IllegalArgumentException(e.getMessage(), e);
    } catch (OrmException e) {
      throw new IllegalArgumentException("database error", e);
    }
  }

  @Option(name = "--project", aliases = "-p", usage = "project containing the patch set")
  private ProjectControl projectControl;

  @Option(name = "--message", aliases = "-m", usage = "cover message to publish on change", metaVar = "MESSAGE")
  private String changeComment;

  @Option(name = "--abandon", usage = "abandon the patch set")
  private boolean abandonChange;

  @Option(name = "--restore", usage = "restore an abandoned the patch set")
  private boolean restoreChange;

  @Option(name = "--submit", aliases = "-s", usage = "submit the patch set")
  private boolean submitChange;

  @Option(name = "--force-message", usage = "publish the message, "
      + "even if the label score cannot be applied due to change being closed")
  private boolean forceMessage = false;

  @Option(name = "--publish", usage = "publish a draft patch set")
  private boolean publishPatchSet;

  @Option(name = "--delete", usage = "delete a draft patch set")
  private boolean deleteDraftPatchSet;

  @Inject
  private ReviewDb db;

  @Inject
  private IdentifiedUser currentUser;

  @Inject
  private ApprovalTypes approvalTypes;

  @Inject
  private ChangeControl.Factory changeControlFactory;

  @Inject
  private DeleteDraftPatchSet.Factory deleteDraftPatchSetFactory;

  @Inject
  private AbandonChange.Factory abandonChangeFactory;

  @Inject
  private FunctionState.Factory functionStateFactory;

  @Inject
  private PublishComments.Factory publishCommentsFactory;

  @Inject
  private PublishDraft.Factory publishDraftFactory;

  @Inject
  private RestoreChange.Factory restoreChangeFactory;

  @Inject
  private Submit.Factory submitFactory;

  private List<ApproveOption> optionList;

  @Override
  public final void start(final Environment env) {
    startThread(new CommandRunnable() {
      @Override
      public void run() throws Failure {
        initOptionList();
        parseCommandLine();
        if (abandonChange) {
          if (restoreChange) {
            throw error("abandon and restore actions are mutually exclusive");
          }
          if (submitChange) {
            throw error("abandon and submit actions are mutually exclusive");
          }
          if (publishPatchSet) {
            throw error("abandon and publish actions are mutually exclusive");
          }
          if (deleteDraftPatchSet) {
            throw error("abandon and delete actions are mutually exclusive");
          }
        }
        if (publishPatchSet) {
          if (restoreChange) {
            throw error("publish and restore actions are mutually exclusive");
          }
          if (submitChange) {
            throw error("publish and submit actions are mutually exclusive");
          }
          if (deleteDraftPatchSet) {
            throw error("publish and delete actions are mutually exclusive");
          }
        }

        boolean ok = true;
        for (final PatchSet.Id patchSetId : patchSetIds) {
          try {
            approveOne(patchSetId);
          } catch (UnloggedFailure e) {
            ok = false;
            writeError("error: " + e.getMessage() + "\n");
          } catch (Exception e) {
            ok = false;
            writeError("fatal: internal server error while approving "
                + patchSetId + "\n");
            log.error("internal error while approving " + patchSetId, e);
          }
        }

        if (!ok) {
          throw new UnloggedFailure(1, "one or more approvals failed;"
              + " review output above");
        }

      }
    });
  }

  private void approveOne(final PatchSet.Id patchSetId) throws
      NoSuchChangeException, OrmException, EmailException, Failure {
<commit_msg>Handle NoSuchChangeException in 'review' SSH command<commit_after>// Copyright (C) 2009 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.gerrit.sshd.commands;

import com.google.gerrit.common.data.ApprovalType;
import com.google.gerrit.common.data.ApprovalTypes;
import com.google.gerrit.common.data.ReviewResult;
import com.google.gerrit.reviewdb.ApprovalCategory;
import com.google.gerrit.reviewdb.ApprovalCategoryValue;
import com.google.gerrit.reviewdb.Change;
import com.google.gerrit.reviewdb.PatchSet;
import com.google.gerrit.reviewdb.PatchSetApproval;
import com.google.gerrit.reviewdb.RevId;
import com.google.gerrit.reviewdb.ReviewDb;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.changedetail.AbandonChange;
import com.google.gerrit.server.changedetail.DeleteDraftPatchSet;
import com.google.gerrit.server.changedetail.PublishDraft;
import com.google.gerrit.server.changedetail.RestoreChange;
import com.google.gerrit.server.changedetail.Submit;
import com.google.gerrit.server.mail.EmailException;
import com.google.gerrit.server.patch.PublishComments;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.InvalidChangeOperationException;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.server.workflow.FunctionState;
import com.google.gerrit.sshd.BaseCommand;
import com.google.gerrit.util.cli.CmdLineParser;
import com.google.gwtorm.client.OrmException;
import com.google.gwtorm.client.ResultSet;
import com.google.inject.Inject;

import org.apache.sshd.server.Environment;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.Option;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class ReviewCommand extends BaseCommand {
  private static final Logger log =
      LoggerFactory.getLogger(ReviewCommand.class);

  @Override
  protected final CmdLineParser newCmdLineParser() {
    final CmdLineParser parser = super.newCmdLineParser();
    for (ApproveOption c : optionList) {
      parser.addOption(c, c);
    }
    return parser;
  }

  private final Set<PatchSet.Id> patchSetIds = new HashSet<PatchSet.Id>();

  @Argument(index = 0, required = true, multiValued = true, metaVar = "{COMMIT | CHANGE,PATCHSET}", usage = "patch to review")
  void addPatchSetId(final String token) {
    try {
      patchSetIds.addAll(parsePatchSetId(token));
    } catch (UnloggedFailure e) {
      throw new IllegalArgumentException(e.getMessage(), e);
    } catch (OrmException e) {
      throw new IllegalArgumentException("database error", e);
    }
  }

  @Option(name = "--project", aliases = "-p", usage = "project containing the patch set")
  private ProjectControl projectControl;

  @Option(name = "--message", aliases = "-m", usage = "cover message to publish on change", metaVar = "MESSAGE")
  private String changeComment;

  @Option(name = "--abandon", usage = "abandon the patch set")
  private boolean abandonChange;

  @Option(name = "--restore", usage = "restore an abandoned the patch set")
  private boolean restoreChange;

  @Option(name = "--submit", aliases = "-s", usage = "submit the patch set")
  private boolean submitChange;

  @Option(name = "--force-message", usage = "publish the message, "
      + "even if the label score cannot be applied due to change being closed")
  private boolean forceMessage = false;

  @Option(name = "--publish", usage = "publish a draft patch set")
  private boolean publishPatchSet;

  @Option(name = "--delete", usage = "delete a draft patch set")
  private boolean deleteDraftPatchSet;

  @Inject
  private ReviewDb db;

  @Inject
  private IdentifiedUser currentUser;

  @Inject
  private ApprovalTypes approvalTypes;

  @Inject
  private ChangeControl.Factory changeControlFactory;

  @Inject
  private DeleteDraftPatchSet.Factory deleteDraftPatchSetFactory;

  @Inject
  private AbandonChange.Factory abandonChangeFactory;

  @Inject
  private FunctionState.Factory functionStateFactory;

  @Inject
  private PublishComments.Factory publishCommentsFactory;

  @Inject
  private PublishDraft.Factory publishDraftFactory;

  @Inject
  private RestoreChange.Factory restoreChangeFactory;

  @Inject
  private Submit.Factory submitFactory;

  private List<ApproveOption> optionList;

  @Override
  public final void start(final Environment env) {
    startThread(new CommandRunnable() {
      @Override
      public void run() throws Failure {
        initOptionList();
        parseCommandLine();
        if (abandonChange) {
          if (restoreChange) {
            throw error("abandon and restore actions are mutually exclusive");
          }
          if (submitChange) {
            throw error("abandon and submit actions are mutually exclusive");
          }
          if (publishPatchSet) {
            throw error("abandon and publish actions are mutually exclusive");
          }
          if (deleteDraftPatchSet) {
            throw error("abandon and delete actions are mutually exclusive");
          }
        }
        if (publishPatchSet) {
          if (restoreChange) {
            throw error("publish and restore actions are mutually exclusive");
          }
          if (submitChange) {
            throw error("publish and submit actions are mutually exclusive");
          }
          if (deleteDraftPatchSet) {
            throw error("publish and delete actions are mutually exclusive");
          }
        }

        boolean ok = true;
        for (final PatchSet.Id patchSetId : patchSetIds) {
          try {
            approveOne(patchSetId);
          } catch (UnloggedFailure e) {
            ok = false;
            writeError("error: " + e.getMessage() + "\n");
          } catch (NoSuchChangeException e) {
            ok = false;
            writeError("no such change " + patchSetId.getParentKey().get());
          } catch (Exception e) {
            ok = false;
            writeError("fatal: internal server error while approving "
                + patchSetId + "\n");
            log.error("internal error while approving " + patchSetId, e);
          }
        }

        if (!ok) {
          throw new UnloggedFailure(1, "one or more approvals failed;"
              + " review output above");
        }

      }
    });
  }

  private void approveOne(final PatchSet.Id patchSetId) throws
      NoSuchChangeException, OrmException, EmailException, Failure {

{'fraction_non_alphanumeric': 0.09715172024816694, 'fraction_numerical': 0.0019740552735476595, 'mean_word_length': 3.3998138957816377, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2772196', 'n_tokens_mistral': 4026, 'n_tokens_neox': 3888, 'n_words': 1098}