lincd-quora-ads
Version:
An API wrapper for Quora's Ads API.
406 lines • 16.5 kB
JavaScript
;
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