Skip to content

Commit

Permalink
feat: Sign as a Service Integration #121
Browse files Browse the repository at this point in the history
* GraphQL Gateway
* Apollo Federation Support on backend
* Swap out ASL-LEX search process
* Ability to pass env to Angular during build process
  • Loading branch information
cbolles committed Aug 22, 2023
1 parent 12b6823 commit 9ad209d
Show file tree
Hide file tree
Showing 37 changed files with 31,760 additions and 12,724 deletions.
15 changes: 15 additions & 0 deletions .github/workflows/on-pr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,11 @@ jobs:
docker-build:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.ref }}

- name: Checkout Repository
uses: actions/checkout@v3
with:
Expand All @@ -141,3 +146,13 @@ jobs:
uses: docker/build-push-action@v3
with:
push: false
build-args: |
PRODUCTION="false"
GRAPHQL_ENDPOINT=${{ secrets.GRAPHQL_ENDPOINT }}
ASL_LEX_ID=${{ secrets.ASL_LEX_ID }}
- name: Verify Build Works (Gateway)
uses: docker/build-push-action@v3
with:
context: ./gateway
push: false
18 changes: 17 additions & 1 deletion .github/workflows/on-push-main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ jobs:
runs-on: ubuntu-latest
name: Build And Push to Docker Hub
steps:
- name: Checkout Repository
uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.ref }}

- name: Set up QEMU
uses: docker/setup-qemu-action@v2

Expand All @@ -21,11 +26,22 @@ jobs:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Build and push
- name: Build and push SignLab
uses: docker/build-push-action@v3
with:
push: true
tags: hicsail/signlab:unstable
build-args: |
PRODUCTION="false"
GRAPHQL_ENDPOINT=${{ secrets.GRAPHQL_ENDPOINT }}
ASL_LEX_ID=${{ secrets.ASL_LEX_ID }}
- name: Build and push Gateway
uses: docker/build-push-action@v3
with:
context: ./gateway
push: true
tags: hicsail/signlab-gateway:unstable

- name: Push to Staging
uses: fjogeleit/http-request-action@v1
Expand Down
18 changes: 17 additions & 1 deletion .github/workflows/on-release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ jobs:
runs-on: ubuntu-latest
name: Build And Push to Docker Hub
steps:
- name: Checkout Repository
uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.ref }}

- name: Set up QEMU
uses: docker/setup-qemu-action@v2

Expand All @@ -21,11 +26,22 @@ jobs:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Build and push
- name: Build and push SignLab
uses: docker/build-push-action@v3
with:
push: true
tags: hicsail/signlab:${{github.ref_name}},hicsail/signlab:latest
build-args: |
PRODUCTION="true"
GRAPHQL_ENDPOINT=${{ secrets.GRAPHQL_ENDPOINT_PRODUCTION }}
ASL_LEX_ID=${{ secrets.ASL_LEX_ID_PRODUCTION }}
- name: Build and push Gateway
uses: docker/build-push-action@v3
with:
context: ./gateway
push: true
tags: hicsail/signlab-gateway:${{github.ref_name}},hicsail/signlab-gateway:latest

- name: Push to Prod
uses: fjogeleit/http-request-action@v1
Expand Down
10 changes: 9 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
FROM node:16-alpine AS signlab
FROM node:18-alpine AS signlab

ARG PRODUCTION
ARG GRAPHQL_ENDPOINT
ARG ASL_LEX_ID

ENV PRODUCTION ${PRODUCTION}
ENV GRAPHQL_ENDPOINT ${GRAPHQL_ENDPOINT}
ENV ASL_LEX_ID ${ASL_LEX_ID}

# Copy over the source
WORKDIR /usr/src/signlab
Expand Down
3 changes: 3 additions & 0 deletions client/.env.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
PRODUCTION=false
GRAPHQL_ENDPOINT=<GRAPHQL ENDPOINT>
ASL_LEX_ID=<ID OF ASL_LEX LEXICON>
3 changes: 3 additions & 0 deletions client/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,6 @@ ServiceAccountKey.json
# System Files
.DS_Store
Thumbs.db

src/environments/environment.ts
.env
6 changes: 0 additions & 6 deletions client/angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,6 @@
"buildOptimizer": false
},
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
Expand Down
2 changes: 1 addition & 1 deletion client/graphql-codegen.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
overwrite: true
schema: ../server/schema.gql
schema: https://signlab-staging-gateway.sail.codes/graphql
documents: src/app/graphql/**/*.graphql
generates:
src/app/graphql/graphql.ts:
Expand Down
13 changes: 7 additions & 6 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@
"license": "MIT",
"scripts": {
"ng": "ng",
"start:watch": "ng build --watch --configuration development",
"build:dev": "ng build --configuration development",
"build:prod": "ng build --configuration production",
"test": "ng test --browsers=ChromeHeadless --watch=false --code-coverage",
"test:watch": "ng test",
"config": "ts-node src/environments/make-env.ts",
"start:watch": "npm run config && ng build --watch --configuration development",
"build:dev": "npm run config && ng build --configuration development",
"build:prod": "npm run config && ng build --configuration production",
"test": "npm run config && ng test --browsers=ChromeHeadless --watch=false --code-coverage",
"test:watch": "npm run config && ng test",
"lint:fix": "ng lint --fix",
"lint": "ng lint",
"prettier": "prettier -l \"src/**/*.ts\"",
"prettier:fix": "prettier -wl \"src/**/*.ts\"",
"setup": "node setup.js",
"setup": "npm run config && node setup.js",
"introspection": "graphql-codegen --config graphql-codegen.yml"
},
"private": true,
Expand Down
4 changes: 2 additions & 2 deletions client/src/app/graphql.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import { HttpLink } from 'apollo-angular/http';
import { setContext } from '@apollo/client/link/context';
import { Router, RouterModule } from '@angular/router';
import { onError } from '@apollo/client/link/error';
import { environment } from '../environments/environment';

const uri = `${window.location.protocol}//${window.location.host}/graphql`;
export function createApollo(httpLink: HttpLink, router: Router): ApolloClientOptions<any> {
// Logic to get the token from local storage
const auth = setContext((_operation, _context) => {
Expand Down Expand Up @@ -34,7 +34,7 @@ export function createApollo(httpLink: HttpLink, router: Router): ApolloClientOp
});

return {
link: ApolloLink.from([auth, errorLink, httpLink.create({ uri })]),
link: ApolloLink.from([auth, errorLink, httpLink.create({ uri: environment.graphqlEndpoint })]),
cache: new InMemoryCache()
};
}
Expand Down
10 changes: 10 additions & 0 deletions client/src/app/graphql/lex/lex.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
query lexiconSearch($lexicon: String!, $search: String!) {
lexiconSearch(lexicon: $lexicon, search: $search) {
key,
primary,
lexicon,
video,
associates,
fields
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ export class AslLexSignBankField extends JsonFormsControl implements OnInit {
* the AslLexService to get the results.
*/
async searchUpdate(search: string) {
if (search.length == 0) {
return;
}
const signs = await this.aslLexService.getAslLexView(search);

// Convert the sign data into options for the videos select field
Expand Down
79 changes: 15 additions & 64 deletions client/src/app/shared/services/asl-lex.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
import { LexiconSearchGQL } from '../../graphql/lex/lex.generated';
import { firstValueFrom } from 'rxjs';
import { environment } from '../../../environments/environment';

export interface TagSearchResult {
code: string;
Expand All @@ -9,81 +11,30 @@ export interface TagSearchResult {

@Injectable({ providedIn: 'root' })
export class AslLexService {
constructor(private httpClient: HttpClient) {}
constructor(private lexiconSearchGQL: LexiconSearchGQL) {}

/**
* Get list of ASL-LEX signs that match the given search result. This
* will produce an array of `TagSearchResult`s which contains the information
* needed to display and identify the sign.
*
* This method makes a call to `Knack` which holds the ASL-LEX sign data.
* Knack provides public endpoints that allow users to gain access to
* "Views". The "Views" produce data as JSON with ofuscated property
* names (thus the row.field_51 non-sense). For more information on Knack's
* view API, see
* [this link]{@link https://docs.knack.com/docs/view-based-requests}
*
* @param search The search string to use to find matching tags.
* @return List of `TagSearchResult`s with the info need to display and
* identify ASL-LEX tags.
*/
async getAslLexView(search: string): Promise<TagSearchResult[]> {
const baseURL = 'https://api.knack.com/v1/pages/scene_139/views/view_203/records';

// Build the request.
//
// `page`: The page number, only want the top results so use 1
// `rows_per_page`: Number of results per page, 10 is a good number
// `sort_field`: How to search, `field_2` is the English identifier
// `sort_order`: How the search results should be orderd
// `search`: The actual search query
// `X-Knack-Application-Id`: The ID for the Knack application, in this
// case, hard coded for the ASL-LEX project
// `X-Knack-REST-API-KEY`: Knack API key, for the public view API, this
// is always `knack`.
const queryParams = new HttpParams()
.append('format', 'both')
.append('page', 1)
.append('rows_per_page', 10)
.append('sort_field', 'field_2')
.append('sort_order', 'asc')
.append('search', search);
const headers = new HttpHeaders()
.append('X-Knack-Application-Id', '58bedca7d78f7f26e2d7dcbe')
.append('X-Knack-REST-API-KEY', 'knack');

const result = await this.httpClient
.get<any>(baseURL, {
headers: headers,
params: queryParams
})
.toPromise();

if (!result) {
return [];
const searchResult = await firstValueFrom(this.lexiconSearchGQL.fetch({ lexicon: environment.aslLexID, search }));
if (searchResult.data) {
return searchResult.data.lexiconSearch.map((lexiconEntry) => ({
code: lexiconEntry.key,
englishTag: lexiconEntry.primary,
videoURL: lexiconEntry.video
}));
}

// Gets the signs, filter out sign's that don't have proper videos
// associated with them and re-arrange the data into a `TagSearchResult`.
const signs = result.records
.filter((row: any) => {
return row.hasOwnProperty('field_782') && row.field_782.length > 0;
})
.map((row: any) => {
// Have to splice out the vimeo link from this field which containes
// an existing iframe
const link = row.field_782.split('src=')[1].split(/[ >]/)[0].replaceAll('"', '');

// Make a URL that will autoplay and loop
const videoURL = `${link}?&loop=1&autoplay=1&controls=0&background=1`;

return {
code: row.field_52,
englishTag: row.field_2,
videoURL: videoURL
};
});

return signs;
if (searchResult.error) {
console.error(searchResult.error);
}
return [];
}
}
16 changes: 0 additions & 16 deletions client/src/environments/environment.prod.ts

This file was deleted.

16 changes: 0 additions & 16 deletions client/src/environments/environment.ts

This file was deleted.

31 changes: 31 additions & 0 deletions client/src/environments/make-env.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Utility script which makes the environment files. This is an approach
* different from the normal method of using angular to replace the
* environment files.
*
* The script will use dotenv to load in settings from the environment and
* store the results in an environment file.
*
* This was adapted from the following article
* https://ferie.medium.com/how-to-pass-environment-variables-at-building-time-in-an-angular-application-using-env-files-4ae1a80383c
*/
const fs = require('fs/promises');
require('dotenv').config();

const saveEnvironmentFile = async () => {
// Make the config in the format of a regular Angular environment file
const envConfigFileContent = `
export const environment = {
production: '${process.env.PRODUCTION || false}',
graphqlEndpoint: '${process.env.GRAPHQL_ENDPOINT}',
aslLexID: '${process.env.ASL_LEX_ID}'
};
`;

// Save the file
const environmentFilePath = './src/environments/environment.ts';

await fs.writeFile(environmentFilePath, envConfigFileContent);
};

saveEnvironmentFile();
7 changes: 7 additions & 0 deletions client/src/environments/settings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export interface EnvironmentSettings {
production: boolean;
/** GraphQL endpoint to make requests against */
graphqlEndpoint: string;
/** The Lexicon ID (defined by the Lexicon Service) of the ASL-LEX lexicon */
aslLexID: string;
}
8 changes: 8 additions & 0 deletions deployment/docker-compose-prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ services:
ports: ["6001:3000"]
env_file:
- ../stack.env
gateway:
image: hicsail/signlab-gateway:latest
restart: always
depends_on:
- signlab
ports: ["6002:3000"]
env_file:
- ../stack.env
volumes:
signlab:
external: true
Loading

0 comments on commit 9ad209d

Please sign in to comment.