@lighthouse/common 4.37.0-canary-20 → 4.38.0-canary-2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/.vscode/tasks.json +15 -0
  2. package/dist/helpers/build-fetch-url/index.js +14 -0
  3. package/dist/helpers/fetch-image/index.js +67 -4
  4. package/dist/helpers/fetch-image-for-pdf-generator-service/index.js +155 -0
  5. package/dist/helpers/get-audit-entry-details/index.js +0 -2
  6. package/dist/helpers/get-audit-items-data/index.js +4 -1
  7. package/dist/helpers/get-issue-details/index.js +0 -2
  8. package/dist/helpers/get-task-entry-details/index.js +1 -3
  9. package/dist/pdf/audit/index.js +0 -1
  10. package/dist/pdf/helpers/build-audit-content/index.js +6 -2
  11. package/dist/pdf/helpers/default-footer/index.js +6 -0
  12. package/dist/pdf/helpers/default-header/index.js +6 -0
  13. package/dist/pdf/helpers/fields/index.js +17 -4
  14. package/lib/helpers/build-fetch-url/index.js +19 -0
  15. package/lib/helpers/build-fetch-url/index.js.map +1 -1
  16. package/lib/helpers/fetch-image/index.js +70 -7
  17. package/lib/helpers/fetch-image/index.js.map +1 -1
  18. package/lib/helpers/fetch-image-for-pdf-generator-service/index.js +247 -0
  19. package/lib/helpers/fetch-image-for-pdf-generator-service/index.js.map +1 -0
  20. package/lib/helpers/get-audit-entry-details/index.js +0 -2
  21. package/lib/helpers/get-audit-entry-details/index.js.map +1 -1
  22. package/lib/helpers/get-audit-items-data/index.js +3 -0
  23. package/lib/helpers/get-audit-items-data/index.js.map +1 -1
  24. package/lib/helpers/get-issue-details/index.js +0 -2
  25. package/lib/helpers/get-issue-details/index.js.map +1 -1
  26. package/lib/helpers/get-task-entry-details/index.js +1 -3
  27. package/lib/helpers/get-task-entry-details/index.js.map +1 -1
  28. package/lib/pdf/audit/index.js +0 -1
  29. package/lib/pdf/audit/index.js.map +1 -1
  30. package/lib/pdf/helpers/build-audit-content/index.js +6 -1
  31. package/lib/pdf/helpers/build-audit-content/index.js.map +1 -1
  32. package/lib/pdf/helpers/default-footer/index.js +7 -1
  33. package/lib/pdf/helpers/default-footer/index.js.map +1 -1
  34. package/lib/pdf/helpers/default-header/index.js +7 -1
  35. package/lib/pdf/helpers/default-header/index.js.map +1 -1
  36. package/lib/pdf/helpers/fields/index.js +29 -13
  37. package/lib/pdf/helpers/fields/index.js.map +1 -1
  38. package/mise.toml +2 -0
  39. package/package.json +2 -1
  40. package/yarn-error.log +13549 -0
@@ -0,0 +1,15 @@
1
+ {
2
+ "version": "2.0.0",
3
+ "tasks": [
4
+ {
5
+ "label": "Run Unit Tests",
6
+ "type": "shell",
7
+ "command": "yarn test",
8
+ "isBackground": false,
9
+ "problemMatcher": [
10
+ "$eslint-stylish"
11
+ ],
12
+ "group": "test"
13
+ }
14
+ ]
15
+ }
@@ -8,12 +8,26 @@ exports.buildFetchUrl = buildFetchUrl;
8
8
  function buildFetchUrl(url, options) {
9
9
  const {
10
10
  awsS3BaseUrl,
11
+ cloudfrontBaseUrl = '',
11
12
  cloudinaryBaseUrl,
12
13
  fit,
13
14
  height,
14
15
  width,
15
16
  quality
16
17
  } = options;
18
+ const shouldUseCloudfront = cloudfrontBaseUrl && cloudfrontBaseUrl.includes('.cloudfront.net');
19
+
20
+ if (shouldUseCloudfront) {
21
+ const transformations = [];
22
+ if (width) transformations.push(`width=${width.toString()}`);
23
+ if (height) transformations.push(`height=${height.toString()}`);
24
+ if (quality) transformations.push(`quality=${quality.toString()}`);
25
+ transformations.push(`fit=contain`);
26
+ const transformationsString = transformations.join('&');
27
+ const fetchUrl = `${cloudfrontBaseUrl}/${url}?${transformationsString}`;
28
+ return fetchUrl;
29
+ }
30
+
17
31
  const transformations = [];
18
32
  let transformationsString = '';
19
33
  if (width) transformations.push(`w_${width.toString()}`);
@@ -20,6 +20,8 @@ var _constants = require("../../constants");
20
20
 
21
21
  var _images = require("../../images");
22
22
 
23
+ var _fetchImageForPdfGeneratorService = require("../fetch-image-for-pdf-generator-service");
24
+
23
25
  // NOTE use the native fetch if it's available in the browser, because the
24
26
  // ponyfill (which actually uses the github polyfill) does not support all the
25
27
  // same options as native fetch
@@ -42,13 +44,74 @@ const defaultOptions = {
42
44
  };
43
45
 
44
46
  function fetchImage(url, options = {}) {
45
- const encodedUrl = encodeURI(url);
46
- const fetchOptions = { ...defaultOptions,
47
- ...options
48
- };
47
+ const isWebContext = typeof window === 'object';
49
48
  const {
50
49
  isHeader = false
51
50
  } = options;
51
+ const shouldUseCloudfront = url && url.includes('.cloudfront.net');
52
+
53
+ if (shouldUseCloudfront && isWebContext) {
54
+ const {
55
+ Signature,
56
+ Expires,
57
+ 'Key-Pair-Id': KeyPairId
58
+ } = options;
59
+ const firstParamConnector = new URL(url).searchParams.size > 0 ? '&' : '?';
60
+ const urlToEncode = `${url}${firstParamConnector}Signature=${Signature}&Expires=${Expires}&Key-Pair-Id=${KeyPairId}`;
61
+ const encodedUrl = encodeURI(urlToEncode);
62
+ console.info('Fetching image via CloudFront For Web');
63
+ return fetch(encodedUrl).then(response => {
64
+ const contentHeader = response.headers.get('content-length');
65
+ const contentType = response.headers.get('content-type'); // NOTE: the response will be ok but we won't be able to render any
66
+ // image meaning pdfmake will error. Raise error here and return early.
67
+
68
+ if (contentHeader === '0') {
69
+ return _bluebird.default.reject(new Error(`Failed to fetch image as no content length: ${encodedUrl}`));
70
+ }
71
+
72
+ if (!response.ok) {
73
+ return _bluebird.default.reject(new Error(`Failed to fetch image: ${encodedUrl}`));
74
+ }
75
+
76
+ const imageType = contentTypes[contentType];
77
+ return response.arrayBuffer().then(buffer => ({
78
+ buffer,
79
+ imageType
80
+ }));
81
+ }).then(({
82
+ buffer,
83
+ imageType
84
+ }) => {
85
+ const base64Flag = `data:image/${imageType};base64,`;
86
+ const imageStr = arrayBufferToBase64(buffer);
87
+ const base64 = `${base64Flag}${imageStr}`;
88
+ const isValid = validateBase64Image(base64);
89
+
90
+ if (!isValid) {
91
+ return _bluebird.default.reject(new Error('InvalidImageError'));
92
+ }
93
+
94
+ return base64;
95
+ }).catch(error => {
96
+ if (isHeader) {
97
+ // NOTE: Replace failed headers with LH logo
98
+ console.error('FetchImageHeaderError', error);
99
+ return fetchImage(_constants.LIGHTHOUSE_LOGO_URL, defaultOptions);
100
+ }
101
+
102
+ console.error(error);
103
+ return _images.imageNotFound;
104
+ });
105
+ } else if (shouldUseCloudfront && !isWebContext) {
106
+ return (0, _fetchImageForPdfGeneratorService.fetchImageForPdfGeneratorService)(url);
107
+ }
108
+
109
+ const encodedUrl = encodeURI(url);
110
+ const fetchOptions = { ...defaultOptions,
111
+ ...options,
112
+ headers: { ...(options.headers || {})
113
+ }
114
+ };
52
115
  return fetch(encodedUrl, fetchOptions).then(response => {
53
116
  const contentHeader = response.headers.get('content-length');
54
117
  const contentType = response.headers.get('content-type'); // NOTE: the response will be ok but we won't be able to render any
@@ -0,0 +1,155 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+
5
+ Object.defineProperty(exports, "__esModule", {
6
+ value: true
7
+ });
8
+ exports.requestImageTransformation = requestImageTransformation;
9
+ exports.fetchResourceFromS3 = fetchResourceFromS3;
10
+ exports.fetchImageForPdfGeneratorService = void 0;
11
+
12
+ var _awsSdk = _interopRequireDefault(require("aws-sdk"));
13
+
14
+ const REGION = process.env.AWS_REGION;
15
+ const lambda = new _awsSdk.default.Lambda({
16
+ region: REGION
17
+ });
18
+ const s3 = new _awsSdk.default.S3({
19
+ region: REGION
20
+ });
21
+
22
+ const fetchImageForPdfGeneratorService = async function (url) {
23
+ console.info('Fetching image via CloudFront For Serverless Pdf Generator Service');
24
+
25
+ if (!url) {
26
+ throw new Error('URL is required to fetch image for PDF generator service');
27
+ }
28
+
29
+ const urlMatch = url && url.match(/([a-f0-9]{24})\//);
30
+ const applicationId = urlMatch && urlMatch[1];
31
+
32
+ if (!applicationId) {
33
+ throw new Error('Requestor has insufficient permissions');
34
+ }
35
+
36
+ const key = url.split(applicationId)[1];
37
+ const alreadyTransformedImage = await fetchResourceFromS3({
38
+ bucketName: process.env.TRANSFORMED_IMAGES_S3_BUCKET,
39
+ key
40
+ });
41
+
42
+ if (alreadyTransformedImage && alreadyTransformedImage.body) {
43
+ return alreadyTransformedImage.body.toString('base64');
44
+ }
45
+
46
+ const transformerResponse = await requestImageTransformation(key);
47
+ const statusCode = transformerResponse.statusCode;
48
+
49
+ switch (statusCode) {
50
+ case 200:
51
+ console.log('Image transformation successful');
52
+ return transformerResponse.body;
53
+
54
+ case 302:
55
+ {
56
+ console.log('Image transformation successful but image is too big for lambda delivery, fetching directly from S3');
57
+ const redirectLocation = transformerResponse.headers?.Location;
58
+
59
+ if (redirectLocation) {
60
+ const newlyTransformedImage = await fetchResourceFromS3({
61
+ bucketName: process.env.TRANSFORMED_IMAGES_S3_BUCKET,
62
+ key
63
+ });
64
+ console.log('Image successfully fetched from S3 at:', redirectLocation);
65
+ return newlyTransformedImage.body.toString('base64');
66
+ }
67
+
68
+ throw new Error('Redirect response received but no location provided');
69
+ }
70
+
71
+ case 400:
72
+ throw new Error(`Bad request to image transformer: ${transformerResponse.body}`);
73
+
74
+ case 403:
75
+ throw new Error('Requested transformed image is too big');
76
+
77
+ case 404:
78
+ throw new Error('The requested image does not exist');
79
+
80
+ case 500:
81
+ throw new Error(`Image transformation failed: ${transformerResponse.body}`);
82
+
83
+ default:
84
+ throw new Error(`Unexpected response from image transformer: ${statusCode} - ${transformerResponse.body}`);
85
+ }
86
+ };
87
+
88
+ exports.fetchImageForPdfGeneratorService = fetchImageForPdfGeneratorService;
89
+
90
+ async function requestImageTransformation(key) {
91
+ if (!key) {
92
+ throw new Error('Image Path is required for image transformation');
93
+ }
94
+
95
+ console.log('ImageTransformation: Invoking image transformer lambda for path:', key);
96
+ const lambdaEvent = {
97
+ requestContext: {
98
+ http: {
99
+ method: 'GET',
100
+ path: key
101
+ }
102
+ }
103
+ };
104
+ const params = {
105
+ FunctionName: process.env.IMAGE_TRANSFORMER_ARN,
106
+ InvocationType: 'RequestResponse',
107
+ Payload: JSON.stringify(lambdaEvent)
108
+ };
109
+
110
+ try {
111
+ const result = await lambda.invoke(params).promise();
112
+ const response = JSON.parse(result.Payload);
113
+ return response;
114
+ } catch (error) {
115
+ const errorMessage = `Failed to invoke image transformer lambda: ${error.message}`;
116
+ console.error(errorMessage, error);
117
+ throw new Error(errorMessage);
118
+ }
119
+ }
120
+
121
+ async function fetchResourceFromS3({
122
+ bucketName,
123
+ key
124
+ }) {
125
+ console.log(`Fetching resource from S3 Bucket: '${bucketName}' at path: '${key}'`);
126
+
127
+ if (!bucketName || !key) {
128
+ throw new Error('bucketName and key are required for S3 resource fetch ' + JSON.stringify({
129
+ bucketName,
130
+ key
131
+ }));
132
+ }
133
+
134
+ const params = {
135
+ Bucket: bucketName,
136
+ Key: key
137
+ };
138
+
139
+ try {
140
+ const result = await s3.getObject(params).promise();
141
+ return {
142
+ body: result.Body,
143
+ contentType: result.ContentType,
144
+ contentLength: result.ContentLength,
145
+ lastModified: result.LastModified,
146
+ etag: result.ETag
147
+ };
148
+ } catch (error) {
149
+ if (error.code !== 'NoSuchKey') {
150
+ throw new Error(`Failed to fetch image: ${bucketName}/${key}`);
151
+ }
152
+
153
+ console.log('Image not found in transformed bucket, invoking transformer');
154
+ }
155
+ }
@@ -18,7 +18,6 @@ function getAuditEntryDetails(data) {
18
18
  createdAt,
19
19
  gps,
20
20
  items,
21
- templateRevision,
22
21
  sequenceId,
23
22
  score,
24
23
  target,
@@ -44,7 +43,6 @@ function getAuditEntryDetails(data) {
44
43
  locationText,
45
44
  referenceLabel: referenceDetails.label,
46
45
  referenceValue: referenceDetails.value,
47
- templateRevision,
48
46
  scoreText,
49
47
  sequenceId,
50
48
  target,
@@ -13,7 +13,8 @@ function getAuditItemsData(items, data) {
13
13
  const {
14
14
  settings: {
15
15
  awsS3BaseUrl,
16
- cloudinaryBaseUrl
16
+ cloudinaryBaseUrl,
17
+ cloudfrontBaseUrl
17
18
  } = {},
18
19
  entity: {
19
20
  groupScores
@@ -56,6 +57,7 @@ function getAuditItemsData(items, data) {
56
57
  const assets = rawAssets.map((asset, assetIndex) => {
57
58
  const assetUrl = (0, _.buildFetchUrl)(asset, {
58
59
  awsS3BaseUrl,
60
+ cloudfrontBaseUrl,
59
61
  cloudinaryBaseUrl,
60
62
  fit: true,
61
63
  height: 400,
@@ -65,6 +67,7 @@ function getAuditItemsData(items, data) {
65
67
  const link = `${awsS3BaseUrl}/${asset}`;
66
68
  const thumbnailUrl = (0, _.buildFetchUrl)(asset, {
67
69
  awsS3BaseUrl,
70
+ cloudfrontBaseUrl,
68
71
  cloudinaryBaseUrl,
69
72
  width: 100,
70
73
  quality: 50
@@ -24,7 +24,6 @@ function getIssueDetails(data) {
24
24
  createdAt,
25
25
  duration,
26
26
  gps,
27
- templateRevision,
28
27
  sequenceId,
29
28
  status,
30
29
  user
@@ -53,7 +52,6 @@ function getIssueDetails(data) {
53
52
  fullName,
54
53
  isClosedStatus,
55
54
  locationText,
56
- templateRevision,
57
55
  sequenceId,
58
56
  statusStyle,
59
57
  statusColor,
@@ -17,8 +17,7 @@ function getTaskEntryDetails(data) {
17
17
  const {
18
18
  createdAt,
19
19
  gps,
20
- sequenceId,
21
- templateRevision
20
+ sequenceId
22
21
  } = entity;
23
22
  const gpsText = (0, _lodash.get)(gps, 'reverseGeocoded.label', 'Unknown Location');
24
23
  const locationText = (0, _.getLocationReference)(data);
@@ -38,7 +37,6 @@ function getTaskEntryDetails(data) {
38
37
  referenceLabel: referenceDetails.label,
39
38
  referenceValue: referenceDetails.value,
40
39
  sequenceId,
41
- templateRevision,
42
40
  timezoneHourTime,
43
41
  timezoneDatetime
44
42
  };
@@ -49,7 +49,6 @@ function buildAuditPdf(pdfOptions, data) {
49
49
  const {
50
50
  flags = {}
51
51
  } = pdfOptions;
52
- console.info('buildAuditPdf');
53
52
  const sequenceId = entity.sequenceId;
54
53
  const timestamp = entity.createdAt;
55
54
  const title = entity.title || 'Unknown';
@@ -17,14 +17,18 @@ var _ = require("../");
17
17
 
18
18
  var _table = require("../table");
19
19
 
20
- const buildAuditContent = _bluebird.default.method(items => {
20
+ const buildAuditContent = _bluebird.default.method((items, settings = {}) => {
21
21
  return _bluebird.default.map(items, group => {
22
22
  return _bluebird.default.map(group.items, (item, index) => {
23
23
  return _bluebird.default.map(item.assets, ({
24
24
  assetUrl,
25
25
  link
26
26
  }) => {
27
- return (0, _helpers.fetchImage)(assetUrl).then(base64String => ({
27
+ return (0, _helpers.fetchImage)(assetUrl, {
28
+ Signature: settings.Signature,
29
+ Expires: settings.Expires,
30
+ KeyPairId: settings.KeyPairId
31
+ }).then(base64String => ({
28
32
  alignment: 'center',
29
33
  fit: [210, 210],
30
34
  image: base64String,
@@ -8,6 +8,9 @@ exports.defaultFooter = defaultFooter;
8
8
  var _helpers = require("../../../helpers");
9
9
 
10
10
  function defaultFooter({
11
+ Signature,
12
+ Expires,
13
+ KeyPairId,
11
14
  logoUrl,
12
15
  timestamp,
13
16
  timezone,
@@ -23,6 +26,9 @@ function defaultFooter({
23
26
  const footerText = sequenceId ? `${type} ${sequenceId}` : `${type}`; // eslint-disable-next-line no-unused-vars
24
27
 
25
28
  return (0, _helpers.fetchImage)(logoUrl, {
29
+ Signature,
30
+ Expires,
31
+ KeyPairId,
26
32
  isHeader: true
27
33
  }).then(image => _page => ({
28
34
  columns: [{
@@ -8,6 +8,9 @@ exports.defaultHeader = defaultHeader;
8
8
  var _helpers = require("../../../helpers");
9
9
 
10
10
  function defaultHeader({
11
+ Signature,
12
+ Expires,
13
+ KeyPairId,
11
14
  logoUrl,
12
15
  timestamp,
13
16
  timezone
@@ -19,6 +22,9 @@ function defaultHeader({
19
22
  timezone
20
23
  });
21
24
  return (0, _helpers.fetchImage)(logoUrl, {
25
+ Signature,
26
+ Expires,
27
+ KeyPairId,
22
28
  isHeader: true
23
29
  }).then(image => ({
24
30
  columns: [{
@@ -50,8 +50,12 @@ function buildImage(options) {
50
50
  } = options;
51
51
  const {
52
52
  awsS3BaseUrl,
53
- cloudinaryBaseUrl
54
- } = settings;
53
+ cloudinaryBaseUrl,
54
+ cloudfrontBaseUrl,
55
+ Signature,
56
+ Expires,
57
+ KeyPairId
58
+ } = settings || {};
55
59
  const isVideoType = new RegExp('.mp4$').test(filepath);
56
60
  const link = `${awsS3BaseUrl}/${filepath}`;
57
61
 
@@ -66,13 +70,18 @@ function buildImage(options) {
66
70
 
67
71
  const url = (0, _helpers.buildFetchUrl)(filepath, {
68
72
  awsS3BaseUrl,
73
+ cloudfrontBaseUrl,
69
74
  cloudinaryBaseUrl,
70
75
  fit: true,
71
76
  height: 400,
72
77
  width: 600,
73
78
  quality: 50
74
79
  });
75
- return (0, _helpers.fetchImage)(url).then(base64String => ({
80
+ return (0, _helpers.fetchImage)(url, {
81
+ Signature,
82
+ Expires,
83
+ KeyPairId
84
+ }).then(base64String => ({
76
85
  alignment,
77
86
  fit: [width, height],
78
87
  image: base64String,
@@ -253,7 +262,11 @@ function buildTemplateFieldRow({
253
262
 
254
263
  if (isSignatureField) {
255
264
  if (!value) return [labelText, ''];
256
- return (0, _helpers.fetchImage)(value).then(base64String => {
265
+ return (0, _helpers.fetchImage)(value, {
266
+ Signature: settings.Signature,
267
+ Expires: settings.Expires,
268
+ KeyPairId: settings.KeyPairId
269
+ }).then(base64String => {
257
270
  const values = {
258
271
  alignment: 'left',
259
272
  image: base64String,
@@ -1,10 +1,29 @@
1
1
  export function buildFetchUrl(url, options) {
2
2
  var awsS3BaseUrl = options.awsS3BaseUrl,
3
+ _options$cloudfrontBa = options.cloudfrontBaseUrl,
4
+ cloudfrontBaseUrl = _options$cloudfrontBa === void 0 ? '' : _options$cloudfrontBa,
3
5
  cloudinaryBaseUrl = options.cloudinaryBaseUrl,
4
6
  fit = options.fit,
5
7
  height = options.height,
6
8
  width = options.width,
7
9
  quality = options.quality;
10
+ var shouldUseCloudfront = cloudfrontBaseUrl && cloudfrontBaseUrl.includes('.cloudfront.net');
11
+
12
+ if (shouldUseCloudfront) {
13
+ var _transformations = [];
14
+ if (width) _transformations.push("width=".concat(width.toString()));
15
+ if (height) _transformations.push("height=".concat(height.toString()));
16
+ if (quality) _transformations.push("quality=".concat(quality.toString()));
17
+
18
+ _transformations.push("fit=contain");
19
+
20
+ var _transformationsString = _transformations.join('&');
21
+
22
+ var _fetchUrl = "".concat(cloudfrontBaseUrl, "/").concat(url, "?").concat(_transformationsString);
23
+
24
+ return _fetchUrl;
25
+ }
26
+
8
27
  var transformations = [];
9
28
  var transformationsString = '';
10
29
  if (width) transformations.push("w_".concat(width.toString()));
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/helpers/build-fetch-url/index.js"],"names":["buildFetchUrl","url","options","awsS3BaseUrl","cloudinaryBaseUrl","fit","height","width","quality","transformations","transformationsString","push","toString","join","fetchUrl"],"mappings":"AAAA,OAAO,SAASA,aAAT,CAAuBC,GAAvB,EAA4BC,OAA5B,EAAqC;AAAA,MAExCC,YAFwC,GAQtCD,OARsC,CAExCC,YAFwC;AAAA,MAGxCC,iBAHwC,GAQtCF,OARsC,CAGxCE,iBAHwC;AAAA,MAIxCC,GAJwC,GAQtCH,OARsC,CAIxCG,GAJwC;AAAA,MAKxCC,MALwC,GAQtCJ,OARsC,CAKxCI,MALwC;AAAA,MAMxCC,KANwC,GAQtCL,OARsC,CAMxCK,KANwC;AAAA,MAOxCC,OAPwC,GAQtCN,OARsC,CAOxCM,OAPwC;AAU1C,MAAMC,eAAe,GAAG,EAAxB;AACA,MAAIC,qBAAqB,GAAG,EAA5B;AAEA,MAAIH,KAAJ,EAAWE,eAAe,CAACE,IAAhB,aAA0BJ,KAAK,CAACK,QAAN,EAA1B;AACX,MAAIN,MAAJ,EAAYG,eAAe,CAACE,IAAhB,aAA0BL,MAAM,CAACM,QAAP,EAA1B;AACZ,MAAIJ,OAAJ,EAAaC,eAAe,CAACE,IAAhB,aAA0BH,OAAO,CAACI,QAAR,EAA1B;AACb,MAAIP,GAAJ,EAASI,eAAe,CAACE,IAAhB,CAAqB,OAArB;AAETD,EAAAA,qBAAqB,aAAMD,eAAe,CAACI,IAAhB,CAAqB,GAArB,CAAN,MAArB;AAEA,MAAMC,QAAQ,aAAMV,iBAAN,cAA2BM,qBAA3B,SAAmDP,YAAnD,cAAmEF,GAAnE,CAAd;AAEA,SAAOa,QAAP;AACD","sourcesContent":["export function buildFetchUrl(url, options) {\n const {\n awsS3BaseUrl,\n cloudinaryBaseUrl,\n fit,\n height,\n width,\n quality,\n } = options\n\n const transformations = []\n let transformationsString = ''\n\n if (width) transformations.push(`w_${width.toString()}`)\n if (height) transformations.push(`h_${height.toString()}`)\n if (quality) transformations.push(`q_${quality.toString()}`)\n if (fit) transformations.push('c_fit')\n\n transformationsString = `${transformations.join(',')}/`\n\n const fetchUrl = `${cloudinaryBaseUrl}/${transformationsString}${awsS3BaseUrl}/${url}`\n\n return fetchUrl\n}\n"],"file":"index.js"}
1
+ {"version":3,"sources":["../../../src/helpers/build-fetch-url/index.js"],"names":["buildFetchUrl","url","options","awsS3BaseUrl","cloudfrontBaseUrl","cloudinaryBaseUrl","fit","height","width","quality","shouldUseCloudfront","includes","transformations","push","toString","transformationsString","join","fetchUrl"],"mappings":"AAAA,OAAO,SAASA,aAAT,CAAuBC,GAAvB,EAA4BC,OAA5B,EAAqC;AAAA,MAExCC,YAFwC,GAStCD,OATsC,CAExCC,YAFwC;AAAA,8BAStCD,OATsC,CAGxCE,iBAHwC;AAAA,MAGxCA,iBAHwC,sCAGpB,EAHoB;AAAA,MAIxCC,iBAJwC,GAStCH,OATsC,CAIxCG,iBAJwC;AAAA,MAKxCC,GALwC,GAStCJ,OATsC,CAKxCI,GALwC;AAAA,MAMxCC,MANwC,GAStCL,OATsC,CAMxCK,MANwC;AAAA,MAOxCC,KAPwC,GAStCN,OATsC,CAOxCM,KAPwC;AAAA,MAQxCC,OARwC,GAStCP,OATsC,CAQxCO,OARwC;AAW1C,MAAMC,mBAAmB,GACvBN,iBAAiB,IAAIA,iBAAiB,CAACO,QAAlB,CAA2B,iBAA3B,CADvB;;AAGA,MAAID,mBAAJ,EAAyB;AACvB,QAAME,gBAAe,GAAG,EAAxB;AAEA,QAAIJ,KAAJ,EAAWI,gBAAe,CAACC,IAAhB,iBAA8BL,KAAK,CAACM,QAAN,EAA9B;AACX,QAAIP,MAAJ,EAAYK,gBAAe,CAACC,IAAhB,kBAA+BN,MAAM,CAACO,QAAP,EAA/B;AACZ,QAAIL,OAAJ,EAAaG,gBAAe,CAACC,IAAhB,mBAAgCJ,OAAO,CAACK,QAAR,EAAhC;;AACbF,IAAAA,gBAAe,CAACC,IAAhB;;AAEA,QAAME,sBAAqB,GAAGH,gBAAe,CAACI,IAAhB,CAAqB,GAArB,CAA9B;;AAEA,QAAMC,SAAQ,aAAMb,iBAAN,cAA2BH,GAA3B,cAAkCc,sBAAlC,CAAd;;AACA,WAAOE,SAAP;AACD;;AAED,MAAML,eAAe,GAAG,EAAxB;AACA,MAAIG,qBAAqB,GAAG,EAA5B;AAEA,MAAIP,KAAJ,EAAWI,eAAe,CAACC,IAAhB,aAA0BL,KAAK,CAACM,QAAN,EAA1B;AACX,MAAIP,MAAJ,EAAYK,eAAe,CAACC,IAAhB,aAA0BN,MAAM,CAACO,QAAP,EAA1B;AACZ,MAAIL,OAAJ,EAAaG,eAAe,CAACC,IAAhB,aAA0BJ,OAAO,CAACK,QAAR,EAA1B;AACb,MAAIR,GAAJ,EAASM,eAAe,CAACC,IAAhB,CAAqB,OAArB;AAETE,EAAAA,qBAAqB,aAAMH,eAAe,CAACI,IAAhB,CAAqB,GAArB,CAAN,MAArB;AAEA,MAAMC,QAAQ,aAAMZ,iBAAN,cAA2BU,qBAA3B,SAAmDZ,YAAnD,cAAmEF,GAAnE,CAAd;AAEA,SAAOgB,QAAP;AACD","sourcesContent":["export function buildFetchUrl(url, options) {\n const {\n awsS3BaseUrl,\n cloudfrontBaseUrl = '',\n cloudinaryBaseUrl,\n fit,\n height,\n width,\n quality,\n } = options\n\n const shouldUseCloudfront =\n cloudfrontBaseUrl && cloudfrontBaseUrl.includes('.cloudfront.net')\n\n if (shouldUseCloudfront) {\n const transformations = []\n\n if (width) transformations.push(`width=${width.toString()}`)\n if (height) transformations.push(`height=${height.toString()}`)\n if (quality) transformations.push(`quality=${quality.toString()}`)\n transformations.push(`fit=contain`)\n\n const transformationsString = transformations.join('&')\n\n const fetchUrl = `${cloudfrontBaseUrl}/${url}?${transformationsString}`\n return fetchUrl\n }\n\n const transformations = []\n let transformationsString = ''\n\n if (width) transformations.push(`w_${width.toString()}`)\n if (height) transformations.push(`h_${height.toString()}`)\n if (quality) transformations.push(`q_${quality.toString()}`)\n if (fit) transformations.push('c_fit')\n\n transformationsString = `${transformations.join(',')}/`\n\n const fetchUrl = `${cloudinaryBaseUrl}/${transformationsString}${awsS3BaseUrl}/${url}`\n\n return fetchUrl\n}\n"],"file":"index.js"}
@@ -9,7 +9,8 @@ import { atob, btoa } from '@lighthouse/abab';
9
9
  import fetchPonyfill from 'fetch-ponyfill';
10
10
  import Promise from 'bluebird';
11
11
  import { LIGHTHOUSE_LOGO_URL } from '../../constants';
12
- import { imageNotFound } from '../../images'; // NOTE use the native fetch if it's available in the browser, because the
12
+ import { imageNotFound } from '../../images';
13
+ import { fetchImageForPdfGeneratorService } from '../fetch-image-for-pdf-generator-service'; // NOTE use the native fetch if it's available in the browser, because the
13
14
  // ponyfill (which actually uses the github polyfill) does not support all the
14
15
  // same options as native fetch
15
16
 
@@ -32,12 +33,74 @@ var defaultOptions = {
32
33
  };
33
34
  export function fetchImage(url) {
34
35
  var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
36
+ var isWebContext = (typeof window === "undefined" ? "undefined" : _typeof(window)) === 'object';
37
+ var _options$isHeader = options.isHeader,
38
+ isHeader = _options$isHeader === void 0 ? false : _options$isHeader;
39
+ var shouldUseCloudfront = url && url.includes('.cloudfront.net');
40
+
41
+ if (shouldUseCloudfront && isWebContext) {
42
+ var Signature = options.Signature,
43
+ Expires = options.Expires,
44
+ KeyPairId = options['Key-Pair-Id'];
45
+ var firstParamConnector = new URL(url).searchParams.size > 0 ? '&' : '?';
46
+ var urlToEncode = "".concat(url).concat(firstParamConnector, "Signature=").concat(Signature, "&Expires=").concat(Expires, "&Key-Pair-Id=").concat(KeyPairId);
47
+
48
+ var _encodedUrl = encodeURI(urlToEncode);
49
+
50
+ console.info('Fetching image via CloudFront For Web');
51
+ return fetch(_encodedUrl).then(function (response) {
52
+ var contentHeader = response.headers.get('content-length');
53
+ var contentType = response.headers.get('content-type'); // NOTE: the response will be ok but we won't be able to render any
54
+ // image meaning pdfmake will error. Raise error here and return early.
55
+
56
+ if (contentHeader === '0') {
57
+ return Promise.reject(new Error("Failed to fetch image as no content length: ".concat(_encodedUrl)));
58
+ }
59
+
60
+ if (!response.ok) {
61
+ return Promise.reject(new Error("Failed to fetch image: ".concat(_encodedUrl)));
62
+ }
63
+
64
+ var imageType = contentTypes[contentType];
65
+ return response.arrayBuffer().then(function (buffer) {
66
+ return {
67
+ buffer: buffer,
68
+ imageType: imageType
69
+ };
70
+ });
71
+ }).then(function (_ref) {
72
+ var buffer = _ref.buffer,
73
+ imageType = _ref.imageType;
74
+ var base64Flag = "data:image/".concat(imageType, ";base64,");
75
+ var imageStr = arrayBufferToBase64(buffer);
76
+ var base64 = "".concat(base64Flag).concat(imageStr);
77
+ var isValid = validateBase64Image(base64);
78
+
79
+ if (!isValid) {
80
+ return Promise.reject(new Error('InvalidImageError'));
81
+ }
82
+
83
+ return base64;
84
+ }).catch(function (error) {
85
+ if (isHeader) {
86
+ // NOTE: Replace failed headers with LH logo
87
+ console.error('FetchImageHeaderError', error);
88
+ return fetchImage(LIGHTHOUSE_LOGO_URL, defaultOptions);
89
+ }
90
+
91
+ console.error(error);
92
+ return imageNotFound;
93
+ });
94
+ } else if (shouldUseCloudfront && !isWebContext) {
95
+ return fetchImageForPdfGeneratorService(url);
96
+ }
97
+
35
98
  var encodedUrl = encodeURI(url);
36
99
 
37
- var fetchOptions = _objectSpread(_objectSpread({}, defaultOptions), options);
100
+ var fetchOptions = _objectSpread(_objectSpread(_objectSpread({}, defaultOptions), options), {}, {
101
+ headers: _objectSpread({}, options.headers || {})
102
+ });
38
103
 
39
- var _options$isHeader = options.isHeader,
40
- isHeader = _options$isHeader === void 0 ? false : _options$isHeader;
41
104
  return fetch(encodedUrl, fetchOptions).then(function (response) {
42
105
  var contentHeader = response.headers.get('content-length');
43
106
  var contentType = response.headers.get('content-type'); // NOTE: the response will be ok but we won't be able to render any
@@ -58,9 +121,9 @@ export function fetchImage(url) {
58
121
  imageType: imageType
59
122
  };
60
123
  });
61
- }).then(function (_ref) {
62
- var buffer = _ref.buffer,
63
- imageType = _ref.imageType;
124
+ }).then(function (_ref2) {
125
+ var buffer = _ref2.buffer,
126
+ imageType = _ref2.imageType;
64
127
  var base64Flag = "data:image/".concat(imageType, ";base64,");
65
128
  var imageStr = arrayBufferToBase64(buffer);
66
129
  var base64 = "".concat(base64Flag).concat(imageStr);