mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-20 09:20:16 +00:00
Initial Move from Azure DevOps to GitHub
This commit is contained in:
128
src/Juno/JunoClient.test.ts
Normal file
128
src/Juno/JunoClient.test.ts
Normal file
@@ -0,0 +1,128 @@
|
||||
import ko from "knockout";
|
||||
import { HttpStatusCodes } from "../Common/Constants";
|
||||
import * as ViewModels from "../Contracts/ViewModels";
|
||||
import { IPinnedRepo, JunoClient } from "./JunoClient";
|
||||
|
||||
const sampleDatabaseAccount: ViewModels.DatabaseAccount = {
|
||||
id: "id",
|
||||
name: "name",
|
||||
location: "location",
|
||||
type: "type",
|
||||
kind: "kind",
|
||||
tags: [],
|
||||
properties: {
|
||||
documentEndpoint: "documentEndpoint",
|
||||
gremlinEndpoint: "gremlinEndpoint",
|
||||
tableEndpoint: "tableEndpoint",
|
||||
cassandraEndpoint: "cassandraEndpoint"
|
||||
}
|
||||
};
|
||||
|
||||
const samplePinnedRepos: IPinnedRepo[] = [
|
||||
{
|
||||
owner: "owner",
|
||||
name: "name",
|
||||
private: false,
|
||||
branches: [
|
||||
{
|
||||
name: "name"
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
describe("Pinned repos", () => {
|
||||
const junoClient = new JunoClient(ko.observable<ViewModels.DatabaseAccount>(sampleDatabaseAccount));
|
||||
|
||||
beforeEach(() => {
|
||||
window.fetch = jest.fn().mockImplementation(() => {
|
||||
return {
|
||||
status: HttpStatusCodes.OK,
|
||||
text: () => JSON.stringify(samplePinnedRepos)
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it("updatePinnedRepos invokes pinned repos subscribers", async () => {
|
||||
const callback = jest.fn().mockImplementation((pinnedRepos: IPinnedRepo[]) => {});
|
||||
|
||||
junoClient.subscribeToPinnedRepos(callback);
|
||||
const response = await junoClient.updatePinnedRepos(samplePinnedRepos);
|
||||
|
||||
expect(response.status).toBe(HttpStatusCodes.OK);
|
||||
expect(callback).toBeCalledWith(samplePinnedRepos);
|
||||
});
|
||||
|
||||
it("getPinnedRepos invokes pinned repos subscribers", async () => {
|
||||
const callback = jest.fn().mockImplementation((pinnedRepos: IPinnedRepo[]) => {});
|
||||
|
||||
junoClient.subscribeToPinnedRepos(callback);
|
||||
const response = await junoClient.getPinnedRepos("scope");
|
||||
|
||||
expect(response.status).toBe(HttpStatusCodes.OK);
|
||||
expect(callback).toBeCalledWith(samplePinnedRepos);
|
||||
});
|
||||
});
|
||||
|
||||
describe("GitHub", () => {
|
||||
const junoClient = new JunoClient(ko.observable<ViewModels.DatabaseAccount>(sampleDatabaseAccount));
|
||||
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it("getGitHubToken", async () => {
|
||||
let fetchUrl: string;
|
||||
window.fetch = jest.fn().mockImplementation((url: string) => {
|
||||
fetchUrl = url;
|
||||
|
||||
return {
|
||||
status: HttpStatusCodes.OK,
|
||||
text: () => JSON.stringify({ access_token: "token" })
|
||||
};
|
||||
});
|
||||
|
||||
const response = await junoClient.getGitHubToken("code");
|
||||
|
||||
expect(response.status).toBe(HttpStatusCodes.OK);
|
||||
expect(response.data.access_token).toBeDefined();
|
||||
expect(window.fetch).toBeCalled();
|
||||
|
||||
const fetchUrlParams = new URLSearchParams(new URL(fetchUrl).search);
|
||||
let fetchUrlParamsCount = 0;
|
||||
fetchUrlParams.forEach(() => fetchUrlParamsCount++);
|
||||
|
||||
expect(fetchUrlParamsCount).toBe(2);
|
||||
expect(fetchUrlParams.get("code")).toBeDefined();
|
||||
expect(fetchUrlParams.get("client_id")).toBeDefined();
|
||||
});
|
||||
|
||||
it("deleteAppauthorization", async () => {
|
||||
let fetchUrl: string;
|
||||
window.fetch = jest.fn().mockImplementation((url: string) => {
|
||||
fetchUrl = url;
|
||||
|
||||
return {
|
||||
status: HttpStatusCodes.NoContent,
|
||||
text: () => undefined as string
|
||||
};
|
||||
});
|
||||
|
||||
const response = await junoClient.deleteAppAuthorization("token");
|
||||
|
||||
expect(response.status).toBe(HttpStatusCodes.NoContent);
|
||||
expect(window.fetch).toBeCalled();
|
||||
|
||||
const fetchUrlParams = new URLSearchParams(new URL(fetchUrl).search);
|
||||
let fetchUrlParamsCount = 0;
|
||||
fetchUrlParams.forEach(() => fetchUrlParamsCount++);
|
||||
|
||||
expect(fetchUrlParamsCount).toBe(2);
|
||||
expect(fetchUrlParams.get("access_token")).toBeDefined();
|
||||
expect(fetchUrlParams.get("client_id")).toBeDefined();
|
||||
});
|
||||
});
|
||||
151
src/Juno/JunoClient.ts
Normal file
151
src/Juno/JunoClient.ts
Normal file
@@ -0,0 +1,151 @@
|
||||
import ko from "knockout";
|
||||
import { HttpStatusCodes } from "../Common/Constants";
|
||||
import { config } from "../Config";
|
||||
import * as ViewModels from "../Contracts/ViewModels";
|
||||
import { IGitHubResponse } from "../GitHub/GitHubClient";
|
||||
import { IGitHubOAuthToken } from "../GitHub/GitHubOAuthService";
|
||||
import { getAuthorizationHeader } from "../Utils/AuthorizationUtils";
|
||||
import { AuthorizeAccessComponent } from "../Explorer/Controls/GitHub/AuthorizeAccessComponent";
|
||||
|
||||
export interface IJunoResponse<T> {
|
||||
status: number;
|
||||
data: T;
|
||||
}
|
||||
|
||||
export interface IPinnedRepo {
|
||||
owner: string;
|
||||
name: string;
|
||||
private: boolean;
|
||||
branches: IPinnedBranch[];
|
||||
}
|
||||
|
||||
export interface IPinnedBranch {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export class JunoClient {
|
||||
private cachedPinnedRepos: ko.Observable<IPinnedRepo[]>;
|
||||
|
||||
constructor(public databaseAccount: ko.Observable<ViewModels.DatabaseAccount>) {
|
||||
this.cachedPinnedRepos = ko.observable<IPinnedRepo[]>([]);
|
||||
}
|
||||
|
||||
public subscribeToPinnedRepos(callback: ko.SubscriptionCallback<IPinnedRepo[], void>): ko.Subscription {
|
||||
return this.cachedPinnedRepos.subscribe(callback);
|
||||
}
|
||||
|
||||
public async getPinnedRepos(scope: string): Promise<IJunoResponse<IPinnedRepo[]>> {
|
||||
const response = await window.fetch(`${this.getJunoGitHubUrl()}/pinnedrepos`, {
|
||||
headers: this.getHeaders()
|
||||
});
|
||||
|
||||
let pinnedRepos: IPinnedRepo[];
|
||||
if (response.status === HttpStatusCodes.OK) {
|
||||
pinnedRepos = JSON.parse(await response.text());
|
||||
|
||||
// In case we're restricted to public only scope, we return only public repos
|
||||
if (scope === AuthorizeAccessComponent.Scopes.Public.key) {
|
||||
pinnedRepos = pinnedRepos.filter(repo => !repo.private);
|
||||
}
|
||||
|
||||
this.cachedPinnedRepos(pinnedRepos);
|
||||
}
|
||||
|
||||
return {
|
||||
status: response.status,
|
||||
data: pinnedRepos
|
||||
};
|
||||
}
|
||||
|
||||
public async updatePinnedRepos(repos: IPinnedRepo[]): Promise<IJunoResponse<undefined>> {
|
||||
const response = await window.fetch(`${this.getJunoGitHubUrl()}/pinnedrepos`, {
|
||||
method: "PUT",
|
||||
body: JSON.stringify(repos),
|
||||
headers: this.getHeaders()
|
||||
});
|
||||
|
||||
if (response.status === HttpStatusCodes.OK) {
|
||||
this.cachedPinnedRepos(repos);
|
||||
}
|
||||
|
||||
return {
|
||||
status: response.status,
|
||||
data: undefined
|
||||
};
|
||||
}
|
||||
|
||||
public async deleteGitHubInfo(): Promise<IJunoResponse<undefined>> {
|
||||
const response = await window.fetch(this.getJunoGitHubUrl(), {
|
||||
method: "DELETE",
|
||||
headers: this.getHeaders()
|
||||
});
|
||||
|
||||
return {
|
||||
status: response.status,
|
||||
data: undefined
|
||||
};
|
||||
}
|
||||
|
||||
public async getGitHubToken(code: string): Promise<IGitHubResponse<IGitHubOAuthToken>> {
|
||||
const githubParams = JunoClient.getGitHubClientParams();
|
||||
githubParams.append("code", code);
|
||||
|
||||
const response = await window.fetch(`${this.getJunoGitHubUrl()}/token?${githubParams.toString()}`, {
|
||||
headers: this.getHeaders()
|
||||
});
|
||||
|
||||
let data: IGitHubOAuthToken;
|
||||
const body = await response.text();
|
||||
if (body) {
|
||||
data = JSON.parse(body);
|
||||
} else {
|
||||
data = {
|
||||
error: response.statusText
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
status: response.status,
|
||||
data
|
||||
};
|
||||
}
|
||||
|
||||
public async deleteAppAuthorization(token: string): Promise<IJunoResponse<string>> {
|
||||
const githubParams = JunoClient.getGitHubClientParams();
|
||||
githubParams.append("access_token", token);
|
||||
|
||||
const response = await window.fetch(`${this.getJunoGitHubUrl()}/token?${githubParams.toString()}`, {
|
||||
method: "DELETE",
|
||||
headers: this.getHeaders()
|
||||
});
|
||||
|
||||
return {
|
||||
status: response.status,
|
||||
data: await response.text()
|
||||
};
|
||||
}
|
||||
|
||||
private getJunoGitHubUrl(): string {
|
||||
return `${config.JUNO_ENDPOINT}/api/notebooks/${this.databaseAccount().name}/github`;
|
||||
}
|
||||
|
||||
private getHeaders(): HeadersInit {
|
||||
const authorizationHeader = getAuthorizationHeader();
|
||||
return {
|
||||
[authorizationHeader.header]: authorizationHeader.token,
|
||||
"content-type": "application/json"
|
||||
};
|
||||
}
|
||||
|
||||
public static getGitHubClientParams(): URLSearchParams {
|
||||
const githubParams = new URLSearchParams({
|
||||
client_id: config.GITHUB_CLIENT_ID
|
||||
});
|
||||
|
||||
if (config.GITHUB_CLIENT_SECRET) {
|
||||
githubParams.append("client_secret", config.GITHUB_CLIENT_SECRET);
|
||||
}
|
||||
|
||||
return githubParams;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user