The Vertical Slice
Oct 5, 2021 – One down, three to go
See the code for this post on GitHub
I've now got a full vertical slice for retrieving a user's observations from CosmosDB and pulling them into the Svelte observation store. I was able to make a few improvements to the app's architecture along the way.
I simply could not let the AutoMapper configuration continue taking up space directly in the Azure Function code. I moved mapper configuration to its own extension method and configured DI to inject an IMapper instance for use where it's needed.
public static void AddMapperConfigurations(this IServiceCollection services)
{
services.AddSingleton<IMapper>(serviceProvider => {
var cfg = new MapperConfiguration(cfg => {
cfg.CreateMap<Observation, ObservationModel>()
.ReverseMap();
cfg.AddCelestialObjectMapperConfiguration();
});
return cfg.CreateMapper();
});
}
More complicated mapping configurations, like the one for Celestial Objects, can live in their own files. I've seen mapping configuration files grow to unreasonable sizes, so I like to break them up when it makes sense.
One challenge I had to solve for was retrieving the user's ID from within the Azure function. I repurposed some example code from Microsoft's documentation for retrieving the user's claims principal. Azure Static Web Apps adds this data under the x-ms-client-principal
header from the edge node when a logged-in user makes a request to a Function endpoint. The UserExtensions class has methods for retrieving this data and parsing the User's ID from it.
Another improvement I made in this merge was to move repository calls (remember them from the last post?) to their own services. This allows me to move a bit more logic out of the Function endpoint code itself. For example, constructing the Celestial Object query definition, calling the repository, and mapping the results have all been moved to a service method.
public async Task<List<CelestialObjectModel>> GetCelestialObjectsBySearchTermAsync(string searchTerm)
{
var queryDef = new QueryDefinition(Constants.QueryStrings.GetCelestialObjectsBySearchTerm)
.WithParameter("@term", searchTerm);
var items = await _celestialObjectRepository.GetItemsAsync<CelestialObject>(queryDef);
List<CelestialObjectModel> results
= items.Select(item => _mapper.Map<CelestialObjectModel>(item)).ToList();
return results;
}
Turning client side, this latest merge adds some code to the observation store to fetch the user's observations on page load and set a loading indicator when a server call is in progress.
export const isLoading = readable(true, (set: Subscriber<boolean>) => {
_setIsLoading = set;
});
export class ObservationStore implements IObservationStore {
constructor() {
_setIsLoading(true);
fetch(`/api/Observations`)
.then(result => result.json())
.then((observations: ObservationModel[]) => {
this._store.set(observations.map(this.reviveDate));
})
.finally(() => _setIsLoading(false));
}
/* class definition continues... */
}
That's one vertical slice for complete Observation CRUD operations. Next up, I'll tackle inserting new observations.
StarLog Links
All Posts
- MVP Wrap-Up [11/12/2021]
- Creating and Deleting [10/31/2021]
- The Vertical Slice [10/5/2021]
- Celestial Searches Revisited [9/23/2021]
- Authentication [9/17/2021]
- The SvelteKit Refactor [9/12/2021]
- Celestial Searches [8/29/2021]
- The Carbon Refactor [8/20/2021]
- Proof of Concept [6/16/2021]
- Laying the Foundation [5/24/2021]
- The Project Roadmap [5/19/2021]
- Introduction [5/18/2021]