Skip to content

Commit

Permalink
chore: enhance typing
Browse files Browse the repository at this point in the history
  • Loading branch information
htdangkhoa committed Oct 7, 2024
1 parent 233453e commit c6241bc
Show file tree
Hide file tree
Showing 8 changed files with 74 additions and 131 deletions.
23 changes: 0 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -296,29 +296,6 @@ const service = new GoogleAds(

See more at [Node.js gRPC Library](https://grpc.github.io/grpc/node/module-src_client_interceptors.html) and some examples [here](https://github.com/grpc/proposal/blob/master/L5-node-client-interceptors.md).

## Bonus

### Type helper

If you are using the generated types from the protos, you may run into an issue where the types are referring to values instead of types or missing the following properties from types. You can use the following helper to fix this issue.

```ts
import { ads, MessageType, MessageFnsKeys } from '@htdangkhoa/google-ads';

const {
services: { GoogleAdsRow },
} = ads.googleads.v17;

const row: MessageType<typeof GoogleAdsRow> = {
// ... properties
};

// or
const row: Omit<typeof GoogleAdsRow, MessageFnsKeys> = {
// ... properties
};
```

## Development

### Prerequisites
Expand Down
3 changes: 1 addition & 2 deletions src/LoggingInterceptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ interface Event {
}

export class LoggingInterceptor {
private requestLogging: boolean | LoggingOptions;
private summaryLogger: Logger;
private detailLogger: Logger;

Expand All @@ -67,7 +66,7 @@ export class LoggingInterceptor {
private responseHeaders?: Metadata;
private responseStatus?: StatusObject;

constructor(requestLogging: boolean | LoggingOptions) {
constructor(private requestLogging: boolean | LoggingOptions) {
this.requestLogging = requestLogging;

log4js.configure({
Expand Down
7 changes: 6 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
export * from './generated/index.google.js';
export * as enums from './generated/index.google.ads.googleads.v17.enums.js';
export * as common from './generated/index.google.ads.googleads.v17.common.js';
export * as errors from './generated/index.google.ads.googleads.v17.errors.js';
export * as resources from './generated/index.google.ads.googleads.v17.resources.js';
export * as services from './generated/index.google.ads.googleads.v17.services.js';
export * as rpc from './generated/index.google.rpc.js';
export * from './constants.js';
export * from './Customer.js';
export * from './GoogleAds.js';
Expand Down
10 changes: 0 additions & 10 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,3 @@ export interface OrderBy {
export interface Interceptor {
interceptCall: GRPCInterceptor;
}

export type MessageFnsKeys =
| 'create'
| 'encode'
| 'decode'
| 'fromJSON'
| 'toJSON'
| 'fromPartial';

export type MessageType<T> = Omit<T, MessageFnsKeys>;
58 changes: 28 additions & 30 deletions tests/google-ads.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import {
VERSION,
decodePartialFailureError,
getGoogleAdsError,
ads,
services,
errors,
rpc,
} from '../src';
import {
Expand All @@ -23,18 +24,6 @@ import {
MOCK_OAUTH2_CLIENT,
} from './test-utils';

const {
services: { GoogleAdsRow },
errors: {
GoogleAdsFailure,
ErrorCode,
AuthenticationErrorEnum_AuthenticationError,
RequestErrorEnum_RequestError,
},
} = ads.googleads.v17;

const { Status } = rpc;

let service: MockGoogleAds;

beforeAll(async () => {
Expand Down Expand Up @@ -66,30 +55,35 @@ describe('search', () => {
it('should be able to search customer_client', async () => {
const metadata = new Metadata();

const { results } = await service
const { results } = (await service
.setCustomerId(MOCK_MANAGER_ID)
.mockSearch({ query }, { results: MOCK_CUSTOMER_CLIENTS }, metadata);
.mockSearch(
{ query },
{ results: MOCK_CUSTOMER_CLIENTS },
metadata,
)) as services.SearchGoogleAdsResponse;

expect(results).toEqual(MOCK_CUSTOMER_CLIENTS);
});

it('should throw ServiceError', async () => {
const internalRepr = new Map();
internalRepr.set(FAILURE_KEY, [
GoogleAdsFailure.fromPartial({
errors.GoogleAdsFailure.fromPartial({
errors: [
{
error_code: ErrorCode.fromPartial({
error_code: errors.ErrorCode.fromPartial({
authentication_error:
AuthenticationErrorEnum_AuthenticationError.CUSTOMER_NOT_FOUND,
errors.AuthenticationErrorEnum_AuthenticationError
.CUSTOMER_NOT_FOUND,
}),
message: 'No customer found for the provided customer id.',
},
],
}),
]);
internalRepr.set('grpc-status-details-bin', [
GoogleAdsFailure.fromPartial({
errors.GoogleAdsFailure.fromPartial({
errors: [
{
message:
Expand Down Expand Up @@ -154,7 +148,7 @@ describe('searchStream', () => {
.setLinkedCustomerId(MOCK_LINKED_CUSTOMER_ID)
.mockSearchStream({ query }, { results: MOCK_CAMPAIGNS });

const campaigns: (typeof GoogleAdsRow)[] = [];
const campaigns: services.GoogleAdsRow[] = [];

while (true) {
const { value, done } = await stream.next();
Expand Down Expand Up @@ -185,11 +179,12 @@ describe('mutate', () => {

describe('partial failure', () => {
it('should decode partial failure errors if present on the response', async () => {
const failureMessage = GoogleAdsFailure.fromPartial({
const failureMessage = errors.GoogleAdsFailure.fromPartial({
errors: [
{
error_code: ErrorCode.fromPartial({
request_error: RequestErrorEnum_RequestError.BAD_RESOURCE_ID,
error_code: errors.ErrorCode.fromPartial({
request_error:
errors.RequestErrorEnum_RequestError.BAD_RESOURCE_ID,
}),
message: 'error message',
location: {
Expand All @@ -204,7 +199,8 @@ describe('mutate', () => {
],
});

const failureBuffer = GoogleAdsFailure.encode(failureMessage).finish();
const failureBuffer =
errors.GoogleAdsFailure.encode(failureMessage).finish();

const response = await service
.setCustomerId(MOCK_CUSTOMER_ID)
Expand All @@ -216,7 +212,7 @@ describe('mutate', () => {
},
{
mutate_operation_responses: [],
partial_failure_error: Status.fromPartial({
partial_failure_error: rpc.Status.fromPartial({
details: [
{
type_url: `google.ads.googleads.${VERSION}.errors.GoogleAdsFailure`,
Expand Down Expand Up @@ -326,11 +322,12 @@ describe('mutate', () => {
});

it('should return the errors for partial failure', async () => {
const failureMessage = GoogleAdsFailure.fromPartial({
const failureMessage = errors.GoogleAdsFailure.fromPartial({
errors: [
{
error_code: ErrorCode.fromPartial({
request_error: RequestErrorEnum_RequestError.BAD_RESOURCE_ID,
error_code: errors.ErrorCode.fromPartial({
request_error:
errors.RequestErrorEnum_RequestError.BAD_RESOURCE_ID,
}),
message: 'error message',
location: {
Expand All @@ -345,7 +342,8 @@ describe('mutate', () => {
],
});

const failureBuffer = GoogleAdsFailure.encode(failureMessage).finish();
const failureBuffer =
errors.GoogleAdsFailure.encode(failureMessage).finish();

const response = await service
.setCustomerId(MOCK_CUSTOMER_ID)
Expand All @@ -357,7 +355,7 @@ describe('mutate', () => {
},
{
mutate_operation_responses: [],
partial_failure_error: Status.fromPartial({
partial_failure_error: rpc.Status.fromPartial({
details: [
{
type_url: `google.ads.googleads.${VERSION}.errors.GoogleAdsFailure`,
Expand Down
34 changes: 17 additions & 17 deletions tests/protos.spec.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,20 @@
import { describe, it, expect } from 'vitest';

import { ads } from '../src';
import { services, enums, resources } from '../src';
import { MOCK_ADDRESS, MOCK_CREDENTIALS } from './test-utils';

const {
services: { CustomerServiceClient, GoogleAdsServiceClient },
enums: { AdvertisingChannelTypeEnum_AdvertisingChannelType },
resources: { Campaign },
} = ads.googleads.v17;

describe('CustomerServiceClient', () => {
it('should be exported', () => {
expect(typeof CustomerServiceClient).toBe('function');
expect(typeof services.CustomerServiceClient).toBe('function');
});

it('should be able to create a client', () => {
const client = new CustomerServiceClient(MOCK_ADDRESS, MOCK_CREDENTIALS);
const client = new services.CustomerServiceClient(
MOCK_ADDRESS,
MOCK_CREDENTIALS,
);

expect(client).toBeInstanceOf(CustomerServiceClient);
expect(client).toBeInstanceOf(services.CustomerServiceClient);

expect(client).toEqual(
expect.objectContaining({
Expand All @@ -31,13 +28,16 @@ describe('CustomerServiceClient', () => {

describe('GoogleAdsServiceClient', () => {
it('should be exported', () => {
expect(typeof GoogleAdsServiceClient).toBe('function');
expect(typeof services.GoogleAdsServiceClient).toBe('function');
});

it('should be able to create a client', () => {
const client = new GoogleAdsServiceClient(MOCK_ADDRESS, MOCK_CREDENTIALS);
const client = new services.GoogleAdsServiceClient(
MOCK_ADDRESS,
MOCK_CREDENTIALS,
);

expect(client).toBeInstanceOf(GoogleAdsServiceClient);
expect(client).toBeInstanceOf(services.GoogleAdsServiceClient);

expect(client).toEqual(
expect.objectContaining({
Expand All @@ -52,14 +52,14 @@ describe('GoogleAdsServiceClient', () => {
});

describe('Campaign', () => {
const campaign = Campaign.fromPartial({
const campaign = resources.Campaign.fromPartial({
name: 'Planet Express',
advertising_channel_type:
AdvertisingChannelTypeEnum_AdvertisingChannelType.SEARCH,
enums.AdvertisingChannelTypeEnum_AdvertisingChannelType.SEARCH,
});

it('should be exported', () => {
expect(typeof Campaign).toBe('object');
expect(typeof resources.Campaign).toBe('object');
});

it('should have a name', () => {
Expand All @@ -68,7 +68,7 @@ describe('Campaign', () => {

it('should have an advertising channel type', () => {
expect(campaign.advertising_channel_type).toBe(
AdvertisingChannelTypeEnum_AdvertisingChannelType.SEARCH,
enums.AdvertisingChannelTypeEnum_AdvertisingChannelType.SEARCH,
);
});
});
10 changes: 4 additions & 6 deletions tests/service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,10 @@ import {
MOCK_DEVELOPER_TOKEN,
MOCK_OAUTH2_CLIENT,
} from './test-utils';
import { ads } from '../src';
import { services } from '../src';

let service: MockService;

const { GoogleAdsServiceClient } = ads.googleads.v17.services;

beforeAll(async () => {
service = new MockService({
auth: MOCK_OAUTH2_CLIENT,
Expand All @@ -22,7 +20,7 @@ describe('Service', () => {
it('should be able to create a service and load client', async () => {
const client = service.loadService('GoogleAdsServiceClient');

expect(client).toBeInstanceOf(GoogleAdsServiceClient);
expect(client).toBeInstanceOf(services.GoogleAdsServiceClient);
});

it('should be loaded from cache', async () => {
Expand All @@ -31,8 +29,8 @@ describe('Service', () => {
const cached = service.getCachedClient('GoogleAdsServiceClient');

await Promise.all([
expect(client).toBeInstanceOf(GoogleAdsServiceClient),
expect(cached).toBeInstanceOf(GoogleAdsServiceClient),
expect(client).toBeInstanceOf(services.GoogleAdsServiceClient),
expect(cached).toBeInstanceOf(services.GoogleAdsServiceClient),
]);
});

Expand Down
Loading

0 comments on commit c6241bc

Please sign in to comment.