UNPKG

lincd-quora-ads

Version:

An API wrapper for Quora's Ads API.

406 lines 16.5 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.QuoraAPI = void 0; const restapi_1 = require("lincd-rest-api/lib/ontologies/restapi"); const API_1 = require("lincd-rest-api/lib/shapes/API"); const utils_1 = require("lincd-rest-api/lib/utils"); const models_1 = require("lincd/lib/models"); const ShapeDecorators_1 = require("lincd/lib/utils/ShapeDecorators"); const Account_1 = __importDefault(require("../mappings/Account")); const Ad_1 = __importDefault(require("../mappings/Ad")); const AdSet_1 = __importDefault(require("../mappings/AdSet")); const Campaign_1 = __importDefault(require("../mappings/Campaign")); const qads_1 = require("../ontologies/qads"); const package_1 = require("../package"); // Just being used to reflect the default fields that are used // in the docs, not necessary to include const DEFAULT_FIELDS = [ "accountId", "clicks", "impressions", "spend", ]; let QuoraAPI = class QuoraAPI extends API_1.API { constructor(nodeOrAccessToken, refreshToken, clientId = "", clientSecret = "") { super(); this.processResponse = (r) => { const remaining = r.headers.get("X-Rate-Limit-Remaining"); console.info(`⏳ ${remaining} request(s) remaining...`); }; if (typeof nodeOrAccessToken === "string") { let accessToken = nodeOrAccessToken; // Headers as per Quora's documentation; // https://t.ly/z47ub this.defaultHeaders = { Authorization: `Bearer ${accessToken}`, }; this.accessToken = accessToken; this.refreshToken = refreshToken; this.clientId = clientId; this.clientSecret = clientSecret; } else super(nodeOrAccessToken); this.host = "https://api.quora.com/ads/v0"; } /** * Helper method for generating URL-ready parameters from * given options. * * This is unique to Quora, and may vary from API to API. * To make this, I looked through the docs to see exactly * what params can be used in the docs: https://t.ly/DJveM * * @param options * @returns A URL-ready string with the provided `options` */ generateParamsFromOptions(options) { if (!options) { return ""; } let params = []; for (var key in options) { let param = key; let val = options[key]; let ignore = false; switch (key) { case "attributionWindows": case "conversionTypes": case "fields": param += `=${val.join(",")}`; break; case "endDate": case "startDate": let date = val; param += `=${date.getFullYear()}-${date.getMonth()}-${date.getDate()}`; break; case "granularity": case "level": case "offset": case "order": case "presetTimeRange": case "sort": case "sortConversionType": param += `=${val}`; break; case "summary": // Since `summary` is a boolean, // check if they set it to true ... if (val) { break; } // ... otherwise, don't add the param // at all: // https://www.quora.com/ads/api9169a6d6e9b42452d500a61717d87d15d5fa49ec5b53030741178130?share=1#/paths/~1campaigns~1{campaign-id}/get param = ""; break; case "limit": ignore = true; break; default: console.warn(`Unknown key: '${key}' with value '${val}'`); break; } if (!ignore) { params.push(param); } } return `?${params.join("&")}`; } /** * Fetches an ad belonging to the authorised client. * @param adId * @param options * @returns */ async getAd(adId, options = {}) { const mappingFn = (res) => { const adData = res.data[0]; const ad = Ad_1.default.initialise(adData); return ad; }; const DEFAULT_OPTIONS = { fields: ["adId"], }; options = { fields: [...(options.fields ? options.fields : DEFAULT_FIELDS)], }; options = (0, utils_1.mergeOptions)(DEFAULT_OPTIONS, options); const params = this.generateParamsFromOptions(options); return (await this.makeRequest(`/ads/${adId}${params}`, mappingFn)); } /** * Retrieve a single ad set by its ID. * @param adSetId The ID belonging to the ad set to fetch * @param options Any extra options to add to the search * @returns A shape of the request ad */ async getAdSet(adSetId, options = {}) { const mappingFn = (res) => { const adSetData = res.data[0]; const adSet = AdSet_1.default.initialise(adSetData); return adSet; }; const DEFAULT_OPTIONS = { fields: ["adSetId"], }; options = { fields: [...(options.fields ? options.fields : DEFAULT_FIELDS)], }; options = (0, utils_1.mergeOptions)(DEFAULT_OPTIONS, options); const params = this.generateParamsFromOptions(options); return (await this.makeRequest(`/ad-sets/${adSetId}${params}`, mappingFn)); } /** * Retrieve a single campaign by its ID * @param campaignId The ID by which to search for the data * @param options Any extra options to search by * @returns The populated campaign shape */ async getCampaign(campaignId, options = {}) { const mappingFn = (res) => { const campaignData = res.data[0]; const campaign = Campaign_1.default.initialise(campaignData); return campaign; }; const DEFAULT_OPTIONS = { fields: ["campaignId"], }; options = { fields: [...(options.fields ? options.fields : DEFAULT_FIELDS)], }; options = (0, utils_1.mergeOptions)(DEFAULT_OPTIONS, options); const params = this.generateParamsFromOptions(options); return this.makeRequest(`/campaigns/${campaignId}${params}`, mappingFn); } async getAdSetsFromCampaign(campaignId, options = {}) { const mappingFn = (res) => { const adSetList = res.data; return adSetList === null || adSetList === void 0 ? void 0 : adSetList.map((adSetJSON) => { const adSet = AdSet_1.default.initialise(adSetJSON); return adSet; }); }; const DEFAULT_OPTIONS = { fields: [ "adSetId", "campaignId", ...(!options.fields ? DEFAULT_FIELDS : []), ], level: "AD_SET", }; options = (0, utils_1.mergeOptions)(DEFAULT_OPTIONS, options); const params = this.generateParamsFromOptions(options); const limit = (options === null || options === void 0 ? void 0 : options.limit) || 0; return (await this.paginatedRequest(`/campaigns/${campaignId}${params}`, mappingFn, "paging", limit)); } /** * Default options for this method are ``{ * fields: ["adId"] * level: "AD" * }`` as these are the bare minimum required for the endpoint to * return meaningful data and construct shapes. * * @param adSetId The ID of the Ad Set from which to collect ads * @param options Any additional options to be applied to the defaults * @returns An array of Ad shape instances */ async getAdsFromAdSet(adSetId, options = {}) { const mappingFn = (res) => { const adList = res.data; return adList === null || adList === void 0 ? void 0 : adList.map((adJSON) => { const ad = Ad_1.default.initialise(adJSON); return ad; }); }; const DEFAULT_OPTIONS = { fields: [ "adId", "adSetId", ...(!options.fields ? DEFAULT_FIELDS : []), ], level: "AD", }; options = (0, utils_1.mergeOptions)(DEFAULT_OPTIONS, options); const params = this.generateParamsFromOptions(options); const limit = (options === null || options === void 0 ? void 0 : options.limit) || 0; return (await this.paginatedRequest(`/ad-sets/${adSetId}${params}`, mappingFn, "paging", limit)); } /** * Default options for this method are ``{ * fields: ["adId"] * level: "AD" * }`` as these are the bare minimum required for the endpoint to * return meaningful data and construct shapes. * * @param campaignId The ID of the campaign from which to collect ads * @param options Any additional options to be applied to the defaults * @returns An array of Ad shape instances */ async getAdsFromCampaign(campaignId, options = {}) { const mappingFn = (res) => { const adList = res.data; return adList === null || adList === void 0 ? void 0 : adList.map((adJSON) => { const ad = Ad_1.default.initialise(adJSON); return ad; }); }; const DEFAULT_OPTIONS = { fields: ["adId", ...(!options.fields ? DEFAULT_FIELDS : [])], level: "AD", }; options = (0, utils_1.mergeOptions)(DEFAULT_OPTIONS, options); const params = this.generateParamsFromOptions(options); const limit = (options === null || options === void 0 ? void 0 : options.limit) || 0; return (await this.paginatedRequest(`/campaigns/${campaignId}${params}`, mappingFn, "paging", limit)); } async getAllAccounts() { const mappingFn = (res) => { const accounts = res.data; return accounts === null || accounts === void 0 ? void 0 : accounts.map((accData) => { const account = Account_1.default.initialise(accData); return account; }); }; return (await this.makeRequest("/accounts/", mappingFn)); // TODO fix so don't need as unknown } async getCampaignsFromAccount(accountId, options = {}, returnOriginalJSON = false) { const mappingFn = (res) => { const campaignData = res.data; const campaignList = campaignData === null || campaignData === void 0 ? void 0 : campaignData.map((campaignJSON) => { const campaign = Campaign_1.default.initialise(campaignJSON); return campaign; }); // Should this be an option at the lincd-rest-api // level? Or should it be up to the API designer? if (returnOriginalJSON) { // Just seems like a bit of a band-aid solution return [campaignList, res]; } return campaignList; }; const DEFAULT_OPTIONS = { fields: ["campaignId", ...(!options.fields ? DEFAULT_FIELDS : [])], level: "CAMPAIGN", }; options = (0, utils_1.mergeOptions)(DEFAULT_OPTIONS, options); const params = this.generateParamsFromOptions(options); const limit = (options === null || options === void 0 ? void 0 : options.limit) || 0; return (await this.paginatedRequest(`/accounts/${accountId}${params}`, mappingFn, "paging", limit)); } async refreshAccessToken() { if (typeof this.clientId === "undefined" || typeof this.clientSecret === "undefined") { throw new Error("Client ID or Secret not provided"); } const body = [ `client_id=${this.clientId}`, `client_secret=${this.clientSecret}`, `refresh_token=${this.refreshToken}`, "grant_type=refresh_token", ].join("&"); let res = await fetch("https://www.quora.com/_/oauth/token", { method: "POST", headers: { "Content-Type": "application/x-www-form-url-encoded", }, body, }); let json = await res.json(); this.accessToken = json.access_token; this.defaultHeaders = { ...this.defaultHeaders, Authorization: `Bearer ${this.accessToken}`, }; } get accessToken() { return this.getValue(restapi_1.restAPI.accessToken); } set accessToken(val) { this.overwrite(restapi_1.restAPI.accessToken, new models_1.Literal(val)); } get clientId() { return this.getValue(restapi_1.restAPI.clientId); } set clientId(val) { this.overwrite(restapi_1.restAPI.clientId, new models_1.Literal(val)); } get clientSecret() { return this.getValue(restapi_1.restAPI.clientSecret); } set clientSecret(val) { this.overwrite(restapi_1.restAPI.clientSecret, new models_1.Literal(val)); } get refreshToken() { return this.getValue(restapi_1.restAPI.refreshToken); } set refreshToken(val) { this.overwrite(restapi_1.restAPI.refreshToken, new models_1.Literal(val)); } }; /** * indicates that instances of this shape need to have this rdf.type */ QuoraAPI.targetClass = qads_1.qads.QuoraAPI; __decorate([ (0, ShapeDecorators_1.literalProperty)({ path: restapi_1.restAPI.accessToken, required: true, maxCount: 1, minLength: 30, maxLength: 30, }), __metadata("design:type", String), __metadata("design:paramtypes", [String]) ], QuoraAPI.prototype, "accessToken", null); __decorate([ (0, ShapeDecorators_1.literalProperty)({ path: restapi_1.restAPI.clientId, required: true, maxCount: 1, minLength: 32, maxLength: 32, }), __metadata("design:type", String), __metadata("design:paramtypes", [String]) ], QuoraAPI.prototype, "clientId", null); __decorate([ (0, ShapeDecorators_1.literalProperty)({ path: restapi_1.restAPI.clientSecret, required: true, maxCount: 1, minLength: 44, maxLength: 44, }), __metadata("design:type", String), __metadata("design:paramtypes", [String]) ], QuoraAPI.prototype, "clientSecret", null); __decorate([ (0, ShapeDecorators_1.literalProperty)({ path: restapi_1.restAPI.refreshToken, required: true, maxCount: 1, minLength: 30, maxLength: 30, }), __metadata("design:type", String), __metadata("design:paramtypes", [String]) ], QuoraAPI.prototype, "refreshToken", null); QuoraAPI = __decorate([ package_1.linkedShape, __metadata("design:paramtypes", [Object, String, String, String]) ], QuoraAPI); exports.QuoraAPI = QuoraAPI; //# sourceMappingURL=QuoraAPI.js.map