Skills now saved / returned
Edging closer to #2 completion; need to finish styles, create display page, and put some interesting stuff on the dashboard
This commit is contained in:
@@ -43,25 +43,31 @@ namespace JobsJobsJobs.Server.Areas.Api.Controllers
|
||||
_clock = clock;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The current citizen ID
|
||||
/// </summary>
|
||||
private CitizenId CurrentCitizenId => CitizenId.Parse(User.FindFirst(ClaimTypes.NameIdentifier)!.Value);
|
||||
|
||||
[HttpGet("")]
|
||||
public async Task<IActionResult> Get()
|
||||
{
|
||||
await _db.OpenAsync();
|
||||
var profile = await _db.FindProfileByCitizen(
|
||||
CitizenId.Parse(User.FindFirst(ClaimTypes.NameIdentifier)!.Value));
|
||||
var profile = await _db.FindProfileByCitizen(CurrentCitizenId);
|
||||
return profile == null ? NoContent() : Ok(profile);
|
||||
}
|
||||
|
||||
[HttpPost("save")]
|
||||
public async Task<IActionResult> Save([FromBody] ProfileForm form)
|
||||
public async Task<IActionResult> Save(ProfileForm form)
|
||||
{
|
||||
var citizenId = CitizenId.Parse(User.FindFirstValue(ClaimTypes.NameIdentifier));
|
||||
await _db.OpenAsync();
|
||||
var existing = await _db.FindProfileByCitizen(citizenId);
|
||||
var txn = await _db.BeginTransactionAsync();
|
||||
|
||||
// Profile
|
||||
var existing = await _db.FindProfileByCitizen(CurrentCitizenId);
|
||||
var profile = existing == null
|
||||
? new Profile(citizenId, form.IsSeekingEmployment, form.IsPublic, ContinentId.Parse(form.ContinentId),
|
||||
form.Region, form.RemoteWork, form.FullTime, new MarkdownString(form.Biography),
|
||||
_clock.GetCurrentInstant(),
|
||||
? new Profile(CurrentCitizenId, form.IsSeekingEmployment, form.IsPublic,
|
||||
ContinentId.Parse(form.ContinentId), form.Region, form.RemoteWork, form.FullTime,
|
||||
new MarkdownString(form.Biography), _clock.GetCurrentInstant(),
|
||||
string.IsNullOrEmpty(form.Experience) ? null : new MarkdownString(form.Experience))
|
||||
: existing with
|
||||
{
|
||||
@@ -76,7 +82,26 @@ namespace JobsJobsJobs.Server.Areas.Api.Controllers
|
||||
Experience = string.IsNullOrEmpty(form.Experience) ? null : new MarkdownString(form.Experience)
|
||||
};
|
||||
await _db.SaveProfile(profile);
|
||||
|
||||
// Skills
|
||||
var skills = new List<Skill>();
|
||||
foreach (var skill in form.Skills) {
|
||||
skills.Add(new Skill(skill.Id.StartsWith("new") ? await SkillId.Create() : SkillId.Parse(skill.Id),
|
||||
CurrentCitizenId, skill.Description, string.IsNullOrEmpty(skill.Notes) ? null : skill.Notes));
|
||||
}
|
||||
|
||||
foreach (var skill in skills) await _db.SaveSkill(skill);
|
||||
await _db.DeleteMissingSkills(CurrentCitizenId, skills.Select(s => s.Id));
|
||||
|
||||
await txn.CommitAsync();
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[HttpGet("skills")]
|
||||
public async Task<IActionResult> GetSkills()
|
||||
{
|
||||
await _db.OpenAsync();
|
||||
return Ok(await _db.FindSkillsByCitizen(CurrentCitizenId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using Npgsql;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace JobsJobsJobs.Server.Data
|
||||
@@ -30,6 +31,15 @@ namespace JobsJobsJobs.Server.Data
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populate a skill object from the given data reader
|
||||
/// </summary>
|
||||
/// <param name="rdr">The data reader from which values should be obtained</param>
|
||||
/// <returns>The populated skill</returns>
|
||||
private static Skill ToSkill(NpgsqlDataReader rdr) =>
|
||||
new Skill(SkillId.Parse(rdr.GetString("id")), CitizenId.Parse(rdr.GetString("citizen_id")),
|
||||
rdr.GetString("skill"), rdr.IsDBNull("notes") ? null : rdr.GetString("notes"));
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve an employment profile by a citizen ID
|
||||
/// </summary>
|
||||
@@ -88,5 +98,74 @@ namespace JobsJobsJobs.Server.Data
|
||||
|
||||
await cmd.ExecuteNonQueryAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve all skills for the given citizen
|
||||
/// </summary>
|
||||
/// <param name="citizenId">The ID of the citizen whose skills should be retrieved</param>
|
||||
/// <returns>The skills defined for this citizen</returns>
|
||||
public static async Task<IEnumerable<Skill>> FindSkillsByCitizen(this NpgsqlConnection conn,
|
||||
CitizenId citizenId)
|
||||
{
|
||||
using var cmd = conn.CreateCommand();
|
||||
cmd.CommandText = "SELECT * FROM skill WHERE citizen_id = @citizen_id";
|
||||
cmd.Parameters.Add(new NpgsqlParameter("citizen_id", citizenId.ToString()));
|
||||
|
||||
var result = new List<Skill>();
|
||||
using var rdr = await cmd.ExecuteReaderAsync().ConfigureAwait(false);
|
||||
while (await rdr.ReadAsync().ConfigureAwait(false))
|
||||
{
|
||||
result.Add(ToSkill(rdr));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save a skill
|
||||
/// </summary>
|
||||
/// <param name="skill">The skill to be saved</param>
|
||||
public static async Task SaveSkill(this NpgsqlConnection conn, Skill skill)
|
||||
{
|
||||
using var cmd = conn.CreateCommand();
|
||||
cmd.CommandText =
|
||||
@"INSERT INTO skill (
|
||||
id, citizen_id, skill, notes
|
||||
) VALUES (
|
||||
@id, @citizen_id, @skill, @notes
|
||||
) ON CONFLICT (id) DO UPDATE
|
||||
SET skill = @skill,
|
||||
notes = @notes
|
||||
WHERE skill.id = excluded.id";
|
||||
cmd.Parameters.Add(new NpgsqlParameter("id", skill.Id.ToString()));
|
||||
cmd.Parameters.Add(new NpgsqlParameter("citizen_id", skill.CitizenId.ToString()));
|
||||
cmd.Parameters.Add(new NpgsqlParameter("skill", skill.Description));
|
||||
cmd.Parameters.Add(new NpgsqlParameter("notes", skill.Notes == null ? DBNull.Value : skill.Notes));
|
||||
|
||||
await cmd.ExecuteNonQueryAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete any skills that are not in the list of current skill IDs
|
||||
/// </summary>
|
||||
/// <param name="citizenId">The ID of the citizen to whom the skills belong</param>
|
||||
/// <param name="ids">The IDs of their current skills</param>
|
||||
public static async Task DeleteMissingSkills(this NpgsqlConnection conn, CitizenId citizenId,
|
||||
IEnumerable<SkillId> ids)
|
||||
{
|
||||
if (!ids.Any()) return;
|
||||
|
||||
var count = 0;
|
||||
using var cmd = conn.CreateCommand();
|
||||
cmd.CommandText = new StringBuilder("DELETE FROM skill WHERE citizen_id = @citizen_id AND id NOT IN (")
|
||||
.Append(string.Join(", ", ids.Select(_ => $"@id{count++}").ToArray()))
|
||||
.Append(')')
|
||||
.ToString();
|
||||
cmd.Parameters.Add(new NpgsqlParameter("citizen_id", citizenId.ToString()));
|
||||
count = 0;
|
||||
foreach (var id in ids) cmd.Parameters.Add(new NpgsqlParameter($"id{count++}", id.ToString()));
|
||||
|
||||
await cmd.ExecuteNonQueryAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.HttpsPolicy;
|
||||
using Microsoft.AspNetCore.ResponseCompression;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
@@ -11,6 +12,7 @@ using NodaTime;
|
||||
using NodaTime.Serialization.SystemTextJson;
|
||||
using Npgsql;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace JobsJobsJobs.Server
|
||||
{
|
||||
@@ -79,11 +81,18 @@ namespace JobsJobsJobs.Server
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
|
||||
static Task send404(HttpContext context)
|
||||
{
|
||||
context.Response.StatusCode = StatusCodes.Status404NotFound;
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapRazorPages();
|
||||
endpoints.MapControllers();
|
||||
endpoints.MapFallbackToFile("index.html");
|
||||
endpoints.MapFallback("api/{**slug}", send404);
|
||||
endpoints.MapFallbackToFile("{**slug}", "index.html");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user