public void Import_from_UserVoice() { string oauthKey = Environment.GetEnvironmentVariable("USERVOICE_KEY"); //required to get emails const int resultsPerPage = 20; var i = 0; List <object> suggestions = null; var client = new JsonServiceClient("http://localhost:16325") { BearerToken = Environment.GetEnvironmentVariable("TECHSTACKS_DEVELOPMENT_TOKEN") }; do { var url = $"https://servicestack.uservoice.com/api/v1/forums/176786/suggestions.json?per_page={resultsPerPage}&page={++i}&oauth_consumer_key={oauthKey}"; var json = url.GetJsonFromUrl(req => req.AddBearerToken(oauthKey)); var response = (Dictionary <string, object>)JSON.parse(json); suggestions = (List <object>)response["suggestions"]; var userVoiceUsers = new Dictionary <string, UserVoiceUser>(StringComparer.OrdinalIgnoreCase); var techstacksUsers = new Dictionary <string, UserRef>(StringComparer.OrdinalIgnoreCase); UserVoiceUser toUser(object oUser) { if (!(oUser is Dictionary <string, object> user)) { return(null); } if (!user.TryGetValue("email", out var oEmail)) { "EMAIL NOT FOUND:".Print(); user.PrintDump(); return(null); } var email = (string)oEmail; if (!userVoiceUsers.ContainsKey("user")) { return(new UserVoiceUser { Id = (int)user["id"], Name = (string)user["name"], Email = email, AvatarUrl = (string)user["avatar_url"], CreatedAt = user["created_at"].ToString().ConvertTo <DateTime>(), UpdatedAt = user["updated_at"].ToString().ConvertTo <DateTime>(), }); } return(null); } foreach (var oSuggestion in suggestions) { var request = new ImportUserVoiceSuggestion { OrganizationId = 1, }; try { if (oSuggestion is Dictionary <string, object> suggestion) { request.Url = (string)suggestion["url"]; request.Id = (int)suggestion["id"]; request.State = (string)suggestion["state"]; request.Title = (string)suggestion["title"]; //Can be null and just have title request.Text = suggestion["text"] as string; if (request.Text != null) { request.Text = JsonTypeSerializer.Unescape(request.Text); } request.FormattedText = suggestion["formatted_text"] as string; if (request.FormattedText != null) { request.FormattedText = JsonTypeSerializer.Unescape(request.FormattedText); } request.VoteCount = (int)suggestion["vote_count"]; request.CreatedAt = suggestion["created_at"].ToString().ConvertTo <DateTime>(); request.UpdatedAt = suggestion["updated_at"].ToString().ConvertTo <DateTime>(); if (suggestion.TryGetValue("category", out var category)) { request.Category = category as string; } if (suggestion.TryGetValue("topic", out var oTopic) && oTopic is Dictionary <string, object> topic) { request.TopicId = (int)topic["id"]; } if (suggestion.TryGetValue("creator", out var creator)) { request.Creator = toUser(creator); } if (suggestion.TryGetValue("closed_at", out var oClosedAt) && oClosedAt is string closeAt) { request.ClosedAt = oClosedAt.ConvertTo <DateTime>(); } if (suggestion.TryGetValue("status_changed_by", out var statusUser)) { request.StatusChangedBy = toUser(statusUser); } if (suggestion.TryGetValue("status", out var oStatus) && oStatus is Dictionary <string, object> status) { var statusKey = (string)status["key"]; if (statusKey != "published") { request.StatusKey = statusKey; request.StatusHexColor = (string)status["hex_color"]; } } if (suggestion.TryGetValue("response", out var statusResponse) && statusResponse is Dictionary <string, object> oStatusResponse) { request.Response = new UserVoiceComment { Text = (string)oStatusResponse["text"], FormattedText = (string)oStatusResponse["formatted_text"], CreatedAt = oStatusResponse["created_at"].ToString().ConvertTo <DateTime>(), }; if (request.Response.Text != null) { request.Response.Text = JsonTypeSerializer.Unescape(request.Response.Text); } if (request.Response.FormattedText != null) { request.Response.FormattedText = JsonTypeSerializer.Unescape(request.Response.FormattedText); } if (oStatusResponse.TryGetValue("creator", out var responseCreator)) { request.Response.Creator = toUser(responseCreator); } } $"Importing Suggestion {request.Id}: {request.Title}".Print(); var newPost = client.Post(request); $"Imported Post {newPost.PostId}: {newPost.PostSlug}".Print(); } } catch (Exception ex) { $"Could not import suggestion: {ex.Message}\n{ex.StackTrace}".Print(); oSuggestion.PrintDump(); } } } while (suggestions.Count >= resultsPerPage); }
public async Task <ImportUserVoiceSuggestionResponse> Any(ImportUserVoiceSuggestion request) { var user = GetUser(); AssertOrganizationModerator(Db, request.OrganizationId, user, out var org, out var orgMember); var creator = await GetOrCreateUser(request.Creator); var existingPost = await Db.SingleAsync <Post>(x => (request.Url != null && x.Url == request.Url) || (x.RefSource == UserVoiceSource && x.RefId == request.Id)); if (existingPost != null) { throw HttpError.Conflict($"Post already imported: /posts/{existingPost.Id}/{existingPost.Slug}"); } var categoryId = await GetOrCreateCategory(request.OrganizationId, request.Category); var now = DateTime.Now; var hidden = request.StatusKey == "declined" || request.StatusKey == "completed" ? now : (DateTime?)null; var statusBy = (await GetOrCreateUser(request.StatusChangedBy))?.UserName ?? user.UserName; var labels = new List <string>(); if (request.State != null && request.State != "closed") { labels.Add(request.State.GenerateSlug()); } if (request.StatusKey != null) { labels.Add(request.StatusKey); var orgLabelExists = await Db.ExistsAsync <OrganizationLabel>(x => x.OrganizationId == request.OrganizationId && x.Slug == request.StatusKey); if (!orgLabelExists) { await Db.InsertAsync(new OrganizationLabel { OrganizationId = request.OrganizationId, Slug = request.StatusKey, Description = $"UserVoice Suggestion", Color = request.StatusHexColor, }); } } Post newPost = null; using (var trans = Db.OpenTransaction()) { newPost = new Post { OrganizationId = request.OrganizationId, UserId = creator.Id, Type = PostType.Request, Url = request.Url, Title = request.Title, Slug = request.Slug ?? request.Title.GenerateSlug(), RefSource = "uservoice", RefId = request.Id, RefUrn = $"urn:uservoice:suggestion:{request.TopicId}:{request.Id}", Created = request.CreatedAt, CreatedBy = creator.UserName, Modified = request.UpdatedAt, ModifiedBy = creator.UserName, CategoryId = categoryId, PointsModifier = request.VoteCount, Hidden = hidden, HiddenBy = statusBy, Content = request.Text, ContentHtml = request.FormattedText, Labels = labels.Count > 0 ? labels.ToArray() : null, }; if (request.State == "closed") { newPost.Status = request.State; newPost.StatusBy = statusBy; newPost.StatusDate = request.StatusChangedBy?.UpdatedAt; } newPost.Id = await Db.InsertAsync(newPost, selectIdentity : true); if (request.Response != null) { var commentUser = await GetOrCreateUser(request.Response.Creator); var newComment = new PostComment { PostId = newPost.Id, Content = request.Response.Text, ContentHtml = request.Response.FormattedText, Created = request.Response.CreatedAt, CreatedBy = commentUser.UserName, Modified = request.Response.CreatedAt, RefSource = "uservoice", RefId = request.Id, RefUrn = $"urn:uservoice:response:{request.TopicId}:{request.Id}", }; newComment.Id = await Db.InsertAsync(newComment, selectIdentity : true); await Db.UpdateOnlyAsync(() => new Post { PinCommentId = newComment.Id }, where : x => x.Id == newPost.Id && x.OrganizationId == request.OrganizationId); } trans.Commit(); } return(new ImportUserVoiceSuggestionResponse { PostId = newPost.Id, PostSlug = newPost.Slug }); }