Commit 9be7eb45 authored by Tomas Vik (OOO back on 2026-08-31)'s avatar Tomas Vik (OOO back on 2026-08-31) 🌴
Browse files

fix: matching instance URL with token is too strict

parent 133083df
Loading
Loading
Loading
Loading
+44 −0
Original line number Diff line number Diff line
import { ExtensionContext } from 'vscode';
import { TokenService } from './token_service';

type TokenMap = Record<string, string | undefined>;

describe('TokenService', () => {
  let tokenMap: TokenMap;
  let tokenService: TokenService;
  beforeEach(() => {
    tokenMap = {};
    const fakeContext = {
      globalState: {
        get: () => tokenMap,
        update: (name: string, tm: TokenMap) => {
          tokenMap = tm;
        },
      },
    };
    tokenService = new TokenService();
    tokenService.init((fakeContext as unknown) as ExtensionContext);
  });

  it.each`
    storedFor                | retrievedFor
    ${'https://gitlab.com'}  | ${'https://gitlab.com'}
    ${'https://gitlab.com'}  | ${'https://gitlab.com/'}
    ${'https://gitlab.com/'} | ${'https://gitlab.com'}
    ${'https://gitlab.com/'} | ${'https://gitlab.com/'}
  `(
    'when token stored for $storedFor, it can be retrieved for $retrievedFor',
    async ({ storedFor, retrievedFor }) => {
      await tokenService.setToken(storedFor, 'abc');

      expect(tokenService.getToken(retrievedFor)).toBe('abc');
    },
  );

  /* This scenario happens when token was introduced before we started removing trailing slashes */
  it('can retrieve token if it was stored for url with trailing slash', async () => {
    tokenMap['https://gitlab.com/'] = 'abc';

    expect(tokenService.getToken('https://gitlab.com/')).toBe('abc');
  });
});
+10 −6
Original line number Diff line number Diff line
import * as assert from 'assert';
import { EventEmitter, ExtensionContext, Event } from 'vscode';

const removeTrailingSlash = (url: string) => url.replace(/\/$/, '');

export class TokenService {
  context?: ExtensionContext;

  private onDidChangeEmitter = new EventEmitter<void>();

  init(context: ExtensionContext) {
  init(context: ExtensionContext): void {
    this.context = context;
  }

@@ -19,20 +21,22 @@ export class TokenService {
    return this.context.globalState.get('glTokens', {});
  }

  getInstanceUrls() {
  getInstanceUrls(): string[] {
    return Object.keys(this.glTokenMap);
  }

  getToken(instanceUrl: string) {
    return this.glTokenMap[instanceUrl];
  getToken(instanceUrl: string): string | undefined {
    // the first part of the return (`this.glTokenMap[instanceUrl]`)
    // can be removed on 2022-08-15 (year after new tokens can't contain trailing slash)
    return this.glTokenMap[instanceUrl] || this.glTokenMap[removeTrailingSlash(instanceUrl)];
  }

  async setToken(instanceUrl: string, token: string | undefined) {
  async setToken(instanceUrl: string, token: string | undefined): Promise<void> {
    assert(this.context);
    const tokenMap = this.glTokenMap;

    if (token) {
      tokenMap[instanceUrl] = token;
      tokenMap[removeTrailingSlash(instanceUrl)] = token;
    } else {
      delete tokenMap[instanceUrl];
    }