Skip to content

Commit

Permalink
feat: Add allowedMentions to v1.status.create
Browse files Browse the repository at this point in the history
  • Loading branch information
neet committed Aug 10, 2023
1 parent a531d3c commit f307eff
Show file tree
Hide file tree
Showing 8 changed files with 139 additions and 37 deletions.
12 changes: 9 additions & 3 deletions src/adapters/action/dispatcher-http.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,12 @@ describe("DispatcherHttp", () => {
httpPost.mockResolvedValueOnce({ id: "1" });

httpGet
.mockRejectedValueOnce(new MastoHttpError(404, "Not Found"))
.mockRejectedValueOnce(new MastoHttpError(404, "Not Found"))
.mockRejectedValueOnce(
new MastoHttpError({ statusCode: 404, message: "Not Found" }),
)
.mockRejectedValueOnce(
new MastoHttpError({ statusCode: 404, message: "Not Found" }),
)
.mockResolvedValueOnce({ id: "1", url: "https://example.com" });

const media = await dispatcher.dispatch({
Expand All @@ -36,7 +40,9 @@ describe("DispatcherHttp", () => {
});

httpPost.mockResolvedValueOnce({ id: "1" });
httpGet.mockRejectedValue(new MastoHttpError(404, "Not Found"));
httpGet.mockRejectedValue(
new MastoHttpError({ statusCode: 404, message: "Not Found" }),
);

const promise = dispatcher.dispatch({
type: "create",
Expand Down
41 changes: 32 additions & 9 deletions src/adapters/errors/masto-http-error.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,34 @@ import { MastoHttpError } from "./masto-http-error";

describe("MastoHttpError", () => {
it("creates MastoHttpError", () => {
const error = new MastoHttpError(400, "message");
const error = new MastoHttpError({ statusCode: 400, message: "message" });
expect(error.message).toEqual("message");
expect(error.statusCode).toEqual(400);
});

it("creates MastoHttpError with description", () => {
const error = new MastoHttpError(400, "message", "description");
const error = new MastoHttpError({
statusCode: 400,
message: "message",
description: "description",
});
expect(error.message).toEqual("message");
expect(error.statusCode).toEqual(400);
expect(error.description).toEqual("description");
});

it("creates MastoHttpError with details", () => {
const error = new MastoHttpError(400, "message", undefined, {
error: [
{
error: "ERR_INVALID",
description: "Invalid value",
},
],
const error = new MastoHttpError({
statusCode: 400,
message: "message",
details: {
error: [
{
error: "ERR_INVALID",
description: "Invalid value",
},
],
},
});
expect(error.message).toEqual("message");
expect(error.statusCode).toEqual(400);
Expand All @@ -34,4 +42,19 @@ describe("MastoHttpError", () => {
],
});
});

it("creates MastoHttpError with additionalProperties", () => {
const error = new MastoHttpError({
statusCode: 400,
message: "message",
additionalProperties: {
foo: "bar",
},
});
expect(error.message).toEqual("message");
expect(error.statusCode).toEqual(400);
expect(error.additionalProperties).toEqual({
foo: "bar",
});
});
});
28 changes: 16 additions & 12 deletions src/adapters/errors/masto-http-error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,22 +23,26 @@ export type MastoHttpErrorDetails = Record<
readonly MastoHttpErrorDetail[]
>;

export interface MastoHttpErrorProps {
readonly statusCode: number;
readonly message: string;
readonly description?: string;
readonly details?: MastoHttpErrorDetails;
readonly additionalProperties?: Record<string, unknown>;
}

export class MastoHttpError extends CustomError {
readonly statusCode: number;
readonly description?: string;
readonly details?: MastoHttpErrorDetails;
readonly additionalProperties?: Record<string, unknown>;

constructor(
statusCode: number,
message: string,
description?: string,
details?: MastoHttpErrorDetails,
options?: ErrorOptions,
) {
super(message, options);
this.statusCode = statusCode;
this.message = message;
this.description = description;
this.details = details;
constructor(props: MastoHttpErrorProps, errorOptions?: ErrorOptions) {
super(props.message, errorOptions);
this.statusCode = props.statusCode;
this.message = props.message;
this.description = props.description;
this.additionalProperties = props.additionalProperties;
this.details = props.details;
}
}
30 changes: 29 additions & 1 deletion src/adapters/http/http-native-impl.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import node_http from "node:http";
import getPort from "get-port";

import { HttpConfigImpl } from "../config";
import { MastoTimeoutError } from "../errors";
import { type MastoHttpError, MastoTimeoutError } from "../errors";
import { SerializerNativeImpl } from "../serializers";
import { HttpNativeImpl } from "./http-native-impl";

Expand Down Expand Up @@ -71,4 +71,32 @@ describe("HttpNativeImpl", () => {

server.close();
});

it("collects any additional properties if provided", async () => {
const server = node_http.createServer((_, res) => {
res.writeHead(400, { "Content-Type": "application/json" });
res.end(JSON.stringify({ error: "unknown error", foo: "bar" }));
});

const port = await getPort();
server.listen(port);

const serializer = new SerializerNativeImpl();
const http = new HttpNativeImpl(
serializer,
new HttpConfigImpl({ url: `http://localhost:${port}` }, serializer),
);

const error = await http.get("/").then(
() => {
throw new Error("should not happen");
},
(error) => error as MastoHttpError,
);

expect(error.message).toEqual("unknown error");
expect((error.additionalProperties as any).foo).toEqual("bar");

server.close();
});
});
17 changes: 13 additions & 4 deletions src/adapters/http/http-native-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,21 @@ export class HttpNativeImpl extends BaseHttp implements Http {
}

const data = this.serializer.deserialize(encoding, await error.text());
const {
error: message,
errorDescription,
details,
...additionalProperties
} = data;

return new MastoHttpError(
error.status,
data.error as string,
data.errorDescription as string,
data.details as MastoHttpErrorDetails,
{
statusCode: error.status,
message: message as string,
description: errorDescription as string,
details: details as MastoHttpErrorDetails,
additionalProperties,
},
{ cause: error },
);
}
Expand Down
2 changes: 2 additions & 0 deletions src/mastodon/rest/v1/status-repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ export interface CreateStatusParamsBase {
readonly visibility?: StatusVisibility | null;
/** ISO 639 language code for this status. */
readonly language?: string | null;
/** https://github.com/mastodon/mastodon/pull/18350 */
readonly allowedMentions?: readonly string[] | null;
}

export interface CreateStatusPollParam {
Expand Down
17 changes: 9 additions & 8 deletions tests/rest/v1/accounts.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,20 @@ describe("account", () => {

it("throws an error if registration is malformed", () => {
return sessions.use(async (session) => {
let error: unknown;

try {
await session.rest.v1.accounts.create({
const error = await session.rest.v1.accounts
.create({
username: "",
email: "",
password: "",
agreement: false,
locale: "hello",
});
} catch (error_) {
error = error_ as Error;
}
})
.then(
() => {
throw new Error("should not be called");
},
(error) => error,
);

assert(error instanceof MastoHttpError);
expect(error.statusCode).toBe(422);
Expand Down
29 changes: 29 additions & 0 deletions tests/rest/v1/statuses.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import crypto from "node:crypto";

import { MastoHttpError } from "../../../src";

describe("status", () => {
it("creates, updates, and removes a status", () => {
return sessions.use(async (client) => {
Expand Down Expand Up @@ -210,4 +212,31 @@ describe("status", () => {
await client.rest.v1.statuses.$select(status.id).remove();
});
});

it("only mentions listed users when allowedMentions is specified", () => {
return sessions.use(2, async ([alice, bob]) => {
const error = await alice.rest.v1.statuses
.create({
status: `@${bob.acct} hello`,
allowedMentions: [],
})
.then(
() => {
throw new Error("Unexpected success");
},
(_error) => {
if (_error instanceof MastoHttpError) return _error;
throw _error;
},
);

expect(error.additionalProperties).toEqual({
unexpectedAccounts: [
expect.objectContaining({
id: bob.id,
}),
],
});
});
});
});

0 comments on commit f307eff

Please sign in to comment.