neuwo-api
Version:
TypeScript/JavaScript SDK client for the Neuwo content classification API
311 lines • 12.7 kB
JavaScript
;
/**
* REST API client for Neuwo API.
*
* This module provides a client for analysis where content is provided directly as text.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.NeuwoRestClient = void 0;
const errors_js_1 = require("./errors.js");
const logger_js_1 = require("./logger.js");
const models_js_1 = require("./models.js");
const utils_js_1 = require("./utils.js");
/**
* Client for Neuwo REST API endpoints.
*
* REST endpoints operate over standard HTTP methods and use a REST API token passed as a query parameter.
* REST endpoints are designed for server-side integration where content is provided directly as text.
* The REST API serves publishers who want to enrich the data before publishing by analysing content.
*/
class NeuwoRestClient {
/**
* Initialise the REST API client.
*
* @param config - Client configuration
* @param config.token - REST API authentication token
* @param config.baseUrl - Base URL for the API server
* @param config.timeout - Request timeout in seconds (default: 60)
*/
constructor(config) {
if (!config.token || typeof config.token !== "string") {
throw new errors_js_1.ValueError("Token must be a non-empty string");
}
if (!config.baseUrl || typeof config.baseUrl !== "string") {
throw new errors_js_1.ValueError("Base URL must be a non-empty string");
}
const token = config.token.trim();
const baseUrl = config.baseUrl.trim().replace(/\/$/, "");
const timeout = config.timeout || NeuwoRestClient.DEFAULT_TIMEOUT;
this.requestHandler = new utils_js_1.RequestHandler(token, baseUrl, timeout);
logger_js_1.logger.info(`Initialised NeuwoRestClient with base URL: ${baseUrl}`);
}
/**
* Retrieve AI-generated tags (raw response).
*
* Returns the raw HTTP response without parsing. Useful for custom processing or debugging.
*
* @returns Raw HTTP Response object
* @throws {ValueError} If content is invalid
* @throws {ValidationError} If parameters are invalid
* @throws {AuthenticationError} If token is invalid
* @throws {ForbiddenError} If token lacks permissions
* @throws {NeuwoAPIError} For other API errors
*/
async getAiTopicsRaw(params) {
params.format || (params.format = "json");
const content = (0, utils_js_1.sanitiseContent)(params.content);
const data = {
content,
format: params.format,
tag_limit: params.tagLimit !== undefined ? params.tagLimit : 15,
tag_min_score: params.tagMinScore !== undefined ? params.tagMinScore : 0.1,
marketing_min_score: params.marketingMinScore !== undefined
? params.marketingMinScore
: 0.3,
include_in_sim: params.includeInSim !== undefined ? params.includeInSim : true,
};
if (params.documentId !== undefined) {
data.documentid = params.documentId;
}
if (params.lang !== undefined) {
data.lang = params.lang;
}
if (params.publicationId !== undefined) {
data.publicationid = params.publicationId;
}
if (params.headline !== undefined) {
data.headline = params.headline;
}
if (params.marketingLimit !== undefined) {
data.marketing_limit = params.marketingLimit;
}
if (params.articleUrl !== undefined) {
data.articleURL = params.articleUrl;
}
logger_js_1.logger.info(`Getting AI topics for content (length: ${content.length})`);
return this.requestHandler.request({
method: "POST",
endpoint: "/GetAiTopics",
data,
});
}
/**
* Retrieve AI-generated tags and classifications for text content.
*
* Sends text content to Neuwo's REST API to obtain AI-generated tag classifications
* including subject tags, brand safety, marketing categories (IAB taxonomies), and
* smart tags. Optionally saves the article in the database if documentId is provided.
*
* @returns GetAiTopicsResponse object containing tags, brand safety, marketing categories, and smart tags
* @throws {ValueError} If content is invalid
* @throws {ValidationError} If parameters are invalid
* @throws {AuthenticationError} If token is invalid
* @throws {ForbiddenError} If token lacks permissions
* @throws {NeuwoAPIError} For other API errors
*/
async getAiTopics(params) {
const response = await this.getAiTopicsRaw({
...params,
format: "json",
});
const data = await (0, utils_js_1.parseJsonResponse)(response);
const result = models_js_1.GetAiTopicsResponse.fromApiResponse(data);
logger_js_1.logger.info(`Retrieved ${result.tags.length} tags and ${result.smartTags.length} smart tags`);
return result;
}
/**
* Find similar articles (raw response).
*
* Returns the raw HTTP response without parsing.
*
* @returns Raw HTTP Response object
* @throws {ValidationError} If parameters are invalid
* @throws {AuthenticationError} If token is invalid
* @throws {ForbiddenError} If token lacks permissions
* @throws {NeuwoAPIError} For other API errors
*/
async getSimilarRaw(params) {
params.format || (params.format = "json");
const queryParams = {
documentid: params.documentId,
format: params.format,
};
if (params.maxRows !== undefined) {
queryParams.max_rows = params.maxRows;
}
if (params.pastDays !== undefined) {
queryParams.past_days = params.pastDays;
}
if (params.publicationIds !== undefined) {
queryParams.publicationid = params.publicationIds;
}
logger_js_1.logger.info(`Getting similar articles for document: ${params.documentId}`);
return this.requestHandler.request({
method: "GET",
endpoint: "/GetSimilar",
params: queryParams,
});
}
/**
* Find articles similar to the specified document.
*
* Returns a list of similar articles with metadata including articleID, headline,
* articleURL, imageURL, similarity score, publication date, and publication ID.
*
* @returns Array of SimilarArticle objects with article metadata and similarity scores
* @throws {ValidationError} If parameters are invalid
* @throws {AuthenticationError} If token is invalid
* @throws {ForbiddenError} If token lacks permissions
* @throws {NeuwoAPIError} For other API errors
*/
async getSimilar(params) {
const response = await this.getSimilarRaw({
...params,
format: "json",
});
const data = await (0, utils_js_1.parseJsonResponse)(response);
if (!Array.isArray(data)) {
logger_js_1.logger.warning(`Expected array response, got: ${typeof data}`);
return [];
}
const similarArticles = data.map((item) => models_js_1.SimilarArticle.fromApiResponse(item));
logger_js_1.logger.info(`Found ${similarArticles.length} similar articles`);
return similarArticles;
}
/**
* Update article fields (raw response).
*
* Returns the raw HTTP response without parsing.
*
* @returns Raw HTTP Response object
* @throws {ValidationError} If parameters are invalid
* @throws {AuthenticationError} If token is invalid
* @throws {ForbiddenError} If token lacks permissions
* @throws {NeuwoAPIError} For other API errors
*/
async updateArticleRaw(params) {
params.format || (params.format = "json");
const data = {
format: params.format,
};
if (params.published !== undefined) {
data.published = (0, utils_js_1.formatDate)(params.published);
}
if (params.headline !== undefined) {
data.headline = params.headline;
}
if (params.writer !== undefined) {
data.writer = params.writer;
}
if (params.category !== undefined) {
data.category = params.category;
}
if (params.content !== undefined) {
data.content = params.content;
}
if (params.summary !== undefined) {
data.summary = params.summary;
}
if (params.publicationId !== undefined) {
data.publicationid = params.publicationId;
}
if (params.articleUrl !== undefined) {
data.articleURL = params.articleUrl;
}
if (params.imageUrl !== undefined) {
data.imageURL = params.imageUrl;
}
if (params.includeInSim !== undefined) {
data.include_in_sim = params.includeInSim;
}
logger_js_1.logger.info(`Updating article: ${params.documentId}`);
return this.requestHandler.request({
method: "PUT",
endpoint: `/UpdateArticle/${params.documentId}`,
data,
});
}
/**
* Update article fields in the database.
*
* This endpoint can only be used with articles that were assigned a documentId
* when analysing with getAiTopics(). Only fields provided in the request
* will be updated. Returns the updated article with all fields.
*
* @returns Article object with all updated fields
* @throws {ValidationError} If parameters are invalid
* @throws {AuthenticationError} If token is invalid
* @throws {ForbiddenError} If token lacks permissions
* @throws {NeuwoAPIError} For other API errors
*/
async updateArticle(params) {
const response = await this.updateArticleRaw({
...params,
format: "json",
});
const data = await (0, utils_js_1.parseJsonResponse)(response);
const article = models_js_1.Article.fromApiResponse(data);
logger_js_1.logger.info(`Successfully updated article: ${params.documentId}`);
return article;
}
/**
* Save training tags for an article (raw response).
*
* Returns the raw HTTP response without parsing.
*
* @returns Raw HTTP Response object
* @throws {ValidationError} If tags array is empty or invalid
* @throws {AuthenticationError} If token is invalid
* @throws {ForbiddenError} If token lacks permissions
* @throws {NeuwoAPIError} For other API errors
*/
async trainAiTopicsRaw(params) {
params.format || (params.format = "json");
if (!params.tags ||
!Array.isArray(params.tags) ||
params.tags.length === 0) {
throw new errors_js_1.ValidationError("You must provide some training tag values");
}
const data = {
documentid: params.documentId,
tags: params.tags,
format: params.format,
};
logger_js_1.logger.info(`Adding ${params.tags.length} training tags to article: ${params.documentId}`);
return this.requestHandler.request({
method: "POST",
endpoint: "/TrainAiTopics",
data,
});
}
/**
* Save training tags for an article.
*
* Saves a list of training tags for an article in the database.
* Returns all newly added TrainingTags (tags that weren't already in the database).
* If all tags already exist, returns an empty array.
*
* @returns Array of TrainingTag objects representing newly added tags
* @throws {ValidationError} If tags array is empty or invalid
* @throws {AuthenticationError} If token is invalid
* @throws {ForbiddenError} If token lacks permissions
* @throws {NeuwoAPIError} For other API errors
*/
async trainAiTopics(params) {
const response = await this.trainAiTopicsRaw({
...params,
format: "json",
});
const data = await (0, utils_js_1.parseJsonResponse)(response);
if (!Array.isArray(data)) {
logger_js_1.logger.warning(`Expected array response, got: ${typeof data}`);
return [];
}
const trainingTags = data.map((item) => models_js_1.TrainingTag.fromApiResponse(item));
logger_js_1.logger.info(`Added ${trainingTags.length} new training tags`);
return trainingTags;
}
}
exports.NeuwoRestClient = NeuwoRestClient;
NeuwoRestClient.DEFAULT_TIMEOUT = 60;
//# sourceMappingURL=rest-client.js.map