pkg/

directory
v0.0.0-...-b3f093e Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 19, 2024 License: Apache-2.0

README

EASi Go Packages

Application Context: appcontext

appcontext is for setting and retrieving per request information across various parts of the codebase.

When using the Context of a request, this package should be updated with a getter and setter to allow for consistent key access and handle errors with marshaling or non existent values.

An example is setting a trace ID to allow for logging a request and tracing it when debugging.

Application Errors: apperrors

apperrors provides custom Error types. These can be used across the stack to assess the root of an error.

The most common case for using these, is to inform a handler what HTTP code to return. For example, if a user is unauthorized to access a certain resource, we can pass a UnauthorizedError back up the stack and return a 403 response.

CEDAR: cedar

The cedar package is for working with the CEDAR API. This allows us to work with CMS data sources.

TranslatedClient is the main type used in this package which provides translation from the generated Swagger code to our application models.

GraphQL: graph

The graph package defines our GraphQL schema, contains autogenerated code for setting up the GraphQL API, and contains handwritten resolver code that fetches the data that will be returned by the API.

The starting point for working in this package is the (handwritten) GraphQL schema in pkg/graph/schema.graphql. After editing the schema, run scripts/dev gql to autogenerate code, which will be placed in pkg/graph/generated and pkg/graph/model. If new resolvers are required by the schema changes, stubs for the resolver functions will be written to pkg/graph/schema.resolvers.go. These stubs will initially just panic:

func (r *queryResolver) GraphExample(ctx context.Context) (*model.GraphExample, error) {
	panic(fmt.Errorf("not implemented"))
}

Their implementation will need to be handwritten, generally by fetching data from the database or CEDAR. Database access methods can be accessed through r.store, the CEDAR API client can be accessed through r.cedarCoreClient.

An important note is that when resolvers should pass through any errors from the data source, even if it's just a ModelNotFoundError indicating that no matching entities were found in the database. The apollo-client frontend library relies on the presence of an errors field in the GraphQL response to indicate that no matching data was returned; returning nil, nil from a resolver method will cause problems in the frontend code.

There will also be similar generated method stubs for accessing specific fields on new data types:

func (r *cedarDeploymentResolver) DeploymentType(ctx context.Context, obj *models.CedarDeployment) (*string, error) {
	panic(fmt.Errorf("not implemented"))
}

These can usually be implemented simply by returning the appropriate field from obj.

Handlers: handlers

handlers are for parsing a request and returning a response. They follow the Go http.Handler standard pattern. They should do minimal work to handle a request and offload business logic to the services package.

A common handler pattern is:

  1. Unmarshal a request from JSON
  2. Offload an operation to the services package
  3. Generate a response based on the return value from services

Integration: integration

integration is for testing only. It provides a way to test the API and all its integrations. Tests here should be limited due to performance and complexity, but should test that a user can access an endpoint as set up in production, including authorization, databases, and third party APIs.

Local: local

local is for local mocks when running the application. The current example, is turning off authorization to make debugging easier.

Models: models

models describe the data used across the application. They're one of the few packages that should be made available across the stack. Since they're propagated so widely, they should not implement many (if any) methods to avoid non-composable API services.

Okta: okta

okta is for code interacting with the Okta identity management server.

Server: server

server is for setting up the server. This is where all the various packages and configurations are tied together.

Access to external APIs via type (vs. abstracted interfaces) should be limited to this package. Access to environment variables and other external configurations should also only reside here.

Services: services

services are the entry point to business logic in the application. They should combine the various portions of the application into a cohesive unit available to handlers. For example, fetching a resource may involve authorization, database calls, or API interaction which should be orchestrated from here.

Services tend to follow a closure pattern, where they're instantiated in the server with any shared components. The returned function operates on a per request level.

Storage: storage

storage is for database interaction. Any database connections or SQL code should be restricted to this package.

Test Helpers: testhelpers

testhelpers provides functions required for only testing and that are needed across packages. If the functions are only needed in a package, write them within. Otherwise, add them to testhelpers. An example is a helper for logging into Okta, which is required for testing in okta and integration

Directories

Path Synopsis
cedar
intake
Package intake serves as an Anti-Corruption Layer (in Domain Driven Design parlance), keeping the implementation details of working with the downstream CEDAR Intake API well separated, so the rest of the internal workings of the EASi code are not using anything defined by the external API
Package intake serves as an Anti-Corruption Layer (in Domain Driven Design parlance), keeping the implementation details of working with the downstream CEDAR Intake API well separated, so the rest of the internal workings of the EASi code are not using anything defined by the external API
Package dataloaders holds functions for batched calls to data sources using the concept of a dataloader
Package dataloaders holds functions for batched calls to data sources using the concept of a dataloader
Package server is for setting up the server.
Package server is for setting up the server.
Package sqlqueries holds references to relevant SQL queries it exports variables to hold references to relevant queries, organized by relevant entity it's sole responsibility is embedding sql queries for use by other modules which use these queries to interact with the database
Package sqlqueries holds references to relevant SQL queries it exports variables to hold references to relevant queries, organized by relevant entity it's sole responsibility is embedding sql queries for use by other modules which use these queries to interact with the database
Package sqlutils contains functionality to wrap existing database functionality
Package sqlutils contains functionality to wrap existing database functionality
Package testhelpers is for test code that needs to be imported across packages DO NOT use this in production code
Package testhelpers is for test code that needs to be imported across packages DO NOT use this in production code

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL