When I call createClickwrap method, I got 404 error Not Found. If I run this method through Quickstart generated demo project, I don't get this error. However, if I run it through my project I get the error. If I debug the demo app and my app, the functions parameters are the same.
This is the code in my app:
docusign controller:
const docuSignService = require('./docusign_esign_service');
const demoDocumentsPath = path.resolve(__dirname, '../demo_documents');
const { createClickwrap } = require('./createClickWrap');
async getDocusignRecieptService() {
const authResponse = await docuSignService.authenticate();
if (authResponse) {
const docTermsPdf = 'Term_Of_Service.pdf';
const docFile = path.resolve(demoDocumentsPath, docTermsPdf);
const { basePath, accessToken, apiAccountId } = authResponse;
const { clickwrapId } = await createClickwrap({ docFile, basePath, accessToken, accountId: apiAccountId });
const res = await activateClickwrap({ clickwrapId, basePath, accessToken, accountId: apiAccountId });
console.log({ res });
}
}
docuSignService.js
const SCOPES = ['signature', 'impersonation', 'openid', 'click.manage', 'click.send'];
const fs = require('fs');
const docusign = require('docusign-esign');
class DocusingService {
async authenticate() {
const jwtLifeSec = 10 * 60, // requested lifetime for the JWT is 10 min
dsApi = new docusign.ApiClient();
dsApi.setOAuthBasePath(process.env.dsOauthServer.replace('https://', '')); // it should be domain only.
let rsaKey = fs.readFileSync(process.env.privateKeyLocation);
try {
const results = await dsApi.requestJWTUserToken(
process.env.dsJWTClientId,
process.env.impersonatedUserGuid,
SCOPES,
rsaKey,
jwtLifeSec
);
const accessToken = results.body.access_token;
// get user info
const userInfoResults = await dsApi.getUserInfo(accessToken);
// use the default account
let userInfo = userInfoResults.accounts.find((account) => account.isDefault === 'true');
return {
accessToken: results.body.access_token,
apiAccountId: userInfo.accountId,
basePath: `${userInfo.baseUri}/restapi`
};
} catch (e) {
let body = e.response && e.response.body;
// Determine the source of the error
if (body) {
// The user needs to grant consent
if (body.error && body.error === 'consent_required') {
if (this.getConsent()) {
return this.authenticate();
}
} else {
// Consent has been granted. Show status code for DocuSign API error
this._debug_log(`\nAPI problem: Status code ${e.response.status}, message body:
${JSON.stringify(body, null, 4)}\n\n`);
}
}
}
}
getConsent() {
var urlScopes = SCOPES.join('+');
// Construct consent URL
var redirectUri = 'https://developers.docusign.com/platform/auth/consent';
var consentUrl =
`${process.env.dsOauthServer}/oauth/auth?response_type=code&` +
`scope=${urlScopes}&client_id=${process.env.dsJWTClientId}&` +
`redirect_uri=${redirectUri}`;
throw new Error(`Open the following URL in your browser to grant consent to the application: ${consentUrl}`);
}
getArgs(apiAccountId, accessToken, basePath, signerEmail, signerName, id, agreementData, redirect_uri) {
const envelopeArgs = {
signerEmail: signerEmail,
signerName: signerName,
status: 'sent',
signerClientId: id,
dsReturnUrl: redirect_uri,
agreement: agreementData
};
const args = {
accessToken: accessToken,
basePath: basePath,
accountId: apiAccountId,
envelopeArgs: envelopeArgs
};
return args;
}
}
module.exports = new DocusingService();
createClickWrap.js
const createClickwrap = async ({ docFile, clickwrapName = 'clickwrapName', basePath, accessToken, accountId }) => {
// Step 3. Construct the request Body
// Create display settings model
const displaySettings = docusignClick.DisplaySettings.constructFromObject({
consentButtonText: 'I Agree',
displayName: 'Terms of Service',
downloadable: true,
format: 'modal',
hasAccept: true,
mustRead: true,
requireAccept: true,
documentDisplay: 'document'
});
// Create document model
// Read and encode file. Put encoded value to Document entity.
// The reads could raise an exception if the file is not available!
const documentPdfExample = fs.readFileSync(docFile);
const encodedExampleDocument = Buffer.from(documentPdfExample).toString('base64');
const document = docusignClick.Document.constructFromObject({
documentBase64: encodedExampleDocument,
documentName: 'Terms of Service',
fileExtension: 'pdf',
order: 0
});
// Create clickwrapRequest model
const clickwrapRequest = docusignClick.ClickwrapRequest.constructFromObject({
displaySettings,
documents: [document],
name: clickwrapName,
requireReacceptance: true
});
// Step 4. Call the Click API
const dsApiClient = new docusignClick.ApiClient();
dsApiClient.setBasePath(basePath);
dsApiClient.addDefaultHeader('Authorization', 'Bearer ' + accessToken);
const accountApi = new docusignClick.AccountsApi(dsApiClient);
// Create a clickwrap
let result = null;
try {
result = await accountApi.createClickwrap(accountId, {
clickwrapRequest
});
} catch (e) {
debugger;
console.log(e);
}
debugger;
console.log(`Clickwrap was created. ClickwrapId ${result.clickwrapId}`);
return result;
};
module.exports = { createClickwrap };
Parameters look like this in the demo app and it works:
and these are the parameters in my app:
The first parameter accountId is the same. Why I am getting this issue in my app if function gets the same parameters?
"Error: Not Found
at Request.callback (/Users/and/test/node_modules/docusign-click/node_modules/superagent/lib/node/index.js:696:15)
at IncomingMessage.<anonymous> (/Users/and/test/node_modules/docusign-click/node_modules/superagent/lib/node/index.js:906:18)
at IncomingMessage.emit (node:events:539:35)
at IncomingMessage.emit (node:domain:475:12)
at endReadableNT (node:internal/streams/readable:1345:12)
at processTicksAndRejections (node:internal/process/task_queues:83:21)"
Thanks to pointing out in the comments, when I changed basePath ${userInfo.baseUri}/restapi to ${userInfo.baseUri}/clickapi, it works now.
Related
my code:
const { Api, TelegramClient } = require("telegram");
const { StringSession } = require("telegram/sessions");
const { Logger } = require("telegram/extensions");
const apiId = 22;
const apiHash = "333333";
const phoneNumber = "9996627328";
const phoneCode = "22222";
const createClient = async (stringSession) => {
const session = new StringSession(stringSession);
const options = { connectionRetries: 5, baseLogger: new Logger("debug") };
const client = new TelegramClient(session, apiId, apiHash, options);
client.session.setDC(2, "149.154.167.40", 443);
await client.connect();
return client;
};
const getLoginCodeCommand = (phoneNumber) => {
const settings = new Api.CodeSettings();
const args = { phoneNumber, apiId, apiHash, settings };
return new Api.auth.SendCode(args);
};
(async () => {
const client = await createClient("");
const response = await client.invoke(getLoginCodeCommand(phoneNumber));
console.log('response:', response);
const { phoneCodeHash } = response;
const result22 = await client.invoke(
new Api.auth.SignUp({
phoneNumber: phoneNumber,
phoneCodeHash: phoneCodeHash,
firstName: "liu",
lastName: "some",
})
);
console.log("RESULT22", result22);
})();
response:
/Users/xizao/work/fm/project/telegram/node_modules/telegram/errors/index.js:28
return new RPCBaseErrors_1.RPCError(rpcError.errorMessage, request, rpcError.errorCode);
^
RPCError: 400: PHONE_CODE_INVALID (caused by auth.SignUp)
at RPCMessageToError (/Users/xizao/work/fm/project/telegram/node_modules/telegram/errors/index.js:28:12)
at MTProtoSender._handleRPCResult (/Users/xizao/work/fm/project/telegram/node_modules/telegram/network/MTProtoSender.js:517:58)
at MTProtoSender._processMessage (/Users/xizao/work/fm/project/telegram/node_modules/telegram/network/MTProtoSender.js:442:15)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async MTProtoSender._recvLoop (/Users/xizao/work/fm/project/telegram/node_modules/telegram/network/MTProtoSender.js:418:17) {
code: 400,
errorMessage: 'PHONE_CODE_INVALID'
}
No matter how you adjust it, the phone code is always invalid
The correct result should be direct registration success.
I haven't been able to find the problem because of what? Please help me to see why?
doc link: https://gram.js.org/tl/auth/SignUp#authsignup
I am using node to list all users from domain. I had created service account with domain wide delegation.
The domain admin gave access to the service account to required scopes.
Code:
const { JWT } = require('google-auth-library');
const {google, chat_v1} = require('googleapis');
const keys = require('./keys.json')
async function main() {
const client = new JWT(keys.client_email, keys, keys.private_key,
['https://www.googleapis.com/auth/admin.directory.user',
'https://www.googleapis.com/auth/admin.directory.user.readonly',
'https://www.googleapis.com/auth/cloud-platform'],
"admin#domain.com"
);
await client.authorize();
const service = google.admin("directory_v1");
service.users.list({
domain: "domain.com",
maxResults: "10",
orderBy: "email",
}, (err, res) => {
if (err) return console.error('The API returned an error:', err.message);
const users = res.data.users;
if (users.length) {
console.log('Users:');
users.forEach((user) => {
console.log(`${user.primaryEmail} (${user.name.fullName})`);
});
} else {
console.log('No users found.');
}
});
}
main();
but only thing I recive is:
The API returned an error: Login Required.
also enabled admin sdk api for this service account
any idea why this is happening?
I'm not sure if JWT is the correct way, but this is how I do it.
const google = require("googleapis").google;
const SRVC_ACCOUNT_CREDS = require('./keys.json');
const getClient = async (scopes: string[], user: string)=>{
const auth = new google.auth.GoogleAuth({
credentials: SRVC_ACCOUNT_CREDS,
scopes: scopes
});
const client = await auth.getClient();
client.subject = user;
return client;
};
const listUsers = async (query = "", limit = 500, pageToken = null, user, fields, getAll = false)=>{
const scopes = ["https://www.googleapis.com/auth/admin.directory.user"];
const client = await getClient(scopes, user);
const service = google.admin({version: "directory_v1", auth: client});
const result = {
users: [],
nextPageToken: ""
};
if(!fields) {
fields = "users(name.fullName,primaryEmail,organizations(department,primary,title),thumbnailPhotoUrl),nextPageToken";
}
do{
const request = await service.users.list({
customer: "my_customer",
fields: fields,
orderBy: "givenName",
maxResults: limit,
pageToken: pageToken,
query: query,
viewType: "admin_view"
});
pageToken = getAll ? request.data.nextPageToken : null;
const users = request.data.users;
if(users && users.length){
result.users.push(...users);
result.nextPageToken = request.data.nextPageToken;
}
} while(pageToken);
return result;
};
I'm trying to send an envelope from a template in a webhook listener. I'm using node.js with .mjs files. When I send the envelope via the docusign dashboard it has the tabs - full name, SSN, phone number, address. But when the API sends the envelope, it has those words but no fields next to them. This is a problem because we need that info and there's nowhere for the signer to input it. What could be causing the tabs to not appear when the envelope is sent from the api?
Here's the code to create an envelope and use a template (based off docs):
import docusign from 'docusign-esign';
export function makeEnvelope(args){
// Create the envelope definition
let env = new docusign.EnvelopeDefinition();
env.templateId = args.templateId;
// Create template role elements to connect the signer and cc recipients
// to the template
// We're setting the parameters via the object creation
let signer1 = docusign.TemplateRole.constructFromObject({
email: args.signerEmail,
name: args.signerName,
roleName: 'signer'});
// Create a cc template role.
// We're setting the parameters via setters
let cc1 = new docusign.TemplateRole();
cc1.email = args.ccEmail;
cc1.name = args.ccName;
cc1.roleName = 'cc';
// Add the TemplateRole objects to the envelope object
env.templateRoles = [signer1, cc1];
env.status = 'sent'; // We want the envelope to be sent
return env;
}
export async function useTemplate(args) {
let dsApiClient = new docusign.ApiClient();
dsApiClient.setBasePath(args.basePath);
dsApiClient.addDefaultHeader('Authorization', 'Bearer ' + args.accessToken);
let envelopesApi = new docusign.EnvelopesApi(dsApiClient);
// Make the envelope request body
let envelope = makeEnvelope(args.envelopeArgs);
// Call Envelopes::create API method
// Exceptions will be caught by the calling function
let results = await envelopesApi.createEnvelope(
args.accountId, {envelopeDefinition: envelope});
return results;
};
Here's the code for the webhook listener where I call useTemplate (please excuse the commented out code and the console logs - I'm still in the midst of figuring it all out):
import express from 'express';
import { useTemplate } from '../request/docusign/docusign-methods.mjs';
import opportunities from '../request/prosperworks/opportunities.mjs';
import people from '../request/prosperworks/people.js';
import customFields from '../request/prosperworks/custom-fields.mjs';
import { findTemplateIdByCohortName } from '../request/docusign/templates.mjs';
import { findSentEnvelopesByStudentEmail, voidEnvelope, findTemplateFromEnvelopeTemplateUri } from '../request/docusign/envelopes.mjs';
import { createJWT, getAccessTokenFromJWT } from '../request/docusign/token.mjs';
import { getAccessToken } from '../request/quickbooks/tokens.mjs';
import { findCustomerByEmail } from '../request/quickbooks/customer.mjs';
import { createCustomer } from '../request/quickbooks/customer.mjs';
import { createInvoice, sendInvoice } from '../request/quickbooks/invoice.mjs';
import { findItemByName } from '../request/quickbooks/item.mjs';
const router = express.Router();
export default router
.post('/copper/opportunity/updated', express.json(), async (req, res, next) => {
const { body } = req;
console.log('webhook received', body);
if(!Object.keys(body.updated_attributes).length) return res.send('irrelevant webhook');
const cohortChanged = !!body.updated_attributes?.custom_fields?.['94620']
console.log('cohort changed?', cohortChanged);
const interviewScheduledToAccepted = !!(body.updated_attributes?.stage?.[0] === 'Interview Scheduled' && body.updated_attributes?.stage?.[1] === 'Accepted')
console.log('interview scheduled to accepted?', interviewScheduledToAccepted);
const fullConditional = cohortChanged || interviewScheduledToAccepted;
console.log('full conditional', fullConditional);
if(fullConditional) {
try {
const jwt = await createJWT();
const docusign_access_token = await getAccessTokenFromJWT(jwt);
const opportunity = await opportunities.get(body.ids[0]);
const cohortId = opportunity?.custom_fields?.find(field => field.custom_field_definition_id === 94620)?.value || null;
const cohortName = customFields.getCohortNameById(cohortId);
console.log('cohort name', cohortName);
const templateId = await findTemplateIdByCohortName(cohortName, docusign_access_token);
const person = await people.findById(opportunity.primary_contact_id);
const email = person.emails[0].email;
console.log('email', email);
const { name } = person;
// if(interviewScheduledToAccepted) {
// const quickbooks_access_token = await getAccessToken();
// let customer = await findCustomerByEmail(email, quickbooks_access_token);
// if(customer === null) {
// customer = await createCustomer(cohortName, person, quickbooks_access_token);
// };
// console.log('customer', customer);
// const product = await findItemByName('Deposit', quickbooks_access_token);
// const invoice = await createInvoice(customer, product, cohortName, quickbooks_access_token);
// const sentInvoice = await sendInvoice(email, invoice.Invoice.Id, quickbooks_access_token)
// console.log('sent invoice', sentInvoice);
// }
const sentEnvelopes = await findSentEnvelopesByStudentEmail(email, docusign_access_token);
await Promise.all(
sentEnvelopes.filter(envelope => {
return envelope.emailSubject.includes('Enrollment Agreement');
})
.map(envelope => {
if(envelope.status === 'sent') return voidEnvelope(envelope.envelopeId, docusign_access_token);
})
);
const sentEnvelopesTemplates = await Promise.all(
sentEnvelopes
.filter(envelope => {
return envelope.status !== 'voided'
})
.map(envelope => {
return findTemplateFromEnvelopeTemplateUri(envelope.templatesUri, docusign_access_token);
})
);
const templateAlreadyUsedCheck = sentEnvelopesTemplates.reduce((outerAcc, templateArr) => {
if(templateArr.reduce((innerAcc, template) => {
if(template.templateId === templateId) innerAcc=true;
return innerAcc;
}, false)) {
outerAcc=true;
}
return outerAcc;
}, false);
if(templateAlreadyUsedCheck) return res.send('envelope already sent');
const envelopeArgs = {
templateId: templateId,
signerEmail: email,
signerName: name
}
console.log(envelopeArgs);
const envelope = await useTemplate({
basePath: process.env.DOCUSIGN_BASE_PATH,
accessToken: docusign_access_token,
accountId: process.env.DOCUSIGN_ACCOUNT_ID,
envelopeArgs
});
return res.send(envelope);
}
catch(err) {
console.log(err.message);
next(err);
}
} else {
return res.send('irrelevant webhook');
}
});
Inbar Gazit figured it out - it turns out I had student as the role name in the template and signer as the role name in my code so that's why it wasn't working.
I just added a route in one of my node router files and would like to add the appropriate unit test for it. The route is the following:
router.post('/addtradables', checkIsAdmin, async function(req, res, next) {
const SteamApp = require('../models/steamapp.model');
const Tradable = require('../models/tradable.model');
const User = require('../models/user.model');
const postedTradables = req.body.tradables;
if(postedTradables) {
res.json({ success: true, result: constants.PROCESS_STARTED, textResult: 'The tradables are being added' });
const adminUser = await User.findOne({ steamId: ADMIN_ID }).exec();
for(let currentKey in postedTradables) {
if(postedTradables.hasOwnProperty(currentKey)) {
const currentTradable = postedTradables[currentKey];
const appFound = currentTradable.sku ? await SteamApp.findOne({ appid: currentTradable.sku }).exec() : null;
await new Tradable( { /* object data here */ } ).save();
}
}
} else {
return res.json({ success: false, result: constants.INVALID_FORMAT, textResult: 'No tradable found, check the format' });
}
} );
As you can see, it returns an object with a code and text message so that the front end can know if the process was started or not. This is because the insertion process can take a while, depending on the input data.
Here is my Jasmine spec file:
const app = require('../../app');
const request = require('supertest');
const MongoDbTest = require('../../lib/mongodb-tests.lib');
const jwt = require('jsonwebtoken');
const SteamApp = require('../../models/steamapp.model');
const Tradable = require('../../models/tradable.model');
const User = require('../../models/user.model');
const JWT_TOKEN = process.env.JTW_KEY;
const ADMIN_ID = process.env.MASTER_STEAMID;
const constants = require('../../config/constants');
const adminUser = {
role: 'admin',
steamId: ADMIN_ID
};
const testTradable = {
sku: 1234,
barterId: 83838,
extra: 2,
type: 'gift',
title: 'Les patates volent!'
};
describe("route /addtradables", () => {
const mongod = new MongoDbTest();
let testOwner;
beforeAll(async () => {
await mongod.connect();
await new SteamApp({ appid: 1234, name: 'patate' }).save();
testOwner = await new User(adminUser).save();
} );
afterAll(async () => {
await SteamApp.deleteMany({});
await User.deleteMany({});
await mongod.close();
} );
it("devrait retourner un message indiquant que le format est incorrect lorsque c'est le cas", async () => {
const userToken = jwt.sign(adminUser, JWT_TOKEN, { algorithm: 'HS256' });
const response = await request(app).post(`/api/addtradables`).send({ patate: 'pouelle' }).set('Authorization', `Bearer ${userToken}`).expect('Content-Type', /json/);
expect(response.statusCode).toBe(200);
expect(response.body.success).toBe(false);
expect(response.body.result).toBe(constants.INVALID_FORMAT);
} );
it("devrait retourner un message indiquant que le processus a débuté lorsque le format est correct", async () => {
const userToken = jwt.sign(adminUser, JWT_TOKEN, { algorithm: 'HS256' });
const response = await request(app).post(`/api/addtradables`).send({ tradables: { 9837489374: testTradable } }).set('Authorization', `Bearer ${userToken}`).expect('Content-Type', /json/);
expect(response.statusCode).toBe(200);
expect(response.body.success).toBe(true);
/*
I WANT TO WAIT HERE! If I don't wait for the router.post call to finish the data will not be available in the database
*/
const insertedTradable = await Tradable.findOne({ appid: 1234 }).exec();
expect(insertedTradable.barterId).toBe(testTradable.barterId);
expect(insertedTradable.isGift).toBe(true);
expect(insertedTradable.copies).toBe(testTradable.extra);
expect(insertedTradable.name).toBe(testTradable.title);
} );
} );
Is there a way to spy on the router.post call and check when everything is completed so that I can extract data from the database and check that the data was inserted properly? Short of that, what would be the best way to sleep / stop execution in the jasmine test routine while the the router.post call wraps up? I've looked at using a done function but since the code is inside the router I don't see how I could use done in this scenario.
I'm using a custom service account (using --service-account parameter in the deploy command). That service account has domain-wide delegation enabled and it's installed in the G Apps Admin panel.
I tried this code:
app.get('/test', async (req, res) => {
const auth = new google.auth.GoogleAuth()
const gmailClient = google.gmail({ version: 'v1' })
const { data } = await gmailClient.users.labels.list({ auth, userId: 'user#domain.com' })
return res.json(data).end()
})
It works if I run it on my machine (having the GOOGLE_APPLICATION_CREDENTIALS env var setted to the path of the same service account that is assigned to the Cloud Run service) but when it's running in Cloud Run, I get this response:
{
"code" : 400,
"errors" : [ {
"domain" : "global",
"message" : "Bad Request",
"reason" : "failedPrecondition"
} ],
"message" : "Bad Request"
}
I saw this solution for this same issue, but it's for Python and I don't know how to replicate that behaviour with the Node library.
After some days of research, I finally got a working solution (porting the Python implementation):
async function getGoogleCredentials(subject: string, scopes: string[]): Promise<JWT | OAuth2Client> {
const auth = new google.auth.GoogleAuth({
scopes: ['https://www.googleapis.com/auth/cloud-platform'],
})
const authClient = await auth.getClient()
if (authClient instanceof JWT) {
return (await new google.auth.GoogleAuth({ scopes, clientOptions: { subject } }).getClient()) as JWT
} else if (authClient instanceof Compute) {
const serviceAccountEmail = (await auth.getCredentials()).client_email
const unpaddedB64encode = (input: string) =>
Buffer.from(input)
.toString('base64')
.replace(/=*$/, '')
const now = Math.floor(new Date().getTime() / 1000)
const expiry = now + 3600
const payload = JSON.stringify({
aud: 'https://accounts.google.com/o/oauth2/token',
exp: expiry,
iat: now,
iss: serviceAccountEmail,
scope: scopes.join(' '),
sub: subject,
})
const header = JSON.stringify({
alg: 'RS256',
typ: 'JWT',
})
const iamPayload = `${unpaddedB64encode(header)}.${unpaddedB64encode(payload)}`
const iam = google.iam('v1')
const { data } = await iam.projects.serviceAccounts.signBlob({
auth: authClient,
name: `projects/-/serviceAccounts/${serviceAccountEmail}`,
requestBody: {
bytesToSign: unpaddedB64encode(iamPayload),
},
})
const assertion = `${iamPayload}.${data.signature!.replace(/=*$/, '')}`
const headers = { 'content-type': 'application/x-www-form-urlencoded' }
const body = querystring.encode({ assertion, grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer' })
const response = await fetch('https://accounts.google.com/o/oauth2/token', { method: 'POST', headers, body }).then(r => r.json())
const newCredentials = new OAuth2Client()
newCredentials.setCredentials({ access_token: response.access_token })
return newCredentials
} else {
throw new Error('Unexpected authentication type')
}
}
What you can do here is define ENV variables in your yaml file as described in this documentation to set the GOOGLE_APPLICATION_CREDENTIALS to the path of the JSON key.
Then use a code such as the one mentioned here.
const authCloudExplicit = async ({projectId, keyFilename}) => {
// [START auth_cloud_explicit]
// Imports the Google Cloud client library.
const {Storage} = require('#google-cloud/storage');
// Instantiates a client. Explicitly use service account credentials by
// specifying the private key file. All clients in google-cloud-node have this
// helper, see https://github.com/GoogleCloudPlatform/google-cloud-node/blob/master/docs/authentication.md
// const projectId = 'project-id'
// const keyFilename = '/path/to/keyfile.json'
const storage = new Storage({projectId, keyFilename});
// Makes an authenticated API request.
try {
const [buckets] = await storage.getBuckets();
console.log('Buckets:');
buckets.forEach(bucket => {
console.log(bucket.name);
});
} catch (err) {
console.error('ERROR:', err);
}
// [END auth_cloud_explicit]
};
Or follow an approach similar to the one mentioned here.
'use strict';
const {auth, Compute} = require('google-auth-library');
async function main() {
const client = new Compute({
serviceAccountEmail: 'some-service-account#example.com',
});
const projectId = await auth.getProjectId();
const url = `https://dns.googleapis.com/dns/v1/projects/${projectId}`;
const res = await client.request({url});
console.log(res.data);
}
main().catch(console.error);