Skip to content

Commit

Permalink
feat: Add an option to disable version check
Browse files Browse the repository at this point in the history
  • Loading branch information
neet committed Nov 9, 2022
1 parent 513048e commit 782fa60
Show file tree
Hide file tree
Showing 45 changed files with 364 additions and 104 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "masto",
"description": "Mastodon API client for JavaScript, TypeScript, Node.js, browsers",
"private": false,
"version": "4.4.1",
"version": "4.5.0-rc1",
"author": "Ryo Igarashi <n33t5hin@gmail.com>",
"license": "AGPL-3.0",
"main": "./dist/index.js",
Expand Down
9 changes: 8 additions & 1 deletion src/clients/admin.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
import { MastoConfig } from '../config';
import { Http } from '../http';
import { AdminRepositories } from '../repositories';

export class MastoAdminClient {
readonly account: AdminRepositories.AccountRepository;
readonly report: AdminRepositories.ReportRepository;

constructor(private readonly http: Http, private readonly version: string) {
constructor(
private readonly http: Http,
private readonly version: string,
private readonly config: MastoConfig,
) {
this.account = new AdminRepositories.AccountRepository(
this.http,
this.version,
this.config,
);

this.report = new AdminRepositories.ReportRepository(
this.http,
this.version,
this.config,
);
}
}
Expand Down
124 changes: 93 additions & 31 deletions src/clients/masto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,50 +95,112 @@ export class MastoClient {
private readonly http: Http,
private readonly ws: Ws,
readonly version: string,
private readonly config: MastoConfig,
readonly config: MastoConfig,
) {
this.admin = new MastoAdminClient(this.http, this.version);
this.stream = new StreamRepository(this.ws, this.version);
this.accounts = new AccountRepository(this.http, this.version);
this.announcements = new AnnouncementRepository(this.http, this.version);
this.apps = new AppRepository(this.http, this.version);
this.blocks = new BlockRepository(this.http, this.version);
this.bookmarks = new BookmarkRepository(this.http, this.version);
this.conversations = new ConversationRepository(this.http, this.version);
this.customEmojis = new CustomEmojiRepository(this.http, this.version);
this.directory = new DirectoryRepository(this.http, this.version);
this.domainBlocks = new DomainBlockRepository(this.http, this.version);
this.endorsements = new EndorsementRepository(this.http, this.version);
this.favourites = new FavouriteRepository(this.http, this.version);
this.featuredTags = new FeaturedTagRepository(this.http, this.version);
this.filters = new FilterRepository(this.http, this.version);
this.followRequests = new FollowRequestRepository(this.http, this.version);
this.instances = new InstanceRepository(this.http, this.version);
this.lists = new ListRepository(this.http, this.version);
this.markers = new MarkerRepository(this.http, this.version);
this.admin = new MastoAdminClient(this.http, this.version, this.config);
this.stream = new StreamRepository(this.ws, this.version, this.config);
this.accounts = new AccountRepository(this.http, this.version, this.config);
this.announcements = new AnnouncementRepository(
this.http,
this.version,
this.config,
);
this.apps = new AppRepository(this.http, this.version, this.config);
this.blocks = new BlockRepository(this.http, this.version, this.config);
this.bookmarks = new BookmarkRepository(
this.http,
this.version,
this.config,
);
this.conversations = new ConversationRepository(
this.http,
this.version,
this.config,
);
this.customEmojis = new CustomEmojiRepository(
this.http,
this.version,
this.config,
);
this.directory = new DirectoryRepository(
this.http,
this.version,
this.config,
);
this.domainBlocks = new DomainBlockRepository(
this.http,
this.version,
this.config,
);
this.endorsements = new EndorsementRepository(
this.http,
this.version,
this.config,
);
this.favourites = new FavouriteRepository(
this.http,
this.version,
this.config,
);
this.featuredTags = new FeaturedTagRepository(
this.http,
this.version,
this.config,
);
this.filters = new FilterRepository(this.http, this.version, this.config);
this.followRequests = new FollowRequestRepository(
this.http,
this.version,
this.config,
);
this.instances = new InstanceRepository(
this.http,
this.version,
this.config,
);
this.lists = new ListRepository(this.http, this.version, this.config);
this.markers = new MarkerRepository(this.http, this.version, this.config);
this.mediaAttachments = new MediaAttachmentRepository(
this.http,
this.version,
this.config.timeout,
this.config,
);
this.mutes = new MuteRepository(this.http, this.version, this.config);
this.notifications = new NotificationsRepository(
this.http,
this.version,
this.config,
);
this.poll = new PollRepository(this.http, this.version, this.config);
this.preferences = new PreferenceRepository(
this.http,
this.version,
this.config,
);
this.mutes = new MuteRepository(this.http, this.version);
this.notifications = new NotificationsRepository(this.http, this.version);
this.poll = new PollRepository(this.http, this.version);
this.preferences = new PreferenceRepository(this.http, this.version);
this.pushSubscriptions = new PushSubscriptionsRepository(
this.http,
this.version,
this.config,
);
this.reports = new ReportRepository(this.http, this.version);
this.reports = new ReportRepository(this.http, this.version, this.config);
this.scheduledStatuses = new ScheduledStatusesRepository(
this.http,
this.version,
this.config,
);
this.statuses = new StatusRepository(this.http, this.version, this.config);
this.suggestions = new SuggestionRepository(
this.http,
this.version,
this.config,
);
this.timelines = new TimelinesRepository(
this.http,
this.version,
this.config,
);
this.statuses = new StatusRepository(this.http, this.version);
this.suggestions = new SuggestionRepository(this.http, this.version);
this.timelines = new TimelinesRepository(this.http, this.version);
this.trends = new TrendRepository(this.http, this.version);
this.email = new EmailRepository(this.http, this.version);
this.trends = new TrendRepository(this.http, this.version, this.config);
this.email = new EmailRepository(this.http, this.version, this.config);
}

/**
Expand Down
3 changes: 3 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,7 @@ export interface MastoConfig {
readonly timeout?: number;
readonly headers?: { [key: string]: string };
readonly proxy?: MastoProxyConfig;
readonly disableVersionCheck?: boolean;
readonly disableDeprecatedWarning?: boolean;
readonly disableExperimentalWarning?: boolean;
}
29 changes: 18 additions & 11 deletions src/decorators/deprecated.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,40 @@
import { MastoConfig } from '../config';

interface Target {
readonly config: MastoConfig;
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
type Fn = (...args: any[]) => any;

/**
* Decorator that verifies the version of the Mastodon instance
* @param parameters Optional params
*/
export const deprecated =
(message: string) =>
(
_obj: unknown,
_target: Target,
name: string | symbol,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
descriptor: TypedPropertyDescriptor<(...args: any[]) => any>,
descriptor: TypedPropertyDescriptor<Fn>,
) => {
const original = descriptor.value;

if (!original) {
throw new Error('available can only apply to a method of a class');
const origin = descriptor.value;
if (!origin) {
throw new Error('deprecated can only apply to a method of a class');
}

descriptor.value = function (
this: unknown,
...args: Parameters<typeof original>
this: Target,
...args: Parameters<typeof origin>
) {
if (
process.env.NODE_ENV !== 'production' ||
!process.env.NODE_IGNORE_MASTO_WARNINGS
!this.config?.disableDeprecatedWarning
) {
// eslint-disable-next-line no-console
console.warn(`#${name.toString()} is deprecated. ${message}`);
}

return original.apply(this, args);
return origin.apply(this, args);
};
};
31 changes: 17 additions & 14 deletions src/decorators/version.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,41 @@
import semver from 'semver';

import { MastoConfig } from '../config';
import { MastoNotFoundError } from '../errors';

export interface Version {
interface Target {
readonly version: string;
readonly config: MastoConfig;
}

export interface AvailableParams {
since?: `${number}.${number}.${number}`;
until?: `${number}.${number}.${number}`;
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
type Fn = (...args: any[]) => any;

/**
* Decorator that verifies the version of the Mastodon instance
* @param parameters Optional params
*/
export const version =
({ since, until }: AvailableParams) =>
(
_version: Version,
name: string | symbol,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
descriptor: TypedPropertyDescriptor<(...args: any[]) => any>,
) => {
const original = descriptor.value;

if (!original) {
throw new Error('available can only apply to a method of a class');
(_target: Target, name: string, descriptor: TypedPropertyDescriptor<Fn>) => {
const origin = descriptor.value;
if (!origin) {
throw new Error('version can only apply to a method of a class');
}

descriptor.value = function (
this: Version,
...args: Parameters<typeof original>
this: Target,
...args: Parameters<typeof origin>
) {
if (this.config.disableVersionCheck) {
return origin.apply(this, args);
}

if (since && semver.lt(this.version, since, { loose: true })) {
throw new MastoNotFoundError(
`${String(name)} is not available with the current ` +
Expand All @@ -49,6 +52,6 @@ export const version =
);
}

return original.apply(this, args);
return origin.apply(this, args);
};
};
4 changes: 2 additions & 2 deletions src/entrypoints/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { SerializerNativeImpl } from '../serializers';
import { WsNodejsImpl } from '../ws';

export const login = async (config: MastoConfig): Promise<MastoClient> => {
if (!process.env.NODE_IGNORE_MASTO_WARNINGS) {
if (!config.disableExperimentalWarning) {
// eslint-disable-next-line no-console
console.warn(
'You are using experimental Fetch API. Please consider using Axios instead until it is fully supported.',
Expand All @@ -15,7 +15,7 @@ export const login = async (config: MastoConfig): Promise<MastoClient> => {

const serializer = new SerializerNativeImpl();
const http = new HttpNativeImpl(config, serializer);
const instance = await new InstanceRepository(http, '1.0.0').fetch();
const instance = await new InstanceRepository(http, '1.0.0', config).fetch();
const ws = new WsNodejsImpl(
instance.urls.streamingApi,
instance.version,
Expand Down
8 changes: 5 additions & 3 deletions src/entrypoints/nodejs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,19 @@ import { SerializerNodejsImpl } from '../serializers';
import { WsNodejsImpl } from '../ws';

export const login = async (config: MastoConfig): Promise<MastoClient> => {
const version = 'yay yay';

const serializer = new SerializerNodejsImpl();
const http = new HttpAxiosImpl(config, serializer);
const instance = await new InstanceRepository(http, '1.0.0').fetch();
const instance = await new InstanceRepository(http, '1.0.0', config).fetch();
const ws = new WsNodejsImpl(
instance.urls.streamingApi,
instance.version,
version,
config,
serializer,
);

return new MastoClient(http, ws, instance.version, config);
return new MastoClient(http, ws, version, config);
};

export * from '../decorators';
Expand Down
4 changes: 3 additions & 1 deletion src/repositories/__tests__/email-repository.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import { EmailRepository } from '../email-repository';

describe('email', () => {
const mockHttp = new HttpMockImpl();
const email = new EmailRepository(mockHttp, '999.0.0');
const email = new EmailRepository(mockHttp, '999.0.0', {
url: 'https://example.com',
});

test('create confirmations', () => {
email.createConfirmation({ email: 'foo@example.com' });
Expand Down
15 changes: 11 additions & 4 deletions src/repositories/__tests__/status-repository.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import { httpDelete, httpGet, HttpMockImpl, httpPost } from '../../http/http-mock-impl';
import {
httpDelete,
httpGet,
HttpMockImpl,
httpPost,
} from '../../http/http-mock-impl';
import { StatusRepository } from '../status-repository';

describe('status', () => {
const mockHttp = new HttpMockImpl();
const status = new StatusRepository(mockHttp, '999.0.0');
const status = new StatusRepository(mockHttp, '999.0.0', {
url: 'https://example.com',
});

beforeEach(() => {
mockHttp.clear();
Expand All @@ -22,7 +29,7 @@ describe('status', () => {
expect(httpPost.mock.calls[0][1]).toStrictEqual({
status: 'hello',
});
})
});

test('type checks', () => {
status.create({
Expand All @@ -33,7 +40,7 @@ describe('status', () => {

// Status can be null when mediaIds provided
status.create({
mediaIds: ['123123']
mediaIds: ['123123'],
});

// @ts-expect-error: Status cannot be null when mediaIds are not provided
Expand Down
Loading

0 comments on commit 782fa60

Please sign in to comment.