Skip to content

Commit 5d6bc7a

Browse files
feat: Add style rule CRUD endpoints
1 parent 2f036d8 commit 5d6bc7a

5 files changed

Lines changed: 489 additions & 18 deletions

File tree

README.md

Lines changed: 77 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -500,36 +500,98 @@ Style rules allow you to customize your translations using a managed, shared lis
500500
of rules for style, formatting, and more. Multiple style rules can be stored with
501501
your account, each with a user-specified name and a uniquely-assigned ID.
502502

503-
#### Creating and managing style rules
503+
#### Creating a style rule
504504

505-
Currently style rules must be created and managed in the DeepL UI via
506-
https://www.deepl.com/en/custom-rules. Full CRUD functionality via the APIs will
507-
come shortly.
505+
Use `createStyleRule()` to create a new style rule with a name and language
506+
code. You can optionally provide `configuredRules` and `customInstructions`.
508507

509-
#### Listing all style rules
508+
```javascript
509+
const rule = await deeplClient.createStyleRule('My Style Rule', 'en');
510+
console.log(`Created: ${rule.name} (${rule.styleId})`);
511+
```
512+
513+
#### Retrieving and listing style rules
514+
515+
Use `getStyleRule()` to retrieve a single style rule by ID, or
516+
`getAllStyleRules()` to list all style rules.
510517

511518
`getAllStyleRules()` returns a list of `StyleRuleInfo` objects
512519
corresponding to all of your stored style rules. The method accepts optional
513520
parameters: `page` (page number for pagination, 0-indexed), `pageSize` (number
514-
of items per page), and `detailed` (whether to include detailed configuration
515-
rules in the `configuredRules` property).
521+
of items per page), and `detailed`. When `true`, the response includes
522+
`configuredRules` and `customInstructions` for each style rule. When `false`
523+
(default), these fields are omitted for faster responses.
516524

517525
```javascript
518-
// Get all style rules
526+
// Get a single style rule by ID
527+
const rule = await deeplClient.getStyleRule('YOUR_STYLE_ID');
528+
console.log(`${rule.name} (${rule.language})`);
529+
530+
// List all style rules
519531
const styleRules = await deeplClient.getAllStyleRules();
520-
for (const rule of styleRules) {
521-
console.log(`${rule.name} (${rule.styleId})`);
532+
for (const r of styleRules) {
533+
console.log(`${r.name} (${r.styleId})`);
522534
}
523535

524-
// Get style rules with detailed configuration
525-
const styleRules = await deeplClient.getAllStyleRules({ detailed: true });
526-
for (const rule of styleRules) {
527-
if (rule.configuredRules) {
528-
console.log(` Number formatting: ${rule.configuredRules.numbers}`);
536+
// List with detailed configuration
537+
const detailed = await deeplClient.getAllStyleRules(undefined, undefined, true);
538+
for (const r of detailed) {
539+
if (r.configuredRules) {
540+
console.log(` Number formatting: ${JSON.stringify(r.configuredRules.numbers)}`);
529541
}
530542
}
531543
```
532544

545+
#### Updating a style rule
546+
547+
Use `updateStyleRuleName()` to rename a style rule, and
548+
`updateStyleRuleConfiguredRules()` to update its configured rules.
549+
550+
```javascript
551+
// Update the name
552+
const updated = await deeplClient.updateStyleRuleName('YOUR_STYLE_ID', 'New Name');
553+
554+
// Update configured rules
555+
const updatedRules = await deeplClient.updateStyleRuleConfiguredRules(
556+
'YOUR_STYLE_ID',
557+
{ style_and_tone: { formality: 'formal' } },
558+
);
559+
```
560+
561+
#### Managing custom instructions
562+
563+
Custom instructions allow you to add free-text prompts to a style rule. Use
564+
`createStyleRuleCustomInstruction()`, `getStyleRuleCustomInstruction()`,
565+
`updateStyleRuleCustomInstruction()`, and
566+
`deleteStyleRuleCustomInstruction()` to manage them.
567+
568+
```javascript
569+
// Create a custom instruction
570+
const instruction = await deeplClient.createStyleRuleCustomInstruction(
571+
'YOUR_STYLE_ID', 'Formal tone', 'Always use formal language');
572+
console.log(`Created instruction: ${instruction.id}`);
573+
574+
// Get a custom instruction
575+
const fetched = await deeplClient.getStyleRuleCustomInstruction(
576+
'YOUR_STYLE_ID', instruction.id);
577+
578+
// Update a custom instruction
579+
const updated = await deeplClient.updateStyleRuleCustomInstruction(
580+
'YOUR_STYLE_ID', instruction.id, 'Updated label', 'Use very formal language');
581+
582+
// Delete a custom instruction
583+
await deeplClient.deleteStyleRuleCustomInstruction(
584+
'YOUR_STYLE_ID', instruction.id);
585+
```
586+
587+
#### Deleting a style rule
588+
589+
Use `deleteStyleRule()` to delete a style rule by ID.
590+
591+
```javascript
592+
await deeplClient.deleteStyleRule('YOUR_STYLE_ID');
593+
```
594+
533595
### Checking account usage
534596

535597
To check account usage, use the `getUsage()` function.

src/deeplClient.ts

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ import {
1414
GlossaryId,
1515
ListMultilingualGlossaryApiResponse,
1616
StyleRuleInfo,
17+
StyleRuleInfoApiResponse,
18+
StyleId,
19+
CustomInstruction,
1720
} from './types';
1821
import {
1922
parseMultilingualGlossaryDictionaryInfo,
@@ -22,6 +25,8 @@ import {
2225
parseMultilingualGlossaryDictionaryEntries,
2326
parseListMultilingualGlossaries,
2427
parseStyleRuleInfoList,
28+
parseStyleRuleInfo,
29+
parseCustomInstruction,
2530
} from './parsing';
2631
import {
2732
appendCsvDictionaryEntries,
@@ -30,6 +35,18 @@ import {
3035
extractGlossaryId,
3136
} from './utils';
3237
import { ArgumentError, GlossaryNotFoundError } from './errors';
38+
export type CustomInstructionRequestBody = {
39+
label: string;
40+
prompt: string;
41+
source_language?: string;
42+
};
43+
44+
export type CreateStyleRuleRequestBody = {
45+
name: string;
46+
language: string;
47+
configured_rules?: Record<string, Record<string, string>>;
48+
custom_instructions?: CustomInstructionRequestBody[];
49+
};
3350

3451
export enum WritingStyle {
3552
ACADEMIC = 'academic',
@@ -580,4 +597,238 @@ export class DeepLClient extends Translator {
580597
await checkStatusCode(statusCode, content);
581598
return parseStyleRuleInfoList(content);
582599
}
600+
601+
/**
602+
* Creates a new style rule.
603+
*
604+
* @param styleRule: The style rule parameters including name, language, and optional configured_rules and custom_instructions.
605+
* @returns {Promise<StyleRuleInfo>} The created style rule info.
606+
*
607+
* @throws {DeepLError} If any error occurs while communicating with the DeepL API.
608+
*/
609+
async createStyleRule(styleRule: CreateStyleRuleRequestBody): Promise<StyleRuleInfo> {
610+
if (!styleRule.name) {
611+
throw new ArgumentError('Parameter "name" must not be empty');
612+
}
613+
if (!styleRule.language) {
614+
throw new ArgumentError('Parameter "language" must not be empty');
615+
}
616+
const { statusCode, content } = await this.httpClient.sendRequestWithBackoff<string>(
617+
'POST',
618+
'/v3/style_rules',
619+
{ jsonBody: styleRule },
620+
);
621+
await checkStatusCode(statusCode, content);
622+
return parseStyleRuleInfo(JSON.parse(content) as StyleRuleInfoApiResponse);
623+
}
624+
625+
/**
626+
* Retrieves a single style rule by ID.
627+
*
628+
* @param styleId: The ID of the style rule to retrieve.
629+
* @returns {Promise<StyleRuleInfo>} The style rule info.
630+
*
631+
* @throws {DeepLError} If any error occurs while communicating with the DeepL API.
632+
*/
633+
async getStyleRule(styleId: StyleId): Promise<StyleRuleInfo> {
634+
if (!styleId) {
635+
throw new ArgumentError('Parameter "styleId" must not be empty');
636+
}
637+
const { statusCode, content } = await this.httpClient.sendRequestWithBackoff<string>(
638+
'GET',
639+
`/v3/style_rules/${encodeURIComponent(styleId)}`,
640+
);
641+
await checkStatusCode(statusCode, content);
642+
return parseStyleRuleInfo(JSON.parse(content) as StyleRuleInfoApiResponse);
643+
}
644+
645+
/**
646+
* Updates the name of a style rule.
647+
*
648+
* @param styleId: The ID of the style rule to update.
649+
* @param name: The new name for the style rule.
650+
* @returns {Promise<StyleRuleInfo>} The updated style rule info.
651+
*
652+
* @throws {DeepLError} If any error occurs while communicating with the DeepL API.
653+
*/
654+
async updateStyleRuleName(styleId: StyleId, name: string): Promise<StyleRuleInfo> {
655+
if (!styleId) {
656+
throw new ArgumentError('Parameter "styleId" must not be empty');
657+
}
658+
if (!name) {
659+
throw new ArgumentError('Parameter "name" must not be empty');
660+
}
661+
const { statusCode, content } = await this.httpClient.sendRequestWithBackoff<string>(
662+
'PATCH',
663+
`/v3/style_rules/${encodeURIComponent(styleId)}`,
664+
{ jsonBody: { name } },
665+
);
666+
await checkStatusCode(statusCode, content);
667+
return parseStyleRuleInfo(JSON.parse(content) as StyleRuleInfoApiResponse);
668+
}
669+
670+
/**
671+
* Deletes a style rule.
672+
*
673+
* @param styleId: The ID of the style rule to delete.
674+
*
675+
* @throws {DeepLError} If any error occurs while communicating with the DeepL API.
676+
*/
677+
async deleteStyleRule(styleId: StyleId): Promise<void> {
678+
if (!styleId) {
679+
throw new ArgumentError('Parameter "styleId" must not be empty');
680+
}
681+
const { statusCode, content } = await this.httpClient.sendRequestWithBackoff<string>(
682+
'DELETE',
683+
`/v3/style_rules/${encodeURIComponent(styleId)}`,
684+
);
685+
await checkStatusCode(statusCode, content);
686+
}
687+
688+
/**
689+
* Updates the configured rules of a style rule.
690+
*
691+
* @param styleId: The ID of the style rule to update.
692+
* @param configuredRules: The new configured rules mapping.
693+
* @returns {Promise<StyleRuleInfo>} The updated style rule info.
694+
*
695+
* @throws {DeepLError} If any error occurs while communicating with the DeepL API.
696+
*/
697+
async updateStyleRuleConfiguredRules(
698+
styleId: StyleId,
699+
configuredRules: Record<string, Record<string, string>>,
700+
): Promise<StyleRuleInfo> {
701+
if (!styleId) {
702+
throw new ArgumentError('Parameter "styleId" must not be empty');
703+
}
704+
const { statusCode, content } = await this.httpClient.sendRequestWithBackoff<string>(
705+
'PUT',
706+
`/v3/style_rules/${encodeURIComponent(styleId)}/configured_rules`,
707+
{ jsonBody: configuredRules },
708+
);
709+
await checkStatusCode(statusCode, content);
710+
return parseStyleRuleInfo(JSON.parse(content) as StyleRuleInfoApiResponse);
711+
}
712+
713+
/**
714+
* Creates a custom instruction for a style rule.
715+
*
716+
* @param styleId: The ID of the style rule.
717+
* @param instruction: The custom instruction parameters including label, prompt, and optional source_language.
718+
* @returns {Promise<CustomInstruction>} The created custom instruction.
719+
*
720+
* @throws {DeepLError} If any error occurs while communicating with the DeepL API.
721+
*/
722+
async createStyleRuleCustomInstruction(
723+
styleId: StyleId,
724+
instruction: CustomInstructionRequestBody,
725+
): Promise<CustomInstruction> {
726+
if (!styleId) {
727+
throw new ArgumentError('Parameter "styleId" must not be empty');
728+
}
729+
if (!instruction.label) {
730+
throw new ArgumentError('Parameter "label" must not be empty');
731+
}
732+
if (!instruction.prompt) {
733+
throw new ArgumentError('Parameter "prompt" must not be empty');
734+
}
735+
const { statusCode, content } = await this.httpClient.sendRequestWithBackoff<string>(
736+
'POST',
737+
`/v3/style_rules/${encodeURIComponent(styleId)}/custom_instructions`,
738+
{ jsonBody: instruction },
739+
);
740+
await checkStatusCode(statusCode, content);
741+
return parseCustomInstruction(JSON.parse(content));
742+
}
743+
744+
/**
745+
* Retrieves a custom instruction for a style rule.
746+
*
747+
* @param styleId: The ID of the style rule.
748+
* @param instructionId: The ID of the custom instruction.
749+
* @returns {Promise<CustomInstruction>} The custom instruction.
750+
*
751+
* @throws {DeepLError} If any error occurs while communicating with the DeepL API.
752+
*/
753+
async getStyleRuleCustomInstruction(
754+
styleId: StyleId,
755+
instructionId: string,
756+
): Promise<CustomInstruction> {
757+
if (!styleId) {
758+
throw new ArgumentError('Parameter "styleId" must not be empty');
759+
}
760+
if (!instructionId) {
761+
throw new ArgumentError('Parameter "instructionId" must not be empty');
762+
}
763+
const { statusCode, content } = await this.httpClient.sendRequestWithBackoff<string>(
764+
'GET',
765+
`/v3/style_rules/${encodeURIComponent(
766+
styleId,
767+
)}/custom_instructions/${encodeURIComponent(instructionId)}`,
768+
);
769+
await checkStatusCode(statusCode, content);
770+
return parseCustomInstruction(JSON.parse(content));
771+
}
772+
773+
/**
774+
* Updates a custom instruction for a style rule.
775+
*
776+
* @param styleId: The ID of the style rule.
777+
* @param instructionId: The ID of the custom instruction to update.
778+
* @param instruction: The custom instruction parameters including label, prompt, and optional source_language.
779+
* @returns {Promise<CustomInstruction>} The updated custom instruction.
780+
*
781+
* @throws {DeepLError} If any error occurs while communicating with the DeepL API.
782+
*/
783+
async updateStyleRuleCustomInstruction(
784+
styleId: StyleId,
785+
instructionId: string,
786+
instruction: CustomInstructionRequestBody,
787+
): Promise<CustomInstruction> {
788+
if (!styleId) {
789+
throw new ArgumentError('Parameter "styleId" must not be empty');
790+
}
791+
if (!instructionId) {
792+
throw new ArgumentError('Parameter "instructionId" must not be empty');
793+
}
794+
if (!instruction.label) {
795+
throw new ArgumentError('Parameter "label" must not be empty');
796+
}
797+
if (!instruction.prompt) {
798+
throw new ArgumentError('Parameter "prompt" must not be empty');
799+
}
800+
const { statusCode, content } = await this.httpClient.sendRequestWithBackoff<string>(
801+
'PUT',
802+
`/v3/style_rules/${encodeURIComponent(
803+
styleId,
804+
)}/custom_instructions/${encodeURIComponent(instructionId)}`,
805+
{ jsonBody: instruction },
806+
);
807+
await checkStatusCode(statusCode, content);
808+
return parseCustomInstruction(JSON.parse(content));
809+
}
810+
811+
/**
812+
* Deletes a custom instruction from a style rule.
813+
*
814+
* @param styleId: The ID of the style rule.
815+
* @param instructionId: The ID of the custom instruction to delete.
816+
*
817+
* @throws {DeepLError} If any error occurs while communicating with the DeepL API.
818+
*/
819+
async deleteStyleRuleCustomInstruction(styleId: StyleId, instructionId: string): Promise<void> {
820+
if (!styleId) {
821+
throw new ArgumentError('Parameter "styleId" must not be empty');
822+
}
823+
if (!instructionId) {
824+
throw new ArgumentError('Parameter "instructionId" must not be empty');
825+
}
826+
const { statusCode, content } = await this.httpClient.sendRequestWithBackoff<string>(
827+
'DELETE',
828+
`/v3/style_rules/${encodeURIComponent(
829+
styleId,
830+
)}/custom_instructions/${encodeURIComponent(instructionId)}`,
831+
);
832+
await checkStatusCode(statusCode, content);
833+
}
583834
}

0 commit comments

Comments
 (0)