Coverage for src/lanraragi_api/api/stamp.py: 96%

24 statements  

« prev     ^ index     » next       coverage.py v7.16.0, created at 2026-09-22 23:20 +0000

1from typing import Any, cast 

2 

3from lanraragi_api.api.base import ( 

4 BaseAPICall, 

5) 

6from lanraragi_api.entity.base import OperationResponse 

7from lanraragi_api.entity.stamp import AddStampResponse, StampsData, StampsResponse 

8from lanraragi_api.error import APIResponseDecodeError 

9 

10 

11class StampAPI(BaseAPICall): 

12 """Stamps. 

13 

14 Shared request and error behavior is documented on ``BaseAPICall``. 

15 """ 

16 

17 def get_stamped_pages(self, archive_id: str) -> StampsResponse: 

18 """Get pages that contain at least one stamp in the archive. 

19 

20 Args: 

21 archive_id: ID of the archive. 

22 

23 Returns: 

24 StampsResponse: Page indices of the archive that contain a stamp. 

25 

26 Raises: 

27 APIHttpError: 400 if the server rejected the request. 

28 APIResponseDecodeError: If the response does not match 

29 ``StampsResponse``. 

30 """ 

31 return self.request_model( 

32 "GET", f"/api/archives/{archive_id}/stamps", StampsResponse 

33 ) 

34 

35 def get_stamps_by_page(self, archive_id: str, index: int) -> list[StampsData]: 

36 """Get the stamps linked to the page. 

37 

38 Args: 

39 archive_id: ID of the archive. 

40 index: Page of the archive. 

41 

42 Returns: 

43 list[StampsData]: Stamps of the page. 

44 

45 Raises: 

46 APIHttpError: 400 if the server rejected the request. 

47 APIResponseDecodeError: If the response has no ``result`` list, or 

48 if an item does not match ``StampsData``. 

49 """ 

50 path = f"/api/archives/{archive_id}/stamps/{index}" 

51 payload = self.request_json("GET", path) 

52 result = payload.get("result") 

53 if not isinstance(result, list): 

54 raise APIResponseDecodeError(self._to_url(path), "missing result list") 

55 return [ 

56 self.parse_model(StampsData, item, path) for item in cast(list[Any], result) 

57 ] 

58 

59 def add_stamp( 

60 self, 

61 archive_id: str, 

62 index: int, 

63 content: str | None = None, 

64 position: str | None = None, 

65 ) -> AddStampResponse: 

66 """Add a new Stamp to the page at the given coordinates. 

67 

68 Args: 

69 archive_id: ID of the archive. 

70 index: Page of the archive. 

71 content: Text of the stamp. Defaults to None. 

72 position: Position of the stamp in the page. Defaults to None. 

73 

74 Returns: 

75 AddStampResponse: Result of the operation, with the ``stamp_id`` of 

76 the created stamp. 

77 

78 Raises: 

79 APIResponseDecodeError: If the response body is not valid JSON, or 

80 does not match ``AddStampResponse``. 

81 APIOperationError: If the operation failed and raising is enabled. 

82 

83 Note: 

84 A 400 response is returned in the operation result, with ``success`` 

85 set to 0, instead of raising. 

86 """ 

87 return self.request_operation( 

88 "PUT", 

89 f"/api/archives/{archive_id}/stamps/{index}", 

90 model=AddStampResponse, 

91 params={"content": content, "position": position}, 

92 ) 

93 

94 def get_stamp(self, id: str) -> StampsData: 

95 """Get a stamp from an Archive. 

96 

97 Args: 

98 id: ID of the stamp. 

99 

100 Returns: 

101 StampsData: Stamp data. 

102 

103 Raises: 

104 APIHttpError: 400 if the server rejected the request; 423 if the 

105 Stamp is currently locked for modification. 

106 APIResponseDecodeError: If the response does not match 

107 ``StampsData``. 

108 """ 

109 path = f"/api/stamps/{id}" 

110 return self.request_model("GET", path, StampsData) 

111 

112 def update_stamp( 

113 self, 

114 id: str, 

115 content: str | None = None, 

116 position: str | None = None, 

117 ) -> OperationResponse: 

118 """Update a stamp from an Archive. 

119 

120 Args: 

121 id: ID of the stamp. 

122 content: Text of the stamp. Defaults to None. 

123 position: Position of the stamp in the page. Defaults to None. 

124 

125 Returns: 

126 OperationResponse: Result of the operation. 

127 

128 Raises: 

129 APIResponseDecodeError: If the response body is not valid JSON, or 

130 does not match ``OperationResponse``. 

131 APIOperationError: If the operation failed and raising is enabled. 

132 

133 Note: 

134 Failure responses such as 400 (error response) or 423 (Stamp locked 

135 for modification) are returned in the operation result, with 

136 ``success`` set to 0, instead of raising. 

137 """ 

138 return self.request_operation( 

139 "PUT", 

140 f"/api/stamps/{id}", 

141 params={"content": content, "position": position}, 

142 ) 

143 

144 def delete_stamp(self, id: str) -> OperationResponse: 

145 """Remove a stamp from an Archive. 

146 

147 Args: 

148 id: ID of the stamp. 

149 

150 Returns: 

151 OperationResponse: Result of the operation. 

152 

153 Raises: 

154 APIResponseDecodeError: If the response body is not valid JSON, or 

155 does not match ``OperationResponse``. 

156 APIOperationError: If the operation failed and raising is enabled. 

157 

158 Note: 

159 Failure responses such as 400 (error response) or 423 (Stamp locked 

160 for modification) are returned in the operation result, with 

161 ``success`` set to 0, instead of raising. 

162 """ 

163 return self.request_operation("DELETE", f"/api/stamps/{id}")