Unverified Commit d55f3083 authored by Denys Mishunov's avatar Denys Mishunov 🔴
Browse files

feat(chat): accept platform as a param for 'getChatSupport'

Switching to platform instead of a platform manager opens an
opportunity to check for duo chat support on per-platform basis
from GitLabPlatformManagerForChat in particular
parent ef896923
Loading
Loading
Loading
Loading
+0 −4
Original line number Diff line number Diff line
@@ -4,7 +4,6 @@ import { activateCommon } from '../common/main';
import * as featureFlags from '../common/feature_flags';
import { registerLanguageServer } from '../common/language_server/register_language_server';
import { CodeSuggestions } from '../common/code_suggestions/code_suggestions';
import { getChatSupport } from '../common/chat/api/get_chat_support';
import { browserLanguageClientFactory } from './language_server/browser_language_client_factory';
import { createDependencyContainer } from './dependency_container_browser';

@@ -17,9 +16,6 @@ export const activate = async (context: vscode.ExtensionContext) => {
  await vscode.commands.executeCommand('setContext', 'gitlab:noAccount', false);
  await vscode.commands.executeCommand('setContext', 'gitlab:validState', true);

  const duoChatAvailable: boolean = await getChatSupport(dependencyContainer.gitLabPlatformManager);
  await vscode.commands.executeCommand('setContext', 'gitlab:chatAvailable', duoChatAvailable);

  await activateCommon(context, dependencyContainer, outputChannel);

  if (featureFlags.isEnabled(featureFlags.FeatureFlag.LanguageServerWebIDE)) {
+26 −33
Original line number Diff line number Diff line
import { GitLabPlatformManager } from '../../platform/gitlab_platform';
import { GitLabPlatformManagerForChat } from '../get_platform_manager_for_chat';
import { gitlabPlatformForAccount } from '../../test_utils/entities';
import { createFakePartial } from '../../test_utils/create_fake_partial';
import { log } from '../../log';
import { getChatSupport, ChatAvailableResponseType } from './get_chat_support';

jest.mock('../get_platform_manager_for_chat');
import {
  getChatSupport,
  ChatAvailableResponseType,
  ChatSupportResponseInterface,
} from './get_chat_support';

describe('getChatSupport', () => {
  let manager: GitLabPlatformManager;
  let platformManagerForChat: GitLabPlatformManagerForChat;
  const mockApiResponse = (duoChatAvailable: boolean = true) => {
    const apiResponse: ChatAvailableResponseType = {
      currentUser: {
        duoChatAvailable,
      },
    };
    jest.mocked(platformManagerForChat.getGitLabPlatform).mockResolvedValue({
      ...gitlabPlatformForAccount,
      fetchFromApi: jest.fn().mockResolvedValue(apiResponse),
    });
    gitlabPlatformForAccount.fetchFromApi = jest.fn().mockResolvedValue(apiResponse);
  };

  const platformWithoutChatEnabled: ChatSupportResponseInterface = { hasSupportForChat: false };

  beforeEach(() => {
    manager = createFakePartial<GitLabPlatformManager>({
      getForAllAccounts: jest.fn(),
    });
    platformManagerForChat = createFakePartial<GitLabPlatformManagerForChat>({
      getGitLabPlatform: jest.fn(),
    });
    jest.mocked(GitLabPlatformManagerForChat).mockImplementation(() => platformManagerForChat);
    jest.spyOn(log, 'error');
  });

@@ -37,33 +26,37 @@ describe('getChatSupport', () => {
    jest.clearAllMocks();
  });

  it('returns false if there is no platform', async () => {
    jest.mocked(platformManagerForChat.getGitLabPlatform).mockResolvedValue(undefined);
    const result = await getChatSupport(manager);
    expect(result).toBe(false);
  it(`returns ${JSON.stringify(platformWithoutChatEnabled)} if there is no platform`, async () => {
    const result1 = await getChatSupport();
    expect(result1).toEqual(platformWithoutChatEnabled);
    const result2 = await getChatSupport(undefined);
    expect(result2).toEqual(platformWithoutChatEnabled);
  });

  it('returns false and logs if fetching `duoChatAvailable` fails', async () => {
    jest.mocked(platformManagerForChat.getGitLabPlatform).mockResolvedValue({
  it(`returns ${JSON.stringify(
    platformWithoutChatEnabled,
  )} and logs if fetching 'duoChatAvailable' fails`, async () => {
    const result = await getChatSupport({
      ...gitlabPlatformForAccount,
      fetchFromApi: jest.fn().mockRejectedValueOnce('foo'),
    });
    const result = await getChatSupport(manager);
    expect(result).toBe(false);
    expect(result).toEqual(platformWithoutChatEnabled);
    expect(log.error).toHaveBeenCalledWith('foo');
  });

  it('returns false and does not log if the user does not have chat support', async () => {
  it(`returns ${JSON.stringify(
    platformWithoutChatEnabled,
  )} and does not log if the user does not have chat support`, async () => {
    mockApiResponse(false);
    const result = await getChatSupport(manager);
    expect(result).toBe(false);
    const result = await getChatSupport(gitlabPlatformForAccount);
    expect(result).toEqual(platformWithoutChatEnabled);
    expect(log.error).not.toHaveBeenCalled();
  });

  it('returns true if the user has chat support', async () => {
  it('returns gitlab platform if it has chat support', async () => {
    mockApiResponse();
    const result = await getChatSupport(manager);
    expect(result).toBe(true);
    const result = await getChatSupport(gitlabPlatformForAccount);
    expect(result).toEqual({ hasSupportForChat: true, platform: gitlabPlatformForAccount });
    expect(log.error).not.toHaveBeenCalled();
  });
});
+23 −10
Original line number Diff line number Diff line
import { gql } from 'graphql-request';
import { GraphQLRequest } from '../../platform/web_ide';
import { GitLabPlatformManager } from '../../platform/gitlab_platform';
import { GitLabPlatformManagerForChat } from '../get_platform_manager_for_chat';
import { GitLabPlatformForAccount } from '../../platform/gitlab_platform';
import { log } from '../../log';

const queryGetChatAvailability = gql`
@@ -12,29 +11,43 @@ const queryGetChatAvailability = gql`
  }
`;

export interface ChatSupportResponseInterface {
  hasSupportForChat: boolean;
  platform?: GitLabPlatformForAccount;
}

export type ChatAvailableResponseType = {
  currentUser: {
    duoChatAvailable: boolean;
  };
};

export async function getChatSupport(manager: GitLabPlatformManager): Promise<boolean> {
  let user;
  const platformManagerForChat = new GitLabPlatformManagerForChat(manager);
export async function getChatSupport(
  platform?: GitLabPlatformForAccount | undefined,
): Promise<ChatSupportResponseInterface> {
  const request: GraphQLRequest<ChatAvailableResponseType> = {
    type: 'graphql',
    query: queryGetChatAvailability,
    variables: {},
  };
  const platform = await platformManagerForChat.getGitLabPlatform();
  const noSupportResponse: ChatSupportResponseInterface = { hasSupportForChat: false };

  if (!platform) {
    return false;
    return noSupportResponse;
  }
  try {
    user = await platform.fetchFromApi(request);
    return user.currentUser.duoChatAvailable;
    const {
      currentUser: { duoChatAvailable },
    } = await platform.fetchFromApi(request);
    if (duoChatAvailable) {
      return {
        hasSupportForChat: duoChatAvailable,
        platform,
      };
    }
    return noSupportResponse;
  } catch (e) {
    log.error(e);
    return false;
    return noSupportResponse;
  }
}
+43 −3
Original line number Diff line number Diff line
@@ -3,8 +3,12 @@ import { GitLabPlatformManagerForChat } from './get_platform_manager_for_chat';
import { account, gitlabPlatformForAccount } from '../test_utils/entities';
import { Account } from '../platform/gitlab_account';
import { createFakePartial } from '../test_utils/create_fake_partial';
import { getChatSupport, ChatSupportResponseInterface } from './api/get_chat_support';

jest.mock('../utils/extension_configuration');
jest.mock('./api/get_chat_support', () => ({
  getChatSupport: jest.fn(),
}));

describe('GitLabPlatformManagerForChat', () => {
  let platformManagerForChat: GitLabPlatformManagerForChat;
@@ -60,20 +64,56 @@ describe('GitLabPlatformManagerForChat', () => {
        .mockResolvedValueOnce([customGitlabPlatformForAccount]);
    });

    it('returns gitlab platform for that account', async () => {
    it('returns undefined if the platform for the account does not have chat enabled', async () => {
      jest.mocked(getChatSupport).mockResolvedValue({ hasSupportForChat: false });
      expect(await platformManagerForChat.getGitLabPlatform()).toBeUndefined();
    });

    it('returns gitlab platform for that account if chat is available for the platform', async () => {
      jest
        .mocked(getChatSupport)
        .mockResolvedValue({ hasSupportForChat: true, platform: customGitlabPlatformForAccount });
      expect(await platformManagerForChat.getGitLabPlatform()).toBe(customGitlabPlatformForAccount);
    });
  });

  describe('when multiple gitlab accounts are available', () => {
    const firstPlatformWithChatEnabled: ChatSupportResponseInterface = {
      hasSupportForChat: true,
      platform: firstGitlabPlatformForAccount,
    };
    const secondPlatformWithChatEnabled: ChatSupportResponseInterface = {
      hasSupportForChat: true,
      platform: secondGitLabPlatformForAccount,
    };
    const platformWithoutChatEnabled: ChatSupportResponseInterface = { hasSupportForChat: false };

    beforeEach(() => {
      jest
        .mocked(gitlabPlatformManager.getForAllAccounts)
        .mockResolvedValueOnce([firstGitlabPlatformForAccount, secondGitLabPlatformForAccount]);
    });

    it('returns gitlab platform for the first linked account', async () => {
      expect(await platformManagerForChat.getGitLabPlatform()).toBe(firstGitlabPlatformForAccount);
    it.each`
      desc                | firstResolve                    | secondResolve                    | expectedPlatform
      ${'the first has'}  | ${firstPlatformWithChatEnabled} | ${platformWithoutChatEnabled}    | ${firstGitlabPlatformForAccount}
      ${'the second has'} | ${platformWithoutChatEnabled}   | ${secondPlatformWithChatEnabled} | ${secondGitLabPlatformForAccount}
      ${'several have'}   | ${firstPlatformWithChatEnabled} | ${secondPlatformWithChatEnabled} | ${firstGitlabPlatformForAccount}
    `(
      'returns the correct gitlab platform when $desc the chat enabled',
      async ({ firstResolve, secondResolve, expectedPlatform }) => {
        jest
          .mocked(getChatSupport)
          .mockResolvedValue(platformWithoutChatEnabled)
          .mockResolvedValueOnce(firstResolve)
          .mockResolvedValueOnce(secondResolve);
        expect(await platformManagerForChat.getGitLabPlatform()).toBe(expectedPlatform);
      },
    );

    it('correctly returns undefined if none of the platforms have chat enabled', async () => {
      jest.mocked(getChatSupport).mockResolvedValue(platformWithoutChatEnabled);
      expect(await platformManagerForChat.getGitLabPlatform()).toBe(undefined);
    });
  });
});
+14 −2
Original line number Diff line number Diff line
import { GitLabPlatformManager, GitLabPlatformForAccount } from '../platform/gitlab_platform';
import { getChatSupport } from './api/get_chat_support';

export class GitLabPlatformManagerForChat {
  readonly #platformManager: GitLabPlatformManager;
@@ -17,10 +18,21 @@ export class GitLabPlatformManagerForChat {
  async getGitLabPlatform(): Promise<GitLabPlatformForAccount | undefined> {
    const platforms = await this.#platformManager.getForAllAccounts();

    if (platforms.length === 0) {
    if (!platforms.length) {
      return undefined;
    }

    return platforms[0];
    let platform: GitLabPlatformForAccount | undefined;

    // Using a for await loop in this context because we want to stop
    // evaluating accounts as soon as we find one with code suggestions enabled
    for await (const result of platforms.map(getChatSupport)) {
      if (result.hasSupportForChat) {
        platform = result.platform;
        break;
      }
    }

    return platform;
  }
}
Loading