-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(core): introduce a new core package (#34)
closes #29
- Loading branch information
Showing
46 changed files
with
2,151 additions
and
33 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
# @har-sdk/core | ||
|
||
The base package can be used to import specification files (i.e. HAR, OAS and Postman Collection) and detect their type. | ||
|
||
## Setup | ||
|
||
```bash | ||
npm i --save @har-sdk/core | ||
``` | ||
|
||
## Usage | ||
|
||
To import a specification you just need to create an instance of `SpecImporter` and call its `import` method passing the data file. The importer performs syntactic analysis and parses a provided file. | ||
|
||
```ts | ||
import { SpecImporter } from '@har-sdk/core'; | ||
import petstore from './petstore.collection.json'; | ||
|
||
const result = await new SpecImporter().tryToImportSpec(petstore); | ||
console.log(result); | ||
// { | ||
// type: 'postman', | ||
// format: 'json', | ||
// doc: { | ||
// info: { | ||
// name: 'Swagger Petstore', | ||
// schema: 'https://schema.getpostman.com/json/collection/v2.1.0/collection.json' | ||
// }, | ||
// item: [ | ||
// // ... | ||
// ] | ||
// }, | ||
// name: 'Swagger Petstore.json' | ||
// } | ||
``` | ||
|
||
To configure the list of importers, you can pass them as an array to the constructor. | ||
|
||
```ts | ||
import { SpecImporter, HarImporter } from '@har-sdk/core'; | ||
|
||
const explorer = new SpecImporter([new HarImporter()]); | ||
``` | ||
|
||
To extend an explorer by adding a new custom importer, you can easily implement an `Importer` interface. | ||
|
||
```ts | ||
import { Importer, Doc, Spec, Importer } from '@har-sdk/core'; | ||
|
||
class RamlImporter implements Importer<'raml'> { | ||
get type(): 'raml' { | ||
return 'raml'; | ||
} | ||
|
||
async import(content: string): Promise<Spec<'raml'>> { | ||
// your code | ||
|
||
return { | ||
// other fields | ||
type: this.type, | ||
format: 'yaml' | ||
} | ||
} | ||
} | ||
``` |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
{ | ||
"name": "@har-sdk/core", | ||
"version": "0.0.0", | ||
"private": false, | ||
"description": "The base package can be used to import specification files (i.e. HAR, OAS and Postman Collection) and detect their type.", | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/NeuraLegion/har-sdk.git" | ||
}, | ||
"author": { | ||
"name": "Artem Derevnjuk", | ||
"email": "[email protected]" | ||
}, | ||
"license": "MIT", | ||
"bugs": { | ||
"url": "https://github.com/NeuraLegion/har-sdk/issues" | ||
}, | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"files": [ | ||
"dist/**" | ||
], | ||
"keywords": [ | ||
"postman", | ||
"oas", | ||
"openapi", | ||
"swagger", | ||
"har", | ||
"api" | ||
], | ||
"main": "./dist/bundle.umd.js", | ||
"module": "./dist/bundle.esm5.js", | ||
"es2015": "./dist/index.js", | ||
"types": "./dist/index.d.ts", | ||
"homepage": "https://github.com/NeuraLegion/har-sdk/tree/master/packages/core#readme", | ||
"scripts": { | ||
"prebuild": "npm run compile -- --clean", | ||
"build": "npm run compile && npm run bundle", | ||
"bundle": "rollup -c ../../rollup.config.js", | ||
"compile": "tsc -b tsconfig.build.json", | ||
"lint": "eslint --ignore-path ../../.eslintignore .", | ||
"format": "prettier --ignore-path ../../.prettierignore --check **/*.ts", | ||
"test": "cross-env NODE_ENV=test mocha tests/**/*.spec.ts", | ||
"coverage": "nyc npm t -- --reporter xunit --reporter-options output=.mocha/testspec.xunit.xml" | ||
}, | ||
"dependencies": { | ||
"js-yaml": "^4.1.0", | ||
"openapi-types": "^9.1.0", | ||
"tslib": "~2.2.0" | ||
}, | ||
"devDependencies": { | ||
"@types/js-yaml": "^4.0.4" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import { Importer } from './Importer'; | ||
import { DocFormat, Spec, Doc, DocType } from './Spec'; | ||
import { load } from 'js-yaml'; | ||
|
||
interface LoadedFile { | ||
doc: unknown; | ||
format: DocFormat; | ||
} | ||
|
||
export abstract class BaseImporter< | ||
TDocType extends DocType, | ||
TDoc = Doc<TDocType> | ||
> implements Importer<TDocType, TDoc> | ||
{ | ||
protected constructor() { | ||
// noop | ||
} | ||
|
||
abstract get type(): TDocType; | ||
|
||
public abstract isSupported(spec: unknown): spec is TDoc; | ||
|
||
public async import( | ||
content: string | ||
): Promise<Spec<TDocType, TDoc> | undefined> { | ||
const file: LoadedFile | undefined = this.load(content.trim()); | ||
|
||
if (file && this.isSupported(file.doc)) { | ||
const { format, doc } = file; | ||
const name = this.fileName({ format, doc }); | ||
|
||
return { | ||
doc, | ||
name, | ||
format, | ||
type: this.type | ||
}; | ||
} | ||
} | ||
|
||
protected fileName(_: { doc: TDoc; format: DocFormat }): string | undefined { | ||
return; | ||
} | ||
|
||
protected load(content: string): LoadedFile | undefined { | ||
let doc: unknown | undefined = this.loadFromJson(content); | ||
let format: DocFormat = 'json'; | ||
|
||
if (!doc) { | ||
doc = this.loadFromYaml(content); | ||
format = 'yaml'; | ||
} | ||
|
||
return doc ? { doc, format } : undefined; | ||
} | ||
|
||
private loadFromJson(source: string): unknown | undefined { | ||
try { | ||
return JSON.parse(source); | ||
} catch { | ||
// noop | ||
} | ||
} | ||
|
||
private loadFromYaml(source: string): unknown | undefined { | ||
try { | ||
return load(source, { json: true }); | ||
} catch { | ||
// noop | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import { BaseImporter } from './BaseImporter'; | ||
import { OpenAPI } from '../types'; | ||
import { DocFormat } from './Spec'; | ||
import { ImporterType } from './ImporterType'; | ||
|
||
export abstract class BaseOASImporter< | ||
TDocType extends ImporterType.OASV2 | ImporterType.OASV3 | ||
> extends BaseImporter<TDocType> { | ||
protected constructor() { | ||
super(); | ||
} | ||
|
||
protected fileName({ | ||
doc, | ||
format | ||
}: { | ||
doc: OpenAPI.Document; | ||
format: DocFormat; | ||
}): string { | ||
return `${[doc.info.title, doc.info.version] | ||
.map((x: string) => x.trim()) | ||
.join(' ')}.${format}`; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import { BaseImporter } from './BaseImporter'; | ||
import { ImporterType } from './ImporterType'; | ||
import { Har } from '../types'; | ||
|
||
export class HARImporter extends BaseImporter<ImporterType.HAR> { | ||
private readonly SUPPORTED_HAR_VERSION = /^1\.\d+$/; // 1.x | ||
|
||
constructor() { | ||
super(); | ||
} | ||
|
||
get type(): ImporterType.HAR { | ||
return ImporterType.HAR; | ||
} | ||
|
||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types | ||
public isSupported(spec: any): spec is Har { | ||
return !!( | ||
spec?.log?.version && | ||
Array.isArray(spec.log.entries) && | ||
this.SUPPORTED_HAR_VERSION.test(spec.log.version) | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import { Doc, DocType, Spec } from './Spec'; | ||
|
||
export interface Importer<TDocType extends DocType, TDoc = Doc<TDocType>> { | ||
import(content: string): Promise<Spec<TDocType, TDoc> | undefined>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
export const enum ImporterType { | ||
HAR = 'har', | ||
OASV3 = 'oasv3', | ||
OASV2 = 'oasv2', | ||
POSTMAN = 'postman' | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { ImporterType } from './ImporterType'; | ||
import { OpenAPIV2 } from '../types'; | ||
import { BaseOASImporter } from './BaseOASImporter'; | ||
|
||
export class OASV2Importer extends BaseOASImporter<ImporterType.OASV2> { | ||
private readonly SUPPORTED_SWAGGER_VERSION = '2.0'; | ||
|
||
constructor() { | ||
super(); | ||
} | ||
|
||
get type(): ImporterType.OASV2 { | ||
return ImporterType.OASV2; | ||
} | ||
|
||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types | ||
public isSupported(spec: any): spec is OpenAPIV2.Document { | ||
return spec?.swagger?.trim() === this.SUPPORTED_SWAGGER_VERSION; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { ImporterType } from './ImporterType'; | ||
import { OpenAPIV3 } from '../types'; | ||
import { BaseOASImporter } from './BaseOASImporter'; | ||
|
||
export class OASV3Importer extends BaseOASImporter<ImporterType.OASV3> { | ||
private readonly SUPPORTED_OPENAPI_VERSION = /^3\.0\.\d+$/; // 3.0.x | ||
|
||
constructor() { | ||
super(); | ||
} | ||
|
||
get type(): ImporterType.OASV3 { | ||
return ImporterType.OASV3; | ||
} | ||
|
||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types | ||
public isSupported(spec: any): spec is OpenAPIV3.Document { | ||
return !!( | ||
spec?.openapi && this.SUPPORTED_OPENAPI_VERSION.test(spec.openapi) | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import { BaseImporter } from './BaseImporter'; | ||
import { ImporterType } from './ImporterType'; | ||
import { Postman } from '../types'; | ||
import { DocFormat } from './Spec'; | ||
|
||
export class PostmanImporter extends BaseImporter<ImporterType.POSTMAN> { | ||
private readonly POSTMAN_SCHEMAS: ReadonlySet<string> = new Set([ | ||
'https://schema.getpostman.com/json/collection/v2.0.0/collection.json', | ||
'https://schema.getpostman.com/json/collection/v2.1.0/collection.json' | ||
]); | ||
|
||
constructor() { | ||
super(); | ||
} | ||
|
||
get type(): ImporterType.POSTMAN { | ||
return ImporterType.POSTMAN; | ||
} | ||
|
||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types | ||
public isSupported(spec: any): spec is Postman.Document { | ||
const schemaId = spec?.info?.schema; | ||
|
||
return this.POSTMAN_SCHEMAS.has(schemaId?.trim().toLowerCase()); | ||
} | ||
|
||
protected fileName({ | ||
doc, | ||
format | ||
}: { | ||
doc: Postman.Document; | ||
format: DocFormat; | ||
}): string | undefined { | ||
return doc.info.name ? `${doc.info.name.trim()}.${format}` : undefined; | ||
} | ||
} |
Oops, something went wrong.