public class FanoutReplicationConfig implements ReplicationConfig {
  private final SitePaths site;
  private final FileBasedConfig secureConf;

  @Inject
  public FanoutReplicationConfig(final SitePaths site) {
    this.site = site;
    this.secureConf = new FileBasedConfig(site.secure_config.toFile(), FS.DETECTED);
  }

  @Override
  public void addSecureCredentials(String authUsername, String authToken)
      throws IOException, ConfigInvalidException {
    secureConf.load();
    secureConf.setString("remote", authUsername, "username", authUsername);
    secureConf.setString("remote", authUsername, "password", authToken);
    secureConf.save();
  }

  @Override
  public void addReplicationRemote(String githubUsername, String url, String projectName)
      throws IOException, ConfigInvalidException {

    FileBasedConfig replicationConf =
        new FileBasedConfig(
            new File(site.etc_dir.toFile(), String.format("replication/%s.config", githubUsername)),
            FS.DETECTED);

    replicationConf.load();
    replicationConf.setString("remote", null, "url", url);
    List<String> projects =
        new ArrayList<>(Arrays.asList(replicationConf.getStringList("remote", null, "projects")));
    projects.add(projectName);
    replicationConf.setStringList("remote", null, "projects", projects);
    replicationConf.setString("remote", null, "push", "refs/*:refs/*");
    replicationConf.save();
  }
} public class FileBasedReplicationConfig implements ReplicationConfig {
  private final FileBasedConfig secureConf;
  private final FileBasedConfig replicationConf;

  @Inject
  public FileBasedReplicationConfig(final SitePaths site) {
    replicationConf =
        new FileBasedConfig(new File(site.etc_dir.toFile(), "replication.config"), FS.DETECTED);
    secureConf = new FileBasedConfig(site.secure_config.toFile(), FS.DETECTED);
  }

  @Override
  public synchronized void addSecureCredentials(String authUsername, String authToken)
      throws IOException, ConfigInvalidException {
    secureConf.load();
    secureConf.setString("remote", authUsername, "username", authUsername);
    secureConf.setString("remote", authUsername, "password", authToken);
    secureConf.save();
  }

  @Override
  public synchronized void addReplicationRemote(String username, String url, String projectName)
      throws IOException, ConfigInvalidException {
    replicationConf.load();
    replicationConf.setString("remote", username, "url", url);
    List<String> projects =
        new ArrayList<>(
            Arrays.asList(replicationConf.getStringList("remote", username, "projects")));
    projects.add(projectName);
    replicationConf.setStringList("remote", username, "projects", projects);
    replicationConf.setString("remote", username, "push", "refs/*:refs/*");
    replicationConf.save();
  }
} public interface ReplicationConfig {

  void addSecureCredentials(String authUsername, String authToken)
      throws IOException, ConfigInvalidException;

  void addReplicationRemote(String username, String url, String projectName)
      throws IOException, ConfigInvalidException;
} public class GitHubGroupBackend implements GroupBackend {
  private static final Logger log = LoggerFactory.getLogger(GitHubGroupBackend.class);
  private final GitHubGroupMembership.Factory ghMembershipProvider;
  private final GitHubGroupsCache ghOrganisationCache;

  @Inject
  GitHubGroupBackend(
      GitHubGroupMembership.Factory ghMembershipProvider, GitHubGroupsCache ghOrganisationCache) {
    this.ghMembershipProvider = ghMembershipProvider;
    this.ghOrganisationCache = ghOrganisationCache;
  }

  @Override
  public boolean isVisibleToAll(AccountGroup.UUID uuid) {
    return true;
  }

  @Override
  public boolean handles(UUID uuid) {
    return uuid.get().startsWith(UUID_PREFIX);
  }

  @Override
  public Basic get(UUID uuid) {
    checkArgument(handles(uuid), "{} is not a valid GitHub Group UUID", uuid.get());
    return GitHubOrganisationGroup.fromUUID(uuid);
  }

  @Override
  public Collection<GroupReference> suggest(String name, ProjectState project) {
    if (!name.startsWith(NAME_PREFIX)) {
      return Collections.emptyList();
    }
    String orgNamePrefix = name.substring(NAME_PREFIX.length());
    return listByPrefix(orgNamePrefix);
  }

  public Set<GroupReference> listByPrefix(String orgNamePrefix) {
    try {
      log.debug("Listing user's organisations starting with '{}'", orgNamePrefix);

      String[] namePrefixParts = orgNamePrefix.toLowerCase().split("/");
      String orgNamePrefixLowercase = namePrefixParts.length > 0 ? namePrefixParts[0] : "";
      String teamNameLowercase = namePrefixParts.length > 1 ? namePrefixParts[1] : "";

      Set<String> ghOrgs = ghOrganisationCache.getOrganizationsForCurrentUser();
      log.debug("Full list of user's organisations: {}", ghOrgs);

      Builder<GroupReference> orgGroups = new ImmutableSet.Builder<>();
      for (String organizationName : ghOrgs) {
        if (organizationName.toLowerCase().startsWith(orgNamePrefixLowercase)) {
          GroupReference teamGroupRef = GitHubOrganisationGroup.groupReference(organizationName);

          if ((orgNamePrefixLowercase.length() > 0 && orgNamePrefix.endsWith("/"))
              || teamNameLowercase.length() > 0) {
            for (String teamName : ghOrganisationCache.getTeamsForCurrentUser(organizationName)) {
              if (teamName.toLowerCase().startsWith(teamNameLowercase)) {
                orgGroups.add(GitHubTeamGroup.groupReference(teamGroupRef, teamName));
              }
            }
          } else {
            orgGroups.add(teamGroupRef);
          }
        }
      }
      return orgGroups.build();
    } catch (ExecutionException e) {
      log.warn("Cannot get GitHub organisations matching '" + orgNamePrefix + "'", e);
    }

    return Collections.emptySet();
  }

  @Override
  public GroupMembership membershipsOf(CurrentUser user) {
    String username = user.getUserName().orElse(null);
    if (Strings.isNullOrEmpty(username)) {
      return GroupMembership.EMPTY;
    }
    return ghMembershipProvider.get(username);
  }
} public class GitHubGroupMembership implements GroupMembership {
  private final Set<UUID> groups;

  public interface Factory {
    GitHubGroupMembership get(@Assisted String username);
  }

  @Inject
  GitHubGroupMembership(GitHubGroupsCache ghOrganisationCache, @Assisted String username) {
    this.groups =
        new ImmutableSet.Builder<UUID>()
            .addAll(ghOrganisationCache.getGroupsForUser(username))
            .build();
  }

  @Override
  public boolean contains(UUID groupId) {
    return groups.contains(groupId);
  }

  @Override
  public boolean containsAnyOf(Iterable<UUID> groupIds) {
    return !intersection(groupIds).isEmpty();
  }

  @Override
  public Set<UUID> intersection(Iterable<UUID> groupIds) {
    ImmutableSet.Builder<UUID> groupBuilder = new ImmutableSet.Builder<>();
    for (UUID uuid : groupIds) {
      if (contains(uuid)) {
        groupBuilder.add(uuid);
      }
    }
    return groupBuilder.build();
  }

  @Override
  public Set<UUID> getKnownGroups() {
    return groups;
  }
} @Singleton
public class GitHubGroupsCache {
  private static final Logger log = LoggerFactory.getLogger(GitHubGroupsCache.class);
  private static final String ORGS_CACHE_NAME = "groups";
  protected static final long GROUPS_CACHE_TTL_MINS = 60;
  public static final String EVERYONE_TEAM_NAME = "Everyone";

  public static class OrganisationLoader extends CacheLoader<String, OrganizationStructure> {
    private static final Logger logger = LoggerFactory.getLogger(OrganisationLoader.class);
    private final UserScopedProvider<GitHubLogin> ghLoginProvider;

    @Inject
    public OrganisationLoader(UserScopedProvider<GitHubLogin> ghLoginProvider) {
      this.ghLoginProvider = ghLoginProvider;
    }

    @Override
    public OrganizationStructure load(String username) throws Exception {
      OrganizationStructure orgsTeams = new OrganizationStructure();
      GitHubLogin ghLogin = ghLoginProvider.get(username);
      if (ghLogin == null) {
        logger.warn("Cannot login to GitHub on behalf of '{}'", username);
        return orgsTeams;
      }

      try {
        loadOrganisationsAndTeams(username, orgsTeams, ghLogin);
      } catch (FileNotFoundException teamsNotFound) {
        logger.info(
            "Cannot access teams for user '{}': falling back to list of public organisations",
            username);
        loadOrganisations(username, orgsTeams, ghLogin);
      }

      logger.debug("GitHub user '{}' belongs to: {}", username, orgsTeams);
      return orgsTeams;
    }

    private void loadOrganisationsAndTeams(
        String username, OrganizationStructure orgsTeams, GitHubLogin ghLogin) throws IOException {
      logger.debug("Getting list of organisations/teams for user '{}'", username);
      Map<String, Set<GHTeam>> myOrganisationsLogins = ghLogin.getHub().getMyTeams();
      for (Entry<String, Set<GHTeam>> teamsOrg : myOrganisationsLogins.entrySet()) {
        orgsTeams.put(teamsOrg.getKey(), EVERYONE_TEAM_NAME);
        for (GHTeam team : teamsOrg.getValue()) {
          orgsTeams.put(teamsOrg.getKey(), team.getName());
        }
      }
    }

    private void loadOrganisations(
        String username, OrganizationStructure orgsTeams, GitHubLogin ghLogin) throws IOException {
      logger.debug("Getting list of public organisations for user '{}'", username);
      Set<String> organisations = ghLogin.getMyOrganisationsLogins();
      for (String org : organisations) {
        orgsTeams.put(org, EVERYONE_TEAM_NAME);
      }
    }
  }

  public static Module module() {
    return new CacheModule() {
      @Override
      protected void configure() {
        persist(ORGS_CACHE_NAME, String.class, OrganizationStructure.class)
            .expireAfterWrite(Duration.of(GROUPS_CACHE_TTL_MINS, MINUTES))
            .loader(OrganisationLoader.class);
        bind(GitHubGroupsCache.class);
      }
    };
  }

  private final LoadingCache<String, OrganizationStructure> orgTeamsByUsername;
  private final Provider<IdentifiedUser> userProvider;

  @Inject
  GitHubGroupsCache(
      @Named(ORGS_CACHE_NAME) LoadingCache<String, OrganizationStructure> byUsername,
      Provider<IdentifiedUser> userProvider) {
    this.orgTeamsByUsername = byUsername;
    this.userProvider = userProvider;
  }

  Set<String> getOrganizationsForUser(String username) {
    try {
      return orgTeamsByUsername.get(username).keySet();
    } catch (ExecutionException e) {
      log.warn("Cannot get GitHub organisations for user '" + username + "'", e);
      return Collections.emptySet();
    }
  }

  Set<String> getOrganizationsForCurrentUser() throws ExecutionException {
    return orgTeamsByUsername.get(userProvider.get().getUserName().get()).keySet();
  }

  Set<String> getTeamsForUser(String organizationName, String username) {
    try {
      return new ImmutableSet.Builder<String>()
          .addAll(orgTeamsByUsername.get(username).get(organizationName))
          .build();
    } catch (ExecutionException e) {
      log.warn(
          "Cannot get Teams membership for organisation '"
              + organizationName
              + "' and user '"
              + username
              + "'",
          e);
      return Collections.emptySet();
    }
  }

  Set<String> getTeamsForCurrentUser(String organizationName) {
    return getTeamsForUser(organizationName, userProvider.get().getUserName().get());
  }

  public Set<UUID> getGroupsForUser(String username) {
    ImmutableSet.Builder<UUID> groupsBuilder = new ImmutableSet.Builder<>();
    for (String org : getOrganizationsForUser(username)) {
      groupsBuilder.add(GitHubOrganisationGroup.uuid(org));

      for (String team : getTeamsForUser(org, username)) {
        groupsBuilder.add(GitHubTeamGroup.uuid(GitHubOrganisationGroup.uuid(org), team));
      }
    }
    return groupsBuilder.build();
  }
} public class GitHubOrganisationGroup extends GitHubGroup implements Basic {
  public interface Factory {
    GitHubOrganisationGroup get(
        @Assisted("orgName") String orgName, @Assisted("orgUrl") @Nullable String orgUrl);
  }

  private final String orgName;

  @Inject
  GitHubOrganisationGroup(
      @Assisted("orgName") String orgName, @Assisted("orgUrl") @Nullable String orgUrl) {
    super(uuid(orgName), orgUrl);
    this.orgName = orgName;
  }

  @Override
  public String getName() {
    return NAME_PREFIX + orgName;
  }

  public static GitHubOrganisationGroup fromUUID(UUID uuid) {
    checkArgument(uuid.get().startsWith(UUID_PREFIX), "Invalid GitHub UUID '" + uuid + "'");
    return new GitHubOrganisationGroup(uuid.get().substring(UUID_PREFIX.length()), null);
  }

  public static UUID uuid(String orgName) {
    return AccountGroup.uuid(UUID_PREFIX + orgName);
  }

  public static GroupReference groupReference(String orgName) {
    return GroupReference.create(uuid(orgName), NAME_PREFIX + orgName);
  }
} public class GitHubLogin implements Serializable {
  private static final long serialVersionUID = 1L;
  private static final Logger log = LoggerFactory.getLogger(GitHubLogin.class);
  private static final List<Scope> DEFAULT_SCOPES =
      Arrays.asList(Scope.PUBLIC_REPO, Scope.USER_EMAIL, Scope.READ_ORG);
  private static final long SCOPE_COOKIE_NEVER_EXPIRES = DAYS.toSeconds(50 * 365);

  @Singleton
  public static class Provider extends HttpSessionProvider<GitHubLogin> {
    @Override
    public GitHubLogin get(HttpServletRequest request) {
      return super.get(request);
    }
  }

  @Getter private AccessToken token;

  private String state;

  private SortedSet<Scope> loginScopes;
  private final GitHubOAuthConfig config;
  private final HttpConnector httpConnector;

  public GHMyself getMyself() throws IOException {
    if (isLoggedIn()) {
      return new GitHubMyselfWrapper(getHub().getMyself());
    }
    return null;
  }

  public Set<String> getMyOrganisationsLogins() throws IOException {
    if (isLoggedIn()) {
      return getHub().getMyOrganizations().keySet();
    }
    return Collections.emptySet();
  }

  @Inject
  public GitHubLogin(GitHubOAuthConfig config, GitHubHttpConnector httpConnector) {
    this.config = config;
    this.httpConnector = httpConnector;
  }

  public boolean isLoggedIn() {
    return token != null;
  }

  public void login(
      HttpServletRequest request,
      HttpServletResponse response,
      OAuthProtocol oauth,
      Scope... scopes)
      throws IOException {

    log.debug("Login " + this);
    if (OAuthProtocol.isOAuthFinal(request)) {
      log.debug("Login-FINAL " + this);
      login(oauth.loginPhase2(request, response, state));
      this.state = ""; // Make sure state is used only once

      if (isLoggedIn()) {
        log.debug("Login-SUCCESS " + this);
        response.sendRedirect(OAuthProtocol.getTargetUrl(request));
      }
    } else {
      Set<ScopeKey> configuredScopesProfiles = config.scopes.keySet();
      String scopeRequested = getScopesKey(request, response);
      if (Strings.isNullOrEmpty(scopeRequested) && configuredScopesProfiles.size() > 1) {
        response.sendRedirect(config.getScopeSelectionUrl(request));
      } else {
        this.loginScopes = getScopes(MoreObjects.firstNonNull(scopeRequested, "scopes"), scopes);
        log.debug("Login-PHASE1 " + this);
        state = oauth.loginPhase1(request, response, loginScopes);
      }
    }
  }

  public void logout() {
    token = null;
  }

  public GitHub login(AccessToken authToken) throws IOException {
    log.debug("Logging in using access token {}", authToken.accessToken);
    this.token = authToken;
    return getHub();
  }

  @Override
  public String toString() {
    return "GitHubLogin [token=" + token + ", scopes=" + loginScopes + "]";
  }

  public GitHub getHub() throws IOException {
    if (token == null) {
      return null;
    }
    return new GitHubBuilder()
        .withEndpoint(config.gitHubApiUrl)
        .withOAuthToken(token.accessToken)
        .withConnector(httpConnector)
        .build();
  }

  private String getScopesKey(HttpServletRequest request, HttpServletResponse response) {
    String scopeRequested = request.getParameter("scope");
    if (scopeRequested == null) {
      scopeRequested = getScopesKeyFromCookie(request);
    }

    boolean rememberScope =
        Strings.nullToEmpty(request.getParameter("rememberScope")).equalsIgnoreCase("on");
    if (scopeRequested != null && rememberScope) {
      Cookie scopeCookie = new Cookie("scope", scopeRequested);
      scopeCookie.setPath("/");
      scopeCookie.setMaxAge((int) SCOPE_COOKIE_NEVER_EXPIRES);
      response.addCookie(scopeCookie);
    }

    return scopeRequested;
  }

  public String getScopesKeyFromCookie(HttpServletRequest request) {
    Cookie[] cookies = request.getCookies();
    if (cookies == null) {
      return null;
    }

    for (Cookie cookie : cookies) {
      if (cookie.getName().equalsIgnoreCase("scope")) {
        return cookie.getValue();
      }
    }
    return null;
  }

  private SortedSet<Scope> getScopes(String baseScopeKey, Scope... scopes) {
    HashSet<Scope> fullScopes = new HashSet<>(scopesForKey(baseScopeKey));
    fullScopes.addAll(Arrays.asList(scopes));

    return new TreeSet<>(fullScopes);
  }

  private List<Scope> scopesForKey(String baseScopeKey) {
    return config.scopes.entrySet().stream()
        .filter(entry -> entry.getKey().name.equals(baseScopeKey))
        .map(entry -> entry.getValue())
        .findFirst()
        .orElse(DEFAULT_SCOPES);
  }
} @Singleton
public class IdentifiedUserGitHubLoginProvider implements UserScopedProvider<GitHubLogin> {
  private static final Logger log =
      LoggerFactory.getLogger(IdentifiedUserGitHubLoginProvider.class);
  public static final String EXTERNAL_ID_PREFIX =
      ExternalId.SCHEME_EXTERNAL + ":" + OAuthWebFilter.GITHUB_EXT_ID;

  private final Provider<IdentifiedUser> userProvider;
  private final GitHubOAuthConfig config;
  private final AccountCache accountCache;
  private final GitHubHttpConnector httpConnector;

  @Inject
  public IdentifiedUserGitHubLoginProvider(
      Provider<IdentifiedUser> identifiedUserProvider,
      GitHubOAuthConfig config,
      GitHubHttpConnector httpConnector,
      AccountCache accountCache) {
    this.userProvider = identifiedUserProvider;
    this.config = config;
    this.accountCache = accountCache;
    this.httpConnector = httpConnector;
  }

  @Override
  public GitHubLogin get() {
    IdentifiedUser currentUser = userProvider.get();
    return get(currentUser.getUserName().get());
  }

  @Override
  @Nullable
  public GitHubLogin get(String username) {
    try {
      AccessToken accessToken = newAccessTokenFromUser(username);
      if (accessToken != null) {
        GitHubLogin login = new GitHubLogin(config, httpConnector);
        login.login(accessToken);
        return login;
      }
      return null;
    } catch (IOException e) {
      log.error("Cannot login to GitHub as '" + username + "'", e);
      return null;
    }
  }

  private AccessToken newAccessTokenFromUser(String username) {
    AccountState account = accountCache.getByUsername(username).get();
    Collection<ExternalId> externalIds = account.externalIds();
    for (ExternalId accountExternalId : externalIds) {
      String key = accountExternalId.key().get();
      if (key.startsWith(EXTERNAL_ID_PREFIX)) {
        return new AccessToken(key.substring(EXTERNAL_ID_PREFIX.length()));
      }
    }

    return null;
  }
} @Singleton
public class GerritGsonProvider implements Provider<Gson> {
  private final Gson gerritStyleGson = OutputFormat.JSON_COMPACT.newGson();

  @Override
  public Gson get() {
    return gerritStyleGson;
  }
} public class ListProjectReplicationStatus implements RestReadView<ProjectResource> {
  private final ReplicationStatusStore statusStore;

  @Inject
  public ListProjectReplicationStatus(ReplicationStatusStore statusStore) {
    this.statusStore = statusStore;
  }

  @Override
  public Response<Object> apply(ProjectResource resource)
      throws AuthException, BadRequestException, ResourceConflictException, Exception {
    return Response.ok(statusStore.list(resource.getNameKey()));
  }
} public class ReplicationStatusFlatFile implements ReplicationStatusStore {
  private final Path pluginData;
  private final Gson gson;

  @Inject
  public ReplicationStatusFlatFile(@PluginData Path pluginData, Provider<Gson> gsonProvider) {
    this.pluginData = pluginData;
    this.gson = gsonProvider.get();
  }

  @Override
  public void set(Project.NameKey projectKey, String refKey, JsonObject statusEvent)
      throws IOException {
    Path replicationStatusPath = getReplicationStatusPath(projectKey.get() + "/" + refKey);
    Files.write(
        replicationStatusPath, statusEvent.toString().getBytes(), TRUNCATE_EXISTING, CREATE, WRITE);
  }

  private Path getReplicationStatusPath(String key) throws IOException {
    Path projectPath = pluginData.resolve(getSanitizedKey(key) + ".replication-error.json");
    Files.createDirectories(projectPath.getParent());
    return projectPath;
  }

  private String getSanitizedKey(String key) {
    String sanitizedKey = key.replace(".", "_").replace(" ", "_");
    return sanitizedKey;
  }

  @Override
  public List<JsonObject> list(Project.NameKey parentKey) throws IOException {
    Path projectPath = pluginData.resolve(getSanitizedKey(parentKey.get()));
    final List<JsonObject> entries = new ArrayList<>();
    Files.walkFileTree(
        projectPath,
        new SimpleFileVisitor<Path>() {

          @Override
          public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
              throws IOException {
            try (Reader fileReader = Files.newBufferedReader(file, StandardCharsets.UTF_8)) {
              JsonObject json = gson.fromJson(fileReader, JsonObject.class);
              entries.add(json);
            }
            return FileVisitResult.CONTINUE;
          }
        });
    return entries;
  }
} @Singleton
public class ReplicationStatusListener implements EventListener {
  private static final String REF_REPLICATED_EVENT = "ref-replicated";
  private static Logger log = LoggerFactory.getLogger(ReplicationStatusListener.class);

  private final ReplicationStatusStore statusStore;
  private final Gson gson;

  @Inject
  public ReplicationStatusListener(
      ReplicationStatusStore statusStore, Provider<Gson> gsonProvider) {
    this.statusStore = statusStore;
    this.gson = gsonProvider.get();
  }

  @Override
  public void onEvent(Event event) {
    if (RefEvent.class.isAssignableFrom(event.getClass())
        && event.getType().equals(REF_REPLICATED_EVENT)) {
      RefEvent refEvent = (RefEvent) event;
      NameKey projectNameKey = refEvent.getProjectNameKey();
      JsonObject eventJson = (JsonObject) gson.toJsonTree(event);
      String refKey = eventJson.get("ref").getAsString();

      try {
        statusStore.set(projectNameKey, refKey, eventJson);
      } catch (IOException e) {
        log.error(
            "Unable to update replication status for event "
                + eventJson
                + " on project "
                + projectNameKey,
            e);
      }
    }
  }
} public interface ReplicationStatusStore {

  public void set(Project.NameKey projectKey, String refKey, JsonObject statusEvent)
      throws IOException;

  public List<JsonObject> list(Project.NameKey projectKey) throws IOException;
}
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.extensions.restapi.RestApiModule;
import com.google.gerrit.extensions.webui.TopMenu;
import com.google.gerrit.server.account.GroupBackend;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.events.EventListener;
import com.google.gerrit.server.project.ProjectResource;
import com.google.gson.Gson;
import com.google.inject.AbstractModule;
import com.google.inject.Inject;
import com.google.inject.Scopes;
import com.google.inject.TypeLiteral;
import com.google.inject.assistedinject.FactoryModuleBuilder;
import com.googlesource.gerrit.plugins.github.git.FanoutReplicationConfig;
import com.googlesource.gerrit.plugins.github.git.FileBasedReplicationConfig;
import com.googlesource.gerrit.plugins.github.git.ReplicationConfig;
import com.googlesource.gerrit.plugins.github.group.GitHubGroupBackend;
import com.googlesource.gerrit.plugins.github.group.GitHubGroupMembership;
import com.googlesource.gerrit.plugins.github.group.GitHubGroupsCache;
import com.googlesource.gerrit.plugins.github.group.GitHubOrganisationGroup;
import com.googlesource.gerrit.plugins.github.oauth.GitHubLogin;
import com.googlesource.gerrit.plugins.github.oauth.IdentifiedUserGitHubLoginProvider;
import com.googlesource.gerrit.plugins.github.oauth.UserScopedProvider;
import com.googlesource.gerrit.plugins.github.replication.GerritGsonProvider;
import com.googlesource.gerrit.plugins.github.replication.ListProjectReplicationStatus;
import com.googlesource.gerrit.plugins.github.replication.ReplicationStatusFlatFile;
import com.googlesource.gerrit.plugins.github.replication.ReplicationStatusListener;
import com.googlesource.gerrit.plugins.github.replication.ReplicationStatusStore;
import java.nio.file.Files;
// Copyright (C) 2013 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.googlesource.gerrit.plugins.github;


public class GuiceModule extends AbstractModule {

  private final SitePaths site;

  @Inject
  public GuiceModule(SitePaths site) {
    this.site = site;
  }

  @Override
  protected void configure() {
    bind(new TypeLiteral<UserScopedProvider<GitHubLogin>>() {})
        .to(IdentifiedUserGitHubLoginProvider.class);

    install(GitHubGroupsCache.module());

    DynamicSet.bind(binder(), TopMenu.class).to(GitHubTopMenu.class);
    DynamicSet.bind(binder(), GroupBackend.class).to(GitHubGroupBackend.class);
    DynamicSet.bind(binder(), EventListener.class).to(ReplicationStatusListener.class);

    install(new FactoryModuleBuilder().build(GitHubOrganisationGroup.Factory.class));
    install(new FactoryModuleBuilder().build(GitHubGroupMembership.Factory.class));

    install(
        new RestApiModule() {
          @Override
          protected void configure() {
            get(ProjectResource.PROJECT_KIND, "replication").to(ListProjectReplicationStatus.class);
          }
        });

    if (Files.exists(site.etc_dir.resolve("replication"))) {
      bind(ReplicationConfig.class).to(FanoutReplicationConfig.class).in(Scopes.SINGLETON);
    } else {
      bind(ReplicationConfig.class).to(FileBasedReplicationConfig.class).in(Scopes.SINGLETON);
    }

    bind(ReplicationStatusStore.class).to(ReplicationStatusFlatFile.class).in(Scopes.SINGLETON);