Skip to content

Commit

Permalink
Merge branch 'nasa-gcn:main' into attachments
Browse files Browse the repository at this point in the history
  • Loading branch information
Vidushi-GitHub authored Oct 18, 2023
2 parents 299a6fb + cc8a947 commit 2ebe02a
Show file tree
Hide file tree
Showing 26 changed files with 904 additions and 458 deletions.
2 changes: 1 addition & 1 deletion app.arc
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ legacy_users
email_notification_subscription
topic *String
name byTopic

email_notification
recipient *String
name byRecipient
Expand Down
2 changes: 1 addition & 1 deletion app/components/ClientSampleCode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -473,7 +473,7 @@ export function ClientSampleCode({
listTopics
? `
// List all topics
consumer.Subscription.ForEach(topic => Console.WriteLine(topic));
consumer.Subscription.ForEach(topic => Console.WriteLine(topic));
`
: ''
}
Expand Down
5 changes: 5 additions & 0 deletions app/components/NoticeTypeCheckboxes/NoticeTypeCheckboxes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -183,11 +183,15 @@ const NoticeTypeLinks: { [key: string]: string | undefined } = {
}

const JsonNoticeTypes = {
IceCube: ['gcn.notices.icecube.LvkNuTrackSearch'],
LVK: ['igwn.gwalert'],
Swift: ['gcn.notices.swift.bat.guano.Alert'],
}

const JsonNoticeTypeLinks: { [key: string]: string | undefined } = {
IceCube: '/missions/icecube',
LVK: 'https://emfollow.docs.ligo.org/userguide/tutorial/receiving/gcn.html#receiving-and-parsing-notices',
Swift: '/missions/swift',
}

interface NoticeTypeCheckboxProps {
Expand Down Expand Up @@ -230,6 +234,7 @@ export function NoticeTypeCheckboxes({
return (
<>
<NestedCheckboxes
key={selectedFormat}
nodes={Object.entries(
selectedFormat == 'json' ? JsonNoticeTypes : NoticeTypes
).map(([mission, noticeTypes]) => ({
Expand Down
6 changes: 3 additions & 3 deletions app/components/nested-checkboxes/NestedCheckboxes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ function NestedCheckboxNode({
/>
<ul hidden={!expanded} className={styles.leaf}>
{nodes.map((node, index) => (
<li role="treeitem" key={index} aria-selected={false}>
<li role="treeitem" key={node.id} aria-selected={false}>
<Checkbox
{...node}
inputRef={(instance) => {
Expand Down Expand Up @@ -142,9 +142,9 @@ export function NestedCheckboxes({
}: NestedCheckboxesProps) {
return (
<ul role="tree" aria-multiselectable>
{nodes.map((node, index) => (
{nodes.map((node) => (
<NestedCheckboxNode
key={index}
key={node.id}
{...node}
childoncheckhandler={childoncheckhandler}
/>
Expand Down
6 changes: 3 additions & 3 deletions app/email-incoming/circulars/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ function successMessage(
explanation: string
) {
return `Your GCN Circular from ${userEmail} (subject: ${subject}) was received and distributed.
${explanation}`
}

Expand All @@ -169,7 +169,7 @@ function failedMessage(
explanation: string
) {
return `Your GCN Circular from ${userEmail} (subject: ${subject}) was not processed for the following reasons:
${explanation}
If you believe this to be a mistake, please contact us using the form at ${origin}/contact, and we will look into resolving it as soon as possible.`
Expand All @@ -184,7 +184,7 @@ const sharedEmailBody = `
As of April 12, 2023, GCN Circulars are being administered through the new General Coordinates Network (GCN; ${origin}), and no longer through the GCN Classic service (https://gcn.gsfc.nasa.gov).
The new GCN Circulars allow you to:
- Browse and search Circulars in our all-new archive.
Expand Down
29 changes: 21 additions & 8 deletions app/routes/api.circulars.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/
import {
AdminGetUserCommand,
AdminListGroupsForUserCommand,
type AttributeType,
} from '@aws-sdk/client-cognito-identity-provider'
import type { ActionFunctionArgs } from '@remix-run/node'
Expand Down Expand Up @@ -56,7 +57,6 @@ async function parseAccessToken(jwt: string): Promise<{
exp: string
iat: string
scope: string
'cognito:groups': string[]
username: string
}> {
const client = await getOpenIDClient()
Expand All @@ -69,7 +69,6 @@ async function parseAccessToken(jwt: string): Promise<{
'exp',
'iat',
'scope',
'cognito:groups',
'username',
])
return payload
Expand Down Expand Up @@ -101,6 +100,18 @@ async function getUserAttributes(Username: string) {
}
}

async function getUserGroups(Username: string) {
const UserPoolId = getEnvOrDie('COGNITO_USER_POOL_ID')

const { Groups } = await cognito.send(
new AdminListGroupsForUserCommand({ UserPoolId, Username })
)

return Groups?.map(({ GroupName }) => GroupName).filter(Boolean) as
| string[]
| undefined
}

/**
* GCN Circulars submission by third parties on behalf of users via an API.
*
Expand All @@ -116,10 +127,10 @@ async function getUserAttributes(Username: string) {
* needs to "renew" permission for user account linking
* - Allowed callback URLs: must be provided by the third party
* - Identity providers: select all
* - OpenID Connect Scopes: Phone, Email, OpenID. Do NOT select
* - OpenID Connect Scopes: Phone, Email, OpenID, Profile. Do NOT select
* aws.cognito.signin.user.admin.
* - Custom scopes: gcn.nasa.gov/circular-submitter
* - Attribute read and write permissions: turn off all write permissions
* - Attribute read and write permissions: turn on all
*
* 2. We send the client ID, client secret, and OIDC autodiscovery URL to the
* third party. The client secret must be encrypted.
Expand All @@ -129,8 +140,8 @@ async function getUserAttributes(Username: string) {
*
* a. Do the [authorization code flow] to sign the user in with GCN's IdP.
* When requesting the authorization endpoint, be sure to reuqest
* scope=gcn.nasa.gov/circular-submitter. (Note: the name of this scope
* may change in the future.)
* scope="openid profile gcn.nasa.gov/circular-submitter". (Note: the
* name of the last scope may change in the future.)
*
* b. For now, if resulting ID token contains the 'existingIdp' claim, then
* the third party MUST redo the authentication code flow passing the
Expand Down Expand Up @@ -163,7 +174,6 @@ export async function action({ request }: ActionFunctionArgs) {

const {
username: cognitoUserName,
'cognito:groups': groups,
sub,
scope,
} = await parseAccessToken(bearer)
Expand All @@ -172,7 +182,10 @@ export async function action({ request }: ActionFunctionArgs) {
if (!scope.split(' ').includes(group))
throw new Response('Invalid scope', { status: 403 })

const { existingIdp, ...attrs } = await getUserAttributes(cognitoUserName)
const [{ existingIdp, ...attrs }, groups] = await Promise.all([
getUserAttributes(cognitoUserName),
getUserGroups(cognitoUserName),
])
if (existingIdp) throw new Response('Wrong IdP', { status: 400 })

const user: User = {
Expand Down
28 changes: 7 additions & 21 deletions app/routes/circulars.$circularId/AstroData.components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,47 +5,33 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
import { Link } from '@remix-run/react'
import { AstroDataLink } from './AstroDataContext'

export function GcnCircular({
children,
value,
}: JSX.IntrinsicElements['data']) {
return (
<Link className="usa-link" to={`/circulars/${value}`}>
{children}
</Link>
)
return <AstroDataLink to={`/circulars/${value}`}>{children}</AstroDataLink>
}

export function Arxiv({ children, value }: JSX.IntrinsicElements['data']) {
return (
<a
className="usa-link"
rel="external"
href={`https://arxiv.org/abs/${value}`}
>
<AstroDataLink to={`https://arxiv.org/abs/${value}`}>
{children}
</a>
</AstroDataLink>
)
}

export function Doi({ children, value }: JSX.IntrinsicElements['data']) {
return (
<a className="usa-link" rel="external" href={`https://doi.org/${value}`}>
{children}
</a>
<AstroDataLink to={`https://doi.org/${value}`}>{children}</AstroDataLink>
)
}

export function Tns({ children, value }: JSX.IntrinsicElements['data']) {
return (
<a
className="usa-link"
rel="external"
href={`https://www.wis-tns.org/object/${value}`}
>
<AstroDataLink to={`https://www.wis-tns.org/object/${value}`}>
{children}
</a>
</AstroDataLink>
)
}
38 changes: 38 additions & 0 deletions app/routes/circulars.$circularId/AstroDataContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*!
* Copyright © 2023 United States Government as represented by the
* Administrator of the National Aeronautics and Space Administration.
* All Rights Reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/
import { Link, type LinkProps } from '@remix-run/react'
import classNames from 'classnames'
import { createContext, useContext } from 'react'

export type AstroDataContextProps = Pick<
JSX.IntrinsicElements['a'],
'rel' | 'target'
>

export const AstroDataContext = createContext<AstroDataContextProps>({})

export function AstroDataLink({
children,
className,
rel: origRel,
...props
}: Omit<LinkProps, 'target'>) {
const context = useContext(AstroDataContext)
const rel = [origRel, context.rel].filter(Boolean).join(' ') || undefined

return (
<Link
className={classNames('usa-link', className)}
target={context.target}
rel={rel}
{...props}
>
{children}
</Link>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { rehypeAstro } from '@nasa-gcn/remark-rehype-astro'
import { Link } from '@remix-run/react'
import classNames from 'classnames'
import type { Root } from 'mdast'
import { Fragment, createElement } from 'react'
import rehypeReact from 'rehype-react'
import remarkParse from 'remark-parse'
import remarkRehype from 'remark-rehype'
import { type Plugin, unified } from 'unified'
import { u } from 'unist-builder'

import { AstroData } from './AstroData'
import { AstroDataLink } from './AstroDataContext'
import rehypeAutolinkLiteral from './rehypeAutolinkLiteral'

import styles from './PlainTextBody.module.css'
Expand All @@ -30,16 +32,44 @@ function LinkWrapper({
}: Omit<JSX.IntrinsicElements['a'], 'ref'>) {
if (props.href) {
return (
<Link className="usa-link" to={props.href} {...props}>
<AstroDataLink to={props.href} {...props}>
{children}
</Link>
</AstroDataLink>
)
} else {
return <a {...props}>{children}</a>
}
}

export function PlainTextBody({ children }: { children: string }) {
export function MarkdownBody({
className,
children,
}: {
className?: string
children: string
}) {
const { result } = unified()
.use(remarkParse)
.use(remarkRehype)
.use(rehypeAstro)
.use(rehypeAutolinkLiteral)
.use(rehypeReact, {
Fragment,
createElement,
components: { a: LinkWrapper, data: AstroData },
})
.processSync(children)

return <div className={className}>{result}</div>
}

export function PlainTextBody({
className,
children,
}: {
className?: string
children: string
}) {
const tree = u('root', [u('code', children)])

const { result } = unified()
Expand All @@ -54,5 +84,7 @@ export function PlainTextBody({ children }: { children: string }) {
})
.processSync()

return <div className={styles.PlainTextBody}>{result}</div>
return (
<div className={classNames(styles.PlainTextBody, className)}>{result}</div>
)
}
5 changes: 5 additions & 0 deletions app/routes/circulars.$circularId/PlainTextBody.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,9 @@
white-space: pre-wrap;
background: none;
}

& pre,
&code {
margin: 0;
}
}
4 changes: 2 additions & 2 deletions app/routes/circulars.$circularId/route.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import { Link, useLoaderData } from '@remix-run/react'
import { Button, ButtonGroup, Icon } from '@trussworks/react-uswds'

import { get } from '../circulars/circulars.server'
import { PlainTextBody } from './Body'
import { FrontMatter } from './FrontMatter'
import { PlainTextBody } from './PlainTextBody'
import { origin } from '~/lib/env.server'
import { getCanonicalUrlHeaders, pickHeaders } from '~/lib/headers.server'
import { useSearchString } from '~/lib/utils'
Expand Down Expand Up @@ -93,7 +93,7 @@ export default function () {
</ButtonGroup>
<h1>GCN Circular {circularId}</h1>
<FrontMatter {...frontMatter} />
<PlainTextBody>{body}</PlainTextBody>
<PlainTextBody className="margin-y-2">{body}</PlainTextBody>
</>
)
}
Loading

0 comments on commit 2ebe02a

Please sign in to comment.