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

Feature - gwa cli v2 #845

Merged
merged 27 commits into from
Sep 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
30bc4da
upd controllers
ikethecoder Jul 3, 2023
af0e5c5
add openid as api auth
ikethecoder Jul 3, 2023
add25e2
add namespace creation
ikethecoder Jul 4, 2023
4ffeca3
fix build error
ikethecoder Jul 4, 2023
7be103d
fix required field for create namespace
ikethecoder Jul 4, 2023
ff83bb7
upd roles for tsoa user
ikethecoder Jul 4, 2023
ce74539
filter out inactive environments from drop down (#825)
Elson9 Jul 5, 2023
93bce6a
Fix BCDC Datasets out of sync (#837)
ikethecoder Jul 5, 2023
32dbad6
Merge branch 'dev' into feature/gwa-cli-v2
ikethecoder Jul 6, 2023
4742827
add description to namespace
ikethecoder Jul 7, 2023
34cab4a
use name for namespace
ikethecoder Jul 7, 2023
4a4eac5
upg http-proxy
ikethecoder Jul 17, 2023
b54d82a
move around proxy
ikethecoder Jul 17, 2023
03d7027
use displayName for namespace
ikethecoder Jul 19, 2023
569fb1a
have create namespace using application json
ikethecoder Jul 20, 2023
b7754d9
change from lodash to just-kebab-case for shared idp client id
ikethecoder Jul 21, 2023
afc5c12
change from lodash to just-kebab-case for shared idp client id
ikethecoder Jul 21, 2023
33bcfcf
service account failing with cannot read properties of null
ikethecoder Jul 25, 2023
dbafb35
order the namespaces in the api
ikethecoder Jul 25, 2023
586f1e1
upd feed worker put bubble up child results
ikethecoder Jul 25, 2023
8797891
upd feed worker put bubble up child results
ikethecoder Jul 25, 2023
55ab761
make issuer env details optional
ikethecoder Jul 26, 2023
f2b94c7
make issuer env details optional
ikethecoder Jul 26, 2023
805b881
upd the batch default array
ikethecoder Jul 26, 2023
2610fa8
upds for handling delete namespace
ikethecoder Jul 26, 2023
df24602
upd delete ns for issuers
ikethecoder Jul 26, 2023
aad41dd
chg delete ns put back gw deletion
ikethecoder Jul 26, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 9 additions & 14 deletions src/api-openapi.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,6 @@ class ApiOpenapiApp {

RegisterRoutes(app);

// RFC 8631 service-desc link relation
// https://datatracker.ietf.org/doc/html/rfc8631
app.get('/ds/api', (req, res) => {
res.setHeader('Link', '</ds/api/openapi.yaml>; rel="service-desc"');
res.status(204).end();
});

app.get('/ds/api/openapi.yaml', (req, res) => {
res.setHeader('Content-Type', 'application/yaml');
res.send(spec);
Expand All @@ -54,14 +47,9 @@ class ApiOpenapiApp {
*/
specObject.components.securitySchemes.jwt.flows.clientCredentials.tokenUrl = `${process.env.OIDC_ISSUER}/protocol/openid-connect/token`;

RegisterRoutes(app);
specObject.components.securitySchemes.openid.openIdConnectUrl = `${process.env.OIDC_ISSUER}/.well-known/openid-configuration`;

// RFC 8631 service-desc link relation
// https://datatracker.ietf.org/doc/html/rfc8631
app.get('/ds/api/v2', (req, res) => {
res.setHeader('Link', '</ds/api/openapi.yaml>; rel="service-desc"');
res.status(204).end();
});
RegisterRoutes(app);

app.get('/ds/api/v2/openapi.yaml', (req, res) => {
res.setHeader('Content-Type', 'application/yaml');
Expand All @@ -86,6 +74,13 @@ class ApiOpenapiApp {
this.prepareV2(app);
this.prepareV1(app);

// RFC 8631 service-desc link relation
// https://datatracker.ietf.org/doc/html/rfc8631
app.get('/ds/api', (req, res) => {
res.setHeader('Link', '</ds/api/v2/openapi.yaml>; rel="service-desc"');
res.status(204).end();
});

app.use(function errorHandler(err, req, res, next) {
if (err instanceof UnauthorizedError) {
return res.status(err.status).json({
Expand Down
52 changes: 29 additions & 23 deletions src/api-proxy.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

const express = require('express');
const pathModule = require('path');

Expand All @@ -11,31 +10,38 @@ class ApiProxyApp {

prepareMiddleware({ keystone }) {
const app = express();

const apiProxy = createProxyMiddleware({
target: this._gwaApiUrl,
changeOrigin: true,
pathRewrite: { '^/gw/api/': '/v2/' },
onProxyReq: (proxyReq, req) => {
// console.log(req.headers)
// proxyReq.removeHeader("cookie");
proxyReq.removeHeader("cookie");
proxyReq.setHeader('Accept', 'application/json')
proxyReq.setHeader('Authorization', `Bearer ${req.header('x-forwarded-access-token')}`) },
onError:(err, req, res, target) => {
console.log(err)
res.writeHead(400, {
'Content-Type': 'text/plain',
});
res.end('error reaching api');
}
})
app.all(/^\/gw\/api\//, apiProxy)

const apiProxy = createProxyMiddleware({
target: this._gwaApiUrl,
changeOrigin: true,
logLevel: 'debug',
pathRewrite: { '^/gw/api/': '/v2/' },
onProxyReq: (proxyReq, req) => {
//console.log(req.headers)
// proxyReq.removeHeader("cookie");
proxyReq.removeHeader('cookie');
proxyReq.setHeader('Accept', 'application/json');
proxyReq.setHeader(
'Authorization',
'authorization' in req.headers
? req.header('authorization')
: `Bearer ${req.header('x-forwarded-access-token')}`
);
},
onError: (err, req, res, target) => {
console.log(err);
res.writeHead(400, {
'Content-Type': 'text/plain',
});
res.end('error reaching api');
},
});
app.all(/^\/gw\/api\//, apiProxy);

return app;
}
}

module.exports = {
ApiProxyApp,
};
ApiProxyApp,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

mutation CreateNamespace($name: String!) {
createNamespace(name: $name) {
id
name
}
}
4 changes: 2 additions & 2 deletions src/batch/data-rules.js
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ const metadata = {
Namespace: {
query: 'allNamespaces',
refKey: 'extRefId',
sync: ['name'],
sync: ['name', 'displayName'],
transformations: {
// members: {
// name: 'connectExclusiveList',
Expand Down Expand Up @@ -474,7 +474,7 @@ const metadata = {
resourceScopes: { name: 'toStringDefaultArray' },
clientRoles: { name: 'toStringDefaultArray' },
clientMappers: { name: 'toStringDefaultArray' },
environmentDetails: { name: 'toString' },
environmentDetails: { name: 'toStringDefaultArray' },
inheritFrom: {
name: 'connectOne',
list: 'allSharedIdPs',
Expand Down
7 changes: 6 additions & 1 deletion src/batch/feed-worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -476,9 +476,14 @@ export const syncRecords = async function (
}
if (Object.keys(data).length === 0) {
logger.debug('[%s] [%s] no update', entity, localRecord.id);
const firstChildResult = childResults
.filter((r) => r.result != 'no-change')
.pop();
return {
status: 200,
result: 'no-change',
result: firstChildResult
? firstChildResult.result + '-child'
: 'no-change',
id: localRecord['id'],
childResults,
ownedBy:
Expand Down
4 changes: 4 additions & 0 deletions src/batch/transformations/toStringDefaultArray.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ export function toStringDefaultArray(
inputData: any,
key: string
) {
// if new and not passed, then set an empty array as a default
if (inputData[key] == null && currentData == null) {
return '[]';
}
return inputData[key] == null ||
(currentData != null && currentData[key] === stringify(inputData[key]))
? null
Expand Down
4 changes: 3 additions & 1 deletion src/controllers/ioc/keystoneInjector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,14 @@ export class KeystoneService {
public createContext(request: any, skipAccessControl: boolean = false): any {
const _scopes = scopes(request.user.scope);

const identityProvider = request.user.identity_provider;

const identity = {
id: null,
name: resolveName(request.user),
username: resolveUsername(request.user),
namespace: request.params.ns,
roles: JSON.stringify(scopesToRoles(null, _scopes)),
roles: JSON.stringify(scopesToRoles(identityProvider, _scopes)),
scopes: _scopes,
userId: null,
} as any;
Expand Down
14 changes: 14 additions & 0 deletions src/controllers/v2/GatewayController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import {
Security,
Body,
Tags,
FormField,
UploadedFile,
} from 'tsoa';
import { KeystoneService } from '../ioc/keystoneInjector';
import { inject, injectable } from 'tsyringe';
Expand All @@ -20,6 +22,7 @@ import {
removeKeys,
} from '../../batch/feed-worker';
import { GatewayRoute } from './types';
import { PublishResult } from './types-extra';

@injectable()
@Route('/namespaces/{ns}/gateway')
Expand All @@ -31,6 +34,17 @@ export class GatewayController extends Controller {
this.keystone = _keystone;
}

@Put()
@OperationId('publish-gateway-config')
@Security('jwt', ['Gateway.Config'])
public async put(
@FormField() dryRun: boolean,
@UploadedFile() configFile: Express.Multer.File
): Promise<PublishResult> {
// stub - gwa-api implements this
return { error: 'Stub - not implemented' };
}

/**
* Get a summary of your Gateway Services
* > `Required Scope:` Namespace.Manage
Expand Down
51 changes: 49 additions & 2 deletions src/controllers/v2/NamespaceController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ import {
Tags,
Delete,
Query,
Post,
Body,
} from 'tsoa';
import { ValidateError, FieldErrors } from 'tsoa';
import { KeystoneService } from '../ioc/keystoneInjector';
import { inject, injectable } from 'tsyringe';
import { gql } from 'graphql-request';
import { WorkbookService } from '../../services/report/workbook.service';
import { Namespace } from '../../services/keystone/types';
import { Namespace, NamespaceInput } from '../../services/keystone/types';

import { Readable } from 'stream';
import {
Expand All @@ -33,6 +35,7 @@ import { Activity } from './types';
import { getActivity } from '../../services/keystone/activity';
import { transformActivity } from '../../services/workflow';
import { ActivityDetail } from './types-extra';

const logger = Logger('controllers.Namespace');

/**
Expand Down Expand Up @@ -107,7 +110,7 @@ export class NamespaceController extends Controller {
query: list,
});
logger.debug('Result %j', result);
return result.data.allNamespaces.map((ns: Namespace) => ns.name);
return result.data.allNamespaces.map((ns: Namespace) => ns.name).sort();
}

/**
Expand Down Expand Up @@ -136,6 +139,40 @@ export class NamespaceController extends Controller {
return result.data.namespace;
}

/**
* Create a namespace
*
* @summary Create Namespace
* @param ns
* @param request
* @returns
*/
@Post()
@OperationId('create-namespace')
@Security('jwt', [])
public async create(
@Request() request: any,
@Body() vars: NamespaceInput
): Promise<Namespace> {
const result = await this.keystone.executeGraphQL({
context: this.keystone.createContext(request),
query: createNS,
variables: vars,
});
logger.debug('Result %j', result);
if (result.errors) {
const errors: FieldErrors = {};
result.errors.forEach((err: any, ind: number) => {
errors[`d${ind}`] = { message: err.message };
});
throw new ValidateError(errors, 'Unable to create namespace');
}
return {
name: result.data.createNamespace.name,
displayName: result.data.createNamespace.displayName,
};
}

/**
* Delete the namespace
* > `Required Scope:` Namespace.Manage
Expand Down Expand Up @@ -214,6 +251,7 @@ const item = gql`
query Namespace($ns: String!) {
namespace(ns: $ns) {
name
displayName
scopes {
name
}
Expand All @@ -234,3 +272,12 @@ const deleteNS = gql`
forceDeleteNamespace(namespace: $ns, force: $force)
}
`;

const createNS = gql`
mutation CreateNamespace($name: String, $displayName: String) {
createNamespace(name: $name, displayName: $displayName) {
name
displayName
}
}
`;
Loading
Loading