Skip to content

Commit

Permalink
Merge pull request #25 from jmelis/add-resources
Browse files Browse the repository at this point in the history
Add resources query
  • Loading branch information
jmelis authored Feb 20, 2019
2 parents 29763e9 + 9dfc284 commit c29a273
Show file tree
Hide file tree
Showing 9 changed files with 285 additions and 98 deletions.
59 changes: 46 additions & 13 deletions assets/schema.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
- name: Resource
version: '1'
fields:
- { name: path, type: string, isRequired: true}
- { name: content, type: string, isRequired: true}
- { name: sha256sum, type: string, isRequired: true}

- name: VaultSecret
version: '1'
fields:
Expand All @@ -16,12 +23,6 @@
- { name: managedTeams, type: string, isList: true, isRequired: true }
- { name: automationToken, type: VaultSecret }

- name: ClusterManagedRole
version: '1'
fields:
- { name: namespace, type: string, isRequired: true }
- { name: role, type: string, isRequired: true }

- name: Cluster
version: '1'
fields:
Expand All @@ -32,7 +33,37 @@
- { name: description, type: string, isRequired: true }
- { name: serverUrl, type: string, isRequired: true }
- { name: automationToken, type: VaultSecret }
- { name: managedRoles, type: ClusterManagedRole, isList: true }

- name: NamespaceOpenshiftResource
version: '1'
isInterface: true
interfaceResolve:
strategy: fieldMap
field: provider
fieldMap:
resource: NamespaceOpenshiftResourceResource
fields:
- { name: provider, type: string, isRequired: true }

- name: NamespaceOpenshiftResourceResource
version: '1'
interface: NamespaceOpenshiftResource
fields:
- { name: provider, type: string, isRequired: true }
- { name: path, type: string, isRequired: true }

- name: Namespace
version: '1'
fields:
- { name: schema, type: string, isRequired: true }
- { name: path, type: string, isRequired: true }
- { name: labels, type: json }
- { name: name, type: string, isRequired: true }
- { name: description, type: string, isRequired: true }
- { name: cluster, type: Cluster, isRequired: true }
- { name: managedRoles, type: string, isList: true}
- { name: managedResourceTypes, type: string, isList: true}
- { name: openshiftResources, type: NamespaceOpenshiftResource, isList: true, isInterface: true}

- name: AppServiceOwner
version: '1'
Expand Down Expand Up @@ -180,9 +211,11 @@

- name: Query
fields:
- { name: user, type: User, isList: true, datafileSchema: /access/user-1.yml }
- { name: bot, type: Bot, isList: true, datafileSchema: /access/bot-1.yml }
- { name: role, type: Role, isList: true, datafileSchema: /access/role-1.yml }
- { name: cluster, type: Cluster, isList: true, datafileSchema: /openshift/cluster-1.yml }
- { name: quay_org, type: QuayOrg, isList: true, datafileSchema: /dependencies/quay-org-1.yml }
- { name: app, type: App, isList: true, datafileSchema: /app-sre/app-1.yml }
- { name: users, type: User, isList: true, datafileSchema: /access/user-1.yml }
- { name: bots, type: Bot, isList: true, datafileSchema: /access/bot-1.yml }
- { name: roles, type: Role, isList: true, datafileSchema: /access/role-1.yml }
- { name: clusters, type: Cluster, isList: true, datafileSchema: /openshift/cluster-1.yml }
- { name: namespaces, type: Namespace, isList: true, datafileSchema: /openshift/namespace-1.yml }
- { name: quay_orgs, type: QuayOrg, isList: true, datafileSchema: /dependencies/quay-org-1.yml }
- { name: apps, type: App, isList: true, datafileSchema: /app-sre/app-1.yml }
- { name: resources, type: Resource, isResource: true, isRequired: true, isList: true }
13 changes: 0 additions & 13 deletions assets/schemas/openshift/cluster-1.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,6 @@ properties:
"$ref": "/common-1.json#/definitions/vaultSecret"
description:
type: string
managedRoles:
type: array
items:
type: object
additionalProperties: false
properties:
namespace:
type: string
role:
type: string
required:
- namespace
- role
required:
- "$schema"
- labels
Expand Down
71 changes: 71 additions & 0 deletions assets/schemas/openshift/namespace-1.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
---
"$schema": /metaschema-1.json
version: '1.0'
type: object

additionalProperties: false
properties:
"$schema":
type: string
enum:
- /openshift/namespace-1.yml

labels:
"$ref": "/common-1.json#/definitions/labels"

name:
type: string

description:
type: string

cluster:
"$ref": "/common-1.json#/definitions/crossref"
"$schemaRef": "/openshift/cluster-1.yml"

managedRoles:
type: array
items:
type: string
enum:
- view
- edit
- admin

managedResourceTypes:
type: array
items:
type: string
# For the moment we want to limit this list.
# A complete list can be obtained from here:
# oc api-resources --verbs=list --no-headers | awk '{print $NF}' | sort -u
enum:
- ConfigMap

openshiftResources:
type: array
items:
type: object
properties:
provider:
type: string
oneOf:
- additionalProperties: false
properties:
provider:
type: string
enum:
- resource
path:
type: string
required:
- path
required:
- provider

required:
- "$schema"
- labels
- name
- description
- cluster
64 changes: 56 additions & 8 deletions src/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,23 @@ interface IDatafile {
path: string;
}

interface IResource {
path: string;
content: string;
sha256sum: string;
}

interface IDatafilesDict {
[key: string]: any;
}

interface IResourcesDict {
[key: string]: any;
}

// module variables
let datafiles: IDatafilesDict = new Map<string, IDatafile>();
let resources: IResourcesDict = new Map<string, IResource>();
let sha256sum: string = '';

// utils
Expand All @@ -27,11 +38,6 @@ const getRefExpr = (ref: string): string => {
return m ? m[0] : '';
};

// filters
export function getDatafilesBySchema(schema: string): IDatafile[] {
return Object.values(datafiles).filter((d: any) => d.$schema === schema);
}

export function resolveRef(itemRef: any) {
const path = getRefPath(itemRef.$ref);
const expr = getRefExpr(itemRef.$ref);
Expand All @@ -51,6 +57,20 @@ export function resolveRef(itemRef: any) {
return resolvedData;
}

// filters
export function getDatafilesBySchema(schema: string): IDatafile[] {
return Object.values(datafiles).filter((d: any) => d.$schema === schema);
}

export function getResource(path: string): IResource {
return resources[path];
}

export function getResources(): IResource[] {
return Object.values(resources);

This comment has been minimized.

Copy link
@jzelinskie

jzelinskie Feb 22, 2019

Contributor

I don't think this works? Perhaps you meant resources.values()?

> let a = new Map();
undefined
> a.set('hello', 'world');
Map { 'hello' => 'world' }
> a
Map { 'hello' => 'world' }
> Object.values(a);
[]
> Array.from(a.values())
[ 'world' ]
}

// loader
function validateDatafile(d: any) {
const datafilePath: any = d[0];
const datafileData: any = d[1];
Expand All @@ -64,17 +84,33 @@ function validateDatafile(d: any) {
!('$schema' in datafileData)) {
throw new Error('Invalid datafileData object');
}
}

function validateResource(d: any) {
const resourcePath: string = d[0];
const resourceData: IResource = d[1];

if (typeof (resourcePath) !== 'string') {
throw new Error('Expecting string for resourcePath');
}

if (typeof (resourceData) !== 'object' ||
Object.keys(resourceData).length === 0 ||
!('path' in resourceData) ||
!('content' in resourceData) ||
!('sha256sum' in resourceData)) {
throw new Error('Invalid datafileData object');
}
}
// datafile Loading functions
function loadUnpack(raw: string) {
const dbDatafilesNew: any = {};

const bundle = JSON.parse(raw);
const dbResourcesNew: any = {};

const sha256hex = forgeMd.sha256.create().update(raw).digest().toHex();
const bundle = JSON.parse(raw);

Object.entries(bundle).forEach((d) => {
Object.entries(bundle.data).forEach((d) => {
validateDatafile(d);

const datafilePath: string = d[0];
Expand All @@ -85,7 +121,19 @@ function loadUnpack(raw: string) {
dbDatafilesNew[datafilePath] = datafileData;
});

Object.entries(bundle.resources).forEach((d) => {
validateResource(d);

const resourcePath: string = d[0];
const resourceData: any = d[1];

resourceData.path = resourcePath;

dbResourcesNew[resourcePath] = resourceData;
});

datafiles = dbDatafilesNew;
resources = dbResourcesNew;
sha256sum = sha256hex;

console.log(`End datafile reload: ${new Date()}`);
Expand Down
26 changes: 21 additions & 5 deletions src/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,18 +132,25 @@ const createSchemaType = function (schemaTypes: any, interfaceTypes: any, conf:

fieldDef['type'] = t;

// schema
if (fieldInfo.datafileSchema) {
// schema
fieldDef['resolve'] = () => db.getDatafilesBySchema(fieldInfo.datafileSchema);
}

// synthetic
if (fieldInfo.synthetic) {
} else if (fieldInfo.synthetic) {
// synthetic
fieldDef['resolve'] = (root: any) => resolveSyntheticField(
root,
fieldInfo.synthetic.schema,
fieldInfo.synthetic.subAttr,
);
} else if (fieldInfo.isResource) {
// resource
fieldDef['args'] = { path: { type: GraphQLString } };
fieldDef['resolve'] = (root: any, args: any) => {
if (args.path) {
return [db.getResource(args.path)];
}
return db.getResources();
};
}

// return
Expand Down Expand Up @@ -197,6 +204,15 @@ const jsonType = new GraphQLScalarType({
serialize: JSON.stringify,
});

const resourceType = new GraphQLObjectType({
name: 'Resource_v1',
fields: {
sha256sum: { type: new GraphQLNonNull(GraphQLString) },
path: { type: new GraphQLNonNull(GraphQLString) },
content: { type: new GraphQLNonNull(GraphQLString) },
},
});

export function generateAppSchema(path: string): GraphQLSchema {
const schemaData = yaml.safeLoad(fs.readFileSync(path, 'utf8'));

Expand Down
15 changes: 9 additions & 6 deletions test/schemas/cluster.data.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
{
"/cluster.yml": {
"name": "example cluster",
"labels": {},
"serverUrl": "https://example.com",
"$schema": "/openshift/cluster-1.yml",
"description": "example cluster"
"resources": {},
"data": {
"/cluster.yml": {
"name": "example cluster",
"labels": {},
"serverUrl": "https://example.com",
"$schema": "/openshift/cluster-1.yml",
"description": "example cluster"
}
}
}
6 changes: 3 additions & 3 deletions test/schemas/cluster.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@ import chaiHttp = require('chai-http');
chai.use(chaiHttp);
const should = chai.should();

describe('cluster', () => {
describe('clusters', () => {
before(() => {
db.loadFromFile('test/schemas/cluster.data.json');
});

it('serves a basic graphql query', (done) => {
chai.request(server)
.get('/graphql')
.query({ query: '{ cluster { name } }' })
.query({ query: '{ clusters { name } }' })
.end((err, res) => {
res.should.have.status(200);
res.body.data.cluster[0].name.should.equal('example cluster');
res.body.data.clusters[0].name.should.equal('example cluster');
done();
});
});
Expand Down
Loading

0 comments on commit c29a273

Please sign in to comment.