Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#IP-84] Change strict configuration to true #109

Draft
wants to merge 8 commits into
base: master
Choose a base branch
from

Conversation

fabriziopapi
Copy link
Contributor

Set the stirct configuration to true.
Fixed the compilation errors caused by the new settings.
The loigc/functions has not been rewritten or refactored: properly type cast has been introduced in order to pass the strict compiler check.

@pagopa-github-bot
Copy link
Contributor

pagopa-github-bot commented Mar 30, 2021

Warnings
⚠️

Please include a Pivotal story at the beginning of the PR title (see below).

Example of PR titles that include pivotal stories:

  • single story: [#123456] my PR title
  • multiple stories: [#123456,#123457,#123458] my PR title

New dependencies added: @types/node-fetch.

@types/node-fetch

Author: Unknown

Description: TypeScript definitions for node-fetch

Homepage: http://npmjs.com/package/@types/node-fetch

Createdover 4 years ago
Last Updated1 day ago
LicenseMIT
Maintainers1
Releases36
Direct Dependencies@types/node and form-data
README

Installation

npm install --save @types/node-fetch

Summary

This package contains type definitions for node-fetch (https://github.com/bitinn/node-fetch).

Details

Files were exported from https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/node-fetch.

Additional Details

Credits

These definitions were written by Torsten Werner, Niklas Lindgren, Vinay Bedre, Antonio Román, Andrew Leedham, Jason Li, Steve Faulkner, ExE Boss, Alex Savin, Alexis Tyler, and Jakub Kisielewski.

Generated by 🚫 dangerJS

@codecov
Copy link

codecov bot commented Mar 30, 2021

Codecov Report

Merging #109 (cb34d59) into master (cad0523) will increase coverage by 0.20%.
The diff coverage is 97.29%.

❗ Current head cb34d59 differs from pull request most recent head ad839f4. Consider uploading reports for the commit ad839f4 to get more accurate results
Impacted file tree graph

@@            Coverage Diff             @@
##           master     #109      +/-   ##
==========================================
+ Coverage   81.82%   82.02%   +0.20%     
==========================================
  Files          27       27              
  Lines         886      907      +21     
  Branches       89       95       +6     
==========================================
+ Hits          725      744      +19     
- Misses        160      162       +2     
  Partials        1        1              
Impacted Files Coverage Δ
GetLimitedProfileByPOST/handler.ts 83.33% <ø> (ø)
WebhookNotificationActivity/client.ts 100.00% <ø> (ø)
CreateMessage/handler.ts 63.46% <83.33%> (ø)
utils/comma-separated-list.ts 90.00% <90.00%> (ø)
utils/api.ts 93.93% <96.00%> (-0.18%) ⬇️
CreateService/handler.ts 87.50% <100.00%> (ø)
EmailNotificationActivity/handler.ts 71.42% <100.00%> (ø)
EmailNotificationActivity/utils.ts 63.15% <100.00%> (ø)
GetLimitedProfile/handler.ts 83.87% <100.00%> (ø)
GetMessage/handler.ts 87.27% <100.00%> (ø)
... and 14 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 3d6b1ce...ad839f4. Read the comment docs.

Copy link
Contributor

@balanza balanza left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Manual casting must be considered the very very last resort. Every time we do that, we are poisoning our type system. It may happen once, but usually it's just problem-shifting - or worse: problem-hiding.

We must use alternatives; not that easy, though.

@@ -376,7 +376,7 @@ export function CreateMessageHandler(
telemetryClient.trackEvent({
name: "api.messages.create",
properties: {
error: isSuccess ? undefined : r.kind,
error: isSuccess ? "" : r.kind,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this?

@@ -112,7 +115,7 @@ const getUserTask = (
() =>
apiClient.getUser({
email: userEmail
}),
}) as Promise<t.Validation<IResponseType<number, any, never>>>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We must try not to use manual casting; when we do it, we are injecting unsafe assumptions into the type system, and that makes all other assumptions unsafe.

@@ -33,7 +33,7 @@ const telemetryClient = initTelemetryClient(
export const trackMessageProcessing = (
event: MessageProcessingEvent,
isReplaying: boolean
): void =>
): void|null =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function can just return void on the dead branch, so you don't have to handle a union type

@@ -74,7 +77,7 @@ const getServiceTask = (
() =>
apiClient.getService({
service_id: serviceId
}),
}) as Promise<t.Validation<IResponseType<number, any, never>>>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

@@ -88,7 +91,7 @@ const getSubscriptionKeysTask = (
() =>
apiClient.getSubscriptionKeys({
service_id: serviceId
}),
}) as Promise<t.Validation<IResponseType<number, any, never>>>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

@@ -108,7 +108,7 @@ describe("GetUserServicesHandler", () => {
expect(result.kind).toBe("IResponseSuccessJson");
if (result.kind === "IResponseSuccessJson") {
expect(result.value).toEqual({
items: aUserInfo.subscriptions.map(it => it.id)
items: (aUserInfo.subscriptions as Subscription[]).map(it => it.id)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto: manual casting

@@ -66,7 +70,7 @@ const getUserTask = (
() =>
apiClient.getUser({
email: userEmail
}),
}) as Promise<t.Validation<IResponseType<number, any, never>>>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto: manual casting

@@ -84,7 +88,7 @@ export function GetUserServicesHandler(
)
.map(userInfo =>
ResponseSuccessJson({
items: userInfo.subscriptions.map(it =>
items: (userInfo.subscriptions as Subscription[]).map(it =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto: manual casting

@@ -86,7 +89,7 @@ const regenerateServiceKeyTask = (
apiClient.RegenerateSubscriptionKeys({
body: subscriptionKeyTypePayload,
service_id: serviceId
}),
}) as Promise<t.Validation<IResponseType<number, any, never>>>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto: manual casting

@@ -82,9 +84,9 @@ export const newMessageArb = fc
markdown,
subject
}
}).getOrElse(undefined)
}).fold(l=>undefined, r=>r)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getOrElse is actually a shortcut for this

@balanza
Copy link
Contributor

balanza commented Apr 8, 2021

I think we cannot avoid to manual cast things around, at the moment. We can group casts by their source problem into 2 groups:

1. withApiRequestWrapper method fails to narrow the type T

This happens because the type T is inferred from the type of apiCallWithParams

Promise<t.Validation<IResponseType<number, T, never>>>

which is not correct, as the payload might be not only T but also other types. A better definition would probably be

Promise<
    | t.Validation<IResponseType<C, T, never>>
    | t.Validation<IResponseType<number, ErrorResponses, never>>
  >

where C is a subset of number which defines our acceptance status (eg: 200).
The problem with the latter definition is that we're no longer able to tell T from T | ErrorResponses - or better: we never were able, but we didn't notice as we weren't on strict mode.
Me and @fabriziopapi spent a morning trying to refactor such functions, but we failed.
The simplest workaround would be to just get rid of it (its only purpose is to avoid too much boilerplate code due to type lifting.

2. Service entity is modeled loosey

Service model is defined starting from ServicePayload, which is the DTO we expect from our API's users. To allow partial payloads (that is: you don't need to provide all fields until the service is visible), ServicePayload is defined by the union of VisibleServicePayload and HiddenServicePayload, with the latter having service_metadata nullable. This has been introduced after the discussion in #85.

As a result, api operations such as createService expect either an empty or fully-defined service_metadata field, but as we're reading such field from a given ServicePayload we simply cannot do it, so we arrange an object and we manually cast it as service_metadata like in https://github.com/pagopa/io-functions-services/blob/IP-84--enforce-strict-to-true/createservice/handler.ts#L141.

The issue is the field token_name into service_metadata must never be null, and that conflicts with the above definition; so we have no strong solution other than remodel Service data type.


Given the issue above, I think we should abandon this task and wait until we have them solved.

@balanza
Copy link
Contributor

balanza commented Apr 8, 2021

  1. Service entity is modeled loosey

Addendum: one issue is token_name is defined into service_metadata but both can be valued and/or null for independent reasons (that is: token_name can have a value while service_metadata is null). That conflicts with our model which includes the first inside the latter, so that if we have token_name defined and service_metadata undefined, we have to patch a dirty ServiceMetadata object just to include the token. Such operation can be only made with a manual cast.

Are we experiencing production issues? Not at the moment.
The chart below shows the failure count happening on CreateService endpoint exposed by fn-admin (i.e. the one we call from this very application, here for example).
By just reading the model, I would expect some 400 due to the fact that we are eventually sending a ServiceMetadata without a valued scope field; instead, production traffic prove me wrong.

Other said: the case in which we receive an empty service_metadata, even for hidden Services, just never happens.

I don't have enough data to state if it is just contingency and if such event may happen.

Screenshot 2021-04-08 at 17 08 25

@balanza balanza marked this pull request as draft April 27, 2021 12:32
@balanza balanza added the invalid This doesn't seem right label Apr 27, 2021
@sonarqubecloud
Copy link

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 2 Code Smells

No Coverage information No Coverage information
0.0% 0.0% Duplication

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
invalid This doesn't seem right
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants