Skip to content

Commit

Permalink
Neynar V2 Client
Browse files Browse the repository at this point in the history
Neynar V2 Client
  • Loading branch information
Shreyaschorge authored Oct 31, 2023
2 parents ad376f8 + 4d2530a commit 5cfddf6
Show file tree
Hide file tree
Showing 72 changed files with 5,434 additions and 5 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ build

# Temporary generated files
src/neynar-api/neynar-v1-api/openapi-tmp
src/neynar-api/neynar-v2-api/openapi-tmp
src/neynar-api/neynar-v2-api/openapi-farcaster-tmp
src/neynar-api/neynar-v2-api/openapi-neynar-tmp
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@neynar/nodejs-sdk",
"version": "0.4.0",
"version": "0.5.0",
"description": "SDK to interact with Neynar APIs (https://docs.neynar.com/)",
"main": "./build/index.js",
"types": "./build/index.d.ts",
Expand All @@ -11,7 +11,7 @@
"clean": "del-cli ./build/*",
"build": "yarn run clean && tsc",
"generate:neynar-oas": "rm -rf src/neynar-api/neynar-v1-api/openapi-tmp; openapi-generator-cli generate -i src/oas/src/v1/spec.yaml -g typescript-axios -o src/neynar-api/neynar-v1-api/openapi-tmp --config src/oas/openapi-generator-config.json",
"generate:neynar-oas-v2": "rm -rf src/neynar-api/neynar-v2-api/openapi-tmp; openapi-generator-cli generate -i src/oas/src/v2/spec.yaml -g typescript-axios -o src/neynar-api/neynar-v2-api/openapi --config src/oas/openapi-generator-config.json"
"generate:neynar-oas-v2-farcaster": "rm -rf src/neynar-api/neynar-v2-api/openapi-farcaster-tmp; openapi-generator-cli generate -i src/oas/src/v2/spec.yaml -g typescript-axios -o src/neynar-api/neynar-v2-api/openapi-farcaster --config src/oas/openapi-generator-config.json"
},
"author": "Neynar",
"license": "ISC",
Expand Down
3 changes: 2 additions & 1 deletion src/neynar-api/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from "./neynar-v1-api";
export * from "./neynar-v1-api";
export * from "./neynar-v2-api";
38 changes: 38 additions & 0 deletions src/neynar-api/neynar-api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { NeynarV1APIClient } from "./neynar-v1-api";
import { NeynarV2APIClient } from "./neynar-v2-api";
import { AxiosInstance } from "axios";
import { silentLogger, Logger } from "./common/logger";

export class NeynarAPIClient {
private readonly logger: Logger;

public readonly clients: {
v1: NeynarV1APIClient;
v2: NeynarV2APIClient;
};

/**
* Instantiates a NeynarAPIClient
*
* Creates NeynarV1APIClient and NeynarV2APIClients
*/
constructor(
apiKey: string,
{
logger = silentLogger,
axiosInstance,
}: { logger?: Logger; axiosInstance?: AxiosInstance } = {}
) {
this.logger = logger;

if (apiKey === "") {
throw new Error(
"Attempt to use an authenticated API method without first providing an api key"
);
}
this.clients = {
v1: new NeynarV1APIClient(apiKey, { logger, axiosInstance }),
v2: new NeynarV2APIClient(apiKey, { logger, axiosInstance }),
};
}
}
2 changes: 2 additions & 0 deletions src/neynar-api/neynar-v2-api/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./openapi-farcaster";
export * from "./neynar-api-v2-client";
209 changes: 209 additions & 0 deletions src/neynar-api/neynar-v2-api/neynar-api-v2-client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
import {
CastApi,
SignerApi,
Signer,
Cast,
CastParamType,
PostCastResponseCast,
DeleteCastReqBody,
ReactionApi,
ReactionReqBody,
ReactionType,
OperationResponse,
FollowReqBody,
BulkFollowResponse,
EmbeddedCast,
Configuration,
ErrorRes,
FeedApi,
UserApi,
CastApiPostCastRequest,
CastWithInteractions,
SearchedUser,
} from "./openapi-farcaster";
import axios, { AxiosError, AxiosInstance } from "axios";
import { silentLogger, Logger } from "../common/logger";
import type { SetRequired } from "type-fest";

const BASE_PATH = "https://api.neynar.com/v2";

export class NeynarV2APIClient {
private readonly logger: Logger;

public readonly apis: {
cast: CastApi;
};

/**
* Instantiates a NeynarV1APIClient
*
* Note: A Wallet must be provided if the API client is to mint new AuthTokens
*/
constructor(
apiKey: string,
{
logger = silentLogger,
axiosInstance,
}: { logger?: Logger; axiosInstance?: AxiosInstance } = {}
) {
this.logger = logger;

if (apiKey === "") {
throw new Error(
"Attempt to use an authenticated API method without first providing an api key"
);
}

if (axiosInstance === undefined) {
axiosInstance = axios.create();
}
axiosInstance.defaults.decompress = true;
axiosInstance.interceptors.response.use(
(response) => response,
(error) => {
if (NeynarV2APIClient.isApiErrorResponse(error)) {
const apiErrors = error.response.data;
this.logger.warn(`API errors: ${JSON.stringify(apiErrors)}`);
}
throw error;
}
);

const config: Configuration = new Configuration({
basePath: BASE_PATH,
apiKey: apiKey,
});
this.apis = {
cast: new CastApi(config, undefined, axiosInstance),
};
}

/**
* Utility for parsing errors returned by the Neynar API servers. Returns true
* if the given error is caused by an error response from the server, and
* narrows the type of `error` accordingly.
*/
public static isApiErrorResponse(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
error: any
): error is SetRequired<AxiosError<ErrorRes>, "response"> {
if (!(error instanceof AxiosError)) return false;
return (
error.response?.data !== undefined && "message" in error.response.data
);
}

/**
* Gets information about an individual cast by cast hash.
* See [Neynar documentation](https://docs.neynar.com/reference/cast)
*
*/
public async fetchCastByHash(castHash: string): Promise<Cast | null> {
try {
const response = await this.apis.cast.cast({
type: CastParamType.Hash,
identifier: castHash,
});
return response.data.cast;
} catch (error) {
if (NeynarV2APIClient.isApiErrorResponse(error)) {
const status = error.response.status;
if (status === 404) {
return null;
}
}
throw error;
}
}

/**
* Gets information about an individual cast by cast hash.
* See [Neynar documentation](https://docs.neynar.com/reference/cast)
*
*/
public async fetchCastByUrl(castUrl: string): Promise<Cast | null> {
try {
const response = await this.apis.cast.cast({
type: CastParamType.Url,
identifier: castUrl,
});
return response.data.cast;
} catch (error) {
if (NeynarV2APIClient.isApiErrorResponse(error)) {
const status = error.response.status;
if (status === 404) {
return null;
}
}
throw error;
}
}

/**
* Gets information about an array of casts.
* See [Neynar documentation](https://docs.neynar.com/reference/casts)
*
*/
public async fetchCasts(castHashes: string[]): Promise<Cast[] | null> {
try {
const response = await this.apis.cast.casts({
getCastsReqBody: {
casts: castHashes.map((hash) => ({ hash })),
},
});
return response.data.result.casts;
} catch (error) {
if (NeynarV2APIClient.isApiErrorResponse(error)) {
const status = error.response.status;
if (status === 404) {
return null;
}
}
throw error;
}
}

/**
* Publishes a cast for the currently authenticated user.
* See [Neynar documentation](https://docs.neynar.com/reference/post-cast)
*
*/
public async publishCast(
signerUuid: string,
text: string,
options?: { embeds?: EmbeddedCast[]; replyTo?: string }
): Promise<PostCastResponseCast> {
const request: CastApiPostCastRequest = {
postCastReqBody: {
signer_uuid: signerUuid,
text: text,
embeds: options?.embeds,
parent: options?.replyTo,
},
};
const response = await this.apis.cast.postCast(request);
return response.data.cast;
}

/**
* Delete a cast.
* See [Neynar documentation](https://docs.neynar.com/reference/delete-cast)
*
*/
public async deleteCast(
signerUuid: string,
castOrCastHash: Cast | string
): Promise<void> {
let castHash: string;
if (typeof castOrCastHash === "string") {
castHash = castOrCastHash;
} else {
castHash = castOrCastHash.hash;
}
const body: DeleteCastReqBody = {
signer_uuid: signerUuid,
target_hash: castHash,
};
await this.apis.cast.deleteCast({ deleteCastReqBody: body });
}
}
4 changes: 4 additions & 0 deletions src/neynar-api/neynar-v2-api/openapi-farcaster/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
wwwroot/*.js
node_modules
typings
dist
1 change: 1 addition & 0 deletions src/neynar-api/neynar-v2-api/openapi-farcaster/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# empty npmignore to ensure all required files (e.g., in the dist folder) are published by npm
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# OpenAPI Generator Ignore
# Generated by openapi-generator https://github.com/openapitools/openapi-generator

# Use this file to prevent files from being overwritten by the generator.
# The patterns follow closely to .gitignore or .dockerignore.

# As an example, the C# client generator defines ApiClient.cs.
# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
#ApiClient.cs

# You can match any string of characters against a directory, file or extension with a single asterisk (*):
#foo/*/qux
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux

# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
#foo/**/qux
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux

# You can also negate patterns with an exclamation (!).
# For example, you can ignore all files in a docs folder with the file extension .md:
#docs/*.md
# Then explicitly reverse the ignore rule for a single file:
#!docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
.gitignore
.npmignore
.openapi-generator-ignore
api.ts
apis/cast-api.ts
apis/feed-api.ts
apis/reaction-api.ts
apis/signer-api.ts
apis/user-api.ts
base.ts
common.ts
configuration.ts
git_push.sh
index.ts
models/active-status.ts
models/add-verification-req-body.ts
models/bulk-follow-response.ts
models/cast-id.ts
models/cast-notification-type.ts
models/cast-param-type.ts
models/cast-parent-author.ts
models/cast-response.ts
models/cast-with-interactions-reactions.ts
models/cast-with-interactions-replies.ts
models/cast-with-interactions.ts
models/cast.ts
models/casts-response-result.ts
models/casts-response.ts
models/delete-cast-req-body.ts
models/embed-cast-id.ts
models/embed-url.ts
models/embedded-cast.ts
models/error-res.ts
models/feed-response.ts
models/follow-req-body.ts
models/follow-response.ts
models/get-casts-req-body.ts
models/index.ts
models/individual-hash-obj.ts
models/next-cursor.ts
models/operation-response.ts
models/post-cast-req-body.ts
models/post-cast-response-cast-author.ts
models/post-cast-response-cast.ts
models/post-cast-response.ts
models/profile-url-pfp.ts
models/profile-url.ts
models/reaction-like.ts
models/reaction-recast.ts
models/reaction-req-body.ts
models/reaction-type.ts
models/register-signer-key-req-body.ts
models/remove-verification-req-body.ts
models/searched-user.ts
models/signer.ts
models/update-user-req-body.ts
models/user-bulk200-response.ts
models/user-profile-bio.ts
models/user-profile.ts
models/user-search-response-result.ts
models/user-search-response.ts
models/user-viewer-context.ts
models/user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
7.0.1
Loading

0 comments on commit 5cfddf6

Please sign in to comment.