TEAMMATES is a Web application that runs on Google App Engine (GAE). Given above is an overview of the main components.
TestNG
is used for Java testing (all levels) and Jest
for JavaScript unit-testing.Selenium (Java)
is used to automate E2E testing with actual Web browsers.The diagram below shows how the code in each component is organized into packages and the dependencies between them.
Notes:
[logic] - [ui::website] - [ui::webapi]
represent an application of Model-View-Controller
pattern.
ui::website
is not a real package; it is a conceptual package representing the front-end of the application.The diagram below shows the object structure of the UI component.
Notes:
ui::website
is not a Java package. It is written in Angular framework and consists of HTML, SCSS, and TypeScript files. The framework will build those files into HTML, CSS and JavaScript files ready to be used by standard Web browsers.The UI component is the first stop for 99% of all requests that are received by the application. Such a request will go through the following steps:
web.xml
, e.g. OriginCheckFilter
.*Servlet
object as specified in web.xml
for further processing, depending on the type of request.There are two general types of requests: user-invoked requests and automated (GAE server-invoked) requests, which are processed differently.
User-invoked requests are all requests made by the users of the application, typically from the Web browser (i.e. by navigating to a particular URL of the application). The request will be processed as follows:
The initial request for the web page will be processed as follows:
WebPageServlet
.WebPageServlet
returns the built single web page (index.html
).Subsequent AJAX requests sent to the server will be processed as follows:
WebApiServlet
.WebApiServlet
uses the ActionFactory
to generate the matching Action
object, e.g. GetFeedbackSessionsAction
.WebApiServlet
executes the action.
Action
object checks the access rights of the user. If the action is allowed, it will be performed, interacting with the Logic
component as necessary.Action
packages and processes the result into an ActionResult
object. The most common format is JsonResult
(requests for obtaining data or processing existing data) and other formats can be defined as necessary.WebApiServlet
sends the result back to the browser which will then process it on the front-end.Requests for static asset files (e.g. CSS, JS files, images) are served directly without going through web.xml
configuration at all.
The Web API is protected by two layers of access control check:
Special keys (csrf key
and backdoor key
) can be used to bypass each of the checks, typically for testing purpose. Those keys are strings known only to the person who deployed the application (typically, the administrator).
Automated requests are all requests sent automatically by the GAE server during specific periods of time. This type of request will be processed as follows:
403 Forbidden
status will be returned.
WebApiServlet
and subsequent actions are the same as user-invoked AJAX requests.GAE server sends such automated requests through two different configurations:
cron.yaml
.queue.yaml
as well as the TaskQueue
nested class of the Const class.Action
classes.On designing API endpoints (for AJAX requests):
FeedbackSession
is a resource. The corresponding endpoint is /session
. We use GET
, POST
, PUT
, DELETE
HTTP methods to get, create, update and delete the resource respectively.Intent
to distinguish the intent for the request. For example, instructors can access /session
with intent INSTRUCTOR_SUBMISSION
or FULL_DETAIL
. Some Intent
will naturally require stricter access rights.POST
and PUT
requests.
request
and output
package respectively.Instant
in the back-end.
output
package contain logic to hide "hacks" in the backend.request
package contain logic to cater the conventions in the backend.On data exchange between front-end and back-end:
api-const.ts
.output
and request
packages are synced to api-output.ts
and api-request.ts
in the frontend.On handling exceptions:
EntityNotFoundException
which corresponds to HTTP 404, UnauthorizedAccessException
which corresponds to HTTP 403, etc.
4XX
responses must be accompanied with logging at warning
level or above. 5XX
responses must be accompanied with severe
level logging.
502
(Bad Gateway) responses may skip the severe
level logging if the upstream components (where the error happened) already did the severe
level logging.The Logic
component handles the business logic of TEAMMATES. In particular, it is responsible for:
Package overview:
logic.api
: Provides the API of the component to be accessed by the UI.logic.core
: Contains the core logic of the system.logic.external
: Holds the logic of external services such as task queue service.Represented by these classes:
Logic
: A Facade class which connects to the several *Logic
classes to handle the logic related to various types of data and to access data from the Storage
component.UserProvision
: Retrieves user information (based on request cookies).EmailGenerator
: Generates emails to be sent.EmailSender
: Sends email with the provider chosen based on the build configuration.TaskQueuer
: Adds tasks to the task queue, i.e. to be executed at a later time.LogsProcessor
: For more advanced usage of logging that cannot be captured by the standard logger class.RecaptchaVerifier
: For verification of the reCAPTCHA token.Many classes in this layer make use of proxy pattern, i.e. they only connect to production services such as Google Cloud Tasks in the staging/production server.
Access control:
GateKeeper
class) before calling a method in the logic component.API for creating entities:
InvalidParametersException
.EntityAlreadyExistsException
(escalated from Storage level).API for retrieving entities:
null
parameters: Causes an assertion failure.null
if the target entity not found. This way, read operations can be used easily for checking the existence of an entity.API for updating entities:
*UpdateOptions
inside every *Attributes
. The UpdateOptions
will specify what is used to identify the entity to update and what will be updated.EntityDoesNotExistException
.InvalidParametersException
.API for deleting entities:
The Storage
component performs CRUD (Create, Read, Update, Delete) operations on data entities individually.
It contains minimal logic beyond what is directly relevant to CRUD operations.
In particular, it is reponsible for:
Logic
component.storage::entity
package are not visible outside this component to hide information specific to data persistence.
*Attributes
(e.g., CourseAttributes
is the data transfer object for Course
entities) object is returned. These datatransfer classes are in common::datatransfer
package, to be explained later.The Storage
component does not perform any cascade delete/create operations. Cascade logic is handled by the Logic
component.
Package overview:
storage.api
: Provides the API of the component to be accessed by the logic component.storage.entity
: Classes that represent persistable entities.storage.search
: Classes for dealing with searching and indexing.Note that the navigability of the association links between entity objects appear to be in the reverse direction of what we see in a normal OOP design. This is because we want to keep the data schema flexible so that new entity types can be added later with minimal modifications to existing elements.
Represented by the *Db
classes. These classes act as the bridge to the database.
Add and Delete operations try to wait until data is persisted in the database before returning. This is not enough to compensate for eventual consistency involving multiple servers in the Google Cloud Datastore environment. However, it is expected to avoid test failures caused by eventual consistency in dev server and reduce such problems in the live server. Note: 'Eventual consistency' here means it takes some time for a database operation to propagate across all replicas of the Google Cloud Datastore. As a result, the data may be in an inconsistent states for short periods of time although things should become consistent 'eventually'. For example, an object we deleted may appear to still exist for a short while.
Implementation of Transaction Control has been minimized due to limitations of Google Cloud Datastore environment and the nature of our data schema.
API for creating:
EntityAlreadyExistsException
.InvalidParametersException
.API for retrieving:
null
.API for updating:
EntityDoesNotExistException
.InvalidParametersException
.API for deleting:
The Common component contains common utilities used across TEAMMATES.
Package overview:
common.util
: Contains utility classes.common.exceptions
: Contains custom exceptions.common.datatransfer
: Contains data transfer objects.common.datatransfer
package contains lightweight "data transfer object" classes for transferring data among components. They can be combined in various ways to transfer structured data between components. Given below are three examples.
Test Driver
can use the DataBundle
in this manner to send an arbitrary number of objects to be persisted in the database.Some of these classes are methodless (and thus more of a data structure rather than a class); these classes use public variables for data for easy access.
This component automates the testing of TEAMMATES.
The test driver component's package structure follows the corresponding production package structure's exactly,
e.g. teammates.logic.core.*
will contain the test cases for the production code inside teammates.logic.core
package.
In the same spirit, for the front end, each *.component.ts
will have the corresponding *.component.spec.ts
in the same folder (similarly for *.service.ts
, *.pipe.ts
, etc.).
The test driver component introduces the following additional packages:
architecture
: Contains test cases used by one of the lint tools to check for architectural integrity.test
: Contains infrastructure and helpers needed for running the tests. Also contains the test cases for the said infrastructure/helpers.Notes:
Jest
. The test cases are found in *.spec.ts
files.This is how TEAMMATES testing maps to standard types of testing.
Normal
|-----acceptance tests-----|-------------------system tests-------------------|-----integration tests-----|-----unit tests-----|
|--------manual testing--------|------automated E2E tests------|-------------------automated component tests-------------------|
TEAMMATES
The E2E component has no knowledge of the internal workings of the application and can only interact either with Web browser (as a whole application) or REST API calls (for the back-end logic). Its primary function is for E2E tests.
Package overview:
e2e.util
: Contains helpers needed for running E2E tests. Also contains the test cases for the said infrastructure/helpers.e2e.pageobjects
: Contains abstractions of the pages as they appear on a Browser (i.e. SUTs).e2e.cases
: Contains system test cases for testing the application as a whole.The Client component contains scripts that can connect directly to the application back-end for administrative purposes, such as migrating data to a new schema and calculating statistics.
Package overview:
client.util
: Contains helpers needed for client scripts.client.connector
: Classes needed to connect to the back end directly.client.scripts
: Scripts that deal with the back end data for administrative purposes.