1"""Queries related to source packages 

2 

3@contact: Debian FTPMaster <ftpmaster@debian.org> 

4@copyright: 2014 Mark Hymers <mhy@debian.org> 

5@copyright: 2014 Joerg Jaspert <joerg@debian.org> 

6@license: GNU General Public License version 2 or later 

7""" 

8 

9import json 

10from typing import Optional 

11 

12import bottle 

13from sqlalchemy import or_ 

14 

15from daklib.dbconn import ( 

16 DBConn, 

17 DBSource, 

18 DSCFile, 

19 MetadataKey, 

20 PoolFile, 

21 SourceMetadata, 

22 Suite, 

23) 

24from dakweb.webregister import QueryRegister 

25 

26 

27@bottle.route("/dsc_in_suite/<suite>/<source>") 

28def dsc_in_suite(suite: Optional[str] = None, source: Optional[str] = None) -> str: 

29 """ 

30 Find all dsc files for a given source package name in a given suite. 

31 

32 .. versionadded: December 2014 

33 

34 :param suite: Name of the suite. 

35 :param source: Source package to query for. 

36 :return: List of dictionaries made out of 

37 - version 

38 - component 

39 - filename 

40 - filesize 

41 - sha256sum 

42 

43 .. seealso:: :func:`~dakweb.queries.suite.suites` on how to receive a list of valid suites. 

44 """ 

45 if suite is None: 

46 return bottle.HTTPError(503, "Suite not specified.") 

47 if source is None: 

48 return bottle.HTTPError(503, "Source package not specified.") 

49 

50 s = DBConn().session() 

51 q = s.query(DSCFile).join(PoolFile) 

52 q = q.join(DBSource).join(Suite, DBSource.suites) 

53 q = q.filter(or_(Suite.suite_name == suite, Suite.codename == suite)) 

54 q = q.filter(DBSource.source == source) 

55 q = q.filter(PoolFile.filename.endswith(".dsc")) 

56 ret = [] 

57 for p in q: 

58 ret.append( 

59 { 

60 "version": p.source.version, 

61 "component": p.poolfile.component.component_name, 

62 "filename": p.poolfile.filename, 

63 "filesize": p.poolfile.filesize, 

64 "sha256sum": p.poolfile.sha256sum, 

65 } 

66 ) 

67 

68 s.close() 

69 

70 bottle.response.content_type = "application/json; charset=UTF-8" 

71 return json.dumps(ret) 

72 

73 

74QueryRegister().register_path("/dsc_in_suite", dsc_in_suite) 

75 

76 

77@bottle.route("/file_in_archive/<filepattern:path>") 

78def file_in_archive(filepattern: Optional[str] = None) -> str: 

79 """ 

80 Check if a file pattern is known to the archive. Note that the 

81 patterns are matched against the location of the files in the 

82 pool, so for %tmux_2.3-1.dsc it will return t/tmux/tmux_2.3-1.dsc 

83 as filename. 

84 

85 .. versionadded:: October 2016 

86 

87 :param filepattern: Pattern of the filenames to match. SQL LIKE 

88 statement wildcard matches are supported, that 

89 is % for zero, one or more characters, _ for a 

90 single character match. 

91 :return: List of dictionaries made out of 

92 - filename 

93 - sha256sum 

94 - component 

95 """ 

96 if filepattern is None: 

97 return bottle.HTTPError(503, "Filepattern not specified.") 

98 

99 s = DBConn().session() 

100 q = s.query(PoolFile) 

101 q = q.filter(PoolFile.filename.like(filepattern)) 

102 ret = [] 

103 

104 for p in q: 

105 ret.append( 

106 { 

107 "filename": p.filename, 

108 "component": p.component.component_name, 

109 "sha256sum": p.sha256sum, 

110 } 

111 ) 

112 

113 s.close() 

114 

115 bottle.response.content_type = "application/json; charset=UTF-8" 

116 return json.dumps(ret) 

117 

118 

119QueryRegister().register_path("/file_in_archive", file_in_archive) 

120 

121 

122@bottle.route("/sha256sum_in_archive/<sha256sum>") 

123def sha256sum_in_archive(sha256sum: Optional[str] = None) -> str: 

124 """ 

125 Check if files with matching sha256sums are known to the archive. 

126 

127 .. versionadded:: June 2018 

128 

129 :param sha256sum: SHA256 sum of the file. 

130 :return: List of dictionaries made out of 

131 - filename 

132 - sha256sum 

133 - component 

134 """ 

135 if sha256sum is None: 

136 return bottle.HTTPError(503, "sha256sum not specified.") 

137 

138 s = DBConn().session() 

139 q = s.query(PoolFile) 

140 q = q.filter(PoolFile.sha256sum == sha256sum) 

141 ret = [] 

142 

143 for p in q: 

144 ret.append( 

145 { 

146 "filename": p.filename, 

147 "component": p.component.component_name, 

148 "sha256sum": p.sha256sum, 

149 } 

150 ) 

151 

152 s.close() 

153 

154 bottle.response.content_type = "application/json; charset=UTF-8" 

155 return json.dumps(ret) 

156 

157 

158QueryRegister().register_path("/sha256sum_in_archive", sha256sum_in_archive) 

159 

160 

161@bottle.route("/sources_in_suite/<suite>") 

162def sources_in_suite(suite: Optional[str] = None) -> str: 

163 """ 

164 Returns all source packages and their versions in a given suite. 

165 

166 .. versionadded:: December 2014 

167 

168 :param suite: Name of the suite. 

169 :return: List of dictionaries made out of 

170 - source 

171 - version 

172 

173 .. seealso:: :func:`~dakweb.queries.suite.suites` on how to receive a list of valid suites. 

174 """ 

175 if suite is None: 

176 return bottle.HTTPError(503, "Suite not specified.") 

177 

178 s = DBConn().session() 

179 q = s.query(DBSource).join(Suite, DBSource.suites) 

180 q = q.filter(or_(Suite.suite_name == suite, Suite.codename == suite)) 

181 ret = [] 

182 for p in q: 

183 ret.append({"source": p.source, "version": p.version}) 

184 

185 s.close() 

186 

187 bottle.response.content_type = "application/json; charset=UTF-8" 

188 return json.dumps(ret) 

189 

190 

191QueryRegister().register_path("/sources_in_suite", sources_in_suite) 

192 

193 

194@bottle.route("/all_sources") 

195def all_sources() -> str: 

196 """ 

197 Returns all source packages and their versions known to the archive 

198 (this includes NEW). 

199 

200 :return: List of dictionaries made out of 

201 - source 

202 - version 

203 """ 

204 

205 s = DBConn().session() 

206 q = s.query(DBSource) 

207 ret = [] 

208 for p in q: 

209 ret.append({"source": p.source, "version": p.version}) 

210 

211 s.close() 

212 

213 bottle.response.content_type = "application/json; charset=UTF-8" 

214 return json.dumps(ret) 

215 

216 

217QueryRegister().register_path("/all_sources", all_sources) 

218 

219 

220@bottle.route("/source/by_metadata/<key>") 

221def source_by_metadata(key: Optional[str] = None) -> str: 

222 """ 

223 

224 Finds all Debian source packages which have the specified metadata set. 

225 

226 E.g., to find out the Maintainer of all source packages, query 

227 /source/by_metadata/Maintainer. 

228 

229 :param key: Metadata key to search for. 

230 :return: A list of dictionaries of 

231 - source 

232 - metadata value 

233 """ 

234 

235 if not key: 

236 return bottle.HTTPError(503, "Metadata key not specified.") 

237 

238 s = DBConn().session() 

239 q = s.query(DBSource.source, SourceMetadata.value) 

240 q = q.join(SourceMetadata).join(MetadataKey) 

241 q = q.filter(MetadataKey.key == key) 

242 ret = [] 

243 for p in q: 

244 ret.append({"source": p.source, "metadata_value": p.value}) 

245 s.close() 

246 

247 bottle.response.content_type = "application/json; charset=UTF-8" 

248 return json.dumps(ret) 

249 

250 

251QueryRegister().register_path("/source/by_metadata", source_by_metadata)