8 changed files with 510 additions and 360 deletions
+1
View File
@@ -17,6 +17,7 @@ jobs:
- name: Check out repository
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.ref }}
token: ${{ secrets.ACTIONS_PAT }}
- name: Setup node
uses: actions/setup-node@v4
+2 -1
View File
@@ -3,5 +3,6 @@
"tabWidth": 2,
"semi": true,
"singleQuote": true,
"bracketSpacing": true
"bracketSpacing": true,
"endOfLine": "auto"
}
+53 -39
View File
@@ -49,7 +49,10 @@
--color-scheme: var(--md-sys-color-scheme); */
--top-app-bar-height: 4.5rem;
--top-app-bar-padding-vertical: .5rem;
--footer-height: 3.5rem;
--safe-area-inset-top: env(safe-area-inset-top);
}
body {
@@ -64,15 +67,33 @@ img {
max-width: 100%;
}
*::-webkit-scrollbar {
width: 8px;
height: 8px;
@supports not selector(::-webkit-scrollbar) {
* {
scrollbar-width: thin;
scrollbar-color: var(--color-accent) transparent;
}
}
*::-webkit-scrollbar-track {
background: none;
}
*::-webkit-scrollbar-thumb {
border: none;
@supports selector(::-webkit-scrollbar) {
*::-webkit-scrollbar {
width: 8px;
height: 8px;
}
*::-webkit-scrollbar-track {
background: none;
margin: 4px;
}
*::-webkit-scrollbar-thumb {
border: none;
}
*::-webkit-scrollbar-thumb:hover,
*::-webkit-scrollbar-thumb:active {
background-color: var(--md-sys-color-secondary);
}
* {
scrollbar-width: unset;
scrollbar-color: unset;
}
}
.container-main {
@@ -82,12 +103,13 @@ img {
overflow: hidden;
box-sizing: border-box;
padding: 1.75rem;
margin-bottom: 1.75rem;
border-radius: 28px;
background-color: var(--md-sys-color-surface);
}
.page-menu {
height: fit-content;
padding: 0.75rem 1.75rem;
padding: 1.25rem 1.75rem;
border-radius: 28px;
background-color: var(--md-sys-color-surface);
}
@@ -96,51 +118,40 @@ img {
position: relative;
}
.title {
display: block;
max-width: calc(100% - 5rem);
display: inline-block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-size: 22px;
}
.tsd-page-title {
word-break: break-all;
}
.tsd-page-toolbar {
padding: 8px 0;
height: calc(var(--top-app-bar-height) - 16px);
padding: 1rem 0;
background-color: var(--color-background);
border-bottom: none;
}
.tsd-page-toolbar .tsd-toolbar-contents {
height: 56px;
}
.tsd-page-toolbar .table-cell {
height: 56px;
margin-left: 1.5rem;
}
.tsd-page-toolbar .tsd-toolbar-icon {
padding: 20px 0;
}
#tsd-search {
line-height: 56px;
border-radius: 38px;
}
#tsd-search-input {
border-radius: 22px;
}
#tsd-search .results {
z-index: -1;
top: calc(56px - 22px);
padding-top: 22px;
box-shadow: 0px 4px 2px rgba(0, 0, 0, 0.125);
background-color: var(--color-background-secondary);
border-bottom-left-radius: 22px;
border-bottom-right-radius: 22px;
overflow: hidden;
#tsd-search-input:focus-visible {
background-color: var(--color-accent);
}
#tsd-search .results li {
#tsd-search-results > li {
background: none;
}
#tsd-search .results a {
padding: 1rem 0.25rem;
#tsd-toolbar-menu-trigger,
#tsd-search-trigger {
border-radius: 50%;
}
.col-sidebar {
html:not(.has-menu) .col-sidebar {
padding-top: 0;
margin-right: 1rem;
}
@@ -165,6 +176,9 @@ img {
text-decoration: none;
background-color: var(--md-sys-color-surface-container-high);
}
.page-menu .tsd-accordion-summary {
margin-bottom: 0.5rem;
}
.page-menu .tsd-accordion-summary svg {
position: absolute;
right: 0;
@@ -189,10 +203,10 @@ img {
.tsd-navigation .tsd-accordion-summary {
width: 100%;
}
.tsd-accordion .tsd-accordion-summary > svg,
.tsd-index-accordion .tsd-accordion-summary > svg {
position: absolute;
right: 1.5rem;
margin-top: 1rem;
}
.tsd-accordion-summary .tsd-kind-icon ~ span {
margin-right: 2.5rem;
@@ -217,9 +231,6 @@ img {
}
@media (max-width: 769px) {
.container {
padding: 1rem;
}
.col-sidebar {
margin-right: 0;
}
@@ -238,6 +249,9 @@ img {
max-height: calc(100vh - var(--footer-height) - var(--top-app-bar-height));
top: var(--top-app-bar-height);
}
.site-menu {
margin-top: 0;
}
.page-menu {
margin-left: 1rem;
}
+1 -1
View File
@@ -215,7 +215,7 @@
<li>An ApiResponse<FileInfo>.</li>
</ul>
<div class="tsd-comment tsd-typography"><h4>Method</h4><p>asFileInfoType - Converts the ApiResponse to an ApiResponse<FileInfo>.</p>
</div><aside class="tsd-sources"><ul><li>Defined in models/ApiResponse.ts:245</li></ul></aside></li></ul></section><section class="tsd-panel tsd-member"><a id="asFileType" class="tsd-anchor"></a><h3 class="tsd-anchor-link"><span>as<wbr/>File<wbr/>Type</span><a href="#asFileType" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3><ul class="tsd-signatures"><li class="tsd-signature tsd-anchor-link"><a id="asFileType.asFileType-1" class="tsd-anchor"></a><span class="tsd-kind-call-signature">as<wbr/>File<wbr/>Type</span><span class="tsd-signature-symbol">(</span><span class="tsd-kind-parameter">response</span><span class="tsd-signature-symbol">)</span><span class="tsd-signature-symbol">: </span><a href="ApiResponse.html" class="tsd-signature-type tsd-kind-class">ApiResponse</a><span class="tsd-signature-symbol">&lt;</span><a href="File.html" class="tsd-signature-type tsd-kind-class">File</a><span class="tsd-signature-symbol">&gt;</span><a href="#asFileType.asFileType-1" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></li><li class="tsd-description"><div class="tsd-parameters"><h4 class="tsd-parameters-title">Parameters</h4><ul class="tsd-parameter-list"><li><span><span class="tsd-kind-parameter">response</span>: <span class="tsd-signature-type">AxiosResponse</span><span class="tsd-signature-symbol">&lt;</span><span class="tsd-signature-type">any</span><span class="tsd-signature-symbol">, </span><span class="tsd-signature-type">any</span><span class="tsd-signature-symbol">&gt;</span></span></li></ul></div><h4 class="tsd-returns-title">Returns <a href="ApiResponse.html" class="tsd-signature-type tsd-kind-class">ApiResponse</a><span class="tsd-signature-symbol">&lt;</span><a href="File.html" class="tsd-signature-type tsd-kind-class">File</a><span class="tsd-signature-symbol">&gt;</span></h4><ul>
</div><aside class="tsd-sources"><ul><li>Defined in models/ApiResponse.ts:245</li></ul></aside></li></ul></section><section class="tsd-panel tsd-member"><a id="asFileType" class="tsd-anchor"></a><h3 class="tsd-anchor-link"><span>as<wbr/>File<wbr/>Type</span><a href="#asFileType" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3><ul class="tsd-signatures"><li class="tsd-signature tsd-anchor-link"><a id="asFileType.asFileType-1" class="tsd-anchor"></a><span class="tsd-kind-call-signature">as<wbr/>File<wbr/>Type</span><span class="tsd-signature-symbol">(</span><span class="tsd-kind-parameter">response</span><span class="tsd-signature-symbol">)</span><span class="tsd-signature-symbol">: </span><a href="ApiResponse.html" class="tsd-signature-type tsd-kind-class">ApiResponse</a><span class="tsd-signature-symbol">&lt;</span><a href="File.html" class="tsd-signature-type tsd-kind-class">File</a><span class="tsd-signature-symbol">&gt;</span><a href="#asFileType.asFileType-1" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></li><li class="tsd-description"><div class="tsd-parameters"><h4 class="tsd-parameters-title">Parameters</h4><ul class="tsd-parameter-list"><li><span><span class="tsd-kind-parameter">response</span>: <span class="tsd-signature-type">AxiosResponse</span><span class="tsd-signature-symbol">&lt;</span><span class="tsd-signature-type">any</span><span class="tsd-signature-symbol">, </span><span class="tsd-signature-type">any</span><span class="tsd-signature-symbol">, </span><span class="tsd-signature-symbol">{}</span><span class="tsd-signature-symbol">&gt;</span></span></li></ul></div><h4 class="tsd-returns-title">Returns <a href="ApiResponse.html" class="tsd-signature-type tsd-kind-class">ApiResponse</a><span class="tsd-signature-symbol">&lt;</span><a href="File.html" class="tsd-signature-type tsd-kind-class">File</a><span class="tsd-signature-symbol">&gt;</span></h4><ul>
<li>An ApiResponse<File>.</li>
</ul>
<div class="tsd-comment tsd-typography"><h4>Method</h4><p>asFileType - Converts the ApiResponse to an ApiResponse<File>.</p>
+340 -311
View File
File diff suppressed because it is too large Load Diff
+7 -8
View File
@@ -53,9 +53,8 @@
"build:esm": "tsc --project tsconfig.esm.json",
"build:cjs": "tsc --project tsconfig.cjs.json",
"build:json": "node ./scripts/create_dist_package_json.js",
"test:unit": "mocha -r ts-node/register",
"test:integration": "mocha -r ts-node/register -r integrationTests/mochaRootHooks.ts",
"tests:unit": "mocha -R progress -r ts-node/register ./tests/**/*.spec.ts",
"tests:unit:watch": "mocha -w --watch-files . -R progress -r ts-node/register ./tests/**/*.spec.ts",
"tests:integration": "mocha -R progress -r ts-node/register -r integrationTests/mochaRootHooks.ts ./integrationTests/**/*.spec.ts",
"tests": "mocha -R progress -r ts-node/register ./tests/**/*.spec.ts && mocha -R progress -r ts-node/register -r integrationTests/mochaRootHooks.ts ./integrationTests/**/*.spec.ts",
"test-coverage": "nyc npm run tests:unit",
@@ -68,16 +67,16 @@
"@istanbuljs/nyc-config-typescript": "^1.0.2",
"@types/chai": "^4.3.20",
"@types/mocha": "^10.0.10",
"@types/node": "^18.19.86",
"@types/node": "^18.19.129",
"@types/sinon": "^10.0.20",
"@typescript-eslint/eslint-plugin": "^5.62.0",
"@typescript-eslint/parser": "^5.62.0",
"chai": "^4.5.0",
"dotenv": "^16.5.0",
"dotenv": "^16.6.1",
"eslint": "^8.57.1",
"eslint-config-prettier": "^8.10.0",
"eslint-config-prettier": "^8.10.2",
"eslint-config-standard-with-typescript": "^34.0.1",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-mocha": "^10.5.0",
"eslint-plugin-n": "^15.7.0",
"eslint-plugin-promise": "^6.6.0",
@@ -93,7 +92,7 @@
"typescript": "^4.9.5"
},
"dependencies": {
"axios": "^1.8.4",
"form-data": "^4.0.2"
"axios": "^1.13.6",
"form-data": "^4.0.5"
}
}
+27
View File
@@ -111,6 +111,33 @@ export class OnspringClient {
return apiResponse.asGetPagedAppsResponseType();
}
/**
* @method getAllApps - Gets an iterable that can be used to page through all.
* @param {PagingRequest} pagingRequest - The paging request that will be used to get the apps.
* @returns
**/
public async *getAllApps(
pagingRequest: PagingRequest = new PagingRequest(1, 50)
): AsyncGenerator<ApiResponse<GetPagedAppsResponse>, void, unknown> {
// TODO: We need to actually honor the page request
// the caller gave us. We will use the page number
// as our starting page number and we will use the
// page size as our page size
for await (const page of this.getAllPagesAsync<GetPagedAppsResponse>(
async (pr) => await this.getApps(pr)
)) {
yield page;
}
}
private async *getAllPagesAsync<T>(
callback: (pagingRequest: PagingRequest) => Promise<ApiResponse<T>>
): AsyncGenerator<ApiResponse<T>, void, unknown> {
const pagingRequest = new PagingRequest(1, 50);
const initialResponse = await callback(pagingRequest);
yield initialResponse;
}
/**
* @method getAppById - Gets an app by its id.
* @param {number} appId - The id of the app to get.
+79
View File
@@ -232,6 +232,85 @@ describe('OnspringClient', function () {
});
});
describe('getAllApps', function () {
it('should be define', function () {
expect(new OnspringClient(baseUrl, apiKey).getAllApps).to.not.be
.undefined;
});
it('should be a function', function () {
expect(new OnspringClient(baseUrl, apiKey).getAllApps).to.be.a(
'function'
);
});
it('should have 0 parameter', function () {
expect(new OnspringClient(baseUrl, apiKey).getAllApps).to.have.lengthOf(
0
);
});
it('should return a async generator', function () {
const result = new OnspringClient(baseUrl, apiKey).getAllApps();
expect(result).to.have.property(Symbol.asyncIterator);
expect(result[Symbol.asyncIterator]).to.be.a('function');
expect(result).to.have.property('next').that.is.a('function');
expect(result).to.have.property('return').that.is.a('function');
expect(result).to.have.property('throw').that.is.a('function');
});
it('should return a promise that resolves to an async iterable of paged response of apps when request is successful', async function () {
const client = new OnspringClient(baseUrl, apiKey);
const mockAxiosClient = axios.create({
baseURL: baseUrl,
headers: {
'x-apikey': apiKey,
'x-api-version': '2',
},
});
sinon.stub(mockAxiosClient, 'get').returns(
Promise.resolve({
status: 200,
statusText: 'OK',
data: {
pageNumber: 1,
pageSize: 2,
totalPages: 1,
totalRecords: 2,
items: [
{
href: 'https://api.onspring.dev/Apps/id/1',
id: '1',
name: 'Test App 1',
},
{
href: 'https://api.onspring.dev/Apps/id/2',
id: '2',
name: 'Test App 2',
},
],
},
headers: {},
config: {} as InternalAxiosRequestConfig,
} as AxiosResponse)
);
sinon.stub(client, '_client' as any).value(mockAxiosClient);
const collectedPages: Array<ApiResponse<GetPagedAppsResponse>> = [];
for await (const page of client.getAllApps()) {
collectedPages.push(page);
}
expect(collectedPages.length).to.equal(1);
});
});
describe('getApps', function () {
it('should be defined', function () {
expect(new OnspringClient(baseUrl, apiKey).getApps).to.not.be.undefined;