mirror of https://github.com/boxyhq/jackson.git
Pagination fixes (#2347)
* `offset` -> `pageOffset`, `limit`-> `pageLimit` * Be backward compatible in API * Cleanup types and handle pagination qs * Cleanup unused code * Import type * Cleanup and fix lint error * Align params for sso-tracer * Move parsing to a common util function * pageLimit shouldn't be optional * Cap pageLimit to max value, split the boolean * Revert typings and assert non null * Refactor var name * Use util function to normalize pagination params across getAll and getByIndex * Normalize offset/limit for dynamo/mongo * Update query params in `FederatedSAMLApps` * Cap to max limit if passed limit is 0 * Sync lock file * Add a 3rd record and supply opts.pageLimit * Normalize offset/limit for mem/redis * Save the 3rd record in the store * Fix getAll tests * Give precedence to standard params over legacy * Use util function * Parse using util function * Refactor * Standardise pagination for `api/v1/dsync/events` * Standardise pagination for api/admin/connections * Standardise pagination for api/admin/directory-sync * Standardise pagination for `api/v1/dsync/groups` * Standardise pagination for `v1/dsync/users`, `v1/dsync/product` * Standardise pagination in fetchByProduct APIs * Update swagger for groups * Fix pagination params definition, add the params for users api * More swagger updates * Swagger spec update for dsync events * Add pagination params to apis fetching by product * Update qs in internal-ui * Remove type assertion * [Swagger WIP] Fix response format for paginated APIs * Add dsync events to swagger spec * Fix swagger spec for sso tracer * Fix swagger spec for federated-saml apps of a product * Update pageLimit to 50 * Use pageLimit value from internal-ui * Update UI SDK * Cleanup local pagination component * Update swagger version * Remove unused keys from locale * Fix tag for trace api spec * Fix param name for swagger * Fix swagger tag for trace * updated package-lock * updated package-lock --------- Co-authored-by: Deepak Prabhakara <deepak@boxyhq.com>
This commit is contained in:
parent
06c7d38b37
commit
1188dd6396
|
@ -1,44 +0,0 @@
|
||||||
import ArrowLeftIcon from '@heroicons/react/24/outline/ArrowLeftIcon';
|
|
||||||
import ArrowRightIcon from '@heroicons/react/24/outline/ArrowRightIcon';
|
|
||||||
import { useTranslation } from 'next-i18next';
|
|
||||||
import { ButtonOutline } from './ButtonOutline';
|
|
||||||
|
|
||||||
type PaginationProps = {
|
|
||||||
itemsCount: number;
|
|
||||||
offset: number;
|
|
||||||
onPrevClick: () => void;
|
|
||||||
onNextClick: () => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const pageLimit = 15;
|
|
||||||
|
|
||||||
export const Pagination = ({ itemsCount, offset, onPrevClick, onNextClick }: PaginationProps) => {
|
|
||||||
const { t } = useTranslation('common');
|
|
||||||
|
|
||||||
// Hide pagination if there are no items to paginate.
|
|
||||||
if ((itemsCount === 0 && offset === 0) || (itemsCount < pageLimit && offset === 0)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const prevDisabled = offset === 0;
|
|
||||||
const nextDisabled = itemsCount < pageLimit || itemsCount === 0;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='flex justify-center space-x-4 py-4'>
|
|
||||||
<ButtonOutline
|
|
||||||
Icon={ArrowLeftIcon}
|
|
||||||
aria-label={t('previous')}
|
|
||||||
onClick={onPrevClick}
|
|
||||||
disabled={prevDisabled}>
|
|
||||||
{t('prev')}
|
|
||||||
</ButtonOutline>
|
|
||||||
<ButtonOutline
|
|
||||||
Icon={ArrowRightIcon}
|
|
||||||
aria-label={t('previous')}
|
|
||||||
onClick={onNextClick}
|
|
||||||
disabled={nextDisabled}>
|
|
||||||
{t('next')}
|
|
||||||
</ButtonOutline>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -3,8 +3,8 @@ import { useTranslation } from 'next-i18next';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { LinkPrimary } from '@components/LinkPrimary';
|
import { LinkPrimary } from '@components/LinkPrimary';
|
||||||
import { InputWithCopyButton } from '@components/ClipboardButton';
|
import { InputWithCopyButton } from '@components/ClipboardButton';
|
||||||
import { pageLimit } from '@components/Pagination';
|
|
||||||
import { ConnectionList } from '@boxyhq/react-ui/sso';
|
import { ConnectionList } from '@boxyhq/react-ui/sso';
|
||||||
|
import { pageLimit } from '@boxyhq/internal-ui';
|
||||||
|
|
||||||
const SSOConnectionList = ({
|
const SSOConnectionList = ({
|
||||||
setupLinkToken,
|
setupLinkToken,
|
||||||
|
|
|
@ -2,8 +2,8 @@ import LinkIcon from '@heroicons/react/24/outline/LinkIcon';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { LinkPrimary } from '@components/LinkPrimary';
|
import { LinkPrimary } from '@components/LinkPrimary';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { pageLimit } from '@components/Pagination';
|
|
||||||
import { DirectoryList } from '@boxyhq/react-ui/dsync';
|
import { DirectoryList } from '@boxyhq/react-ui/dsync';
|
||||||
|
import { pageLimit } from '@boxyhq/internal-ui';
|
||||||
|
|
||||||
const DSyncDirectoryList = ({ setupLinkToken }: { setupLinkToken?: string }) => {
|
const DSyncDirectoryList = ({ setupLinkToken }: { setupLinkToken?: string }) => {
|
||||||
const { t } = useTranslation('common');
|
const { t } = useTranslation('common');
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
|
|
||||||
import jackson from '@lib/jackson';
|
import jackson from '@lib/jackson';
|
||||||
|
import { parsePaginateApiParams } from '@lib/utils';
|
||||||
|
|
||||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
const { method } = req;
|
const { method } = req;
|
||||||
|
@ -35,12 +36,13 @@ const handlePOST = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
const handleGET = async (req: NextApiRequest, res: NextApiResponse) => {
|
const handleGET = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
const { samlFederatedController } = await jackson();
|
const { samlFederatedController } = await jackson();
|
||||||
|
|
||||||
const { offset, limit, pageToken } = req.query as { offset: string; limit: string; pageToken?: string };
|
const { pageOffset, pageLimit, pageToken } = parsePaginateApiParams(req.query);
|
||||||
|
|
||||||
const pageOffset = parseInt(offset);
|
const apps = await samlFederatedController.app.getAll({
|
||||||
const pageLimit = parseInt(limit);
|
pageOffset,
|
||||||
|
pageLimit,
|
||||||
const apps = await samlFederatedController.app.getAll({ pageOffset, pageLimit, pageToken });
|
pageToken,
|
||||||
|
});
|
||||||
|
|
||||||
if (apps.pageToken) {
|
if (apps.pageToken) {
|
||||||
res.setHeader('jackson-pagetoken', apps.pageToken);
|
res.setHeader('jackson-pagetoken', apps.pageToken);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { NextApiRequest, NextApiResponse } from 'next';
|
import { NextApiRequest, NextApiResponse } from 'next';
|
||||||
|
|
||||||
import jackson from '@lib/jackson';
|
import jackson from '@lib/jackson';
|
||||||
|
import { parsePaginateApiParams } from '@lib/utils';
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
try {
|
try {
|
||||||
|
@ -22,17 +23,16 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||||
const handleGET = async (req: NextApiRequest, res: NextApiResponse) => {
|
const handleGET = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
const { samlFederatedController } = await jackson();
|
const { samlFederatedController } = await jackson();
|
||||||
|
|
||||||
const { product, pageOffset, pageLimit, pageToken } = req.query as {
|
const { product } = req.query as {
|
||||||
product: string;
|
product: string;
|
||||||
pageOffset: string;
|
|
||||||
pageLimit: string;
|
|
||||||
pageToken?: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const { pageOffset, pageLimit, pageToken } = parsePaginateApiParams(req.query);
|
||||||
|
|
||||||
const apps = await samlFederatedController.app.getByProduct({
|
const apps = await samlFederatedController.app.getByProduct({
|
||||||
product,
|
product,
|
||||||
pageOffset: parseInt(pageOffset),
|
pageOffset,
|
||||||
pageLimit: parseInt(pageLimit),
|
pageLimit,
|
||||||
pageToken,
|
pageToken,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -21,8 +21,8 @@ export const DirectoryGroups = ({
|
||||||
const { paginate, setPaginate, pageTokenMap } = usePaginate(router!);
|
const { paginate, setPaginate, pageTokenMap } = usePaginate(router!);
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
offset: paginate.offset,
|
pageOffset: paginate.offset,
|
||||||
limit: pageLimit,
|
pageLimit,
|
||||||
};
|
};
|
||||||
|
|
||||||
// For DynamoDB
|
// For DynamoDB
|
||||||
|
|
|
@ -21,8 +21,8 @@ export const DirectoryUsers = ({
|
||||||
const { paginate, setPaginate, pageTokenMap } = usePaginate(router!);
|
const { paginate, setPaginate, pageTokenMap } = usePaginate(router!);
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
offset: paginate.offset,
|
pageOffset: paginate.offset,
|
||||||
limit: pageLimit,
|
pageLimit,
|
||||||
};
|
};
|
||||||
|
|
||||||
// For DynamoDB
|
// For DynamoDB
|
||||||
|
|
|
@ -35,8 +35,8 @@ export const DirectoryWebhookLogs = ({
|
||||||
const { paginate, setPaginate, pageTokenMap } = usePaginate(router!);
|
const { paginate, setPaginate, pageTokenMap } = usePaginate(router!);
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
offset: paginate.offset,
|
pageOffset: paginate.offset,
|
||||||
limit: pageLimit,
|
pageLimit,
|
||||||
};
|
};
|
||||||
|
|
||||||
// For DynamoDB
|
// For DynamoDB
|
||||||
|
|
|
@ -37,7 +37,7 @@ export const FederatedSAMLApps = ({
|
||||||
const { t } = useTranslation('common');
|
const { t } = useTranslation('common');
|
||||||
const { paginate, setPaginate, pageTokenMap } = usePaginate(router!);
|
const { paginate, setPaginate, pageTokenMap } = usePaginate(router!);
|
||||||
|
|
||||||
let getAppsUrl = `${urls.getApps}?offset=${paginate.offset}&limit=${pageLimit}`;
|
let getAppsUrl = `${urls.getApps}?pageOffset=${paginate.offset}&pageLimit=${pageLimit}`;
|
||||||
|
|
||||||
// For DynamoDB
|
// For DynamoDB
|
||||||
if (paginate.offset > 0 && pageTokenMap[paginate.offset - pageLimit]) {
|
if (paginate.offset > 0 && pageTokenMap[paginate.offset - pageLimit]) {
|
||||||
|
|
|
@ -3,7 +3,7 @@ import ArrowLeftIcon from '@heroicons/react/24/outline/ArrowLeftIcon';
|
||||||
import ArrowRightIcon from '@heroicons/react/24/outline/ArrowRightIcon';
|
import ArrowRightIcon from '@heroicons/react/24/outline/ArrowRightIcon';
|
||||||
import { ButtonOutline } from './ButtonOutline';
|
import { ButtonOutline } from './ButtonOutline';
|
||||||
|
|
||||||
export const pageLimit = 15;
|
export const pageLimit = 50;
|
||||||
|
|
||||||
export const Pagination = ({
|
export const Pagination = ({
|
||||||
itemsCount,
|
itemsCount,
|
||||||
|
|
|
@ -20,8 +20,8 @@ export const SSOTracers = ({
|
||||||
const { paginate, setPaginate, pageTokenMap, setPageTokenMap } = usePaginate(router!);
|
const { paginate, setPaginate, pageTokenMap, setPageTokenMap } = usePaginate(router!);
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
offset: paginate.offset,
|
pageOffset: paginate.offset,
|
||||||
limit: pageLimit,
|
pageLimit,
|
||||||
};
|
};
|
||||||
|
|
||||||
// For DynamoDB
|
// For DynamoDB
|
||||||
|
|
27
lib/utils.ts
27
lib/utils.ts
|
@ -2,6 +2,7 @@ import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import micromatch from 'micromatch';
|
import micromatch from 'micromatch';
|
||||||
import type { OIDCSSOConnectionWithDiscoveryUrl, OIDCSSOConnectionWithMetadata } from '@boxyhq/saml-jackson';
|
import type { OIDCSSOConnectionWithDiscoveryUrl, OIDCSSOConnectionWithMetadata } from '@boxyhq/saml-jackson';
|
||||||
import { JacksonError } from 'npm/src/controller/error';
|
import { JacksonError } from 'npm/src/controller/error';
|
||||||
|
import type { PaginateApiParams } from 'types';
|
||||||
|
|
||||||
export const validateEmailWithACL = (email: string) => {
|
export const validateEmailWithACL = (email: string) => {
|
||||||
const NEXTAUTH_ACL = process.env.NEXTAUTH_ACL || undefined;
|
const NEXTAUTH_ACL = process.env.NEXTAUTH_ACL || undefined;
|
||||||
|
@ -73,3 +74,29 @@ export const oidcMetadataParse = (
|
||||||
}
|
}
|
||||||
return body;
|
return body;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const parsePaginateApiParams = (params: NextApiRequest['query']): PaginateApiParams => {
|
||||||
|
let pageOffset, pageLimit;
|
||||||
|
|
||||||
|
if ('pageOffset' in params) {
|
||||||
|
pageOffset = params.pageOffset;
|
||||||
|
} else if ('offset' in params) {
|
||||||
|
pageOffset = params.offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('pageLimit' in params) {
|
||||||
|
pageLimit = params.pageLimit;
|
||||||
|
} else if ('limit' in params) {
|
||||||
|
pageLimit = params.limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
pageOffset = parseInt(pageOffset);
|
||||||
|
pageLimit = parseInt(pageLimit);
|
||||||
|
const pageToken = params.pageToken as string;
|
||||||
|
|
||||||
|
return {
|
||||||
|
pageOffset,
|
||||||
|
pageLimit,
|
||||||
|
pageToken,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
|
@ -30,15 +30,12 @@
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"new_directory": "New Directory",
|
"new_directory": "New Directory",
|
||||||
"new_setup_link": "New Setup Link",
|
"new_setup_link": "New Setup Link",
|
||||||
"next": "Next",
|
|
||||||
"connections": "Connections",
|
"connections": "Connections",
|
||||||
"new_connection": "New Connection",
|
"new_connection": "New Connection",
|
||||||
"no_projects_found": "No projects found.",
|
"no_projects_found": "No projects found.",
|
||||||
"oidc": "OIDC",
|
"oidc": "OIDC",
|
||||||
"open_menu": "Open menu",
|
"open_menu": "Open menu",
|
||||||
"open_sidebar": "Open sidebar",
|
"open_sidebar": "Open sidebar",
|
||||||
"prev": "Prev",
|
|
||||||
"previous": "Previous",
|
|
||||||
"product": "Product",
|
"product": "Product",
|
||||||
"save_changes": "Save Changes",
|
"save_changes": "Save Changes",
|
||||||
"saved": "Saved",
|
"saved": "Saved",
|
||||||
|
|
|
@ -806,12 +806,20 @@ export class ConnectionAPIController implements IConnectionAPIController {
|
||||||
* type: object
|
* type: object
|
||||||
* description: OIDC IdP metadata
|
* description: OIDC IdP metadata
|
||||||
* responses:
|
* responses:
|
||||||
* '200Get':
|
* '200GetByProduct':
|
||||||
* description: Success
|
* description: Success
|
||||||
* schema:
|
* content:
|
||||||
* type: array
|
* application/json:
|
||||||
* items:
|
* schema:
|
||||||
* $ref: '#/definitions/Connection'
|
* type: object
|
||||||
|
* properties:
|
||||||
|
* data:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: '#/definitions/Connection'
|
||||||
|
* pageToken:
|
||||||
|
* type: string
|
||||||
|
* description: token for pagination
|
||||||
* '400Get':
|
* '400Get':
|
||||||
* description: Please provide a `product`.
|
* description: Please provide a `product`.
|
||||||
* '401Get':
|
* '401Get':
|
||||||
|
@ -821,11 +829,14 @@ export class ConnectionAPIController implements IConnectionAPIController {
|
||||||
* summary: Get SSO Connections by product
|
* summary: Get SSO Connections by product
|
||||||
* parameters:
|
* parameters:
|
||||||
* - $ref: '#/parameters/productParamGet'
|
* - $ref: '#/parameters/productParamGet'
|
||||||
|
* - $ref: '#/parameters/pageOffset'
|
||||||
|
* - $ref: '#/parameters/pageLimit'
|
||||||
|
* - $ref: '#/parameters/pageToken'
|
||||||
* operationId: get-connections-by-product
|
* operationId: get-connections-by-product
|
||||||
* tags: [Single Sign On]
|
* tags: [Single Sign On]
|
||||||
* responses:
|
* responses:
|
||||||
* '200':
|
* '200':
|
||||||
* $ref: '#/responses/200Get'
|
* $ref: '#/responses/200GetByProduct'
|
||||||
* '400':
|
* '400':
|
||||||
* $ref: '#/responses/400Get'
|
* $ref: '#/responses/400Get'
|
||||||
* '401':
|
* '401':
|
||||||
|
|
|
@ -393,6 +393,9 @@ export class SetupLinkController {
|
||||||
* summary: Get the Setup Links by product
|
* summary: Get the Setup Links by product
|
||||||
* parameters:
|
* parameters:
|
||||||
* - $ref: '#/parameters/productParamGet'
|
* - $ref: '#/parameters/productParamGet'
|
||||||
|
* - $ref: '#/parameters/pageOffset'
|
||||||
|
* - $ref: '#/parameters/pageLimit'
|
||||||
|
* - $ref: '#/parameters/pageToken'
|
||||||
* operationId: get-sso-setup-link-by-product
|
* operationId: get-sso-setup-link-by-product
|
||||||
* tags: [Setup Links | Single Sign On]
|
* tags: [Setup Links | Single Sign On]
|
||||||
* responses:
|
* responses:
|
||||||
|
@ -407,6 +410,9 @@ export class SetupLinkController {
|
||||||
* summary: Get the Setup Links by product
|
* summary: Get the Setup Links by product
|
||||||
* parameters:
|
* parameters:
|
||||||
* - $ref: '#/parameters/productParamGet'
|
* - $ref: '#/parameters/productParamGet'
|
||||||
|
* - $ref: '#/parameters/pageOffset'
|
||||||
|
* - $ref: '#/parameters/pageLimit'
|
||||||
|
* - $ref: '#/parameters/pageToken'
|
||||||
* operationId: get-dsync-setup-link-by-product
|
* operationId: get-dsync-setup-link-by-product
|
||||||
* tags: [Setup Links | Directory Sync]
|
* tags: [Setup Links | Directory Sync]
|
||||||
* responses:
|
* responses:
|
||||||
|
|
|
@ -183,6 +183,10 @@ class DynamoDB implements DatabaseDriver {
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAll(namespace: string, _?: number, pageLimit?: number, pageToken?: string): Promise<Records> {
|
async getAll(namespace: string, _?: number, pageLimit?: number, pageToken?: string): Promise<Records> {
|
||||||
|
const { limit: Limit } = dbutils.normalizeOffsetAndLimit({
|
||||||
|
pageLimit,
|
||||||
|
maxLimit: this.options.pageLimit!,
|
||||||
|
});
|
||||||
const res = await this.client.send(
|
const res = await this.client.send(
|
||||||
new QueryCommand({
|
new QueryCommand({
|
||||||
KeyConditionExpression: 'namespace = :namespace',
|
KeyConditionExpression: 'namespace = :namespace',
|
||||||
|
@ -190,7 +194,7 @@ class DynamoDB implements DatabaseDriver {
|
||||||
':namespace': { S: namespace },
|
':namespace': { S: namespace },
|
||||||
},
|
},
|
||||||
TableName: tableName,
|
TableName: tableName,
|
||||||
Limit: pageLimit && pageLimit > 0 ? pageLimit : undefined,
|
Limit,
|
||||||
ExclusiveStartKey: pageToken ? JSON.parse(Buffer.from(pageToken, 'base64').toString()) : undefined,
|
ExclusiveStartKey: pageToken ? JSON.parse(Buffer.from(pageToken, 'base64').toString()) : undefined,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
|
@ -59,14 +59,18 @@ class Mem implements DatabaseDriver {
|
||||||
_?: string,
|
_?: string,
|
||||||
sortOrder?: SortOrder
|
sortOrder?: SortOrder
|
||||||
): Promise<Records> {
|
): Promise<Records> {
|
||||||
const offsetAndLimitValueCheck = !dbutils.isNumeric(pageOffset) && !dbutils.isNumeric(pageLimit);
|
|
||||||
const returnValue: string[] = [];
|
const returnValue: string[] = [];
|
||||||
const skip = Number(offsetAndLimitValueCheck ? 0 : pageOffset);
|
|
||||||
|
|
||||||
let take = Number(offsetAndLimitValueCheck ? this.options.pageLimit : pageLimit);
|
const { offset: skip, limit } = dbutils.normalizeOffsetAndLimit({
|
||||||
|
pageOffset,
|
||||||
|
pageLimit,
|
||||||
|
maxLimit: this.options.pageLimit!,
|
||||||
|
});
|
||||||
|
|
||||||
|
let take = limit;
|
||||||
let count = 0;
|
let count = 0;
|
||||||
|
|
||||||
take += skip;
|
take! += skip!;
|
||||||
|
|
||||||
if (namespace) {
|
if (namespace) {
|
||||||
const index = dbutils.keyFromParts(dbutils.createdAtPrefix, namespace);
|
const index = dbutils.keyFromParts(dbutils.createdAtPrefix, namespace);
|
||||||
|
@ -79,11 +83,11 @@ class Mem implements DatabaseDriver {
|
||||||
const iterator: IterableIterator<string> = sortOrder === 'ASC' ? val.values() : val.reverse().values();
|
const iterator: IterableIterator<string> = sortOrder === 'ASC' ? val.values() : val.reverse().values();
|
||||||
|
|
||||||
for (const value of iterator) {
|
for (const value of iterator) {
|
||||||
if (count >= take) {
|
if (count >= take!) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count >= skip) {
|
if (count >= skip!) {
|
||||||
returnValue.push(this.store[dbutils.keyFromParts(namespace, value)]);
|
returnValue.push(this.store[dbutils.keyFromParts(namespace, value)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,30 +107,30 @@ class Mem implements DatabaseDriver {
|
||||||
_?: string,
|
_?: string,
|
||||||
sortOrder?: SortOrder
|
sortOrder?: SortOrder
|
||||||
): Promise<Records> {
|
): Promise<Records> {
|
||||||
const offsetAndLimitValueCheck = !dbutils.isNumeric(pageOffset) && !dbutils.isNumeric(pageLimit);
|
const { offset: skip, limit } = dbutils.normalizeOffsetAndLimit({
|
||||||
const skip = Number(offsetAndLimitValueCheck ? 0 : pageOffset);
|
pageOffset,
|
||||||
|
pageLimit,
|
||||||
|
maxLimit: this.options.pageLimit!,
|
||||||
|
});
|
||||||
|
|
||||||
|
let take = limit;
|
||||||
|
|
||||||
let take = Number(offsetAndLimitValueCheck ? this.options.pageLimit : pageLimit);
|
|
||||||
let count = 0;
|
let count = 0;
|
||||||
|
|
||||||
take += skip;
|
take! += skip!;
|
||||||
const dbKeys = Array.from((await this.indexes[dbutils.keyForIndex(namespace, idx)]) || []) as string[];
|
const dbKeys = Array.from((await this.indexes[dbutils.keyForIndex(namespace, idx)]) || []) as string[];
|
||||||
const iterator: IterableIterator<string> =
|
const iterator: IterableIterator<string> =
|
||||||
sortOrder === 'ASC' ? dbKeys.values() : dbKeys.reverse().values();
|
sortOrder === 'ASC' ? dbKeys.values() : dbKeys.reverse().values();
|
||||||
const ret: string[] = [];
|
const ret: string[] = [];
|
||||||
for (const dbKey of iterator || []) {
|
for (const dbKey of iterator || []) {
|
||||||
if (offsetAndLimitValueCheck) {
|
if (count >= take!) {
|
||||||
ret.push(await this.get(namespace, dbKey));
|
break;
|
||||||
} else {
|
|
||||||
if (count >= take) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (count >= skip) {
|
|
||||||
ret.push(await this.get(namespace, dbKey));
|
|
||||||
}
|
|
||||||
|
|
||||||
count++;
|
|
||||||
}
|
}
|
||||||
|
if (count >= skip!) {
|
||||||
|
ret.push(await this.get(namespace, dbKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return { data: ret };
|
return { data: ret };
|
||||||
|
|
|
@ -86,10 +86,20 @@ class Mongo implements DatabaseDriver {
|
||||||
_?: string,
|
_?: string,
|
||||||
sortOrder?: SortOrder
|
sortOrder?: SortOrder
|
||||||
): Promise<Records> {
|
): Promise<Records> {
|
||||||
|
const { offset: skip, limit } = dbutils.normalizeOffsetAndLimit({
|
||||||
|
pageOffset,
|
||||||
|
pageLimit,
|
||||||
|
maxLimit: this.options.pageLimit!,
|
||||||
|
});
|
||||||
|
|
||||||
const docs = await this.collection
|
const docs = await this.collection
|
||||||
.find(
|
.find(
|
||||||
{ namespace: namespace },
|
{ namespace: namespace },
|
||||||
{ sort: { createdAt: sortOrder === 'ASC' ? 1 : -1 }, skip: pageOffset, limit: pageLimit }
|
{
|
||||||
|
sort: { createdAt: sortOrder === 'ASC' ? 1 : -1 },
|
||||||
|
skip,
|
||||||
|
limit,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
.toArray();
|
.toArray();
|
||||||
|
|
||||||
|
@ -103,31 +113,31 @@ class Mongo implements DatabaseDriver {
|
||||||
async getByIndex(
|
async getByIndex(
|
||||||
namespace: string,
|
namespace: string,
|
||||||
idx: Index,
|
idx: Index,
|
||||||
offset?: number,
|
pageOffset?: number,
|
||||||
limit?: number,
|
pageLimit?: number,
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
_?: string,
|
_?: string,
|
||||||
sortOrder?: SortOrder
|
sortOrder?: SortOrder
|
||||||
): Promise<Records> {
|
): Promise<Records> {
|
||||||
const sort: Sort = { createdAt: sortOrder === 'ASC' ? 'asc' : 'desc' };
|
const sort: Sort = { createdAt: sortOrder === 'ASC' ? 'asc' : 'desc' };
|
||||||
const docs =
|
const { offset: skip, limit } = dbutils.normalizeOffsetAndLimit({
|
||||||
dbutils.isNumeric(offset) && dbutils.isNumeric(limit)
|
pageOffset,
|
||||||
? await this.collection
|
pageLimit,
|
||||||
.find(
|
maxLimit: this.options.pageLimit!,
|
||||||
{
|
});
|
||||||
indexes: dbutils.keyForIndex(namespace, idx),
|
|
||||||
},
|
const docs = await this.collection
|
||||||
{ sort, skip: offset, limit: limit }
|
.find(
|
||||||
)
|
{
|
||||||
.toArray()
|
indexes: dbutils.keyForIndex(namespace, idx),
|
||||||
: await this.collection
|
},
|
||||||
.find(
|
{
|
||||||
{
|
sort,
|
||||||
indexes: dbutils.keyForIndex(namespace, idx),
|
skip,
|
||||||
},
|
limit,
|
||||||
{ sort }
|
}
|
||||||
)
|
)
|
||||||
.toArray();
|
.toArray();
|
||||||
|
|
||||||
const ret: string[] = [];
|
const ret: string[] = [];
|
||||||
for (const doc of docs || []) {
|
for (const doc of docs || []) {
|
||||||
|
|
|
@ -44,10 +44,14 @@ class Redis implements DatabaseDriver {
|
||||||
_?: string,
|
_?: string,
|
||||||
sortOrder?: SortOrder
|
sortOrder?: SortOrder
|
||||||
): Promise<Records> {
|
): Promise<Records> {
|
||||||
const offset = pageOffset ? Number(pageOffset) : 0;
|
|
||||||
const count = pageLimit ? Number(pageLimit) : this.options.pageLimit;
|
|
||||||
const sortedSetKey = dbutils.keyFromParts(dbutils.createdAtPrefix, namespace);
|
const sortedSetKey = dbutils.keyFromParts(dbutils.createdAtPrefix, namespace);
|
||||||
|
|
||||||
|
const { offset, limit: count } = dbutils.normalizeOffsetAndLimit({
|
||||||
|
pageOffset,
|
||||||
|
pageLimit,
|
||||||
|
maxLimit: this.options.pageLimit!,
|
||||||
|
});
|
||||||
|
|
||||||
const zRangeOptions = {
|
const zRangeOptions = {
|
||||||
BY: 'SCORE',
|
BY: 'SCORE',
|
||||||
REV: sortOrder === 'ASC',
|
REV: sortOrder === 'ASC',
|
||||||
|
@ -92,49 +96,43 @@ class Redis implements DatabaseDriver {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
_?: string
|
_?: string
|
||||||
): Promise<Records> {
|
): Promise<Records> {
|
||||||
const offsetAndLimitValueCheck = !dbutils.isNumeric(pageOffset) && !dbutils.isNumeric(pageLimit);
|
const { offset: skip, limit } = dbutils.normalizeOffsetAndLimit({
|
||||||
let take = Number(offsetAndLimitValueCheck ? this.options.pageLimit : pageLimit);
|
pageOffset,
|
||||||
const skip = Number(offsetAndLimitValueCheck ? 0 : pageOffset);
|
pageLimit,
|
||||||
|
maxLimit: this.options.pageLimit!,
|
||||||
|
});
|
||||||
|
let take = limit;
|
||||||
|
|
||||||
let count = 0;
|
let count = 0;
|
||||||
take += skip;
|
take! += skip!;
|
||||||
const returnValue: string[] = [];
|
const returnValue: string[] = [];
|
||||||
const keyArray: string[] = [];
|
const keyArray: string[] = [];
|
||||||
const idxKey = dbutils.keyForIndex(namespace, idx);
|
const idxKey = dbutils.keyForIndex(namespace, idx);
|
||||||
const dbKeys = await this.client.sMembers(dbutils.keyFromParts(dbutils.indexPrefix, idxKey));
|
const dbKeys = await this.client.sMembers(dbutils.keyFromParts(dbutils.indexPrefix, idxKey));
|
||||||
if (!offsetAndLimitValueCheck) {
|
for await (const { value } of this.client.zScanIterator(
|
||||||
for await (const { value } of this.client.zScanIterator(
|
dbutils.keyFromParts(dbutils.createdAtPrefix, namespace),
|
||||||
dbutils.keyFromParts(dbutils.createdAtPrefix, namespace),
|
count + 1
|
||||||
count + 1
|
)) {
|
||||||
)) {
|
if (dbKeys.indexOf(value) !== -1) {
|
||||||
if (dbKeys.indexOf(value) !== -1) {
|
if (count >= take!) {
|
||||||
if (count >= take) {
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (count >= skip) {
|
|
||||||
keyArray.push(dbutils.keyFromParts(namespace, value));
|
|
||||||
}
|
|
||||||
count++;
|
|
||||||
}
|
}
|
||||||
}
|
if (count >= skip!) {
|
||||||
if (keyArray.length > 0) {
|
keyArray.push(dbutils.keyFromParts(namespace, value));
|
||||||
const value = await this.client.MGET(keyArray);
|
|
||||||
for (let i = 0; i < value.length; i++) {
|
|
||||||
const valueObject = JSON.parse(value[i].toString());
|
|
||||||
if (valueObject !== null && valueObject !== '') {
|
|
||||||
returnValue.push(valueObject);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
count++;
|
||||||
}
|
}
|
||||||
return { data: returnValue || [] };
|
|
||||||
} else {
|
|
||||||
const ret: string[] = [];
|
|
||||||
for (const dbKey of dbKeys || []) {
|
|
||||||
if (offsetAndLimitValueCheck) {
|
|
||||||
ret.push(await this.get(namespace, dbKey));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { data: ret };
|
|
||||||
}
|
}
|
||||||
|
if (keyArray.length > 0) {
|
||||||
|
const value = await this.client.MGET(keyArray);
|
||||||
|
for (let i = 0; i < value.length; i++) {
|
||||||
|
const valueObject = JSON.parse(value[i].toString());
|
||||||
|
if (valueObject !== null && valueObject !== '') {
|
||||||
|
returnValue.push(valueObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { data: returnValue || [] };
|
||||||
}
|
}
|
||||||
|
|
||||||
async put(namespace: string, key: string, val: Encrypted, ttl = 0, ...indexes: any[]): Promise<void> {
|
async put(namespace: string, key: string, val: Encrypted, ttl = 0, ...indexes: any[]): Promise<void> {
|
||||||
|
|
|
@ -171,7 +171,11 @@ class Sql implements DatabaseDriver {
|
||||||
_?: string,
|
_?: string,
|
||||||
sortOrder?: SortOrder
|
sortOrder?: SortOrder
|
||||||
): Promise<Records> {
|
): Promise<Records> {
|
||||||
const skipOffsetAndLimitValue = !dbutils.isNumeric(pageOffset) && !dbutils.isNumeric(pageLimit);
|
const { offset: skip, limit: take } = dbutils.normalizeOffsetAndLimit({
|
||||||
|
pageOffset,
|
||||||
|
pageLimit,
|
||||||
|
maxLimit: this.options.pageLimit!,
|
||||||
|
});
|
||||||
|
|
||||||
const res = await this.storeRepository.find({
|
const res = await this.storeRepository.find({
|
||||||
where: { namespace: namespace },
|
where: { namespace: namespace },
|
||||||
|
@ -179,8 +183,8 @@ class Sql implements DatabaseDriver {
|
||||||
order: {
|
order: {
|
||||||
['createdAt']: sortOrder || 'DESC',
|
['createdAt']: sortOrder || 'DESC',
|
||||||
},
|
},
|
||||||
take: skipOffsetAndLimitValue ? this.options.pageLimit : pageLimit,
|
take,
|
||||||
skip: skipOffsetAndLimitValue ? 0 : pageOffset,
|
skip,
|
||||||
});
|
});
|
||||||
|
|
||||||
return { data: res || [] };
|
return { data: res || [] };
|
||||||
|
@ -195,21 +199,20 @@ class Sql implements DatabaseDriver {
|
||||||
_?: string,
|
_?: string,
|
||||||
sortOrder?: SortOrder
|
sortOrder?: SortOrder
|
||||||
): Promise<Records> {
|
): Promise<Records> {
|
||||||
const skipOffsetAndLimitValue = !dbutils.isNumeric(pageOffset) && !dbutils.isNumeric(pageLimit);
|
const { offset: skip, limit: take } = dbutils.normalizeOffsetAndLimit({
|
||||||
|
pageOffset,
|
||||||
|
pageLimit,
|
||||||
|
maxLimit: this.options.pageLimit!,
|
||||||
|
});
|
||||||
const sort = {
|
const sort = {
|
||||||
id: sortOrder || 'DESC',
|
id: sortOrder || 'DESC',
|
||||||
};
|
};
|
||||||
const res = skipOffsetAndLimitValue
|
const res = await this.indexRepository.find({
|
||||||
? await this.indexRepository.find({
|
where: { key: dbutils.keyForIndex(namespace, idx) },
|
||||||
where: { key: dbutils.keyForIndex(namespace, idx) },
|
take,
|
||||||
order: sort,
|
skip,
|
||||||
})
|
order: sort,
|
||||||
: await this.indexRepository.find({
|
});
|
||||||
where: { key: dbutils.keyForIndex(namespace, idx) },
|
|
||||||
take: skipOffsetAndLimitValue ? this.options.pageLimit : pageLimit,
|
|
||||||
skip: skipOffsetAndLimitValue ? 0 : pageOffset,
|
|
||||||
order: sort,
|
|
||||||
});
|
|
||||||
|
|
||||||
const ret: Encrypted[] = [];
|
const ret: Encrypted[] = [];
|
||||||
if (res) {
|
if (res) {
|
||||||
|
|
|
@ -23,6 +23,22 @@ export const sleep = (ms: number): Promise<void> => {
|
||||||
export function isNumeric(num) {
|
export function isNumeric(num) {
|
||||||
return !isNaN(num);
|
return !isNaN(num);
|
||||||
}
|
}
|
||||||
|
export const normalizeOffsetAndLimit = ({
|
||||||
|
pageLimit,
|
||||||
|
pageOffset,
|
||||||
|
maxLimit,
|
||||||
|
}: {
|
||||||
|
pageOffset?: number;
|
||||||
|
pageLimit?: number;
|
||||||
|
maxLimit: number;
|
||||||
|
}) => {
|
||||||
|
const skipOffset = pageOffset === undefined || !isNumeric(pageOffset);
|
||||||
|
// maxLimit capped to 50 by default unless set from env (db.options.pageLimit)
|
||||||
|
const capToMaxLimit =
|
||||||
|
pageLimit === undefined || pageLimit === 0 || !isNumeric(pageLimit) || pageLimit > maxLimit;
|
||||||
|
|
||||||
|
return { offset: skipOffset ? 0 : pageOffset, limit: capToMaxLimit ? maxLimit : pageLimit };
|
||||||
|
};
|
||||||
export const indexPrefix = '_index';
|
export const indexPrefix = '_index';
|
||||||
export const createdAtPrefix = '_createdAt';
|
export const createdAtPrefix = '_createdAt';
|
||||||
export const modifiedAtPrefix = '_modifiedAt';
|
export const modifiedAtPrefix = '_modifiedAt';
|
||||||
|
|
|
@ -133,6 +133,24 @@ export class DirectoryConfig {
|
||||||
* in: query
|
* in: query
|
||||||
* required: false
|
* required: false
|
||||||
* type: string
|
* type: string
|
||||||
|
* pageOffset:
|
||||||
|
* name: pageOffset
|
||||||
|
* description: Starting point from which the set of records are retrieved
|
||||||
|
* in: query
|
||||||
|
* required: false
|
||||||
|
* type: string
|
||||||
|
* pageLimit:
|
||||||
|
* name: pageLimit
|
||||||
|
* description: Number of records to be fetched for the page
|
||||||
|
* in: query
|
||||||
|
* required: false
|
||||||
|
* type: string
|
||||||
|
* pageToken:
|
||||||
|
* name: pageToken
|
||||||
|
* description: Token used for DynamoDB pagination
|
||||||
|
* in: query
|
||||||
|
* required: false
|
||||||
|
* type: string
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -602,6 +620,9 @@ export class DirectoryConfig {
|
||||||
* summary: Get directory connections by product
|
* summary: Get directory connections by product
|
||||||
* parameters:
|
* parameters:
|
||||||
* - $ref: '#/parameters/product'
|
* - $ref: '#/parameters/product'
|
||||||
|
* - $ref: '#/parameters/pageOffset'
|
||||||
|
* - $ref: '#/parameters/pageLimit'
|
||||||
|
* - $ref: '#/parameters/pageToken'
|
||||||
* tags:
|
* tags:
|
||||||
* - Directory Sync
|
* - Directory Sync
|
||||||
* produces:
|
* produces:
|
||||||
|
@ -609,10 +630,18 @@ export class DirectoryConfig {
|
||||||
* responses:
|
* responses:
|
||||||
* '200':
|
* '200':
|
||||||
* description: Success
|
* description: Success
|
||||||
* schema:
|
* content:
|
||||||
* type: array
|
* application/json:
|
||||||
* items:
|
* schema:
|
||||||
* $ref: '#/definitions/Directory'
|
* type: object
|
||||||
|
* properties:
|
||||||
|
* data:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: '#/definitions/Directory'
|
||||||
|
* pageToken:
|
||||||
|
* type: string
|
||||||
|
* description: token for pagination
|
||||||
*/
|
*/
|
||||||
public async filterBy(
|
public async filterBy(
|
||||||
params: FilterByParams = {}
|
params: FilterByParams = {}
|
||||||
|
|
|
@ -207,6 +207,9 @@ export class Groups extends Base {
|
||||||
* - $ref: '#/parameters/tenant'
|
* - $ref: '#/parameters/tenant'
|
||||||
* - $ref: '#/parameters/product'
|
* - $ref: '#/parameters/product'
|
||||||
* - $ref: '#/parameters/directoryId'
|
* - $ref: '#/parameters/directoryId'
|
||||||
|
* - $ref: '#/parameters/pageOffset'
|
||||||
|
* - $ref: '#/parameters/pageLimit'
|
||||||
|
* - $ref: '#/parameters/pageToken'
|
||||||
* tags:
|
* tags:
|
||||||
* - Directory Sync
|
* - Directory Sync
|
||||||
* produces:
|
* produces:
|
||||||
|
@ -214,10 +217,18 @@ export class Groups extends Base {
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: Success
|
* description: Success
|
||||||
* schema:
|
* content:
|
||||||
* type: array
|
* application/json:
|
||||||
* items:
|
* schema:
|
||||||
* $ref: '#/definitions/Group'
|
* type: object
|
||||||
|
* properties:
|
||||||
|
* data:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: '#/definitions/Group'
|
||||||
|
* pageToken:
|
||||||
|
* type: string
|
||||||
|
* description: token for pagination
|
||||||
*/
|
*/
|
||||||
public async getAll(
|
public async getAll(
|
||||||
params: PaginationParams & {
|
params: PaginationParams & {
|
||||||
|
|
|
@ -197,6 +197,9 @@ export class Users extends Base {
|
||||||
* - $ref: '#/parameters/tenant'
|
* - $ref: '#/parameters/tenant'
|
||||||
* - $ref: '#/parameters/product'
|
* - $ref: '#/parameters/product'
|
||||||
* - $ref: '#/parameters/directoryId'
|
* - $ref: '#/parameters/directoryId'
|
||||||
|
* - $ref: '#/parameters/pageOffset'
|
||||||
|
* - $ref: '#/parameters/pageLimit'
|
||||||
|
* - $ref: '#/parameters/pageToken'
|
||||||
* tags:
|
* tags:
|
||||||
* - Directory Sync
|
* - Directory Sync
|
||||||
* produces:
|
* produces:
|
||||||
|
@ -204,10 +207,18 @@ export class Users extends Base {
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: Success
|
* description: Success
|
||||||
* schema:
|
* content:
|
||||||
* type: array
|
* application/json:
|
||||||
* items:
|
* schema:
|
||||||
* $ref: '#/definitions/User'
|
* type: object
|
||||||
|
* properties:
|
||||||
|
* data:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: '#/definitions/User'
|
||||||
|
* pageToken:
|
||||||
|
* type: string
|
||||||
|
* description: token for pagination
|
||||||
*/
|
*/
|
||||||
public async getAll({
|
public async getAll({
|
||||||
pageOffset,
|
pageOffset,
|
||||||
|
|
|
@ -14,6 +14,55 @@ type GetAllParams = PaginationParams & {
|
||||||
directoryId?: string;
|
directoryId?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* definitions:
|
||||||
|
* Event:
|
||||||
|
* type: object
|
||||||
|
* example:
|
||||||
|
* {
|
||||||
|
* "id": "id1",
|
||||||
|
* "webhook_endpoint": "https://example.com/webhook",
|
||||||
|
* "created_at": "2024-03-05T17:06:26.074Z",
|
||||||
|
* "status_code": 200,
|
||||||
|
* "delivered": true,
|
||||||
|
* "payload": {
|
||||||
|
* "directory_id": "58b5cd9dfaa39d47eb8f5f88631f9a629a232016",
|
||||||
|
* "event": "user.created",
|
||||||
|
* "tenant": "boxyhq",
|
||||||
|
* "product": "jackson",
|
||||||
|
* "data": {
|
||||||
|
* "id": "038e767b-9bc6-4dbd-975e-fbc38a8e7d82",
|
||||||
|
* "first_name": "Deepak",
|
||||||
|
* "last_name": "Prabhakara",
|
||||||
|
* "email": "deepak@boxyhq.com",
|
||||||
|
* "active": true,
|
||||||
|
* "raw": {
|
||||||
|
* "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
|
||||||
|
* "userName": "deepak@boxyhq.com",
|
||||||
|
* "name": {
|
||||||
|
* "givenName": "Deepak",
|
||||||
|
* "familyName": "Prabhakara"
|
||||||
|
* },
|
||||||
|
* "emails": [
|
||||||
|
* {
|
||||||
|
* "primary": true,
|
||||||
|
* "value": "deepak@boxyhq.com",
|
||||||
|
* "type": "work"
|
||||||
|
* }
|
||||||
|
* ],
|
||||||
|
* "title": "CEO",
|
||||||
|
* "displayName": "Deepak Prabhakara",
|
||||||
|
* "locale": "en-US",
|
||||||
|
* "externalId": "00u1ldzzogFkXFmvT5d7",
|
||||||
|
* "groups": [],
|
||||||
|
* "active": true,
|
||||||
|
* "id": "038e767b-9bc6-4dbd-975e-fbc38a8e7d82"
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*/
|
||||||
export class WebhookEventsLogger extends Base {
|
export class WebhookEventsLogger extends Base {
|
||||||
constructor({ db }: { db: DatabaseStore }) {
|
constructor({ db }: { db: DatabaseStore }) {
|
||||||
super({ db });
|
super({ db });
|
||||||
|
@ -43,6 +92,36 @@ export class WebhookEventsLogger extends Base {
|
||||||
return await this.eventStore().get(id);
|
return await this.eventStore().get(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @swagger
|
||||||
|
* /api/v1/dsync/events:
|
||||||
|
* get:
|
||||||
|
* summary: Get event logs for a directory
|
||||||
|
* parameters:
|
||||||
|
* - $ref: '#/parameters/directoryId'
|
||||||
|
* - $ref: '#/parameters/pageOffset'
|
||||||
|
* - $ref: '#/parameters/pageLimit'
|
||||||
|
* - $ref: '#/parameters/pageToken'
|
||||||
|
* tags:
|
||||||
|
* - Directory Sync
|
||||||
|
* produces:
|
||||||
|
* - application/json
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: Success
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: object
|
||||||
|
* properties:
|
||||||
|
* data:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: '#/definitions/Event'
|
||||||
|
* pageToken:
|
||||||
|
* type: string
|
||||||
|
* description: token for pagination
|
||||||
|
*/
|
||||||
// Get the event logs for a directory paginated
|
// Get the event logs for a directory paginated
|
||||||
public async getAll(params: GetAllParams = {}) {
|
public async getAll(params: GetAllParams = {}) {
|
||||||
const { pageOffset, pageLimit, directoryId } = params;
|
const { pageOffset, pageLimit, directoryId } = params;
|
||||||
|
|
|
@ -325,6 +325,9 @@ export class App {
|
||||||
* in: query
|
* in: query
|
||||||
* required: true
|
* required: true
|
||||||
* type: string
|
* type: string
|
||||||
|
* - $ref: '#/parameters/pageOffset'
|
||||||
|
* - $ref: '#/parameters/pageLimit'
|
||||||
|
* - $ref: '#/parameters/pageToken'
|
||||||
* tags:
|
* tags:
|
||||||
* - Identity Federation
|
* - Identity Federation
|
||||||
* produces:
|
* produces:
|
||||||
|
@ -332,10 +335,18 @@ export class App {
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: Success
|
* description: Success
|
||||||
* schema:
|
* content:
|
||||||
* type: array
|
* application/json:
|
||||||
* items:
|
* schema:
|
||||||
* $ref: '#/definitions/SAMLFederationApp'
|
* type: object
|
||||||
|
* properties:
|
||||||
|
* data:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: '#/definitions/SAMLFederationApp'
|
||||||
|
* pageToken:
|
||||||
|
* type: string
|
||||||
|
* description: token for pagination
|
||||||
*/
|
*/
|
||||||
public async getByProduct({ product, pageOffset, pageLimit, pageToken }: GetByProductParams) {
|
public async getByProduct({ product, pageOffset, pageLimit, pageToken }: GetByProductParams) {
|
||||||
await throwIfInvalidLicense(this.opts.boxyhqLicenseKey);
|
await throwIfInvalidLicense(this.opts.boxyhqLicenseKey);
|
||||||
|
|
|
@ -113,7 +113,7 @@ class SSOTracer {
|
||||||
* required: true
|
* required: true
|
||||||
* type: string
|
* type: string
|
||||||
* tags:
|
* tags:
|
||||||
* - SAML Traces
|
* - SSO Traces
|
||||||
* produces:
|
* produces:
|
||||||
* - application/json
|
* - application/json
|
||||||
* responses:
|
* responses:
|
||||||
|
@ -164,17 +164,28 @@ class SSOTracer {
|
||||||
* summary: Get all traces for a product
|
* summary: Get all traces for a product
|
||||||
* parameters:
|
* parameters:
|
||||||
* - $ref: '#/parameters/product'
|
* - $ref: '#/parameters/product'
|
||||||
|
* - $ref: '#/parameters/pageOffset'
|
||||||
|
* - $ref: '#/parameters/pageLimit'
|
||||||
|
* - $ref: '#/parameters/pageToken'
|
||||||
* tags:
|
* tags:
|
||||||
* - SAML Traces
|
* - SSO Traces
|
||||||
* produces:
|
* produces:
|
||||||
* - application/json
|
* - application/json
|
||||||
* responses:
|
* responses:
|
||||||
* '200':
|
* '200':
|
||||||
* description: Success
|
* description: Success
|
||||||
* schema:
|
* content:
|
||||||
* type: array
|
* application/json:
|
||||||
* items:
|
* schema:
|
||||||
* $ref: '#/definitions/SSOTrace'
|
* type: object
|
||||||
|
* properties:
|
||||||
|
* data:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: '#/definitions/SSOTrace'
|
||||||
|
* pageToken:
|
||||||
|
* type: string
|
||||||
|
* description: token for pagination
|
||||||
*/
|
*/
|
||||||
public async getTracesByProduct(params: GetByProductParams) {
|
public async getTracesByProduct(params: GetByProductParams) {
|
||||||
const { product, pageOffset, pageLimit, pageToken } = params;
|
const { product, pageOffset, pageLimit, pageToken } = params;
|
||||||
|
|
|
@ -22,17 +22,24 @@ const record2 = {
|
||||||
city: 'London',
|
city: 'London',
|
||||||
};
|
};
|
||||||
|
|
||||||
const records = [record1, record2];
|
const record3 = {
|
||||||
|
id: '3',
|
||||||
|
name: 'Samuel Jackson',
|
||||||
|
city: 'Delhi',
|
||||||
|
};
|
||||||
|
|
||||||
|
const records = [record1, record2, record3];
|
||||||
|
|
||||||
const memDbConfig = <DatabaseOption>{
|
const memDbConfig = <DatabaseOption>{
|
||||||
engine: 'mem',
|
engine: 'mem',
|
||||||
ttl: 1,
|
ttl: 1,
|
||||||
|
pageLimit: 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
const redisDbConfig = <DatabaseOption>{
|
const redisDbConfig = <DatabaseOption>{
|
||||||
engine: 'redis',
|
engine: 'redis',
|
||||||
url: 'redis://localhost:6379',
|
url: 'redis://localhost:6379',
|
||||||
pageLimit: 50,
|
pageLimit: 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
const postgresDbConfig = <DatabaseOption>{
|
const postgresDbConfig = <DatabaseOption>{
|
||||||
|
@ -41,11 +48,13 @@ const postgresDbConfig = <DatabaseOption>{
|
||||||
type: 'postgres',
|
type: 'postgres',
|
||||||
ttl: 1,
|
ttl: 1,
|
||||||
cleanupLimit: 10,
|
cleanupLimit: 10,
|
||||||
|
pageLimit: 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
const mongoDbConfig = <DatabaseOption>{
|
const mongoDbConfig = <DatabaseOption>{
|
||||||
engine: 'mongo',
|
engine: 'mongo',
|
||||||
url: 'mongodb://localhost:27017/jackson',
|
url: 'mongodb://localhost:27017/jackson',
|
||||||
|
pageLimit: 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
const mysqlDbConfig = <DatabaseOption>{
|
const mysqlDbConfig = <DatabaseOption>{
|
||||||
|
@ -54,6 +63,7 @@ const mysqlDbConfig = <DatabaseOption>{
|
||||||
type: 'mysql',
|
type: 'mysql',
|
||||||
ttl: 1,
|
ttl: 1,
|
||||||
cleanupLimit: 10,
|
cleanupLimit: 10,
|
||||||
|
pageLimit: 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
const planetscaleDbConfig = <DatabaseOption>{
|
const planetscaleDbConfig = <DatabaseOption>{
|
||||||
|
@ -61,6 +71,7 @@ const planetscaleDbConfig = <DatabaseOption>{
|
||||||
url: process.env.PLANETSCALE_URL,
|
url: process.env.PLANETSCALE_URL,
|
||||||
ttl: 1,
|
ttl: 1,
|
||||||
cleanupLimit: 10,
|
cleanupLimit: 10,
|
||||||
|
pageLimit: 2,
|
||||||
// ssl: {
|
// ssl: {
|
||||||
// rejectUnauthorized: true,
|
// rejectUnauthorized: true,
|
||||||
// },
|
// },
|
||||||
|
@ -72,6 +83,7 @@ const mariadbDbConfig = <DatabaseOption>{
|
||||||
type: 'mariadb',
|
type: 'mariadb',
|
||||||
ttl: 1,
|
ttl: 1,
|
||||||
cleanupLimit: 10,
|
cleanupLimit: 10,
|
||||||
|
pageLimit: 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
const mssqlDbConfig = <DatabaseOption>{
|
const mssqlDbConfig = <DatabaseOption>{
|
||||||
|
@ -80,6 +92,7 @@ const mssqlDbConfig = <DatabaseOption>{
|
||||||
url: 'sqlserver://localhost:1433;database=master;username=sa;password=123ABabc!',
|
url: 'sqlserver://localhost:1433;database=master;username=sa;password=123ABabc!',
|
||||||
ttl: 1,
|
ttl: 1,
|
||||||
cleanupLimit: 10,
|
cleanupLimit: 10,
|
||||||
|
pageLimit: 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
const dynamoDbConfig = <DatabaseOption>{
|
const dynamoDbConfig = <DatabaseOption>{
|
||||||
|
@ -87,6 +100,7 @@ const dynamoDbConfig = <DatabaseOption>{
|
||||||
url: process.env.DYNAMODB_URL,
|
url: process.env.DYNAMODB_URL,
|
||||||
ttl: 1,
|
ttl: 1,
|
||||||
cleanupLimit: 10,
|
cleanupLimit: 10,
|
||||||
|
pageLimit: 2,
|
||||||
dynamodb: {
|
dynamodb: {
|
||||||
region: 'us-east-1',
|
region: 'us-east-1',
|
||||||
readCapacityUnits: 5,
|
readCapacityUnits: 5,
|
||||||
|
@ -229,6 +243,24 @@ tap.test('dbs', async () => {
|
||||||
value: record2.name,
|
value: record2.name,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// wait 100ms to ensure that the record is written with a different timestamp
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||||
|
|
||||||
|
await connectionStore.put(
|
||||||
|
record3.id,
|
||||||
|
record3,
|
||||||
|
{
|
||||||
|
// secondary index on city
|
||||||
|
name: 'city',
|
||||||
|
value: record3.city,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// secondary index on name
|
||||||
|
name: 'name',
|
||||||
|
value: record3.name,
|
||||||
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.test('get(): ' + dbType, async (t) => {
|
tap.test('get(): ' + dbType, async (t) => {
|
||||||
|
@ -240,33 +272,23 @@ tap.test('dbs', async () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.test('getAll(): ' + dbType, async (t) => {
|
tap.test('getAll(): ' + dbType, async (t) => {
|
||||||
const allRecords = await connectionStore.getAll();
|
const testMessage =
|
||||||
const allRecordOutput = {};
|
dbType === 'dynamodb' // dynamodb doesn't support sort order
|
||||||
let allRecordInput = {};
|
? 'should return all the records upto options.pageLimit'
|
||||||
for (const keyValue in records) {
|
: 'should return all the records upto options.pageLimit in DESC order by creation time';
|
||||||
const keyVal = records[keyValue.toString()];
|
const wanted = dbType === 'dynamodb' ? records.slice(1, 3) : [...records].reverse().slice(0, 2);
|
||||||
allRecordOutput[keyVal];
|
// getAll without pagination params
|
||||||
}
|
t.same((await connectionStore.getAll()).data, wanted, `without pagination params ` + testMessage);
|
||||||
for (const keyValue in allRecords.data) {
|
// getAll with pagination params
|
||||||
const keyVal = records[keyValue.toString()];
|
t.same((await connectionStore.getAll(0, 2)).data, wanted, `with pagination params ` + testMessage);
|
||||||
allRecordInput[allRecords.data[keyVal]];
|
// getAll with pageLimit set to 0
|
||||||
}
|
t.same((await connectionStore.getAll(0, 0)).data, wanted, `with pageLimit set to 0 ` + testMessage);
|
||||||
t.same(allRecordInput, allRecordOutput, 'unable to getAll records');
|
// getAll with pageLimit > options.pageLimit
|
||||||
allRecordInput = {};
|
t.same(
|
||||||
let allRecordsWithPagination = await connectionStore.getAll(0, 2);
|
(await connectionStore.getAll(0, 3)).data,
|
||||||
for (const keyValue in allRecordsWithPagination.data) {
|
wanted,
|
||||||
const keyVal = records[keyValue.toString()];
|
`with pageLimit > options.pageLimit ` + testMessage
|
||||||
allRecordInput[allRecordsWithPagination.data[keyVal]];
|
);
|
||||||
}
|
|
||||||
|
|
||||||
t.same(allRecordInput, allRecordOutput, 'unable to getAll records');
|
|
||||||
allRecordsWithPagination = await connectionStore.getAll(0, 0);
|
|
||||||
for (const keyValue in allRecordsWithPagination.data) {
|
|
||||||
const keyVal = records[keyValue.toString()];
|
|
||||||
allRecordInput[allRecordsWithPagination.data[keyVal]];
|
|
||||||
}
|
|
||||||
|
|
||||||
t.same(allRecordInput, allRecordOutput, 'unable to getAll records');
|
|
||||||
|
|
||||||
const oneRecordWithPagination = await connectionStore.getAll(0, 1);
|
const oneRecordWithPagination = await connectionStore.getAll(0, 1);
|
||||||
t.same(
|
t.same(
|
||||||
|
@ -291,7 +313,7 @@ tap.test('dbs', async () => {
|
||||||
t.match(sortedRecordsAsc, [record1, record2], 'records are sorted in ASC order');
|
t.match(sortedRecordsAsc, [record1, record2], 'records are sorted in ASC order');
|
||||||
|
|
||||||
const { data: sortedRecordsDesc } = await connectionStore.getAll(0, 2, undefined, 'DESC');
|
const { data: sortedRecordsDesc } = await connectionStore.getAll(0, 2, undefined, 'DESC');
|
||||||
t.match(sortedRecordsDesc, [record2, record1], 'records are sorted in DESC order');
|
t.match(sortedRecordsDesc, [record3, record2], 'records are sorted in DESC order');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { useProjects } from '@lib/ui/retraced';
|
||||||
import Loading from '@components/Loading';
|
import Loading from '@components/Loading';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import router from 'next/router';
|
import router from 'next/router';
|
||||||
import { Pagination, pageLimit } from '@components/Pagination';
|
import { Pagination, pageLimit } from '@boxyhq/internal-ui';
|
||||||
import usePaginate from '@lib/ui/hooks/usePaginate';
|
import usePaginate from '@lib/ui/hooks/usePaginate';
|
||||||
import { LinkPrimary } from '@components/LinkPrimary';
|
import { LinkPrimary } from '@components/LinkPrimary';
|
||||||
import { errorToast } from '@components/Toaster';
|
import { errorToast } from '@components/Toaster';
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
|
|
||||||
import jackson from '@lib/jackson';
|
import jackson from '@lib/jackson';
|
||||||
import { oidcMetadataParse, strategyChecker } from '@lib/utils';
|
import { oidcMetadataParse, parsePaginateApiParams, strategyChecker } from '@lib/utils';
|
||||||
import { adminPortalSSODefaults } from '@lib/env';
|
import { adminPortalSSODefaults } from '@lib/env';
|
||||||
|
|
||||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
|
@ -26,20 +26,15 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
const handleGET = async (req: NextApiRequest, res: NextApiResponse) => {
|
const handleGET = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
const { adminController, connectionAPIController } = await jackson();
|
const { adminController, connectionAPIController } = await jackson();
|
||||||
|
|
||||||
const { pageOffset, pageLimit, isSystemSSO, pageToken } = req.query as {
|
const { isSystemSSO } = req.query as {
|
||||||
pageOffset: string;
|
|
||||||
pageLimit: string;
|
|
||||||
isSystemSSO?: string; // if present will be '' else undefined
|
isSystemSSO?: string; // if present will be '' else undefined
|
||||||
pageToken?: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const { pageOffset, pageLimit, pageToken } = parsePaginateApiParams(req.query);
|
||||||
|
|
||||||
const { tenant: adminPortalSSOTenant, product: adminPortalSSOProduct } = adminPortalSSODefaults;
|
const { tenant: adminPortalSSOTenant, product: adminPortalSSOProduct } = adminPortalSSODefaults;
|
||||||
|
|
||||||
const paginatedConnectionList = await adminController.getAllConnection(
|
const paginatedConnectionList = await adminController.getAllConnection(pageOffset, pageLimit, pageToken);
|
||||||
+(pageOffset || 0),
|
|
||||||
+(pageLimit || 0),
|
|
||||||
pageToken
|
|
||||||
);
|
|
||||||
|
|
||||||
const connections =
|
const connections =
|
||||||
isSystemSSO === undefined
|
isSystemSSO === undefined
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import type { DirectoryType } from '@boxyhq/saml-jackson';
|
import type { DirectoryType } from '@boxyhq/saml-jackson';
|
||||||
import jackson from '@lib/jackson';
|
import jackson from '@lib/jackson';
|
||||||
|
import { parsePaginateApiParams } from '@lib/utils';
|
||||||
|
|
||||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
const { method } = req;
|
const { method } = req;
|
||||||
|
@ -43,19 +44,15 @@ const handlePOST = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
const handleGET = async (req: NextApiRequest, res: NextApiResponse) => {
|
const handleGET = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
const { directorySyncController } = await jackson();
|
const { directorySyncController } = await jackson();
|
||||||
|
|
||||||
const { pageOffset, pageLimit, pageToken } = req.query as {
|
const { pageOffset, pageLimit, pageToken } = parsePaginateApiParams(req.query);
|
||||||
pageOffset: string;
|
|
||||||
pageLimit: string;
|
|
||||||
pageToken?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
error,
|
error,
|
||||||
pageToken: nextPageToken,
|
pageToken: nextPageToken,
|
||||||
} = await directorySyncController.directories.getAll({
|
} = await directorySyncController.directories.getAll({
|
||||||
pageOffset: +(pageOffset || 0),
|
pageOffset,
|
||||||
pageLimit: +(pageLimit || 0),
|
pageLimit,
|
||||||
pageToken,
|
pageToken,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import jackson from '@lib/jackson';
|
import jackson from '@lib/jackson';
|
||||||
|
import { parsePaginateApiParams } from '@lib/utils';
|
||||||
|
|
||||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
const { method } = req;
|
const { method } = req;
|
||||||
|
@ -45,14 +46,13 @@ const handleDELETE = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
const handleGET = async (req: NextApiRequest, res: NextApiResponse) => {
|
const handleGET = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
const { setupLinkController } = await jackson();
|
const { setupLinkController } = await jackson();
|
||||||
|
|
||||||
const { token, service, pageOffset, pageLimit, pageToken } = req.query as {
|
const { token, service } = req.query as {
|
||||||
pageOffset: string;
|
|
||||||
pageLimit: string;
|
|
||||||
pageToken?: string;
|
|
||||||
token: string;
|
token: string;
|
||||||
service: string;
|
service: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const { pageOffset, pageLimit, pageToken } = parsePaginateApiParams(req.query);
|
||||||
|
|
||||||
if (!token && !service) {
|
if (!token && !service) {
|
||||||
return res.status(404).json({
|
return res.status(404).json({
|
||||||
error: {
|
error: {
|
||||||
|
@ -73,8 +73,8 @@ const handleGET = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
if (service) {
|
if (service) {
|
||||||
const setupLinksPaginated = await setupLinkController.filterBy({
|
const setupLinksPaginated = await setupLinkController.filterBy({
|
||||||
service: service as any,
|
service: service as any,
|
||||||
pageLimit: parseInt(pageLimit),
|
pageOffset,
|
||||||
pageOffset: parseInt(pageOffset),
|
pageLimit,
|
||||||
pageToken,
|
pageToken,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import jackson from '@lib/jackson';
|
import jackson from '@lib/jackson';
|
||||||
import type { IAdminController } from '@boxyhq/saml-jackson';
|
import type { IAdminController } from '@boxyhq/saml-jackson';
|
||||||
|
import { parsePaginateApiParams } from '@lib/utils';
|
||||||
|
|
||||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
const { method } = req;
|
const { method } = req;
|
||||||
|
@ -23,10 +24,8 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
|
|
||||||
// Get SAML Traces
|
// Get SAML Traces
|
||||||
const handleGET = async (req: NextApiRequest, res: NextApiResponse, adminController: IAdminController) => {
|
const handleGET = async (req: NextApiRequest, res: NextApiResponse, adminController: IAdminController) => {
|
||||||
const { offset, limit, pageToken } = req.query as { offset: string; limit: string; pageToken?: string };
|
const params = req.query;
|
||||||
|
const { pageOffset, pageLimit, pageToken } = parsePaginateApiParams(params);
|
||||||
const pageOffset = parseInt(offset);
|
|
||||||
const pageLimit = parseInt(limit);
|
|
||||||
|
|
||||||
const tracesPaginated = await adminController.getAllSSOTraces(pageOffset, pageLimit, pageToken);
|
const tracesPaginated = await adminController.getAllSSOTraces(pageOffset, pageLimit, pageToken);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import jackson from '@lib/jackson';
|
import jackson from '@lib/jackson';
|
||||||
|
import { parsePaginateApiParams } from '@lib/utils';
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
try {
|
try {
|
||||||
|
@ -28,14 +29,13 @@ const handleGET = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
tenant: string;
|
tenant: string;
|
||||||
product: string;
|
product: string;
|
||||||
directoryId: string;
|
directoryId: string;
|
||||||
offset: string;
|
|
||||||
limit: string;
|
|
||||||
pageToken: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let tenant = searchParams.tenant || '';
|
let tenant = searchParams.tenant || '';
|
||||||
let product = searchParams.product || '';
|
let product = searchParams.product || '';
|
||||||
|
|
||||||
|
const { pageOffset, pageLimit, pageToken } = parsePaginateApiParams(req.query);
|
||||||
|
|
||||||
// If tenant and product are not provided, retrieve the from directory
|
// If tenant and product are not provided, retrieve the from directory
|
||||||
if ((!tenant || !product) && searchParams.directoryId) {
|
if ((!tenant || !product) && searchParams.directoryId) {
|
||||||
const { data: directory } = await directorySyncController.directories.get(searchParams.directoryId);
|
const { data: directory } = await directorySyncController.directories.get(searchParams.directoryId);
|
||||||
|
@ -49,9 +49,9 @@ const handleGET = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const events = await directorySyncController.webhookLogs.setTenantAndProduct(tenant, product).getAll({
|
const events = await directorySyncController.webhookLogs.setTenantAndProduct(tenant, product).getAll({
|
||||||
pageOffset: parseInt(searchParams.offset || '0'),
|
pageOffset,
|
||||||
pageLimit: parseInt(searchParams.limit || '15'),
|
pageLimit,
|
||||||
pageToken: searchParams.pageToken || undefined,
|
pageToken,
|
||||||
directoryId: searchParams.directoryId,
|
directoryId: searchParams.directoryId,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import jackson from '@lib/jackson';
|
import jackson from '@lib/jackson';
|
||||||
|
import { parsePaginateApiParams } from '@lib/utils';
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
const { method } = req;
|
const { method } = req;
|
||||||
|
@ -21,14 +22,13 @@ const handleGET = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
tenant: string;
|
tenant: string;
|
||||||
product: string;
|
product: string;
|
||||||
directoryId: string;
|
directoryId: string;
|
||||||
offset: string;
|
|
||||||
limit: string;
|
|
||||||
pageToken: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let tenant = searchParams.tenant || '';
|
let tenant = searchParams.tenant || '';
|
||||||
let product = searchParams.product || '';
|
let product = searchParams.product || '';
|
||||||
|
|
||||||
|
const { pageOffset, pageLimit, pageToken } = parsePaginateApiParams(req.query);
|
||||||
|
|
||||||
// If tenant and product are not provided, retrieve the from directory
|
// If tenant and product are not provided, retrieve the from directory
|
||||||
if ((!tenant || !product) && searchParams.directoryId) {
|
if ((!tenant || !product) && searchParams.directoryId) {
|
||||||
const { data: directory } = await directorySyncController.directories.get(searchParams.directoryId);
|
const { data: directory } = await directorySyncController.directories.get(searchParams.directoryId);
|
||||||
|
@ -42,9 +42,9 @@ const handleGET = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const { data, error } = await directorySyncController.groups.setTenantAndProduct(tenant, product).getAll({
|
const { data, error } = await directorySyncController.groups.setTenantAndProduct(tenant, product).getAll({
|
||||||
pageOffset: parseInt(searchParams.offset || '0'),
|
pageOffset,
|
||||||
pageLimit: parseInt(searchParams.limit || '15'),
|
pageLimit,
|
||||||
pageToken: searchParams.pageToken || undefined,
|
pageToken,
|
||||||
directoryId: searchParams.directoryId,
|
directoryId: searchParams.directoryId,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import jackson from '@lib/jackson';
|
import jackson from '@lib/jackson';
|
||||||
|
import { parsePaginateApiParams } from '@lib/utils';
|
||||||
import { NextApiRequest, NextApiResponse } from 'next';
|
import { NextApiRequest, NextApiResponse } from 'next';
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
|
@ -23,21 +24,20 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||||
const handleGET = async (req: NextApiRequest, res: NextApiResponse) => {
|
const handleGET = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
const { directorySyncController } = await jackson();
|
const { directorySyncController } = await jackson();
|
||||||
|
|
||||||
const { product, pageOffset, pageLimit, pageToken } = req.query as {
|
const { product } = req.query as {
|
||||||
product: string;
|
product: string;
|
||||||
pageOffset: string;
|
|
||||||
pageLimit: string;
|
|
||||||
pageToken?: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!product) {
|
if (!product) {
|
||||||
throw new Error('Please provide a product');
|
throw new Error('Please provide a product');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { pageOffset, pageLimit, pageToken } = parsePaginateApiParams(req.query);
|
||||||
|
|
||||||
const connections = await directorySyncController.directories.filterBy({
|
const connections = await directorySyncController.directories.filterBy({
|
||||||
product,
|
product,
|
||||||
pageOffset: parseInt(pageOffset),
|
pageOffset,
|
||||||
pageLimit: parseInt(pageLimit),
|
pageLimit,
|
||||||
pageToken,
|
pageToken,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import type { SetupLinkService } from '@boxyhq/saml-jackson';
|
import type { SetupLinkService } from '@boxyhq/saml-jackson';
|
||||||
import jackson from '@lib/jackson';
|
import jackson from '@lib/jackson';
|
||||||
|
import { parsePaginateApiParams } from '@lib/utils';
|
||||||
|
|
||||||
const service: SetupLinkService = 'dsync';
|
const service: SetupLinkService = 'dsync';
|
||||||
|
|
||||||
|
@ -27,22 +28,21 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||||
const handleGET = async (req: NextApiRequest, res: NextApiResponse) => {
|
const handleGET = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
const { setupLinkController } = await jackson();
|
const { setupLinkController } = await jackson();
|
||||||
|
|
||||||
const { product, pageOffset, pageLimit, pageToken } = req.query as {
|
const { product } = req.query as {
|
||||||
product: string;
|
product: string;
|
||||||
pageOffset: string;
|
|
||||||
pageLimit: string;
|
|
||||||
pageToken?: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!product) {
|
if (!product) {
|
||||||
throw new Error('Please provide a product');
|
throw new Error('Please provide a product');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { pageOffset, pageLimit, pageToken } = parsePaginateApiParams(req.query);
|
||||||
|
|
||||||
const setupLinks = await setupLinkController.filterBy({
|
const setupLinks = await setupLinkController.filterBy({
|
||||||
product,
|
product,
|
||||||
service,
|
service,
|
||||||
pageOffset: parseInt(pageOffset),
|
pageOffset,
|
||||||
pageLimit: parseInt(pageLimit),
|
pageLimit,
|
||||||
pageToken,
|
pageToken,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import jackson from '@lib/jackson';
|
import jackson from '@lib/jackson';
|
||||||
|
import { parsePaginateApiParams } from '@lib/utils';
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
const { method } = req;
|
const { method } = req;
|
||||||
|
@ -21,14 +22,13 @@ const handleGET = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
tenant: string;
|
tenant: string;
|
||||||
product: string;
|
product: string;
|
||||||
directoryId: string;
|
directoryId: string;
|
||||||
offset: string;
|
|
||||||
limit: string;
|
|
||||||
pageToken: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let tenant = searchParams.tenant || '';
|
let tenant = searchParams.tenant || '';
|
||||||
let product = searchParams.product || '';
|
let product = searchParams.product || '';
|
||||||
|
|
||||||
|
const { pageOffset, pageLimit, pageToken } = parsePaginateApiParams(req.query);
|
||||||
|
|
||||||
// If tenant and product are not provided, retrieve the from directory
|
// If tenant and product are not provided, retrieve the from directory
|
||||||
if ((!tenant || !product) && searchParams.directoryId) {
|
if ((!tenant || !product) && searchParams.directoryId) {
|
||||||
const { data: directory } = await directorySyncController.directories.get(searchParams.directoryId);
|
const { data: directory } = await directorySyncController.directories.get(searchParams.directoryId);
|
||||||
|
@ -42,9 +42,9 @@ const handleGET = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const { data, error } = await directorySyncController.users.setTenantAndProduct(tenant, product).getAll({
|
const { data, error } = await directorySyncController.users.setTenantAndProduct(tenant, product).getAll({
|
||||||
pageOffset: parseInt(searchParams.offset || '0'),
|
pageOffset,
|
||||||
pageLimit: parseInt(searchParams.limit || '15'),
|
pageLimit,
|
||||||
pageToken: searchParams.pageToken || undefined,
|
pageToken,
|
||||||
directoryId: searchParams.directoryId,
|
directoryId: searchParams.directoryId,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import jackson from '@lib/jackson';
|
import jackson from '@lib/jackson';
|
||||||
|
import { parsePaginateApiParams } from '@lib/utils';
|
||||||
import { NextApiRequest, NextApiResponse } from 'next';
|
import { NextApiRequest, NextApiResponse } from 'next';
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
|
@ -17,23 +18,17 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the saml traces filtered by the product
|
// Get the sso traces filtered by the product
|
||||||
const handleGET = async (req: NextApiRequest, res: NextApiResponse) => {
|
const handleGET = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
const { adminController } = await jackson();
|
const { adminController } = await jackson();
|
||||||
|
|
||||||
const { product, pageOffset, pageLimit, pageToken } = req.query as {
|
const { product } = req.query as {
|
||||||
product: string;
|
product: string;
|
||||||
pageOffset: string;
|
|
||||||
pageLimit: string;
|
|
||||||
pageToken?: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const traces = await adminController.getTracesByProduct(
|
const { pageOffset, pageLimit, pageToken } = parsePaginateApiParams(req.query);
|
||||||
product,
|
|
||||||
parseInt(pageOffset),
|
const traces = await adminController.getTracesByProduct(product, pageOffset, pageLimit, pageToken);
|
||||||
parseInt(pageLimit),
|
|
||||||
pageToken
|
|
||||||
);
|
|
||||||
|
|
||||||
res.json(traces);
|
res.json(traces);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import jackson from '@lib/jackson';
|
import jackson from '@lib/jackson';
|
||||||
|
import { parsePaginateApiParams } from '@lib/utils';
|
||||||
import { NextApiRequest, NextApiResponse } from 'next';
|
import { NextApiRequest, NextApiResponse } from 'next';
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
|
@ -23,17 +24,16 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||||
const handleGET = async (req: NextApiRequest, res: NextApiResponse) => {
|
const handleGET = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
const { connectionAPIController } = await jackson();
|
const { connectionAPIController } = await jackson();
|
||||||
|
|
||||||
const { product, pageOffset, pageLimit, pageToken } = req.query as {
|
const { product } = req.query as {
|
||||||
product: string;
|
product: string;
|
||||||
pageOffset: string;
|
|
||||||
pageLimit: string;
|
|
||||||
pageToken?: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const { pageOffset, pageLimit, pageToken } = parsePaginateApiParams(req.query);
|
||||||
|
|
||||||
const connections = await connectionAPIController.getConnectionsByProduct({
|
const connections = await connectionAPIController.getConnectionsByProduct({
|
||||||
product,
|
product,
|
||||||
pageOffset: parseInt(pageOffset),
|
pageOffset,
|
||||||
pageLimit: parseInt(pageLimit),
|
pageLimit,
|
||||||
pageToken,
|
pageToken,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import type { SetupLinkService } from '@boxyhq/saml-jackson';
|
import type { SetupLinkService } from '@boxyhq/saml-jackson';
|
||||||
import jackson from '@lib/jackson';
|
import jackson from '@lib/jackson';
|
||||||
|
import { parsePaginateApiParams } from '@lib/utils';
|
||||||
|
|
||||||
const service: SetupLinkService = 'sso';
|
const service: SetupLinkService = 'sso';
|
||||||
|
|
||||||
|
@ -27,22 +28,21 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||||
const handleGET = async (req: NextApiRequest, res: NextApiResponse) => {
|
const handleGET = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
const { setupLinkController } = await jackson();
|
const { setupLinkController } = await jackson();
|
||||||
|
|
||||||
const { product, pageOffset, pageLimit, pageToken } = req.query as {
|
const { product } = req.query as {
|
||||||
product: string;
|
product: string;
|
||||||
pageOffset: string;
|
|
||||||
pageLimit: string;
|
|
||||||
pageToken?: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!product) {
|
if (!product) {
|
||||||
throw new Error('Please provide a product');
|
throw new Error('Please provide a product');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { pageOffset, pageLimit, pageToken } = parsePaginateApiParams(req.query);
|
||||||
|
|
||||||
const setupLinks = await setupLinkController.filterBy({
|
const setupLinks = await setupLinkController.filterBy({
|
||||||
product,
|
product,
|
||||||
service,
|
service,
|
||||||
pageOffset: parseInt(pageOffset),
|
pageOffset,
|
||||||
pageLimit: parseInt(pageLimit),
|
pageLimit,
|
||||||
pageToken,
|
pageToken,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"openapi": "3.0.3",
|
"openapi": "3.0.3",
|
||||||
"info": {
|
"info": {
|
||||||
"title": "Enterprise SSO & Directory Sync",
|
"title": "Enterprise SSO & Directory Sync",
|
||||||
"version": "1.19.2",
|
"version": "1.20.5",
|
||||||
"description": "This is the API documentation for SAML Jackson service.",
|
"description": "This is the API documentation for SAML Jackson service.",
|
||||||
"termsOfService": "https://boxyhq.com/terms.html",
|
"termsOfService": "https://boxyhq.com/terms.html",
|
||||||
"contact": {
|
"contact": {
|
||||||
|
@ -278,6 +278,15 @@
|
||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"$ref": "#/parameters/productParamGet"
|
"$ref": "#/parameters/productParamGet"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/parameters/pageOffset"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/parameters/pageLimit"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/parameters/pageToken"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"operationId": "get-connections-by-product",
|
"operationId": "get-connections-by-product",
|
||||||
|
@ -286,7 +295,7 @@
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"$ref": "#/responses/200Get"
|
"$ref": "#/responses/200GetByProduct"
|
||||||
},
|
},
|
||||||
"400": {
|
"400": {
|
||||||
"$ref": "#/responses/400Get"
|
"$ref": "#/responses/400Get"
|
||||||
|
@ -644,6 +653,15 @@
|
||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"$ref": "#/parameters/productParamGet"
|
"$ref": "#/parameters/productParamGet"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/parameters/pageOffset"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/parameters/pageLimit"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/parameters/pageToken"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"operationId": "get-sso-setup-link-by-product",
|
"operationId": "get-sso-setup-link-by-product",
|
||||||
|
@ -669,6 +687,15 @@
|
||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"$ref": "#/parameters/productParamGet"
|
"$ref": "#/parameters/productParamGet"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/parameters/pageOffset"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/parameters/pageLimit"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/parameters/pageToken"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"operationId": "get-dsync-setup-link-by-product",
|
"operationId": "get-dsync-setup-link-by-product",
|
||||||
|
@ -701,7 +728,7 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"tags": [
|
"tags": [
|
||||||
"SAML Traces"
|
"SSO Traces"
|
||||||
],
|
],
|
||||||
"produces": [
|
"produces": [
|
||||||
"application/json"
|
"application/json"
|
||||||
|
@ -722,10 +749,19 @@
|
||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"$ref": "#/parameters/product"
|
"$ref": "#/parameters/product"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/parameters/pageOffset"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/parameters/pageLimit"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/parameters/pageToken"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"tags": [
|
"tags": [
|
||||||
"SAML Traces"
|
"SSO Traces"
|
||||||
],
|
],
|
||||||
"produces": [
|
"produces": [
|
||||||
"application/json"
|
"application/json"
|
||||||
|
@ -733,10 +769,23 @@
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"description": "Success",
|
"description": "Success",
|
||||||
"schema": {
|
"content": {
|
||||||
"type": "array",
|
"application/json": {
|
||||||
"items": {
|
"schema": {
|
||||||
"$ref": "#/definitions/SSOTrace"
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/SSOTrace"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pageToken": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "token for pagination"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -985,6 +1034,15 @@
|
||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"$ref": "#/parameters/product"
|
"$ref": "#/parameters/product"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/parameters/pageOffset"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/parameters/pageLimit"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/parameters/pageToken"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"tags": [
|
"tags": [
|
||||||
|
@ -996,10 +1054,23 @@
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"description": "Success",
|
"description": "Success",
|
||||||
"schema": {
|
"content": {
|
||||||
"type": "array",
|
"application/json": {
|
||||||
"items": {
|
"schema": {
|
||||||
"$ref": "#/definitions/Directory"
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/Directory"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pageToken": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "token for pagination"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1052,6 +1123,15 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$ref": "#/parameters/directoryId"
|
"$ref": "#/parameters/directoryId"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/parameters/pageOffset"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/parameters/pageLimit"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/parameters/pageToken"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"tags": [
|
"tags": [
|
||||||
|
@ -1063,10 +1143,23 @@
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"description": "Success",
|
"description": "Success",
|
||||||
"schema": {
|
"content": {
|
||||||
"type": "array",
|
"application/json": {
|
||||||
"items": {
|
"schema": {
|
||||||
"$ref": "#/definitions/Group"
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/Group"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pageToken": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "token for pagination"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1119,6 +1212,15 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$ref": "#/parameters/directoryId"
|
"$ref": "#/parameters/directoryId"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/parameters/pageOffset"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/parameters/pageLimit"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/parameters/pageToken"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"tags": [
|
"tags": [
|
||||||
|
@ -1130,10 +1232,72 @@
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"description": "Success",
|
"description": "Success",
|
||||||
"schema": {
|
"content": {
|
||||||
"type": "array",
|
"application/json": {
|
||||||
"items": {
|
"schema": {
|
||||||
"$ref": "#/definitions/User"
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/User"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pageToken": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "token for pagination"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/api/v1/dsync/events": {
|
||||||
|
"get": {
|
||||||
|
"summary": "Get event logs for a directory",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"$ref": "#/parameters/directoryId"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/parameters/pageOffset"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/parameters/pageLimit"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/parameters/pageToken"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Directory Sync"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Success",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/Event"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pageToken": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "token for pagination"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1435,6 +1599,15 @@
|
||||||
"in": "query",
|
"in": "query",
|
||||||
"required": true,
|
"required": true,
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/parameters/pageOffset"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/parameters/pageLimit"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/parameters/pageToken"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"tags": [
|
"tags": [
|
||||||
|
@ -1446,10 +1619,23 @@
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"description": "Success",
|
"description": "Success",
|
||||||
"schema": {
|
"content": {
|
||||||
"type": "array",
|
"application/json": {
|
||||||
"items": {
|
"schema": {
|
||||||
"$ref": "#/definitions/SAMLFederationApp"
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/SAMLFederationApp"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pageToken": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "token for pagination"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1742,6 +1928,53 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Event": {
|
||||||
|
"type": "object",
|
||||||
|
"example": {
|
||||||
|
"id": "id1",
|
||||||
|
"webhook_endpoint": "https://example.com/webhook",
|
||||||
|
"created_at": "2024-03-05T17:06:26.074Z",
|
||||||
|
"status_code": 200,
|
||||||
|
"delivered": true,
|
||||||
|
"payload": {
|
||||||
|
"directory_id": "58b5cd9dfaa39d47eb8f5f88631f9a629a232016",
|
||||||
|
"event": "user.created",
|
||||||
|
"tenant": "boxyhq",
|
||||||
|
"product": "jackson",
|
||||||
|
"data": {
|
||||||
|
"id": "038e767b-9bc6-4dbd-975e-fbc38a8e7d82",
|
||||||
|
"first_name": "Deepak",
|
||||||
|
"last_name": "Prabhakara",
|
||||||
|
"email": "deepak@boxyhq.com",
|
||||||
|
"active": true,
|
||||||
|
"raw": {
|
||||||
|
"schemas": [
|
||||||
|
"urn:ietf:params:scim:schemas:core:2.0:User"
|
||||||
|
],
|
||||||
|
"userName": "deepak@boxyhq.com",
|
||||||
|
"name": {
|
||||||
|
"givenName": "Deepak",
|
||||||
|
"familyName": "Prabhakara"
|
||||||
|
},
|
||||||
|
"emails": [
|
||||||
|
{
|
||||||
|
"primary": true,
|
||||||
|
"value": "deepak@boxyhq.com",
|
||||||
|
"type": "work"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "CEO",
|
||||||
|
"displayName": "Deepak Prabhakara",
|
||||||
|
"locale": "en-US",
|
||||||
|
"externalId": "00u1ldzzogFkXFmvT5d7",
|
||||||
|
"groups": [],
|
||||||
|
"active": true,
|
||||||
|
"id": "038e767b-9bc6-4dbd-975e-fbc38a8e7d82"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"SAMLFederationApp": {
|
"SAMLFederationApp": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@ -1799,6 +2032,28 @@
|
||||||
},
|
},
|
||||||
"401Get": {
|
"401Get": {
|
||||||
"description": "Unauthorized"
|
"description": "Unauthorized"
|
||||||
|
},
|
||||||
|
"200GetByProduct": {
|
||||||
|
"description": "Success",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/Connection"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pageToken": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "token for pagination"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"parameters": {
|
"parameters": {
|
||||||
|
@ -2138,6 +2393,27 @@
|
||||||
"in": "query",
|
"in": "query",
|
||||||
"required": false,
|
"required": false,
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
},
|
||||||
|
"pageOffset": {
|
||||||
|
"name": "pageOffset",
|
||||||
|
"description": "Starting point from which the set of records are retrieved",
|
||||||
|
"in": "query",
|
||||||
|
"required": false,
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"pageLimit": {
|
||||||
|
"name": "pageLimit",
|
||||||
|
"description": "Number of records to be fetched for the page",
|
||||||
|
"in": "query",
|
||||||
|
"required": false,
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"pageToken": {
|
||||||
|
"name": "pageToken",
|
||||||
|
"description": "Token used for DynamoDB pagination",
|
||||||
|
"in": "query",
|
||||||
|
"required": false,
|
||||||
|
"type": "string"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"components": {},
|
"components": {},
|
||||||
|
|
8
types.ts
8
types.ts
|
@ -1,8 +0,0 @@
|
||||||
export type ApiSuccess<T> = { data: T; pageToken?: string };
|
|
||||||
|
|
||||||
export interface ApiError extends Error {
|
|
||||||
info?: string;
|
|
||||||
status: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ApiResponse<T = any> = ApiSuccess<T> | { error: ApiError };
|
|
|
@ -1,10 +1,10 @@
|
||||||
export type ApiError = {
|
export type ApiSuccess<T> = { data: T; pageToken?: string };
|
||||||
code?: string;
|
|
||||||
message: string;
|
|
||||||
values: { [key: string]: string };
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ApiResponse<T> = {
|
export interface ApiError extends Error {
|
||||||
data: T | null;
|
info?: string;
|
||||||
error: ApiError | null;
|
status: number;
|
||||||
};
|
}
|
||||||
|
|
||||||
|
export type ApiResponse<T = any> = ApiSuccess<T> | { error: ApiError };
|
||||||
|
|
||||||
|
export type PaginateApiParams = { pageOffset: number; pageLimit: number } & { pageToken?: string };
|
||||||
|
|
Loading…
Reference in New Issue