diff options
author | partouf <partouf@gmail.com> | 2023-07-06 00:33:51 +0200 |
---|---|---|
committer | partouf <partouf@gmail.com> | 2023-07-06 00:33:51 +0200 |
commit | a9e6aac23a60357f484be7463490a745f16de3ae (patch) | |
tree | ed172323249888d03c3947dfd71af4ed5d646107 | |
parent | 3e3e52a2b39bb0ecf7cad53f5d8c4a6d43058be6 (diff) | |
download | compiler-explorer-gh-8063.tar.gz compiler-explorer-gh-8063.zip |
initial changes to fetch and show creation_date of shortlinkgh-8063
-rw-r--r-- | lib/handlers/api.ts | 2 | ||||
-rw-r--r-- | lib/handlers/route-api.ts | 18 | ||||
-rw-r--r-- | lib/storage/base.ts | 8 | ||||
-rw-r--r-- | lib/storage/local.ts | 6 | ||||
-rw-r--r-- | lib/storage/null.ts | 5 | ||||
-rw-r--r-- | lib/storage/remote.ts | 5 | ||||
-rw-r--r-- | lib/storage/s3.ts | 5 | ||||
-rw-r--r-- | static/main.ts | 30 | ||||
-rw-r--r-- | static/styles/explorer.scss | 4 | ||||
-rw-r--r-- | test/storage/storage-s3-tests.ts | 2 | ||||
-rw-r--r-- | test/unfurl-tests.ts | 3 | ||||
-rw-r--r-- | views/index.pug | 2 |
12 files changed, 76 insertions, 14 deletions
diff --git a/lib/handlers/api.ts b/lib/handlers/api.ts index d3b280b78..92163fa1c 100644 --- a/lib/handlers/api.ts +++ b/lib/handlers/api.ts @@ -154,6 +154,8 @@ export class ApiHandler { .then(result => { const config = JSON.parse(result.config); + res.header('Link-Created', result.created.toUTCString()); + if (config.content) { const normalizer = new ClientStateNormalizer(); normalizer.fromGoldenLayout(config); diff --git a/lib/handlers/route-api.ts b/lib/handlers/route-api.ts index d067aac62..0530dab63 100644 --- a/lib/handlers/route-api.ts +++ b/lib/handlers/route-api.ts @@ -34,6 +34,7 @@ import * as utils from '../utils.js'; import {ApiHandler} from './api.js'; import {SentryCapture} from '../sentry.js'; +import {ExpandedShortLink} from '../storage/base.js'; export type HandlerConfig = { compileHandler: any; @@ -48,6 +49,13 @@ export type HandlerConfig = { contentPolicyHeader: any; }; +type ShortLinkMetaData = { + ogDescription: string | null; + ogAuthor: string | null; + ogTitle: string; + ogCreated: Date | null; +}; + export class RouteAPI { renderGoldenLayout: any; storageHandler: StorageBase; @@ -221,17 +229,19 @@ export class RouteAPI { return lines.map(line => this.escapeLine(req, line)).join('\n'); } - getMetaDataFromLink(req: express.Request, link: {config: string; specialMetadata: any} | null, config) { - const metadata = { - ogDescription: null as string | null, - ogAuthor: null as string | null, + getMetaDataFromLink(req: express.Request, link: ExpandedShortLink | null, config) { + const metadata: ShortLinkMetaData = { + ogDescription: null, + ogAuthor: null, ogTitle: 'Compiler Explorer', + ogCreated: null, }; if (link) { metadata.ogDescription = link.specialMetadata ? link.specialMetadata.description.S : null; metadata.ogAuthor = link.specialMetadata ? link.specialMetadata.author.S : null; metadata.ogTitle = link.specialMetadata ? link.specialMetadata.title.S : 'Compiler Explorer'; + metadata.ogCreated = link.created; } if (!metadata.ogDescription) { diff --git a/lib/storage/base.ts b/lib/storage/base.ts index 4b0eef05e..84b7f37c1 100644 --- a/lib/storage/base.ts +++ b/lib/storage/base.ts @@ -36,6 +36,12 @@ Note that a Hash might end up being longer than this! const USABLE_HASH_CHECK_LENGTH = 9; // Quite generous const MAX_TRIES = 4; +export type ExpandedShortLink = { + config: string; + specialMetadata: any; + created: Date; +}; + export abstract class StorageBase { constructor(protected readonly httpRootDir: string, protected readonly compilerProps: CompilerProps) {} @@ -128,7 +134,7 @@ export abstract class StorageBase { abstract findUniqueSubhash(hash: string): Promise<any>; - abstract expandId(id: string): Promise<{config: string; specialMetadata: any}>; + abstract expandId(id: string): Promise<ExpandedShortLink>; abstract incrementViewCount(id): Promise<any>; } diff --git a/lib/storage/local.ts b/lib/storage/local.ts index 13dd0b7c5..f4968b6af 100644 --- a/lib/storage/local.ts +++ b/lib/storage/local.ts @@ -29,7 +29,7 @@ import _ from 'underscore'; import {logger} from '../logger.js'; -import {StorageBase} from './base.js'; +import {ExpandedShortLink, StorageBase} from './base.js'; const MIN_STORED_ID_LENGTH = 6; @@ -105,14 +105,16 @@ export class StorageLocal extends StorageBase { throw new Error('Hash too small'); } - async expandId(id: string) { + async expandId(id: string): Promise<ExpandedShortLink> { const expectedPath = path.join(this.storageFolder, id); logger.info(`Expanding local id ${id} to ${expectedPath}`); try { + const stats = await fs.stat(expectedPath); const item = await fs.readJson(expectedPath); return { config: item.config, specialMetadata: null, + created: stats.ctime, }; } catch (err) { // IO error/Logic error, we have no way to store this right now. Please try again? What to do here? diff --git a/lib/storage/null.ts b/lib/storage/null.ts index 90fa9ac6e..7206acf57 100644 --- a/lib/storage/null.ts +++ b/lib/storage/null.ts @@ -22,7 +22,7 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. -import {StorageBase} from './base.js'; +import {ExpandedShortLink, StorageBase} from './base.js'; export class StorageNull extends StorageBase { static get key() { @@ -41,10 +41,11 @@ export class StorageNull extends StorageBase { }; } - async expandId(id: string) { + async expandId(id: string): Promise<ExpandedShortLink> { return { config: '{}', specialMetadata: null, + created: new Date(), }; } diff --git a/lib/storage/remote.ts b/lib/storage/remote.ts index f36b8f894..578dab437 100644 --- a/lib/storage/remote.ts +++ b/lib/storage/remote.ts @@ -29,7 +29,7 @@ import request from 'request'; import {logger} from '../logger.js'; -import {StorageBase} from './base.js'; +import {ExpandedShortLink, StorageBase} from './base.js'; export class StorageRemote extends StorageBase { static get key() { @@ -85,7 +85,7 @@ export class StorageRemote extends StorageBase { res.send({url: shortlink}); } - async expandId(id: string) { + async expandId(id: string): Promise<ExpandedShortLink> { const resp = await this.get(`/api/shortlinkinfo/${id}`); if (resp.statusCode !== 200) throw new Error(`ID ${id} not present in remote storage`); @@ -93,6 +93,7 @@ export class StorageRemote extends StorageBase { return { config: resp.body, specialMetadata: null, + created: new Date(), }; } diff --git a/lib/storage/s3.ts b/lib/storage/s3.ts index 3b70f492d..ae17ee597 100644 --- a/lib/storage/s3.ts +++ b/lib/storage/s3.ts @@ -32,7 +32,7 @@ import {logger} from '../logger.js'; import {S3Bucket} from '../s3-handler.js'; import {anonymizeIp} from '../utils.js'; -import {StorageBase} from './base.js'; +import {ExpandedShortLink, StorageBase} from './base.js'; /* * NEVER CHANGE THIS VALUE @@ -157,7 +157,7 @@ export class StorageS3 extends StorageBase { }; } - async expandId(id: string) { + async expandId(id: string): Promise<ExpandedShortLink> { // By just getting the item and not trying to update it, we save an update when the link does not exist // for which we have less resources allocated, but get one extra read (But we do have more reserved for it) const item = await this.dynamoDb.getItem({ @@ -173,6 +173,7 @@ export class StorageS3 extends StorageBase { return { config: unwrap(result.data).toString(), specialMetadata: metadata, + created: new Date(attributes.creation_date?.S || ''), }; } diff --git a/static/main.ts b/static/main.ts index bb30773f3..d091e1202 100644 --- a/static/main.ts +++ b/static/main.ts @@ -391,8 +391,10 @@ function initializeResetLayoutLink() { const currentUrl = document.URL; if (currentUrl.includes('/z/')) { $('#ui-brokenlink').attr('href', currentUrl.replace('/z/', '/resetlayout/')).show(); + initShortlinkInfoButton(); } else { $('#ui-brokenlink').hide(); + hideShortlinkInfoButton(); } } @@ -532,6 +534,34 @@ function getDefaultLangId(subLangId: LanguageKey | undefined, options: CompilerE return defaultLangId; } +function hideShortlinkInfoButton() { + const div = $('.shortlinkInfo'); + div.hide(); +} + +function showShortlinkInfoButton() { + const div = $('.shortlinkInfo'); + div.show(); +} + +function initShortlinkInfoButton() { + const button = $('.shortlinkInfo div'); + if (options.metadata && options.metadata['ogCreated']) { + button.popover({ + html: true, + title: 'Link created at', + content: options.metadata['ogCreated'] || '', + template: + '<div class="popover" role="tooltip">' + + '<div class="arrow"></div>' + + '<h3 class="popover-header"></h3><div class="popover-body"></div>' + + '</div>', + }); + + showShortlinkInfoButton(); + } +} + // eslint-disable-next-line max-statements function start() { initializeResetLayoutLink(); diff --git a/static/styles/explorer.scss b/static/styles/explorer.scss index 80b2fb245..d654ff30f 100644 --- a/static/styles/explorer.scss +++ b/static/styles/explorer.scss @@ -1427,3 +1427,7 @@ html[data-theme='pink'] { margin-top: 5px; } } + +.shortlinkInfo div { + height: 100%; +} diff --git a/test/storage/storage-s3-tests.ts b/test/storage/storage-s3-tests.ts index cc694fcbf..c00691c6f 100644 --- a/test/storage/storage-s3-tests.ts +++ b/test/storage/storage-s3-tests.ts @@ -190,7 +190,7 @@ describe('Retrieves from s3', () => { .resolves({Body: sdkStreamMixin(stream)}); const result = await storage.expandId('ABCDEF'); - result.should.deep.equal({config: 'I am a monkey', specialMetadata: null}); + result.should.deep.equal({config: 'I am a monkey', specialMetadata: null, created: new Date('')}); }); it('should handle failures', async () => { const storage = new StorageS3(httpRootDir, compilerProps, awsProps); diff --git a/test/unfurl-tests.ts b/test/unfurl-tests.ts index 8d611605c..1f96ef15e 100644 --- a/test/unfurl-tests.ts +++ b/test/unfurl-tests.ts @@ -79,6 +79,7 @@ describe('Basic unfurls', () => { ogAuthor: null, ogDescription: '', ogTitle: 'Compiler Explorer', + ogCreated: undefined, }); }); @@ -109,6 +110,7 @@ describe('Basic unfurls', () => { ogDescription: '\ntemplate<typename T>\nconcept TheSameAndAddable = requires(T a, T b) {\n {a+b} -> T;\n};\n\ntemplate<TheSameAndAddable T>\nT sum(T x, T y) {\n return x + y;\n}\n\n#include <string>\n\nint main() {\n int z = 0;\n int w;\n\n return sum(z, w);\n}\n', ogTitle: 'Compiler Explorer - C++', + ogCreated: undefined, }); }); @@ -138,6 +140,7 @@ describe('Basic unfurls', () => { ogAuthor: null, ogDescription: 'project(hello)\n\nadd_executable(output.s\n example.cpp\n square.cpp)\n', ogTitle: 'Compiler Explorer - C++', + ogCreated: undefined, }); }); }); diff --git a/views/index.pug b/views/index.pug index ec86473e0..e0e86913d 100644 --- a/views/index.pug +++ b/views/index.pug @@ -44,6 +44,8 @@ block prepend content | Apply Default Font Scale li.nav-item button.btn.btn-light.nav-link#loadSiteTemplate(role="button") Templates + li.nav-item.shortlinkInfo + .nav-link.btn-light.fa.fa-info-circle(role="button") li.nav-item.ui-presentation-control.d-none a.nav-link.ui-presentation-first(href="javascript:;") span.dropdown-icon.fas.fa-fast-backward |