-
Notifications
You must be signed in to change notification settings - Fork 7
TG Consultancy Checklist
- Abstract
- What to do when starting consultancy
- What to do while performing consultancy
- What to do before creating a pull request
- What to do when reviewing a pull request
- TODO
This document provides some checklists in an environmentally friendly format for preparing and finalising TG-related consultancy activities.
Although some of the entries refer specifically to TTGAMS (TG Air), the document is applicable to all TG-based applications.
-
Update the TG source (if available):
-
$ git pull
-
$ mvn clean
-
$ mvn eclipse:clean
-
$ mvn -DdownloadSources="true" -DdownloadJavadocs="true" eclipse:eclipse
-
$ mvn clean install
-
In Eclipse select all TG modules.
-
Refresh (press F5 or right-click and select Refresh from the popup menu).
-
From the Eclipse menu select Project - Clean, select (or ensure they are selected) the relevant modules, click OK.
-
Any errors at this stage are probably not your fault, and should be brought to the attention of the project (mis-)manager.
-
-
Update the tg-libs source (if available):
-
$ git pull
-
$ mvn clean
-
$ mvn eclipse:clean
-
$ mvn -DdownloadSources="true" -DdownloadJavadocs="true" eclipse:eclipse
-
$ mvn clean install
-
In Eclipse select all tg-libs modules.
-
Refresh (press F5 or right-click and select Refresh from the popup menu).
-
From the Eclipse menu select Project - Clean, select (or ensure they are selected) the relevant modules, click OK.
-
Any errors at this stage are probably not your fault, and should be brought to the attention of the project (mis-)manager.
-
-
Update TTGAMS or the relevant project to ensure all changes have arrived:
-
$ git pull
-
$ mvn clean
-
$ mvn eclipse:clean
-
$ mvn -DdownloadSources="true" -DdownloadJavadocs="true" eclipse:eclipse
-
In Eclipse select all application modules (e.g. ttgams-dao, ttgams-pojo-bl, ttgams-web-server, ttgams-web-ui).
-
Refresh (press F5 or right-click and select Refresh from the popup menu).
-
From the Eclipse menu select Project - Clean, select (or ensure they are selected) the relevant modules, click OK.
-
-
Checkout or create the feature branch in, which you will be working.
-
Create a database against, which to perform consultancy. This should be your own consultancy database, preferably not used by other consultants to avoid overwriting or altering someone else's carefully constructed data. For some projects (e.g. TTGAMS) a copy of the latest production database is preferred. For other projects (e.g. tgpsa) you can run
PopulateDb
to create a sample database. You can either use the shared database server at your site, or create a local database with the server software either installed natively on your consultancy workstation, or running in a VM or under Docker (the tgpsa project contains instructions and scripts and scripts to facilitate local Docker database deployment). -
(Not TTGAMS) Run
PopulateDb
for the project in, which you are working. This is to ensure that it is working before you start making changes. Any unexpected errors at this stage are probably not your fault.Note: Since some time before 16-Jul-2019
PopulateDb
in TTGAMS is deprecated (and even broken) in favour of using a copy of the production database.-
If there are any unexpected errors and you have not already done so, try merging changes from the
develop
(or other appropriate ancestor) branch and repeat these steps from the mvn commands listed above for the feature branch. -
If the unexpected errors persist after merging, they will need to be brought to the attention of the project (mis-)manager, and probably enhanced before further consultancy can take place.
-
-
(Not TTGAMS) Run all unit tests for the project in, which you are working. Ideally the unit tests should be run against the same database type as the production deployment (e.g. TTGAMS unit tests should be run against a MSSQL database; tgpsa unit tests should be run against a PostgreSQL database). This is to ensure that they are working before you start making changes. Any unexpected errors at this stage are probably not your fault.
-
Note that TTGAMS tests have been recorded as taking about 1.5 hours to run. In general it has been advised to let Jenkins' auto-running of tests when your branch is merged into
develop
take care of testing, unless you are going to perform changes likely to break a number of tests (or perhaps have a second workstation where tests can be run in parallel). -
If there are any unexpected errors and you have not already done so, try merging changes from the
develop
(or other appropriate ancestor) branch and repeat these steps from the mvn commands listed above for the feature branch. -
If the unexpected errors persist after merging, they will need to be brought to the attention of the project (mis-)manager, and probably enhanced before further consultancy can take place.
-
-
Prepare a medium for taking notes. A physical paper notebook was suggested, but an electronic version is also suitable (and would allow for easier copy/paste).
-
If schema changes are anticipated (such as adding a new property to an entity, or adjusting an existing property), extract the relevant database schema. This will allow comparing the original schema with a schema extracted once changes are complete in order to craft an SQL script to include with the relevant release. Either use a database tool that can generate schema (such a DBeaver, or perhaps proprietary MSSQL tools) or run class
GenDdl
to take a snapshot of the schema prior to implementing any changes. -
Add the
in progress
GitHub issue label to the issue being addressed.
-
Do not commit any secrets - password, tokens, certificates, credentials, etc. - especially in
.properties
files. Once committed, such information becomes part of the repository's history even if it is removed in a subsequent commit. Although most of FMS's repositories are private,tg
is not, and in some cases other people may be granted access to private repositories. -
Make detailed notes regarding any significant refactoring that is carried out in order to be able to fully justify the refactoring when responding to criticism at a later date.
-
Ensure that all of the following points have been addressed in the changes you are committing:
-
-
Ensure that the word "ensure" is not used in unit test names.
-
Ensure that all field (property) names start with a lowercase character.
-
Ensure that all method names start with a lowercase character.
-
Do not use a
Header
suffix for entity names (e.g. useInvoice
instead ofInvoiceHeader
). Avoid, where possible, other syntactically sugary suffixes likeNo
(e.g.System
instead ofSystemNo
). -
Private static final String variable names used as constants for various messages should have uppercase names starting with
ERR_
(for errors),WARN_
(for warnings) orINFO_
(for informational/informative messages). An older convention of usingMSG_
for informational/informative messages is deprecated, but historical variables named withMSG_
are not required to be changed (see link). -
Naming of synthetic entities.
-
Synthetic entities, which represent separate concepts do not require any special prefixes. This includes reports and entities extending persistent entities. Examples:
WorkBoard
,PmDashboard
,Manpower
,InventoryReorder
,StockValue
(instead of existingReStockValue
),PmRoutineCalendar
(instead of existingRePmRoutineCalendar
). -
Synthetic entities, which extend persistent entities, add additional selection criteria and appear in standalone centres "in place" of persistent entities that they extend, should have prefix
Re
. Examples:ReWorkActivity
,ReWorkOrder
,ReDocument
,ReGlTransaction
. -
All other synthetic entities should have prefix
Syn
. This includes synthetic entities used as properties for special autocompletion logic (e.g.SynSupplierPart
andSynReferTo
instead of existingReReferTo
) and synthetic entities used in embedded centres (e.g.SynRotableNote
,SynLocationSubsystemCertification
andSynRotableAndRotablePartAttachment
).
-
-
Design pattern for Synthetic Entities with multiple sources.
-
Such entities should have an origin property - refer entity
SynLocationSubsystemCertification
for inspiration. -
Such entities should not attempt to remove duplicates - refer rows 79-82 in entity
SynLocationSubsystemCertification
for anti-inspiration. -
Such entities should allow deletion with a meaningful error - refer
SynLocationSubsystemCertificationDao.batchDelete()`` for inspiration, but do not include
@SessionRequired`. -
Editing should be possible for all origins - refer rows 444-447 in
LocationSubsystemWebUiConfig
for inspiration.
-
-
-
-
New
String
properties should be created including:@IsProperty(length = [positive integer]) @BeforeChange(@Handler(MaxLengthValidator.class))
-
Existing
String
properties should be updated to include the above length-related annotations, using the current column length in the database as a reference. -
When adding a new property, check if it would impact any rendering customisers. If so, enhance the rendering customiser/s.
-
When deleting a property, include an SQL script to delete any associated records from tables representing entities like
WaTypeDefault
,WorkActivityStatusRequiredProperty
andWorkActivityStatusNotApplicableProperty
.
-
-
-
@EntityTitle
is necessary in most situations and should include a meaningful description:@EntityTitle(value = "Work Order", desc = "Work Order is usually a job that can be scheduled or assigned to someone.") public class WorkOrder extends ...
-
@Title
is necessary in most situations and should include a meaningful description:@IsProperty @Title(value = "Quality Inspector", desc = "The person performing quality inspection of the work order.") private Technician inspector;
-
All entity and most property titles (with the exception of boolean, refer to the point below) should follow capitalisation rules that are normally applied to titles of newspaper articles: http://grammar.yourdictionary.com/capitalization/rules-for-capitalization-in-titles.html
There is even a website, which does this automagically: https://capitalizemytitle.com/
Abbreviations GST, FX, PO, DC, etc. have to be fully capitalised in both entity and property titles.
-
Titles for boolean properties should avoid excessive capitalisation. Since they contain a question mark at the end, they need to be constructed in such a way that they can be read as a sentence.
@IsProperty @Title(value = "Preferred System only?", desc = "Specifies whether all Systems or only those indicated as Preferred in Rotable Part/System cross-references are used when selecting Rotables based on From/To System.") private boolean preferredSystemaOnly;
Entities titles contained within such titles (i.e.
System
) should still be capitalised. -
Titles should not include single quotes (
'
) as they do not render as expected in the webui. Unicode character \2019 (right single quotation mark) should be used instead. On macOS this can be typed withOpt-Shift-]
. On Windows perhaps try some of these suggestions. -
Double quotes should be replaced wtih unicode U+201C (“) and U+201D (”) (left and right, respectively) e.g.
desc = "The State where the “From Bulk Store” is located."
. Note that while it is possible to include these characters into string constants using their Unicode codes (e.g.desc = "The State where the \u201cFrom Bulk Store\u201d is located."
), it is preferred to include them explicitly, as above. On macOS these can be typed simply by pressing Opt-[ (“) and Opt-{ (”). The equivalent single quote values are typed with Opt-] (‘) and Opt-} (’). For those with a "compose" or "AltGr" key on the keyboard, this post provides a few alternatives to directly type them.
-
-
-
Check whether any data retrieval needs to be instrumental, or can be uninstrumented (i.e. change
co$(Something.class).findByKey(...)
toco(Something.class).findByKey()
- note the absence of the'$'
. See also the page on Fetch Models. Validators and definers should always use uninstrumental versions. Companions should use uninstrumental versions too, unless they intend to modify or save some other entity as part of save(). -
fetchAll()
should generally be avoided, unless you know a reason why it should be used.
-
-
-
Ensure that each decision path in the business rules is covered by a separate unit test.
-
Check that all assertions are appropriate, e.g.
assertTrue()
is asserting a simple boolean value,assertEquals()
is asserting an expected results is the same as an actual result. -
Check that arguments to assertEquals() are in the correct order i.e. expected value first, actual value second.
-
Ensure that
BigDecimal
values are not compared withassertEquals()
. Instead useassertTrue(EntityUtils.equalsEx(...))
. This way the scale of values won't affect the comparison result e.g. 0.000 == 0. -
Ensure that
BigDecimal
values, when compared withassertTrue(...)
(as above), are constructed with strings representing the value rather than digits e.g.assertTrue(EntityUtils.equalsEx(new BigDecimal("12.34"), actualValue));
. -
@SuppressWarnings("unused") should be avoided, and all warnings suppressed.
-
To avoid unit tests undergoing upgrade when run against an MSSQL database in particular (or PostgreSQL, if the application primarily connects to a PostgreSQL database), ensure that all properties that resemble a "transaction date" and are part of a composite key are populated.
-
Ensure that data population is not being saved to a script, or used from a previously saved script.
-
Ensure that the
constants.setNow(prePopulatedNow);
approach is used to set datetime in unit tests. -
Endeavour not to populate fields in valuable (unit) test data that are populated as a result of execution of definers. For example if
Entity.amount
is calculated by a definer asEntity.hours * Entity.rate
, then by all means populateEntity.hours
andEntity.rate
, but do not populateEntity.amount
.
-
-
-
When composing a WebUI, ensure that
width
andminWidth
are set in such way that column titles do not appear truncated, unless there are some special circumstances. The browser window should be reduced in size horizontally until the horizontal scroll bar just appears, and the column titles checked. -
Ensure that all standard actions are present (e.g. insert, delete, sort, export).
-
Ensure that the same master is displayed when clicking the insert button as when clicking the edit button for an existing row.
-
Ensure that desc is on a separate line in masters, especially when using a multi-line editor.
-
Double-check that small details, like spacing in menu and form titles, have been addressed.
-
When designing Webui layouts for smaller masters, which do not need to take the most of the screen width, please refrain from specifying width in % and specify in (logical) pixels instead - this will ensure that the desired layout is consistent regardless of the screen resolution and browser zoom.
-
Ensure that the centre webui layout dimensions are sufficient to display all criteria, and that there are no empty fields with "Element not found!" warnings.
-
When adding menu items to a compound master, ensure that the
Open<class>ActionDao
constructor includes a call toaddViewBinding()
for the new menu item (to ensure that the icons turn blue when the compound master is opened and there is data present). -
Ensure that custom SVG icons are optimised to keep the size of the vulcanised application at the absolute minimum. For example, using a service like SVGOMG!.
-
Do not include unused icons in the iconsets.
-
Access to entities with only simple masters is still primarily controlled via web menu invisibility.
Note that menu invisibility is per user, not per base user (since the implementation of https://github.com/fieldenms/tg/issues/1734).
If the users who will NOT have access to the menu item are known, list them as shown below. If the users who WILL have access to the menu item are known, make the condition below
AND KEY_ NOT IN ('USERNAME1', 'USERNAME2')
.For any new menu entries, create an SQL script in TTGAMS/releases with the date and issue number in the filename e.g. 20181101-#1395.sql and add a line like (for MSSQL):
INSERT INTO WEBMENUITEMINVISIBILITY_(_ID, _VERSION, OWNER_, MENUITEMURI_, CREATEDDATE_) SELECT (NEXT VALUE FOR dbo.TG_ENTITY_ID_SEQ), 0, _ID, 'Users%20%2F%20Personnel/Personnel/Person%20Replacement', GETDATE() FROM USER_ WHERE BASE_ = 'N' AND KEY_ IN ('USERNAME1', 'USERNAME2')
or (for PostgreSQL):
insert into webmenuiteminvisibility_(_id, _version, owner_, menuitemuri_, createddate_) select nextval('tg_entity_id_seq'), 0, _id, 'Users%20%2F%20Personnel/Personnel/Person%20Replacement', current_timestamp from user_ where base_ = 'N' and key_ in ('USERNAME1', 'USERNAME2')
The value for MENUITEMURI (sic) is determined from the full menu path (
Users / Personnel/Personnel/Person Replacement
in this example), with some special characters replaced with their hex ASCII codes.Note that this populates menu invisibility for the specified users i.e. the named users will not be able to see the menu item.
Another example for TTGAMS/MSSQL, basing a new webui's invisibility on that of an existing webui (rather than based on user), is as follows:
INSERT INTO WEBMENUITEMINVISIBILITY_(_ID, _VERSION, OWNER_, MENUITEMURI_, CREATEDBY_, CREATEDDATE_, CREATEDTRANSACTIONGUID_) SELECT (NEXT VALUE FOR dbo.TG_ENTITY_ID_SEQ), 0, OWNER_, 'Equipment/Administration/Equipment%20Part%2FInventory%20Part', CREATEDBY_, GETDATE(), CREATEDTRANSACTIONGUID_ FROM WEBMENUITEMINVISIBILITY_ WHERE MENUITEMURI_ = 'Equipment/Administration/Rotable%20Part%2FInventory%20Part' GO
-
The following wiki page may be of some use when testing webui functionality: Window management test plan.
-
When testing mobile functionality, narrowing the browser window should trigger use of the mobile layout. However in order to test properly, and to ensure that all icons (in the popout sidebar menu of compound masters) are present, select mobile device emulation in a desktop browser (or test on an actual mobile device if available). Testing in both portrait and landscape modes should be considered.
-
When adding a summary to an EGI (typically with
.withSummary()
), the artificial property name should not resemble a real property name. For example for a kount of all rows (when usingCOUNT(SELF)
), the artificial property name should betotal_count_
. For subtotals (SUM()
), the property name should be, perhaps,total_time_
instead oftimeTotal
.
-
-
-
Ensure that method getAllEntities() is only used when it is certain that a limited number of entities will be returned. Otherwise use streaming (see Streaming Data).
-
The following anti-pattern should not be used:
try (final Stream<Something> stream = coSomething.stream(from(query).lightweight().model())) { final List<Something> things = stream.collect(toList()); if (!things.contains(newThing)) { return failure(newThing, ERR_NOT_ACCEPTABLE_THING); } }
Instead, check the existence of
newThing
during query generation and just check if query count equals 1.Alternatively use method
co.exists(query)
, for example:final EntityResultQueryModel<Rotable> qRotable = select(Rotable.class).where() .prop("rotablePart").eq().val(rotablePart012382).and() .prop("serialNo").eq().val(serialNo).model(); assertTrue(co(Rotable.class).exists(qRotable));
-
Most streams, and all streams involving database queries, must be wrapped in try (), for example:
try (final Stream<StocktakeDetail> stream = co(StocktakeDetail.class).stream(qem)) { stream.forEach(stocktakeDetail -> { // do something with stocktakeDetail }); }
-
-
Please note that security is not a dirty word.
Now that bulk of the security tokens have been created and mapped, each issue has to take care of its own.
-
Ensure that you are not committing any secrets - password, tokens, certificates, credentials, etc. - especially in
.properties
files. -
In the dao, ensure that the save() method is overridden and has the appropriate CanSave token, e.g.:
@Override @SessionRequired @Authorise(GlTransactionType_CanSave_Token.class) public GlTransactionType save(final GlTransactionType entity) { return super.save(entity); }
-
If deletion is supported, do the same for the batchDelete() method with a CanDelete token, e.g.:
@Override @SessionRequired @Authorise(Address_CanDelete_Token.class) public int batchDelete(final Collection<Long> entitiesIds) { return defaultBatchDelete(entitiesIds); }
-
Implement a CanSave token, e.g. GlTransactionType_CanSave_Token.java:
package fielden.security.tokens.persistent; import [...] public class GlTransactionType_CanSave_Token extends AdministrationModuleToken { private final static String ENTITY_TITLE = TitlesDescsGetter.getEntityTitleAndDesc(GlTransactionType.class).getKey(); public final static String TITLE = Template.SAVE.forTitle().formatted(ENTITY_TITLE); public final static String DESC = Template.SAVE.forDesc().formatted(ENTITY_TITLE); }
-
If deletion is supported, implement a CanDelete token, e.g. Address_CanDelete_Token.java:
package fielden.security.tokens.persistent; import [...] public class Address_CanDelete_Token extends StoresAndPurchasingModuleToken { private final static String ENTITY_TITLE = TitlesDescsGetter.getEntityTitleAndDesc(Address.class).getKey(); public final static String TITLE = Template.DELETE.forTitle().formatted(ENTITY_TITLE); public final static String DESC = Template.DELETE.forDesc().formatted(ENTITY_TITLE); }
-
Create an SQL script in TTGAMS/releases with the date and issue number in the filename e.g. 20181101-#1395.sql
-
When migrating security functionality from T64 to TTGAMS, in T64 identify the relevant security string, and then the list of user classes allowed write access to that feature, for example:
SELECT * FROM USER_FUNCTIONS WHERE TF_REFNO IN (SELECT TF_REFNO FROM TRIDENT_FUNCTIONS WHERE TF_DESC = 'Personnel Utilities') AND ALLOW = 'w' ORDER BY USER_CODE
This example results in:
USER_CODE TF_REFNO ALLOW ADMIN 904 w SUPER_TI 904 w SUPERUSE 904 w TRIDENT 904 w
-
In the newly created SQL script construct an SQL insert statement like this (specifying the new token and the list of user codes identified above) (for MSSQL):
INSERT INTO SecurityRoleAssociation_(_ID, _VERSION, SECURITYTOKEN_, ROLE_, CREATEDDATE_) SELECT (NEXT VALUE FOR dbo.TG_ENTITY_ID_SEQ), 0, 'fielden.security.tokens.persistent.PersonReplacement_CanSave_Token', _ID, GETDATE() FROM USER_ROLE WHERE KEY_ in ('ADMIN', 'SUPER_TI', 'SUPERUSE', 'TRIDENT')
or (for PostgreSQL):
insert into securityroleassociation_(_id, _version, securitytoken_, role_, createddate_) select nextval('tg_entity_id_seq'), 0, 'fielden.security.tokens.persistent.PersonReplacement_CanSave_Token', _id, current_timestamp from user_rolE where key_ in ('ADMIN', 'SUPER_TI', 'SUPERUSE', 'TRIDENT')
-
-
-
If a new entity is created, or if a new property is added to an existing entity or an existing property is altered, the corresponding database changes must be reflected in an SQL script to be included in the relevant release.
-
Create an SQL script in TTGAMS/releases with the date and issue number in the filename e.g. 20181101-#1395.sql
-
Either use a database tool (e.g. DBeaver) or the
GenDdl
class to extract the relevant schema.GenDdl.java
can be modified to connect to the appropriate database by passing a suitableapplication.properties
file to it in the run configuration, and should have the relevant entity added to the.generateDatabaseDdl()
call near the bottom of the ghoti. A sample run configuration for Eclipse is as follows (adjust it as required to suit your environment):And for IntelliJ is as follows (similarly, adjust as required):
-
Compare the new SQL text with that captured before consultancy commenced, and remove the bits that have not changed. For example if you have added a new property to an existing entity, your final SQL script would simply be an
ALTER TABLE x ADD y
statement (and whatever else was added with it, such as an index or foreign key).
-
-
If activatable dependencies have changed, i.e.:
- Any properties are annotated with, or no longer annotated with,
@SkipActivatableTracking
,@SkipEntityExistsValidation(skipActiveOnly = true)
- Entities are added or removed from
@DeactivatableDependencies
- Properties, which are instances of activatable entities, are added or removed to/from other activatable entities (without any of the above annotations)
Then an SQL script needs to be included to recalculate refcount.
For example, such an SQL script (for MSSQL; for PostgreSQL it would more closely resemble standard SQL) might resemble this:
BEGIN TRANSACTION; UPDATE Rotable_ SET REFCOUNT_ = 0; UPDATE Rotable_ SET REFCOUNT_ = REFCOUNT_ + (SELECT COUNT(*) FROM Equipment_ AS DEPENDENT_ WHERE DEPENDENT_.ACTIVE_ = 'Y' AND DEPENDENT_.Rotable_ = Rotable_._ID ) WHERE ACTIVE_ = 'Y'; COMMIT WORK; GO
The creation of such SQL scripts can be facilitated by the use of the
main()
function inApplicationDomain
. The function should be modified to include the class for, which dependencies are sought. Then simply run it. Messages will be displayed if there are no (activatable) dependencies, otherwise a list of such dependencies will be displayed. The list can be considered authoratative as it is generated from the model itself. - Any properties are annotated with, or no longer annotated with,
-
-
-
Any
Money
properties that are the subject of database migration must be annotated with.amount
. For example, propertiesactualCost
andestimatedCost
are of typeMoney
and thus annotated with.amount
:@Override public SortedMap<String, String> resultFields() { return map( field("workOrder.number", "T.WONO"), field("person.name", "T.CRAFT"), field("actualCost.amount", "COALESCE(T.COST, 0)"), field("actualHours", "COALESCE(T.HOURS_ACT, 0)"), field("estimatedHours", "COALESCE(T.HOURS, 0)"), field("estimatedCost.amount", "COALESCE(T.EST_COST, 0)") ); }
-
Entities with optional composite key members must have all members specified in the retriever, with the optional members yielding null where applicable. This is required because of reliance of the cache to match values by indexes of their key members.
-
Always provide a full field mapping in case of composite entities with a single key member, which effectively replaced
String
-based keys.For example, migration of entities with a property of type
Vehicle
should specifyvehicle.number
instead of justvehicle
(because entityVehicle
has aDynamicEntityKey
with one composite key member -number
).For another example, in the above ghoti extract can be seen references to
workOrder.number
andperson.name
.
-
-
-
Ensure that you check for nulls in definers and validators that pertain to
@CritOnly(Type.SINGLE)
properties. Invalid entities cannot be saved, including the cases of required properties having no values. However, entities representing selection criteria can be saved, even if some of the properties that model selection criteria (namely@CritOnly(Type.SINGLE)
) are null — this is a feature, not a bug. Hence, when an entity withnull
values in@CritOnly(Type.SINGLE)
properties is loaded, definers for such properties get executed. -
Validators should
return failure(ERR_MESSAGE);
rather thanthrow failure(ERR_MESSAGE);
. This is partly because in general, if there are validators on a persistent entity, throwing a result from a validator would put that entity into an inconsistent state. -
Ideally validators will check for error conditions before warning conditions. I.e. any
return Result.failure(newValue, ...)
should occur beforereturn Result.warning(newValue, ...)
. -
Validators return a
Result
, such assuccessful()
orwarningEx()
. These support the passing of an extra parameter -instance
, however when returned from a validator parameterinstance
is not used and should be omitted. Where aResult
is returned from a non-validator,instance
might be used and in such cases should be included, but otherwise should also be omitted. See here for details.
-
-
Grouping properties are used to group the results of reports and analyses. They are typically implemented as entities with an
enum
defining a fixed list of possible values, and displayed to the user as selection criteria in the respective centres.Refer to commits in TG Fleet and TTGAMS for examples.
-
Such entities should be synthetic i.e. have a field
model_
ormodels_
. -
They should be annotated with
@SupportsEntityExistsValidation
. -
The related matcher should always return all values.
-
-
-
"string".formatted(...)
is preferred overformat("string", ...)
.Beware of using formatted on concatenated strings. For example:
final String suggestion = "Hello %s, " + "let's dance %s.".formatted("Friend", "together");
may not provide the expected result, and is better implemented as:
final String suggestion = ("Hello %s, " + "let's dance %s.").formatted("Friend", "together");
-
Instead of inheriting a property (e.g.
active
) from an ancestor class in order to apply a default, the default can be provided in methodnew_()
in the dao. -
Instead of using
BigDecimal.multiply(new BigDecimal("-1.0"))
it is possible to useBigDecimal.negate()
. -
Ensure TODO items that are done have been removed from the ghoti.
-
Ensure that any
System.out.println();
have been removed. -
Without attributing any specific ghoti to any specific consultant, the following pattern:
Eo eo = null; if (!locationSubsystemEoList.isEmpty()) { eo = locationSubsystemEoList.get(0).getEo(); } else { eo = rotableEoList.get(0).getEo(); }
should be replaced by something more like:
final Eo eo; if (!locationSubsystemEoList.isEmpty()) { eo = locationSubsystemEoList.get(0).getEo(); } else { eo = rotableEoList.get(0).getEo(); }
or even something like:
final Eo eo = (!locationSubsystemEoList.isEmpty()) ? locationSubsystemEoList.get(0).getEo() : rotableEoList.get(0).getEo();
-
Ensure that your ghoti complies with the SOLID principle.
-
Check that entitys are compared using
EntityUtils.areEqual()
instead of, for example,wa.getWaType().equals(woTypeEO)
. -
Without attributing any specific ghoti to any specific consultant, the following pattern:
final WorkActivityStatus workActivityStatusF = fetchWorkActivityStatusUsingWaFetchProvider("F"); assertNotEquals(workActivityStatusF, workActivity189510.getStatus());
could be replaced with:
import static fielden.work.common.WorkActivityStatus.FixedStatuses.F; assertNotEquals(F.name(), workActivity101168.getStatus().getKey());
-
Instead of ghoti like
variable.equals(constant)
it is safer to useconstant.equals(variable)
. -
Ensure that properties of type
Money
are not annotated with(precision = 18, scale = 2)
as this is the default. -
Endeavour to not do inside a loop anything that can be done outside a loop, especially database-intensive operations.
-
Ensure that
BigDecimal
is never equated to0
. Instead always use comparison with0
(BigDecimal.ZERO.compareTo(value)
). -
Particularly in @Calculated expressions, such as
ifNull().<some expression>.then().val(Money.zero)
, useMoney.zero
(for Money) ornew BigDecimal("0.00")
for non-Money i.e. do not useBigDecimal.ZERO
as this has a precision of 0 and might not produce the intended result. -
Methods that are only called from one place do not need to be implemented in the dao - they can be implemented exactly (and only) where they are needed. This is to reduce the scope of the methods. Methods that are called from several different places (e.g. business logic and unit tests) should be implemented in the dao.
-
Sufficiently trivial methods can be implemented as a default methods in the companion object interface (e.g.
IWorkActivity
) rather than being declared there and implemented in the dao (e.g.WorkActivityDao
). This is to facilitate tracing the ghoti at a later date such as when performing criticism. -
It is generally not necessary to explicitly retrieve property.key in a fetch provider as it is implied that the key will be retrieved.
-
Injecting a companion into validators, definers and companions has apparently been deprecated. Instead the co() API should be used, as sort of documented in the respective TG issue. Note that matchers do not provide this functionality and thus may still use injected companions. Also see this commit.
-
Joda-Time's DateMidnight is @Deprecated. You are recommended to avoid DateMidnight and use toDateTimeAtStartOfDay() instead. For more information please refer Recommended use for Joda-Time's DateMidnight and Class DateMidnight.
-
If at all possible, ensure that there are not extra blank lines where they are not required, multiple consecutive blank lines or extraneous trailing spaces. Spaces are preferred over tabs - please ensure that tabs are not inserted and any tabs are converted to spaces.
-
Without attributing any specific ghoti to any specific consultant, when confronted with the following pattern:
final AlternateRotable contraryAlternativeRotable = co(AlternateRotable.class).findByKey(entity.getAlternatePart(), entity.getRotablePart()); if (contraryAlternativeRotable == null) { // do the needful }
it is better to simply do:
final boolean contraryExist = co(AlternateRotable.class).entityWithKeyExists(entity.getAlternatePart(), entity.getRotablePart()); if (!contraryExist) { // do the needful }
Note, however, that the default (platform) implementation invokes
findByKeyAndFetch()
to determine if a suitable instance exists. In some casesfindByKeyAndFetch()
might be overridden, for example TTGAMSBinDao
, to either retrieve and return the existing instance or create a new instance if one does not exist. In those casesentityWithKeyExists()
will always returntrue
, which is unlikely to be the desired outcome. -
Instead of
if (predicate() == false)
useif (!predicate())
(and positive equivalent). -
Instead of
if (expression) return false else return true
usereturn !expression
(and positive equivalent). -
Instead of
coPerson.currentPerson().getUser()
usecoPerson.getUser()
.Or even
coPerson.currentPerson().orElseThrow(CURR_PERSON_IS_MISSING)
. -
When enhancing partial query conditions as part of query enhancers and value matchers, exercise caution and care when adding
.or()
condition. Partial queries constructed outside these enhancers and matchers may already have some conditions constructed using.and()
conditions - adding.or()
condition is most likely to produce undesired results.Note that partial queries used as part of user-driven filtering are already enclosed in parentheses in the generated SQL.
-
Instead of declaring a new
ERR_REQUIRED = "Required property [%s] is not specified for entity [%s]."
, reuse the one now available inMetaProperty
i.e.MetaProperty.ERR_REQUIRED
. -
Ensure that
@SessionRequired
annotations are not applied to methods, which areprivate
- the methods must be at leastprotected
orpublic
. -
Ensure that
@SessionRequired
annotations are not applied to methods of classes, which do not implementISessionEnabled
interface. -
Property bindings that have nothing to do with establishing a database connection should not be put into method
mkDbProps()
(inH2DomainDrivenTestCaseRunner.java
). Instead they should be put intoDaoDomainDrivenTestCaseConfiguration.java
. -
When implementing complex logic as part of EntityDao.save(), especially if it involves modifying other entities, please consider starting with making sure that the entity to be saved is valid before you do anything else:
// Make sure that the entity to be saved is valid before we do anything else entity.isValid().ifFailure(Result::throwRuntime);
-
Unless you really know what you are doing, custom matchers should extend
FallbackValueMatcherWith[Centre]Context
. Typically, they should be overridingmakeSearchCriteriaModel()
and ocassinonally other methods. Only in special cases custom matchers should be extendingAbstractSearchEntityByKeyWith[Centre]Context
- in such cases you should really know why you are doing this and not extendingFallbackValueMatcherWith[Centre]Context
. -
string.replace()
is preferred instead ofstring.replaceAll()
where no regular expression is involved - see Micro optimisation: calls to String.replaceAll need to be replaced for details. -
Modules.<MODULE>.newException(...)
is preferred overnew <ModuleException>(...)
. -
CollectionUtil.setOf(...)
is preferred over creation of aHashSet
as a separate variable.
-
-
-
(If applicable) Run
PopulateDb
for the project in, which you are working. Ensure it is successful.Note: Since some time before 16-Jul-2019
PopulateDb
in TTGAMS is deprecated (and even broken) in favour of using a copy of the production database. -
(If applicable i.e. retriever/s were modified and the project is still in the legacy data migration phase) Run
MigrateLegacyDb
for the project in, which you are working. Ensure it is successful.Note: Depending on retriever dependencies, some long-running retrievers can be omitted to optimise the pre-pull request process.
-
(Not TTGAMS - see note above about TTGAMS tests) Run all unit tests for the project in, which you are working. Ensure they all pass.
At this stage it should be the case that the work you have done is successful in and of itself.
-
Merge from
develop
(or from the appropriate ancestor branch).- Note that it is recommended to recompile (Project - Clean) the project again to ensure that all changes have been recognised and compiled.
-
(If applicable) Run
PopulateDb
again. Ensure it is successful.Note: Since some time before 16-Jul-2019
PopulateDb
in TTGAMS is deprecated (and even broken) in favour of using a copy of the production database.-
If it is not successful, it could indicate that some errors were committed into the
develop
(or ancestor) branch, or that your changes are incompatible with other changes already committed/merged into thedevelop
branch. -
Any errors you are pretty sure you did not cause should be brought to the attention of the project (mis-)manager.
-
Any errors you probably did cause will need to be enhanced before starting the commit process again.
-
-
(If applicable) Run
MigrateLegacyDb
for the project in, which you are working. Ensure it is successful.Note: Depending on retriever dependencies, some long-running retrievers can be omitted to optimise the pre-pull request process.
-
(Not TTGAMS - see note above about TTGAMS tests) Run all unit tests again. Ensure they all pass.
-
If any do not pass, it could indicate that some errors were committed into the
develop
(or ancestor) branch, or that your changes are incompatible with other changes already committed/merged into thedevelop
branch. -
Any errors you are pretty sure you did not cause should be brought to the attention of the project (mis-)manager.
-
Any errors you probably did cause will need to be enhanced before starting the commit process again.
-
-
Perform front end testing - ensure it passes.
Note: This is a good opportunity to review the issue requirements and ensure that all have been met.
Note: This is also a good opportunity to capture screenshots of essential parts of the functionality for the preparation of the change overview (TG Air in particular), and for the preparation of release documentation (TG Fleet and WAMS in particular).
-
Double-check that all changes have actually been committed and pushed upwind.
-
For Airways (TG Air) - write the
change overview
section of the issue description, including screen captures of the functionality (where relevant) and some notes that will assist both the pull request reviewer and the end user in assessing the functionality.Note: If the pull request review implements changes during their review that affect the screen captures or the text, it is the responsibility of the pull request reviewer to update the change overview.
-
For SAAS (TG Fleet) and Watco (WAMS) - write release documentation based on the template in the respective
releases
directory, like unto a change overview, including screen captures of the functionality (where relevant) and some notes that will assist both the pull request reviewer and the end user in assessing the functionality. The release documentation should be named following the patternincident #issue - short description.docx
, for example20604 #812 - Support for reset of Rotable readings.docx
, and committed into thereleases
directory.Note: If the pull request reviewer implements changes during the review that affect the screen captures or the text, it is the responsibility of the pull request reviewer to update the release documentation.
Important: Remember to commit the release documentation and push it upwind before proceeding to create the pull request.
-
Create the pull request, carefully selecting the base branch (usually it will be
develop
, but perhaps not if your feature branch was created from some other branch). Ensure that the textResolve #nnn
(referencing the original issue) is included in the pull request description. Nominate one or more reviewers for the pull request - those reviewers should receive an email or other notification advising of their nomination, and the pull request should appear in their list of pending pull requests. -
Remove the
in progress
GitHub issue label from the issue that was addressed and add thepull request
label. -
Send a more direct message to the reviewer/s nominated above advising that their review of the pull request has been requested.
If any of the steps undergo upgrade, report to the author of the pull request and request rectification.
-
Update the pull request to set yourself as the reviewer (not the assignee), and add the
in progress
label. -
Update the feature branch from the
develop
branch (even if there are no conflicts). -
Perform, variously, a mvn build/compile/install for all dependencies and affected repositories - ensure everything succeeds.
-
(Not TTGAMS) Run
PopulateDb
for the project - ensure it succeeds.Note: Since some time before 16-Jul-2019
PopulateDb
in TTGAMS is deprecated (and even broken) in favour of using a copy of the production database. -
If not running
PopulateDb
(see above), then apply to the appropriate database any SQL scripts implemented as part of the change being reviewed. -
(Not TTGAMS - see note above about TTGAMS tests) Run all unit tests for the project (e.g.
mvn clean test
) - ensure all tests pass.Note: If specific tests were modified as part of the issue being reviewed, even for TTGAMS, ensure that those tests pass.
-
Run the application and check, front end, whether it seems that the fundamental requirements in the issue have been addressed.
Note: The
change overview
section of the issue description may be of assistance for Airways changes.Note: The release documentation (in the
releases directory
may be of assistance for SAAS and Watco changes. -
Run through the What to do before creating a pull request checks and ensure that everything has been attended to.
-
Perform spot check audits on the ghoti looking for consistency, simplicity, adherence to standards and practices etc.
-
Sometimes it is simpler to implement small changes on the fly, rather than request them from the issue assignee, wait for them to be implemented, and then start the review again.
In these situations, particularly for Airways issues, if the changes you implemented affect the
change overview
section of the issue description, it is your responsibility to update thechange overview
accordingly, and for SAAS and Watco issues, if the changes you implemented affect the release documentation, it is your responsibility to update the release documentation accordingly. -
Add comments for consumption by the original comitter:
-
For individual lines of ghoti:
-
Scroll down the pull request details page in GitHub and locate a commit to be reviewed - click the link to see commit details including ghoti changes.
-
Move the mouse pointer just to the right of the line number for a line of interesting ghoti - a white on blue "+" button should appear. Click it.
-
Enter a comment relating to this line of ghoti.
-
-
As a preamble to the criticism:
-
Scroll down the pull request details page in GitHub and locate where the
user started a review
text appears. -
Enter a preamble comment in the edit field immediately below.
-
-
-
Scroll down the pull request details page in GitHub and locate where the
user started a review
text appears. -
Under the preamble comment text field are radio buttons to simply add a comment, approve the pull request, or request changes (seen in the above image). Select the appropriate radio button and click the
Submit review
button. -
Update the pull request and remove the
in progress
label. -
Advise the pull request submitter that feedback on the pull request has been provided. They will need to:
-
If changes were requested, address the suggestions and resubmit the pull request.
-
If the pull request was approved:
-
Merge the pull request.
-
Delete the branch.
-
Remove the
pull request
label from the original issue. -
Close the issue (typically only if the feature branch was merged into a non-default branch and the issue was not automatically closed).
-
-
- Nothing much.
Per aspera ad astra
- Web UI Design and Web API
- Safe Communication and User Authentication
- Gitworkflow
- JavaScript: Testing with Maven
- Java Application Profiling
-
TG Development Guidelines
- TLS and HAProxy for development
- TG Development Checklist
- Entities and their validation
- Entity Properties
- Entity Type Enhancement
- EQL
- Tooltip How To
- All about Matchers
- Streaming data
- Synthetic entities
- Activatable entities
- Jasper Reports
- Opening Compound Master from another Compound Master
- Window management test plan
- Multi Time Zone Environment
- GraphQL Web API
- Guice
- Maven
- Full Text Search
- Deployment recipes
- Application Configuration
- JRebel Installation and Integration
- Compile-time mechanisms