Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 35 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@
"yamljs": "0.3.0"
},
"dependencies": {
"axios": "1.3.2",
"native-url": "0.3.4",
"node-fetch": "2.6.1",
"node-gzip": "1.1.2",
Expand Down
4 changes: 2 additions & 2 deletions scripts/populate-cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const getOpenApiMetadata = async () => {
}

return null;
}
};

/**
* Retrieves the API response
Expand Down Expand Up @@ -61,7 +61,7 @@ const getData = async (version, path, cache) => {
}

return cache;
}
};

/**
* Start building the cache
Expand Down
2 changes: 1 addition & 1 deletion src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export async function activate(context: vscode.ExtensionContext) {
pattern: '**'
}];

const disposable = vscode.languages.registerCompletionItemProvider(selector, new AutoCompleteProvider(context), '/', '?', '&', '=', ',');
const disposable = vscode.languages.registerCompletionItemProvider(selector, new AutoCompleteProvider(context), '/', '?', '&', '=', ',', '-');

const clearCache = vscode.commands.registerCommand('msgraph.autocomplete.clearCache', async () => {
const cache: CacheProvider = CacheProvider.getInstance(context, "name");
Expand Down
11 changes: 11 additions & 0 deletions src/providers/AutoCompleteProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { MS_GRAPH, PATH_BETA, PATH_V1 } from '../constants';
import { Suggestion, OpenApiResponse, Value } from '../models';
import { CacheProvider } from './CacheProvider';
import { AutoComplete, GraphTokens, OpenApiParser, sanitizePath, ApiSuggestion } from '../utils';
import { SnippetProvider } from './SnippetProvider';

export class AutoCompleteProvider implements CompletionItemProvider {
private lastApiPath: string = "";
Expand Down Expand Up @@ -63,6 +64,16 @@ export class AutoCompleteProvider implements CompletionItemProvider {
}
}

else if(character === "-"){
const splicedPath = currentLine.slice(0,-1);
const snippetObject = await SnippetProvider.initialize(splicedPath);
const snippet = snippetObject.getSnippet();
const snippetCompletionItem = new CompletionItem(snippet);
snippetCompletionItem.documentation = snippet;
snippetCompletionItem.insertText = "\n" + snippet;
return [snippetCompletionItem];
}

return suggestions.map(s => {
const suggestion = new CompletionItem(s.value, s.completion || CompletionItemKind.Value);
if (s.description) {
Expand Down
2 changes: 1 addition & 1 deletion src/providers/CacheProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export class CacheProvider {
this.put(version, path, null);
}
}
} catch (e) {
} catch (e: any) {
console.log(e.message);
}
return null;
Expand Down
90 changes: 90 additions & 0 deletions src/providers/SnippetProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { nodeFetch } from "../utils/SnippetNodeFetch";
import * as vscode from 'vscode';

interface ISnippetRequestInformation {
requestUrl: string;
version: string;
snippetLanguage: string;
}

export interface IGeneratedSnippetURL {
method: string;
url: string;
data: string,
headers: {
[key: string] : string
}
}

export class SnippetProvider {
private snippet: string = "";

private constructor(snippet: string){
this.snippet = snippet;
}

static async initialize(currentPath: string){
const { version, requestUrl } = SnippetProvider.generateParts(currentPath);
const snippetLanguage = SnippetProvider.getCurrentLanguage();
const generatedParts = { version, requestUrl, snippetLanguage };
const generatedSnippetUrl = SnippetProvider.generateSnippetRequestUrl(generatedParts);
const snippet = await SnippetProvider.makeSnippetRequest(generatedSnippetUrl);
return new SnippetProvider(snippet);
}

private static getCurrentLanguage(){
let activeEditor = vscode.window.activeTextEditor;
let document = activeEditor!.document;
return document.languageId || 'csharp';
}

private static generateParts = (currentPath: string) => {
// https://graph.microsoft.com/v1.0/
const v1Index = currentPath.indexOf('v1.0');
const betaIndex = currentPath.indexOf('beta');
const version = v1Index === -1 ? 'beta' : 'v1.0';
const requestUrl = SnippetProvider.getRequestUrl(version, currentPath, v1Index, betaIndex);
return { version, requestUrl };
};

private static getRequestUrl = (version: string, currentPath: string, vIndex: number, betaIndex: number) => {
let requestUrl = '';
if(version === 'beta'){
requestUrl = currentPath.substring(betaIndex+4);
return requestUrl;
};
requestUrl = currentPath.substring(vIndex+4);
return requestUrl;
};

private static generateSnippetRequestUrl = (snippetRequestInformation: ISnippetRequestInformation): IGeneratedSnippetURL => {
const { requestUrl, version, snippetLanguage } = snippetRequestInformation;

const method = 'post';
let url = 'https://graphexplorerapi.azurewebsites.net/api/graphexplorersnippets';
const headers = {
// eslint-disable-next-line @typescript-eslint/naming-convention
'Content-Type': 'application/http'
};
const data = `GET /${version}/${requestUrl} HTTP/1.1\r\nHost: graph.microsoft.com\r\nContent-Type: application/json\r\n\r\n}`;

if (snippetLanguage !== 'csharp') {
url += `?lang=${snippetLanguage}`;
}
const openApiSnippets: string[] = ['go', 'powershell'];
if (openApiSnippets.includes(snippetLanguage)) {
url += '&generation=openapi';
}
return { method, url, headers, data };
};


private static async makeSnippetRequest(generatedSnippetUrl: IGeneratedSnippetURL): Promise<string>{
const response: string= await nodeFetch(generatedSnippetUrl);
return response;
}

public getSnippet(): string{
return this.snippet;
}
}
4 changes: 2 additions & 2 deletions src/utils/ApiSuggestion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import pluralize = require('pluralize');
import { FILE_APIS } from '../constants';

export class ApiSuggestion {
private static apis: API[]
private static apis: API[];

/**
* Get the API methods for the current path
Expand Down Expand Up @@ -32,7 +32,7 @@ export class ApiSuggestion {

${api.methods.map((m: any) => `- **${m.name.toUpperCase()}**: ${m.description}`).join(`\n`)}`;
}
} catch (e) {
} catch (e: any) {
console.log(e.message, `Path: ${path}`, `Link: ${link}`);
}

Expand Down
2 changes: 1 addition & 1 deletion src/utils/AutoComplete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export class AutoComplete {
}

return null;
} catch (e) {
} catch (e: any) {
console.log(e.message);
return null;
}
Expand Down
2 changes: 1 addition & 1 deletion src/utils/OpenApiParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export class OpenApiParser {
});

return { url, parameters };
} catch (error) {
} catch (error: any) {
throw new Error(error);
}
}
Expand Down
24 changes: 24 additions & 0 deletions src/utils/SnippetNodeFetch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { IGeneratedSnippetURL } from "../providers/SnippetProvider";

// @ts-check
const fetch = require('node-fetch');
export const nodeFetch = async (args: IGeneratedSnippetURL): Promise<string> => {
try {
const { url, method, headers, data } = args;
const response = await fetch.default(url, {
method,
headers,
body: data
} );
if (response && response.ok) {
console.log('Here is the fetch result ', response);
const data = await response.text();
return data;
}
console.log('Something happened ', response);
return response.body.statusText || 'An error occurred while fetching snippet';
}
catch (e: any) {
return 'Error encountered while fetching snippet';
}
};