Skip to content

Commit

Permalink
Allow marking of stubbed API
Browse files Browse the repository at this point in the history
Signed-off-by: Alvaro Sanchez-Leon <[email protected]>
  • Loading branch information
alvsan09 committed Aug 12, 2022
1 parent 9a51ed9 commit abb889b
Show file tree
Hide file tree
Showing 6 changed files with 197 additions and 20 deletions.
33 changes: 26 additions & 7 deletions src/comparator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,20 @@
import { ScannerEntry } from './scanner-entry';
import { Parser } from './parser';
import { DocEntry } from './doc-entry';
import { Context, Infos, resolveStubbedInfo } from './infos';
import { isInRange } from './stubbed-ranges';

export class Comparator {

private globalResult: Map<string, DocEntry[]>;

constructor(private readonly vsCodeEntries: ScannerEntry[], private readonly theiaEntries: ScannerEntry[]) {

constructor(
private readonly vsCodeEntries: ScannerEntry[],
private readonly theiaEntries: ScannerEntry[],
private readonly infos: Infos
) {
// take latest vscode and then add value inside
this.globalResult = new Map<string, DocEntry[]>();

}

result(): Map<string, DocEntry[]> {
Expand Down Expand Up @@ -280,7 +284,9 @@ export class Comparator {
}
} else {
// it's there, add it
docEntryLatestVsCodeCommand.includedIn.unshift({ version: `theia/${theiaEntry.version}`, available: 'yes' });
const context = { namespaceKey: vsCodeNamespaceKey, element: docEntryLatestVsCodeCommand };
const available = this.resolveAvailableYesOrStubbed(context, theiaEntry.version);
docEntryLatestVsCodeCommand.includedIn.unshift({ version: `theia/${theiaEntry.version}`, available });

// now check members
if (docEntryLatestVsCodeCommand.members && docEntryLatestVsCodeCommand.members.length > 0) {
Expand All @@ -291,7 +297,9 @@ export class Comparator {

// it's there, add it
if (searchedMember) {
member.includedIn.unshift({ version: `theia/${theiaEntry.version}`, available: 'yes' });
const memberContext = { ...context, subElement: member };
const available = this.resolveAvailableYesOrStubbed(memberContext, theiaEntry.version);
member.includedIn.unshift({ version: `theia/${theiaEntry.version}`, available });
} else {
member.includedIn.unshift({ version: `theia/${theiaEntry.version}`, available: 'no' });
}
Expand All @@ -307,7 +315,9 @@ export class Comparator {

// it's there, add it
if (searchedConstructor) {
constructor.includedIn.unshift({ version: `theia/${theiaEntry.version}`, available: 'yes' });
const constructorContext = { ...context, subElement: constructor };
const available = this.resolveAvailableYesOrStubbed(constructorContext, theiaEntry.version);
constructor.includedIn.unshift({ version: `theia/${theiaEntry.version}`, available });
} else {
constructor.includedIn.unshift({ version: `theia/${theiaEntry.version}`, available: 'no' });
}
Expand All @@ -323,7 +333,9 @@ export class Comparator {

// it's there, add it
if (searchedUnion) {
union.includedIn.unshift({ version: `theia/${theiaEntry.version}`, available: 'yes' });
const unionContext = { ...context, subElement: union };
const available = this.resolveAvailableYesOrStubbed(unionContext, theiaEntry.version);
union.includedIn.unshift({ version: `theia/${theiaEntry.version}`, available });
} else {
union.includedIn.unshift({ version: `theia/${theiaEntry.version}`, available: 'no' });
}
Expand All @@ -340,6 +352,13 @@ export class Comparator {

}

private resolveAvailableYesOrStubbed(context: Context, version: string): 'stubbed' | 'yes' {
const stubbedInfo = resolveStubbedInfo(this.infos, context);
const isStubbed = stubbedInfo && isInRange(version, stubbedInfo);

return isStubbed ? 'stubbed' : 'yes';
}

/**
* Removes all fully supported entries from the result. Entries that have unsupported children are not removed.
*/
Expand Down
18 changes: 12 additions & 6 deletions src/html-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ a { color: #0056b3; }
row += this.generateNoteColumn(namespaceKey, command);
row += '</div>';

if (command.constructors && command.constructors.length > 0 && command.includedIn[0].available === 'yes') {
const commandAvailable = command.includedIn[0].available;
const isCommandAvailable = commandAvailable === 'yes' || commandAvailable === 'stubbed';
if (command.constructors && command.constructors.length > 0 && isCommandAvailable) {

command.constructors.forEach(constructor => {
let subRow = `<div class="row bg-info"><div class="col-4 command">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;constructor(${this.constructorPrettyName(constructor)})</div>`;
Expand All @@ -79,7 +81,7 @@ a { color: #0056b3; }
}

// has members ? if yes then add lines for methods
if (command.members && command.members.length > 0 && command.includedIn[0].available === 'yes') {
if (command.members && command.members.length > 0 && isCommandAvailable) {

command.members.forEach(member => {
let subRow = `<div class="row bg-info"><div class="col-4 command" title="${this.htmlEntities(member.documentation)}">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;${member.name}</div>`;
Expand All @@ -93,7 +95,7 @@ a { color: #0056b3; }
}

// has unions ? if yes then add lines for methods
if (command.unions && command.unions.length > 0 && command.includedIn[0].available === 'yes') {
if (command.unions && command.unions.length > 0 && isCommandAvailable) {

command.unions.forEach(union => {
let subRow = `<div class="row bg-info"><div class="col-4 command">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;${union.name}</div>`;
Expand Down Expand Up @@ -158,12 +160,17 @@ a { color: #0056b3; }
element.includedIn.forEach(included => {
columns += '<div class="col ';
let txt = '';
let tooltip = included.version;
if (included.available === 'N/A') {
txt = 'N/A';
columns += 'bg-light';
} else if (included.available === 'yes') {
txt = '<i class="fa fa-check"></i>';
columns += 'bg-success';
} else if (included.available === 'stubbed') {
txt = '<i class="fa fa-star-half-alt"></i>';
columns += 'bg-warning';
tooltip = tooltip + '&#013;(stubbed)';
} else if (included.available === 'defined') {
txt = '<i class="fa fa-check"></i>';
columns += 'bg-info';
Expand All @@ -172,15 +179,14 @@ a { color: #0056b3; }
columns += 'bg-danger';
}

columns += `" style="max-width: 90px;" title="${included.version}">${txt}</div>`;
columns += `" style="max-width: 90px;" title="${tooltip}">${txt}</div>`;
});

return columns;
}

private generateNoteColumn(namespace: string, element?: DocEntry, subElement?: DocEntry): string {
const info = resolveInfo(this.infos, namespace, element?.name, subElement?.name);
const column = `<div class="col-auto">${info?._note ?? ''}</div>`;
return column;
return `<div class="col-auto">${info?._note ?? ''}</div>`;
}
}
2 changes: 1 addition & 1 deletion src/included.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@

export interface Included {
version: string;
available: 'yes' | 'defined' | 'no' | 'N/A';
available: 'yes' | 'defined' | 'no' | 'N/A' | 'stubbed';
}
11 changes: 6 additions & 5 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,17 @@ async function init() {
const theiaEntries = await new GrabTheiaVersions().grab();
const vsCodeEntries = await new GrabVSCodeVersions().grab();

// start comparator
const comparator = new Comparator(vsCodeEntries, theiaEntries);
comparator.init();
comparator.compare();

// Parse additional information
console.log('⚙️ Parsing additional information from infos.yml...');
const infoFileContent = fs.readFileSync(path.resolve(__dirname, '..', 'conf', 'infos.yml'), 'utf-8');
const infos = parseInfos(infoFileContent);

// start comparator
const comparator = new Comparator(vsCodeEntries, theiaEntries, infos);
comparator.init();
comparator.compare();


// Generate HTML output
console.log('⚙️ Generating HTML report...');
const htmlGenerator = new HTMLGenerator(vsCodeEntries, theiaEntries, comparator.result(), infos);
Expand Down
29 changes: 28 additions & 1 deletion src/infos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,34 @@
**********************************************************************/

import * as YAML from 'yaml';
import { DocEntry } from './doc-entry'

export interface Context {
namespaceKey: string,
element?: DocEntry,
subElement?: DocEntry
}

export interface Infos {
[element: string]: Record<string, Info>;
}

export interface StubbedRange {
from: string,
to?: string
}

interface StubbedInfo {
// Start with underscore to not conflict with sub element names
_stubbed?: StubbedRange
}

interface Note {
// Start with underscore to not conflict with sub element names
_note?: string;
}
export type Info = Note & Infos;

export type Info = Note & StubbedInfo & Infos;

export function parseInfos(yaml: string): Infos {
try {
Expand Down Expand Up @@ -53,3 +72,11 @@ export function resolveInfo(infos: Infos, namespace: string, element?: string, s
}
return resolved;
}

export function resolveStubbedInfo(infos: Infos, context: Context): StubbedRange | undefined {
const info = resolveInfo(infos, context.namespaceKey, context.element?.name, context.subElement?.name);
if (!info?._stubbed) {
return undefined;
}
return info._stubbed
}
124 changes: 124 additions & 0 deletions src/stubbed-ranges.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// *****************************************************************************
// Copyright (C) 2022 Ericsson and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
// *****************************************************************************

export interface VersionRange {
from: string,
to?: string
}

const semverLength = 3;
export function isInRange(version: string, versionRange: VersionRange): boolean {
const { returning, semanticVersions } = resolveByBranchNames(version, versionRange);
if (returning !== undefined) {
return returning;
}

return isVersionInRange(semanticVersions.input, semanticVersions.from, semanticVersions.to);
}

function removeVersionDecorator(version: string) {
return version.startsWith('v') ? version.substring(1) : version;
}

/**
* Handle provided versions by branch names; either 'master' or 'local'
*/
function resolveByBranchNames(
versionInput: string,
versionRange: VersionRange
): { returning?: boolean; semanticVersions?: { input: string; from: string; to: string } } {
const input = removeVersionDecorator(versionInput);
const from = removeVersionDecorator(versionRange.from);
let to = versionRange.to ? removeVersionDecorator(versionRange.to) : undefined;

// a 'to' version pointing to 'master' or 'local' means there is no version ending a condition (stubbing)
// So we treat those as if the 'to' version was 'undefined'
to = to && (to === 'master' || to === 'local') ? undefined : to;

if (input === 'master' || input == 'local') {
// Handling 'master' and 'local' which are considered the 'latest'
// Assuming, if 'to' version is available then 'latest' is no longer under the condition i.e. stubbed
return { returning: to ? false : true };
}

if (from === 'master' || from === 'local') {
// At this point input is not 'master' or 'local' i.e. input is referring to earlier versions than 'from'
return { returning: false };
}

// Done handling the supported branch names,
// returning strings representing semantic versions
return { semanticVersions: { input, from, to } }
}

/**
* Each argument represents a semantic version
*/
function isVersionInRange(input: string, from: string, to?: string): boolean {
const inputArr = input.split('.');
const fromArr = from.split('.');
const toArr = to ? to.split('.') : undefined;

if (
inputArr.length != semverLength ||
fromArr.length != semverLength ||
!!(toArr && toArr.length != semverLength)
) {
throw new Error('The expected length of semantic versions is 3');
}

const greaterOrEqual = isGreaterOrEqual(inputArr, fromArr);
if (!greaterOrEqual) {
return false;
}

if (!toArr) {
// Not having a 'to' version means the condition is still present i.e. stubbing applies.
return true;
}

return isLessOrEqual(inputArr, toArr);
}

function isGreaterOrEqual(inputArr: string[], referenceArr: string[]): boolean {
for (let i = 0; i < semverLength; i++) {
if (+inputArr[i] > +referenceArr[i]) {
return true;
}

if (+inputArr[i] < +referenceArr[i]) {
return false;
}
}

// Must be equal
return true;
}

function isLessOrEqual(inputArr: string[], referenceArr: string[]): boolean {
for (let i = 0; i < semverLength; i++) {
if (+inputArr[i] < +referenceArr[i]) {
return true;
}

if (+inputArr[i] > +referenceArr[i]) {
return false;
}
}

// Must be equal
return true;
}

0 comments on commit abb889b

Please sign in to comment.