xref: /haiku/src/apps/haikudepot/server/PopulatePkgUserRatingsFromServerProcess.cpp (revision 9a6a20d4689307142a7ed26a1437ba47e244e73f)
1 /*
2  * Copyright 2016-2024, Andrew Lindesay <apl@lindesay.co.nz>.
3  * Copyright 2013-2014, Stephan Aßmus <superstippi@gmx.de>.
4  * All rights reserved. Distributed under the terms of the MIT License.
5  *
6  * Note that this file included code earlier from `Model.cpp` and
7  * copyrights have been latterly been carried across in 2024.
8  */
9 
10 
11 #include "PopulatePkgUserRatingsFromServerProcess.h"
12 
13 #include <Autolock.h>
14 #include <Catalog.h>
15 
16 #include "Logger.h"
17 #include "ServerHelper.h"
18 
19 
20 #undef B_TRANSLATION_CONTEXT
21 #define B_TRANSLATION_CONTEXT "PopulatePkgUserRatingsFromServerProcess"
22 
23 
24 PopulatePkgUserRatingsFromServerProcess::PopulatePkgUserRatingsFromServerProcess(
25 	PackageInfoRef packageInfo, Model* model)
26 	:
27 	fModel(model),
28 	fPackageInfo(packageInfo)
29 {
30 }
31 
32 
33 PopulatePkgUserRatingsFromServerProcess::~PopulatePkgUserRatingsFromServerProcess()
34 {
35 }
36 
37 
38 const char*
39 PopulatePkgUserRatingsFromServerProcess::Name() const
40 {
41 	return "PopulatePkgUserRatingsFromServerProcess";
42 }
43 
44 
45 const char*
46 PopulatePkgUserRatingsFromServerProcess::Description() const
47 {
48 	return B_TRANSLATE("Fetching user ratings for package");
49 }
50 
51 
52 status_t
53 PopulatePkgUserRatingsFromServerProcess::RunInternal()
54 {
55 	// TODO; use API spec to code generation techniques instead of this manually written client.
56 
57 	// Retrieve info from web-app
58 	BMessage info;
59 
60 	BString packageName;
61 	BString webAppRepositoryCode;
62 	BString webAppRepositorySourceCode;
63 
64 	{
65 		BAutolock locker(&fLock);
66 		packageName = fPackageInfo->Name();
67 		const DepotInfo* depot = fModel->DepotForName(fPackageInfo->DepotName());
68 
69 		if (depot != NULL) {
70 			webAppRepositoryCode = depot->WebAppRepositoryCode();
71 			webAppRepositorySourceCode = depot->WebAppRepositorySourceCode();
72 		}
73 	}
74 
75 	status_t status = fModel->GetWebAppInterface()->RetrieveUserRatingsForPackageForDisplay(
76 			packageName, webAppRepositoryCode, webAppRepositorySourceCode, 0,
77 			PACKAGE_INFO_MAX_USER_RATINGS, info);
78 
79 	if (status == B_OK) {
80 		// Parse message
81 		BMessage result;
82 		BMessage items;
83 
84 		status = info.FindMessage("result", &result);
85 
86 		if (status == B_OK) {
87 
88 			if (result.FindMessage("items", &items) == B_OK) {
89 
90 				// TODO; later make the PackageInfo immutable to avoid the need for locking here.
91 
92 				BAutolock locker(&fLock);
93 				fPackageInfo->ClearUserRatings();
94 
95 				int32 index = 0;
96 				while (true) {
97 					BString name;
98 					name << index++;
99 
100 					BMessage item;
101 					if (items.FindMessage(name, &item) != B_OK)
102 						break;
103 
104 					BString code;
105 					if (item.FindString("code", &code) != B_OK) {
106 						HDERROR("corrupt user rating at index %" B_PRIi32, index);
107 						continue;
108 					}
109 
110 					BString user;
111 					BMessage userInfo;
112 					if (item.FindMessage("user", &userInfo) != B_OK
113 							|| userInfo.FindString("nickname", &user) != B_OK) {
114 						HDERROR("ignored user rating [%s] without a user nickname", code.String());
115 						continue;
116 					}
117 
118 					// Extract basic info, all items are optional
119 					BString languageCode;
120 					BString comment;
121 					double rating;
122 					item.FindString("naturalLanguageCode", &languageCode);
123 					item.FindString("comment", &comment);
124 					if (item.FindDouble("rating", &rating) != B_OK)
125 						rating = -1;
126 					if (comment.Length() == 0 && rating == -1) {
127 						HDERROR("rating [%s] has no comment or rating so will"
128 								" be ignored",
129 							code.String());
130 						continue;
131 					}
132 
133 					// For which version of the package was the rating?
134 					BString major = "?";
135 					BString minor = "?";
136 					BString micro = "";
137 					double revision = -1;
138 					BString architectureCode = "";
139 					BMessage version;
140 					if (item.FindMessage("pkgVersion", &version) == B_OK) {
141 						version.FindString("major", &major);
142 						version.FindString("minor", &minor);
143 						version.FindString("micro", &micro);
144 						version.FindDouble("revision", &revision);
145 						version.FindString("architectureCode", &architectureCode);
146 					}
147 					BString versionString = major;
148 					versionString << ".";
149 					versionString << minor;
150 					if (!micro.IsEmpty()) {
151 						versionString << ".";
152 						versionString << micro;
153 					}
154 					if (revision > 0) {
155 						versionString << "-";
156 						versionString << (int)revision;
157 					}
158 
159 					if (!architectureCode.IsEmpty()) {
160 						versionString << " " << STR_MDASH << " ";
161 						versionString << architectureCode;
162 					}
163 
164 					double createTimestamp;
165 					item.FindDouble("createTimestamp", &createTimestamp);
166 
167 					// Add the rating to the PackageInfo
168 					UserRatingRef userRating(
169 						new UserRating(UserInfo(user), rating, comment, languageCode,
170 							// note that language identifiers are "code" in HDS and "id" in Haiku
171 							versionString, (uint64) createTimestamp),
172 						true);
173 					fPackageInfo->AddUserRating(userRating);
174 					HDDEBUG("rating [%s] retrieved from server", code.String());
175 				}
176 
177 				fPackageInfo->SetDidPopulateUserRatings();
178 
179 				HDDEBUG("did retrieve %" B_PRIi32 " user ratings for [%s]", index - 1,
180 					packageName.String());
181 			}
182 		} else {
183 			int32 errorCode = WebAppInterface::ErrorCodeFromResponse(info);
184 
185 			if (errorCode != ERROR_CODE_NONE)
186 				ServerHelper::NotifyServerJsonRpcError(info);
187 		}
188 	} else {
189 		ServerHelper::NotifyTransportError(status);
190 	}
191 
192 	return status;
193 }
194