github.com/hernad/nomad@v1.6.112/ui/app/serializers/job.js (about) 1 /** 2 * Copyright (c) HashiCorp, Inc. 3 * SPDX-License-Identifier: MPL-2.0 4 */ 5 6 import { assign } from '@ember/polyfills'; 7 import ApplicationSerializer from './application'; 8 import queryString from 'query-string'; 9 import classic from 'ember-classic-decorator'; 10 11 @classic 12 export default class JobSerializer extends ApplicationSerializer { 13 attrs = { 14 parameterized: 'ParameterizedJob', 15 }; 16 17 separateNanos = ['SubmitTime']; 18 19 normalize(typeHash, hash) { 20 hash.NamespaceID = hash.Namespace; 21 22 // ID is a composite of both the job ID and the namespace the job is in 23 hash.PlainId = hash.ID; 24 hash.ID = JSON.stringify([hash.ID, hash.NamespaceID || 'default']); 25 26 // ParentID comes in as "" instead of null 27 if (!hash.ParentID) { 28 hash.ParentID = null; 29 } else { 30 hash.ParentID = JSON.stringify([ 31 hash.ParentID, 32 hash.NamespaceID || 'default', 33 ]); 34 } 35 36 // Job Summary is always at /:job-id/summary, but since it can also come from 37 // the job list, it's better for Ember Data to be linked by ID association. 38 hash.SummaryID = hash.ID; 39 40 // Periodic is a boolean on list and an object on single 41 if (hash.Periodic instanceof Object) { 42 hash.PeriodicDetails = hash.Periodic; 43 hash.Periodic = true; 44 } 45 46 // Parameterized behaves like Periodic 47 if (hash.ParameterizedJob instanceof Object) { 48 hash.ParameterizedDetails = hash.ParameterizedJob; 49 hash.ParameterizedJob = true; 50 } 51 52 // If the hash contains summary information, push it into the store 53 // as a job-summary model. 54 if (hash.JobSummary) { 55 this.store.pushPayload('job-summary', { 56 'job-summary': [hash.JobSummary], 57 }); 58 } 59 60 return super.normalize(typeHash, hash); 61 } 62 63 extractRelationships(modelClass, hash) { 64 const namespace = 65 !hash.NamespaceID || hash.NamespaceID === 'default' 66 ? undefined 67 : hash.NamespaceID; 68 const { modelName } = modelClass; 69 70 const apiNamespace = this.store 71 .adapterFor(modelClass.modelName) 72 .get('namespace'); 73 74 const [jobURL] = this.store 75 .adapterFor(modelName) 76 .buildURL(modelName, hash.ID, hash, 'findRecord') 77 .split('?'); 78 79 const variableLookup = hash.ParentID 80 ? JSON.parse(hash.ParentID)[0] 81 : hash.PlainId; 82 83 return assign(super.extractRelationships(...arguments), { 84 allocations: { 85 links: { 86 related: buildURL(`${jobURL}/allocations`, { namespace }), 87 }, 88 }, 89 versions: { 90 links: { 91 related: buildURL(`${jobURL}/versions`, { namespace, diffs: true }), 92 }, 93 }, 94 deployments: { 95 links: { 96 related: buildURL(`${jobURL}/deployments`, { namespace }), 97 }, 98 }, 99 latestDeployment: { 100 links: { 101 related: buildURL(`${jobURL}/deployment`, { namespace }), 102 }, 103 }, 104 evaluations: { 105 links: { 106 related: buildURL(`${jobURL}/evaluations`, { namespace }), 107 }, 108 }, 109 services: { 110 links: { 111 related: buildURL(`${jobURL}/services`, { namespace }), 112 }, 113 }, 114 variables: { 115 links: { 116 related: buildURL(`/${apiNamespace}/vars`, { 117 prefix: `nomad/jobs/${variableLookup}`, 118 namespace, 119 }), 120 }, 121 }, 122 scaleState: { 123 links: { 124 related: buildURL(`${jobURL}/scale`, { namespace }), 125 }, 126 }, 127 recommendationSummaries: { 128 links: { 129 related: buildURL(`/${apiNamespace}/recommendations`, { 130 job: hash.PlainId, 131 namespace: hash.NamespaceID || 'default', 132 }), 133 }, 134 }, 135 }); 136 } 137 } 138 139 function buildURL(path, queryParams) { 140 const qpString = queryString.stringify(queryParams); 141 if (qpString) { 142 return `${path}?${qpString}`; 143 } 144 return path; 145 }