aws-sdk#ServiceQuotas TypeScript Examples

The following examples show how to use aws-sdk#ServiceQuotas. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: handlers.ts    From aws-resource-providers with MIT License 6 votes vote down vote up
/**
     * CloudFormation invokes this handler when the resource is initially created
     * during stack create operations.
     *
     * @param session Current AWS session passed through from caller
     * @param request The request object for the provisioning request passed to the implementor
     * @param callbackContext Custom context object to allow the passing through of additional
     * state or metadata between subsequent retries
     */
    @handlerEvent(Action.Create)
    public async create(session: Optional<SessionProxy>, request: ResourceHandlerRequest<ResourceModel>, callbackContext: CallbackContext): Promise<ProgressEvent> {
        const model = new ResourceModel(request.desiredResourceState);
        const progress = ProgressEvent.progress<ProgressEvent<ResourceModel, CallbackContext>>(model);

        LOGGER.info({ handler: 'create', request, callbackContext });
        model.resourceId = 'cloudformation-quotas'; // there can only be one

        if (session instanceof SessionProxy) {
            const serviceQuotas = session.client('ServiceQuotas') as ServiceQuotas;
            await UpsertQuotas(serviceQuotas, new ResourceModel(), model, quotaCodeForPropertyName, LOGGER);
        } else {
            throw new exceptions.InvalidCredentials('no aws session found - did you forget to register the execution role?');
        }
        progress.status = OperationStatus.Success;
        return progress;
    }
Example #2
Source File: handlers.ts    From aws-resource-providers with MIT License 6 votes vote down vote up
/**
     * CloudFormation invokes this handler when the resource is updated
     * as part of a stack update operation.
     *
     * @param session Current AWS session passed through from caller
     * @param request The request object for the provisioning request passed to the implementor
     * @param callbackContext Custom context object to allow the passing through of additional
     * state or metadata between subsequent retries
     */
    @handlerEvent(Action.Update)
    public async update(session: Optional<SessionProxy>, request: ResourceHandlerRequest<ResourceModel>, callbackContext: CallbackContext): Promise<ProgressEvent> {
        const desired = new ResourceModel(request.desiredResourceState);
        const previous = new ResourceModel(request.previousResourceState);

        const progress = ProgressEvent.progress<ProgressEvent<ResourceModel, CallbackContext>>(desired);

        LOGGER.info({ handler: 'update', request, callbackContext });

        if (session instanceof SessionProxy) {
            const serviceQuotas = session.client('ServiceQuotas') as ServiceQuotas;
            await UpsertQuotas(serviceQuotas, previous, desired, quotaCodeForPropertyName, LOGGER);
        } else {
            throw new exceptions.InvalidCredentials('no aws session found - did you forget to register the execution role?');
        }
        progress.status = OperationStatus.Success;
        return progress;
    }
Example #3
Source File: handlers.ts    From aws-resource-providers with MIT License 6 votes vote down vote up
/**
     * CloudFormation invokes this handler when the resource is deleted, either when
     * the resource is deleted from the stack as part of a stack update operation,
     * or the stack itself is deleted.
     *
     * @param session Current AWS session passed through from caller
     * @param request The request object for the provisioning request passed to the implementor
     * @param callbackContext Custom context object to allow the passing through of additional
     * state or metadata between subsequent retries
     */
    @handlerEvent(Action.Delete)
    public async delete(session: Optional<SessionProxy>): Promise<ProgressEvent> {
        const progress = ProgressEvent.progress<ProgressEvent<ResourceModel, CallbackContext>>();
        if (session instanceof SessionProxy) {
            session.client('ServiceQuotas') as ServiceQuotas;
        } else {
            throw new exceptions.InvalidCredentials('no aws session found - did you forget to register the execution role?');
        }
        progress.status = OperationStatus.Success;
        return progress;
    }
Example #4
Source File: handlers.ts    From aws-resource-providers with MIT License 6 votes vote down vote up
/**
     * CloudFormation invokes this handler as part of a stack update operation when
     * detailed information about the resource's current state is required.
     *
     * @param session Current AWS session passed through from caller
     * @param request The request object for the provisioning request passed to the implementor
     * @param callbackContext Custom context object to allow the passing through of additional
     * state or metadata between subsequent retries
     */
    @handlerEvent(Action.Read)
    public async read(session: Optional<SessionProxy>, request: ResourceHandlerRequest<ResourceModel>): Promise<ProgressEvent> {
        const model = new ResourceModel(request.desiredResourceState);
        // TODO: put code here
        const progress = ProgressEvent.success<ProgressEvent<ResourceModel, CallbackContext>>(model);
        if (session instanceof SessionProxy) {
            session.client('ServiceQuotas') as ServiceQuotas;
        } else {
            throw new exceptions.InvalidCredentials('no aws session found - did you forget to register the execution role?');
        }
        return progress;
    }
Example #5
Source File: handlers.ts    From aws-resource-providers with MIT License 6 votes vote down vote up
@handlerEvent(Action.Create)
    @commonAws({ serviceName: 'ServiceQuotas', debug: true })
    public async create(action: Action, args: HandlerArgs<ResourceModel>, service: ServiceQuotas, model: ResourceModel): Promise<ResourceModel> {
        await UpsertQuotas(service, new ResourceModel(), model, quotaCodeForPropertyName, console);

        const { awsAccountId } = args.request;
        model.arn = 'arn:community:servicequotas::' + awsAccountId + ':dynamodb'; // there can only be one

        return Promise.resolve(model);
    }
Example #6
Source File: handlers.ts    From aws-resource-providers with MIT License 6 votes vote down vote up
private async updateModelWithValuesFromQuotas(
        service: ServiceQuotas,
        identifier: string,
        model: ResourceModel,
        quotaCodeForPropertyName: Record<string, QuotaID>,
        logger: Logger
    ): Promise<ResourceModel> {
        const obj = model as any;
        for (const [propertyName, quota] of Object.entries(quotaCodeForPropertyName)) {
            try {
                obj[propertyName] = await this.getQuotaValue(service, quota, logger);
            } catch (err) {
                if (err && err.code === 'NoSuchResourceException') {
                    throw new exceptions.NotFound(ResourceModel.TYPE_NAME, identifier);
                } else {
                    throw err;
                }
            }
        }
        return model;
    }
Example #7
Source File: handlers.ts    From aws-resource-providers with MIT License 6 votes vote down vote up
/**
     * CloudFormation invokes this handler when the resource is initially created
     * during stack create operations.
     *
     * @param session Current AWS session passed through from caller
     * @param request The request object for the provisioning request passed to the implementor
     * @param callbackContext Custom context object to allow the passing through of additional
     * state or metadata between subsequent retries
     */
    @handlerEvent(Action.Create)
    public async create(session: Optional<SessionProxy>, request: ResourceHandlerRequest<ResourceModel>, callbackContext: CallbackContext): Promise<ProgressEvent> {
        const model = new ResourceModel(request.desiredResourceState);
        const progress = ProgressEvent.progress<ProgressEvent<ResourceModel, CallbackContext>>(model);

        LOGGER.info({ handler: 'create', request, callbackContext });
        model.resourceId = 'iam-quotas'; // there can only be one

        if (session instanceof SessionProxy) {
            const serviceQuotas = session.client('ServiceQuotas') as ServiceQuotas;
            await UpsertQuotas(serviceQuotas, new ResourceModel(), model, quotaCodeForPropertyName, LOGGER);
        } else {
            throw new exceptions.InvalidCredentials('no aws session found - did you forget to register the execution role?');
        }
        progress.status = OperationStatus.Success;
        return progress;
    }
Example #8
Source File: handlers.ts    From aws-resource-providers with MIT License 6 votes vote down vote up
/**
     * CloudFormation invokes this handler when the resource is updated
     * as part of a stack update operation.
     *
     * @param session Current AWS session passed through from caller
     * @param request The request object for the provisioning request passed to the implementor
     * @param callbackContext Custom context object to allow the passing through of additional
     * state or metadata between subsequent retries
     */
    @handlerEvent(Action.Update)
    public async update(session: Optional<SessionProxy>, request: ResourceHandlerRequest<ResourceModel>, callbackContext: CallbackContext): Promise<ProgressEvent> {
        const desired = new ResourceModel(request.desiredResourceState);
        const previous = new ResourceModel(request.previousResourceState);

        const progress = ProgressEvent.progress<ProgressEvent<ResourceModel, CallbackContext>>(desired);

        LOGGER.info({ handler: 'update', request, callbackContext });

        if (session instanceof SessionProxy) {
            const serviceQuotas = session.client('ServiceQuotas') as ServiceQuotas;
            await UpsertQuotas(serviceQuotas, previous, desired, quotaCodeForPropertyName, LOGGER);
        } else {
            throw new exceptions.InvalidCredentials('no aws session found - did you forget to register the execution role?');
        }
        progress.status = OperationStatus.Success;
        return progress;
    }
Example #9
Source File: handlers.ts    From aws-resource-providers with MIT License 6 votes vote down vote up
/**
     * CloudFormation invokes this handler when the resource is deleted, either when
     * the resource is deleted from the stack as part of a stack update operation,
     * or the stack itself is deleted.
     *
     * @param session Current AWS session passed through from caller
     * @param request The request object for the provisioning request passed to the implementor
     * @param callbackContext Custom context object to allow the passing through of additional
     * state or metadata between subsequent retries
     */
    @handlerEvent(Action.Delete)
    public async delete(session: Optional<SessionProxy>): Promise<ProgressEvent> {
        const progress = ProgressEvent.progress<ProgressEvent<ResourceModel, CallbackContext>>();
        if (session instanceof SessionProxy) {
            session.client('ServiceQuotas') as ServiceQuotas;
        } else {
            throw new exceptions.InvalidCredentials('no aws session found - did you forget to register the execution role?');
        }
        progress.status = OperationStatus.Success;
        return progress;
    }
Example #10
Source File: handlers.ts    From aws-resource-providers with MIT License 6 votes vote down vote up
/**
     * CloudFormation invokes this handler as part of a stack update operation when
     * detailed information about the resource's current state is required.
     *
     * @param session Current AWS session passed through from caller
     * @param request The request object for the provisioning request passed to the implementor
     * @param callbackContext Custom context object to allow the passing through of additional
     * state or metadata between subsequent retries
     */
    @handlerEvent(Action.Read)
    public async read(session: Optional<SessionProxy>, request: ResourceHandlerRequest<ResourceModel>): Promise<ProgressEvent> {
        const model = new ResourceModel(request.desiredResourceState);
        // TODO: put code here
        const progress = ProgressEvent.success<ProgressEvent<ResourceModel, CallbackContext>>(model);
        if (session instanceof SessionProxy) {
            session.client('ServiceQuotas') as ServiceQuotas;
        } else {
            throw new exceptions.InvalidCredentials('no aws session found - did you forget to register the execution role?');
        }
        return progress;
    }
Example #11
Source File: handlers.ts    From aws-resource-providers with MIT License 6 votes vote down vote up
@handlerEvent(Action.Create)
    @commonAws({ serviceName: 'ServiceQuotas', debug: true })
    public async create(action: Action, args: HandlerArgs<ResourceModel>, service: ServiceQuotas, model: ResourceModel): Promise<ResourceModel> {
        const accountId = args.request.awsAccountId;
        model.resourceId = accountId;

        await UpsertQuotas(service, new ResourceModel(), model, quotaCodeForPropertyName, LOGGER);

        return Promise.resolve(model);
    }
Example #12
Source File: handlers.ts    From aws-resource-providers with MIT License 6 votes vote down vote up
@handlerEvent(Action.Update)
    @commonAws({ serviceName: 'ServiceQuotas', debug: true })
    public async update(action: Action, args: HandlerArgs<ResourceModel>, service: ServiceQuotas, model: ResourceModel): Promise<ResourceModel> {
        const previousModel = args.request.previousResourceState;

        await UpsertQuotas(service, previousModel, model, quotaCodeForPropertyName, LOGGER);

        return Promise.resolve(model);
    }
Example #13
Source File: handlers.ts    From aws-resource-providers with MIT License 5 votes vote down vote up
private async getQuotaValue(service: ServiceQuotas, quota: QuotaID, logger: Logger): Promise<number> {
        logger.log({ method: 'getting quota value', quota });
        logger.log({ method: 'before get history', quota });

        try {
            const history = await service.listRequestedServiceQuotaChangeHistoryByQuota(quota).promise();
            logger.log({ method: 'after get history', quota, history });

            if (history.RequestedQuotas && history.RequestedQuotas.length > 0) {
                const lastRequest = history.RequestedQuotas.find((x) => x.Status === 'CASE_OPENED' || x.Status === 'PENDING');
                return lastRequest.DesiredValue;
            }
        } catch (err) {
            if (err && err.code === 'NoSuchResourceException') {
                //continue, doesn't necessarily mean the resource was not deployed
                // deploying the resource with its default value will not create any request.
            } else {
                throw err;
            }
        }

        try {
            logger.log({ method: 'before get quota', quota });
            const quotaResponse = await service.getServiceQuota(quota).promise();
            logger.log({ method: 'after get quota', quota, quotaResponse });

            if (quotaResponse.Quota && quotaResponse.Quota.Value !== undefined) {
                return quotaResponse.Quota.Value;
            }
        } catch (err) {
            if (err && err.code === 'NoSuchResourceException') {
                //continue, doesn't necessarily mean the resource was not deployed
                // deploying the resource with its default value will not create any request.
            } else {
                throw err;
            }
        }

        const defaultQuota = await service.getAWSDefaultServiceQuota(quota).promise();

        return defaultQuota.Quota.Value;
    }
Example #14
Source File: service-quotas.ts    From aws-resource-providers with MIT License 5 votes vote down vote up
UpsertQuotas = async (service: ServiceQuotas, previous: BaseModel, desired: BaseModel, quotaCodeForPropertyName: Record<string, QuotaID>, logger: Console): Promise<void> => {
    for (const [key, val] of Object.entries(desired)) {
        const prevVal = (previous as any)[key];
        logger.info({ key, val, prevVal, valType: typeof val });

        const quota = quotaCodeForPropertyName[key];
        if (!quota) continue;
        if (prevVal !== val) {
            if (prevVal && prevVal > val) {
                throw new Error(`Decrease of limit failed because desired value ${val} is lower than previous value ${prevVal}`);
            }

            try {
                const history = await service.listRequestedServiceQuotaChangeHistoryByQuota(quota).promise();
                logger.info({ method: 'get history', history });
                if (history.RequestedQuotas && history.RequestedQuotas.length > 0) {
                    const lastRequest = history.RequestedQuotas.find((x) => x.Status === 'CASE_OPENED' || x.Status === 'PENDING');
                    if (lastRequest && lastRequest.DesiredValue > val) {
                        throw new Error(`Decrease of limit failed because desired value ${val} is lower than last requested value ${lastRequest.DesiredValue}`);
                    } else if (lastRequest && lastRequest.DesiredValue == val) {
                        logger.info(`skipping update because desired value ${val} is equal to last requested value ${lastRequest.DesiredValue}`);
                        return;
                    }
                }
            } catch (err) {
                if (err && err.code === 'NoSuchResourceException') {
                    //continue
                } else {
                    throw err;
                }
            }

            try {
                const quotaResponse = await service.getServiceQuota(quota).promise();
                logger.info({ method: 'get quota', quotaResponse });
                if (quotaResponse.Quota && quotaResponse.Quota.Value > val) {
                    throw new Error(`Decrease of limit failed because desired value ${val} is lower than current quota value ${quotaResponse.Quota.Value}`);
                } else if (quotaResponse.Quota && quotaResponse.Quota.Value == val) {
                    logger.info(`skipping update because desired value ${val} is equal to current quota value ${quotaResponse.Quota.Value}`);
                    return;
                }
            } catch (err) {
                if (err && err.code === 'NoSuchResourceException') {
                    const defaultQuota = await service.getAWSDefaultServiceQuota(quota).promise();
                    if (defaultQuota.Quota && defaultQuota.Quota.Value > val) {
                        throw new Error(`Decrease of limit failed because desired value ${val} is lower than AWS default quota value ${defaultQuota.Quota.Value}`);
                    } else if (defaultQuota.Quota && defaultQuota.Quota.Value == val) {
                        logger.info(`skipping update because desired value ${val} is equal to AWS default quota value ${defaultQuota.Quota.Value}`);
                        return;
                    }
                } else {
                    throw err;
                }
            }

            const valAsNumber = 0 + (new Number(val) as any);
            const increaseRequest: RequestServiceQuotaIncreaseRequest = {
                ...quota,
                DesiredValue: valAsNumber,
            };
            logger.info({
                method: 'requesting service quota increase',
                request: increaseRequest,
            });
            try {
                const response = await service.requestServiceQuotaIncrease(increaseRequest).promise();
                logger.info(response);
            } catch (err) {
                throw err;
            }
        }
    }
}
Example #15
Source File: handlers.ts    From aws-resource-providers with MIT License 5 votes vote down vote up
@handlerEvent(Action.Read)
    @commonAws({ serviceName: 'ServiceQuotas', debug: true })
    public async read(action: Action, args: HandlerArgs<ResourceModel>, service: ServiceQuotas, model: ResourceModel): Promise<ResourceModel> {
        const identifier = args.request.logicalResourceIdentifier || model.resourceId!;
        const updatedModel = await this.updateModelWithValuesFromQuotas(service, identifier, model, quotaCodeForPropertyName, args.logger);
        return updatedModel;
    }
Example #16
Source File: handlers.ts    From aws-resource-providers with MIT License 5 votes vote down vote up
@handlerEvent(Action.Delete)
    @commonAws({ serviceName: 'ServiceQuotas', debug: true })
    public async delete(action: Action, args: HandlerArgs<ResourceModel>, service: ServiceQuotas, model: ResourceModel): Promise<null> {
        args.logger.log({ method: 'delete (no-op)', model });
        return Promise.resolve(null);
    }
Example #17
Source File: handlers.ts    From aws-resource-providers with MIT License 5 votes vote down vote up
@handlerEvent(Action.Update)
    @commonAws({ serviceName: 'ServiceQuotas', debug: true })
    public async update(action: Action, args: HandlerArgs<ResourceModel>, service: ServiceQuotas, model: ResourceModel): Promise<ResourceModel> {
        await UpsertQuotas(service, args.request.previousResourceState, model, quotaCodeForPropertyName, console);
        return Promise.resolve(model);
    }
Example #18
Source File: handlers.test.ts    From aws-resource-providers with MIT License 5 votes vote down vote up
describe('when calling handler', () => {
    let testEntrypointPayload: any;
    let spySession: jest.SpyInstance;
    let spySessionClient: jest.SpyInstance;
    let serviceQuotas: AwsServiceMockBuilder<ServiceQuotas>;
    let fixtureMap: Map<Action, Record<string, any>>;

    beforeAll(() => {
        fixtureMap = new Map<Action, Record<string, any>>();
        fixtureMap.set(Action.Create, createFixture);
        fixtureMap.set(Action.Read, readFixture);
        fixtureMap.set(Action.Update, updateFixture);
        fixtureMap.set(Action.Delete, deleteFixture);
    });

    beforeEach(async () => {
        serviceQuotas = on(ServiceQuotas, { snapshot: false });
        spySession = jest.spyOn(SessionProxy, 'getSession');
        spySessionClient = jest.spyOn<any, any>(SessionProxy.prototype, 'client');
        spySessionClient.mockReturnValue(serviceQuotas.instance);
        testEntrypointPayload = {
            credentials: { accessKeyId: '', secretAccessKey: '', sessionToken: '' },
            region: 'us-east-1',
            action: 'CREATE',
        };
    });

    afterEach(() => {
        jest.clearAllMocks();
        jest.restoreAllMocks();
    });

    test('all operations fail without session - service quotas cloudformation', async () => {
        expect.assertions(fixtureMap.size);
        spySession.mockReturnValue(null);
        for (const [action, request] of fixtureMap) {
            const progress = await resource.testEntrypoint({ ...testEntrypointPayload, action, request }, null);
            expect(progress.errorCode).toBe(exceptions.InvalidCredentials.name);
        }
    });
});
Example #19
Source File: handlers.test.ts    From aws-resource-providers with MIT License 4 votes vote down vote up
describe('when calling handler', () => {
    let testEntrypointPayload: any;
    let spySession: jest.SpyInstance;
    let spySessionClient: jest.SpyInstance;
    let serviceQuotas: AwsServiceMockBuilder<ServiceQuotas>;
    let listRequestedServiceQuotaChangeHistoryByQuotaMock: AwsFunctionMockBuilder<ServiceQuotas>;
    let getServiceQuotaMock: AwsFunctionMockBuilder<ServiceQuotas>;
    let requestServiceQuotaIncreaseMock: AwsFunctionMockBuilder<ServiceQuotas>;
    let getAWSDefaultServiceQuota: AwsFunctionMockBuilder<ServiceQuotas>;
    let fixtureMap: Map<Action, Record<string, any>>;

    beforeAll(() => {
        fixtureMap = new Map<Action, Record<string, any>>();
        fixtureMap.set(Action.Create, createFixture);
        fixtureMap.set(Action.Read, readFixture);
        fixtureMap.set(Action.Update, updateFixture);
        fixtureMap.set(Action.Delete, deleteFixture);
    });

    beforeEach(async () => {
        serviceQuotas = on(ServiceQuotas, { snapshot: false });
        listRequestedServiceQuotaChangeHistoryByQuotaMock = serviceQuotas.mock('listRequestedServiceQuotaChangeHistoryByQuota').resolve({});
        getServiceQuotaMock = serviceQuotas.mock('getServiceQuota').resolve({});
        requestServiceQuotaIncreaseMock = serviceQuotas.mock('requestServiceQuotaIncrease').resolve({});
        getAWSDefaultServiceQuota = serviceQuotas.mock('getAWSDefaultServiceQuota').resolve({});
        spySession = jest.spyOn(SessionProxy, 'getSession');
        spySessionClient = jest.spyOn<any, any>(SessionProxy.prototype, 'client');
        spySessionClient.mockReturnValue(serviceQuotas.instance);
        testEntrypointPayload = {
            credentials: { accessKeyId: '', secretAccessKey: '', sessionToken: '' },
            region: 'us-east-1',
            action: 'CREATE',
        };
    });

    afterEach(() => {
        jest.clearAllMocks();
        jest.restoreAllMocks();
    });

    test('create operation successful - service quotas s3', async () => {
        const request = fixtureMap.get(Action.Create);
        const progress = await resource.testEntrypoint({ ...testEntrypointPayload, action: Action.Create, request }, null);
        expect(progress).toMatchObject({ status: OperationStatus.Success, message: '', callbackDelaySeconds: 0 });
        expect(progress.resourceModel.serialize()).toMatchObject({ ...request.desiredResourceState, ResourceId: IDENTIFIER });

        expect(listRequestedServiceQuotaChangeHistoryByQuotaMock.mock).toBeCalledTimes(1);
        expect(listRequestedServiceQuotaChangeHistoryByQuotaMock.mock).toHaveBeenCalledWith({ QuotaCode: 'L-DC2B2D3D', ServiceCode: 's3' } as any);
        expect(getServiceQuotaMock.mock).toBeCalledTimes(1);
        expect(getServiceQuotaMock.mock).toHaveBeenCalledWith({ QuotaCode: 'L-DC2B2D3D', ServiceCode: 's3' } as any);
        expect(requestServiceQuotaIncreaseMock.mock).toBeCalledTimes(1);
        expect(requestServiceQuotaIncreaseMock.mock).toHaveBeenCalledWith({ QuotaCode: 'L-DC2B2D3D', ServiceCode: 's3', DesiredValue: 100 } as any);
    });

    test('update operation successful - service quotas s3', async () => {
        const request = fixtureMap.get(Action.Update);
        const progress = await resource.testEntrypoint({ ...testEntrypointPayload, action: Action.Update, request }, null);
        expect(progress).toMatchObject({ status: OperationStatus.Success, message: '', callbackDelaySeconds: 0 });
        expect(progress.resourceModel.serialize()).toMatchObject(request.desiredResourceState);

        expect(listRequestedServiceQuotaChangeHistoryByQuotaMock.mock).toBeCalledTimes(1);
        expect(listRequestedServiceQuotaChangeHistoryByQuotaMock.mock).toHaveBeenCalledWith({ QuotaCode: 'L-DC2B2D3D', ServiceCode: 's3' } as any);
        expect(getServiceQuotaMock.mock).toBeCalledTimes(1);
        expect(getServiceQuotaMock.mock).toHaveBeenCalledWith({ QuotaCode: 'L-DC2B2D3D', ServiceCode: 's3' } as any);
        expect(requestServiceQuotaIncreaseMock.mock).toBeCalledTimes(1);
        expect(requestServiceQuotaIncreaseMock.mock).toHaveBeenCalledWith({ QuotaCode: 'L-DC2B2D3D', ServiceCode: 's3', DesiredValue: 110 } as any);
    });

    test('update decrease bucket limit fails - service quotas s3', async () => {
        const request = updateDecreaseBucketsFixture;
        const progress = await resource.testEntrypoint({ ...testEntrypointPayload, action: Action.Update, request }, null);
        expect(progress).toMatchObject({
            status: OperationStatus.Failed,
            message: expect.stringMatching(/Decrease of limit failed because desired value 90 is lower than previous value 100/),
            errorCode: exceptions.InternalFailure.name,
        });

        expect(listRequestedServiceQuotaChangeHistoryByQuotaMock.mock).toBeCalledTimes(0);
        expect(getServiceQuotaMock.mock).toBeCalledTimes(0);
        expect(requestServiceQuotaIncreaseMock.mock).toBeCalledTimes(0);
    });

    test('read operation successful (from change history) - service quotas s3', async () => {
        const history: ServiceQuotas.ListRequestedServiceQuotaChangeHistoryByQuotaResponse = {
            RequestedQuotas: [{ Status: 'CASE_OPENED', DesiredValue: 123 }],
        };
        listRequestedServiceQuotaChangeHistoryByQuotaMock.resolve(history as any);

        const request = fixtureMap.get(Action.Read);
        const progress = await resource.testEntrypoint({ ...testEntrypointPayload, action: Action.Read, request }, null);
        expect(progress).toMatchObject({ status: OperationStatus.Success, message: '', callbackDelaySeconds: 0 });
        const resourceModel = progress.resourceModel as ResourceModel;
        expect(resourceModel.buckets).toBe(123);

        expect(listRequestedServiceQuotaChangeHistoryByQuotaMock.mock).toBeCalledTimes(1);
        expect(listRequestedServiceQuotaChangeHistoryByQuotaMock.mock).toHaveBeenCalledWith({ QuotaCode: 'L-DC2B2D3D', ServiceCode: 's3' } as any);
        expect(getServiceQuotaMock.mock).toBeCalledTimes(0);
    });

    test('read operation successful (from service quota) - service quotas s3', async () => {
        const error = { code: 'NoSuchResourceException', message: 'not found', name: 'Error' } as Error;
        const getQuotaResponse: ServiceQuotas.GetServiceQuotaResponse = { Quota: { Value: 124 } };

        listRequestedServiceQuotaChangeHistoryByQuotaMock.reject(error);
        getServiceQuotaMock.resolve(getQuotaResponse as any);

        const request = fixtureMap.get(Action.Read);
        const progress = await resource.testEntrypoint({ ...testEntrypointPayload, action: Action.Read, request }, null);
        expect(progress).toMatchObject({ status: OperationStatus.Success, message: '', callbackDelaySeconds: 0 });
        const resourceModel = progress.resourceModel as ResourceModel;
        expect(resourceModel.buckets).toBe(124);

        expect(listRequestedServiceQuotaChangeHistoryByQuotaMock.mock).toBeCalledTimes(1);
        expect(listRequestedServiceQuotaChangeHistoryByQuotaMock.mock).toHaveBeenCalledWith({ QuotaCode: 'L-DC2B2D3D', ServiceCode: 's3' } as any);
        expect(getServiceQuotaMock.mock).toBeCalledTimes(1);
        expect(getServiceQuotaMock.mock).toHaveBeenCalledWith({ QuotaCode: 'L-DC2B2D3D', ServiceCode: 's3' } as any);
    });

    test('read operation successful (from aws default) - service quotas s3', async () => {
        const error = { code: 'NoSuchResourceException', message: 'not found', name: 'Error' } as Error;
        const getQuotaResponse: ServiceQuotas.GetAWSDefaultServiceQuotaResponse = { Quota: { Value: 125 } };

        listRequestedServiceQuotaChangeHistoryByQuotaMock.reject(error);
        getServiceQuotaMock.reject(error);
        getAWSDefaultServiceQuota.resolve(getQuotaResponse as any);

        const request = fixtureMap.get(Action.Read);
        const progress = await resource.testEntrypoint({ ...testEntrypointPayload, action: Action.Read, request }, null);
        expect(progress).toMatchObject({ status: OperationStatus.Success, message: '', callbackDelaySeconds: 0 });
        const resourceModel = progress.resourceModel as ResourceModel;
        expect(resourceModel.buckets).toBe(125);

        expect(listRequestedServiceQuotaChangeHistoryByQuotaMock.mock).toBeCalledTimes(1);
        expect(listRequestedServiceQuotaChangeHistoryByQuotaMock.mock).toHaveBeenCalledWith({ QuotaCode: 'L-DC2B2D3D', ServiceCode: 's3' } as any);
        expect(getServiceQuotaMock.mock).toBeCalledTimes(1);
        expect(getServiceQuotaMock.mock).toHaveBeenCalledWith({ QuotaCode: 'L-DC2B2D3D', ServiceCode: 's3' } as any);
        expect(getAWSDefaultServiceQuota.mock).toBeCalledTimes(1);
        expect(getAWSDefaultServiceQuota.mock).toHaveBeenCalledWith({ QuotaCode: 'L-DC2B2D3D', ServiceCode: 's3' } as any);
    });

    test('all operations fail without session - service quotas s3', async () => {
        expect.assertions(fixtureMap.size);
        spySession.mockReturnValue(null);
        for (const [action, request] of fixtureMap) {
            const progress = await resource.testEntrypoint({ ...testEntrypointPayload, action, request }, null);
            expect(progress.errorCode).toBe(exceptions.InvalidCredentials.name);
        }
    });
});